com.sun.jsftemplating.layout.descriptors.LayoutDefinition Maven / Gradle / Ivy
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://jsftemplating.dev.java.net/cddl1.html or
* jsftemplating/cddl1.txt.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at jsftemplating/cddl1.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.jsftemplating.layout.descriptors;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Method;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import com.sun.jsftemplating.component.TemplateComponent;
import com.sun.jsftemplating.layout.descriptors.handler.Handler;
import com.sun.jsftemplating.layout.event.DecodeEvent;
import com.sun.jsftemplating.layout.event.InitPageEvent;
import com.sun.jsftemplating.util.Util;
/**
* This represents the top-level {@link LayoutElement}, it is the
* container for every other {@link LayoutElement}. By itself, it has no
* functionality. Its purpose in life is to group all top-level child
* {@link LayoutElement}s. LayoutDefintion objects can be registered
* with the
* {@link com.sun.jsftemplating.layout.LayoutDefinitionManager}.
*
* This class provide a helper method
* {@link #getChildLayoutElementById(FacesContext, String, LayoutElement, UIComponent)}
* which will search recursively for the given child LayoutElement by id.
*
* @author Ken Paulsen ([email protected])
*/
public class LayoutDefinition extends LayoutElementBase {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*/
public LayoutDefinition(String id) {
// LayoutDefinition objects do not have a parent
super(null, id);
// Set the default StaticText ComponentType
addComponentType(new ComponentType(
STATIC_TEXT_TYPE, STATIC_TEXT_FACTORY_CLASS_NAME));
}
/**
* This method returns the Map
containing the
* {@link ComponentType}s. It ensures that the Map
is
* not null
.
*/
protected Map getComponentTypes() {
if (_types == null) {
_types = new HashMap();
}
return _types;
}
/**
* Retrieve a {@link ComponentType} by typeID.
*
* @param typeID The key used to retrieve the ComponentType
*
* @return The requested ComponentType or null
*
* @deprecated Reader's should use global {@link ComponentType}s (see
* {@link com.sun.jsftemplating.layout.LayoutDefinitionManager#getGlobalComponentType(FacesContext, String)})
* or should cache {@link ComponentType}s locally, this information is
* not needed in the LayoutDefinition
.
*/
public ComponentType getComponentType(String typeID) {
return getComponentTypes().get(typeID);
}
/**
* This will add the given ComponentType
to the map of
* registered ComponentType
's. It will use the
* ComponentType
ID as the key to the Map
.
* This means that if a ComponentType
with the same ID had
* previously been registered, it will be replaced with the
* ComponentType
passed in.
*
* @param type The ComponentType
.
*
* @deprecated Reader's should use global {@link ComponentType}s (see
* {@link com.sun.jsftemplating.layout.LayoutDefinitionManager#addGlobalComponentType(FacesContext, ComponentType)})
* or should cache {@link ComponentType}s locally, this information is
* not needed in the LayoutDefinition
.
*/
public void addComponentType(ComponentType type) {
getComponentTypes().put(type.getId(), type);
}
/**
* This method adds a {@link Resource}. These resources should be
* added to the request scope when this component is used. This is
* mainly used for ResourceBundles (at this time).
*
* @param res The {@link Resource} to associate with the
* LayoutDefinition
.
*/
public void addResource(Resource res) {
_resources.add(res);
}
/**
* This method returns a List of {@link Resource} objects.
*
* @return This method returns a List of {@link Resource} objects.
*/
public List getResources() {
return _resources;
}
/**
* This method allows the List
of {@link Resource}s to
* be set.
*
* @param resources List
to {@link Resource}s.
*/
public void setResources(List resources) {
_resources = resources;
}
/**
* This method searches for the requested {@link LayoutComponent} by
* id.
*
* @param context FacesContext
* @param id id to look for
* @param parent Search starts from this {@link LayoutElement}
* @param parentComponent Parent UIComponent
*
* @return The matching {@link LayoutElement} if found, null otherwise.
*/
public static LayoutElement getChildLayoutElementById(FacesContext context, String id, LayoutElement parent, UIComponent parentComponent) {
// NOTE: I may want to optimize this by putting all values in a Map so
// NOTE: that I don't have to do this search.
// Make sure this isn't what we're looking for
if (parent.getId(context, parentComponent).equals(id)) {
return parent;
}
// Not 'this' so lets check the children
Iterator it = parent.getChildLayoutElements().iterator();
LayoutElement elt = null;
while (it.hasNext()) {
elt = getChildLayoutElementById(
context, id, it.next(), parentComponent);
if (elt != null) {
// Found it!
return elt;
}
}
// Not found...
return null;
}
/**
* Retrieve an attribute by key.
*
* @param key The key used to retrieve the attribute
*
* @return The requested attribute or null.
*/
public Object getAttribute(String key) {
return _attributes.get(key);
}
/**
* Associate the given key with the given Object as an attribute.
*
* @param key The key associated with the given object (if this key
* is already in use, it will replace the previously set attribute
* object).
*
* @param value The Object to store.
*/
public void setAttribute(String key, Object value) {
_attributes.put(key, value);
}
/**
* This function overrides the superclass in order to call
* encodeBegin / encodeEnd on the UIViewRoot (and only for UIViewRoot
* instances). This is especially important for Ajax requests.
*/
@Override
public void encode(FacesContext context, UIComponent component) throws IOException {
if (component instanceof UIViewRoot) {
component.encodeBegin(context);
// FIXME: For now I am treating "@all" Ajax requests as normal requests...
// FIXME: Otherwise the partialviewcontext tries to render the whole view, and
// FIXME: fails b/c JSFT may use Facets for top-level components. Need to find
// FIXME: a better way to handle this.
if (context.getPartialViewContext().isPartialRequest() && !context.getPartialViewContext().isRenderAll()) {
// JSF is now overriding this, so this is required...
component.encodeChildren(context);
} else {
// This is not an ajax request... behave normal
super.encode(context, component);
}
component.encodeEnd(context);
} else {
super.encode(context, component);
}
}
/**
* The LayoutDefinition
does not encode anything for
* itself, this method simply returns true.
*
* @param context The FacesContext
.
* @param component The UIComponent
.
*
* @return true.
*/
protected boolean encodeThis(FacesContext context, UIComponent component) throws IOException {
return true;
}
/**
* This method retrieves the Handlers for the requested type. But
* also includes any handlers that are associated with the instance
* (i.e. the UIComponent).
*
* @param type The type of Handler
s to retrieve.
* @param comp The associated UIComponent
(or null).
*
* @return A List of Handlers.
*/
public List getHandlers(String type, UIComponent comp) {
// 1st get list of handlers for definition of this LayoutElement
List handlers = null;
// Now check to see if there are any on the UIComponent (NOTE: We do
// not pull off handlers if the parent is a TemplateComponent b/c it
// is the responsibility of the parent class to invoke handlers via
// its LayoutComponent. If we do it here, it will happen 2x.)
if ((comp != null)
&& (!(comp.getParent() instanceof TemplateComponent))) {
List instHandlers =
(List) comp.getAttributes().get(type);
if ((instHandlers != null) && (instHandlers.size() > 0)) {
// NOTE: Copy b/c this is instance + static
// Add the UIComponent instance handlers
handlers = new ArrayList(instHandlers);
List defHandlers = getHandlers(type);
if (defHandlers != null) {
// Add the LayoutElement "definition" handlers, if any
handlers.addAll(getHandlers(type));
}
}
}
if (handlers == null) {
handlers = getHandlers(type);
}
return handlers;
}
/**
* This decode method invokes any registered {@link #DECODE}
* handlers.
*
* @param context The FacesContext.
* @param component The UIComponent
.
*/
public void decode(FacesContext context, UIComponent component) {
// Invoke "decode" handlers
dispatchHandlers(context, DECODE, new DecodeEvent(component));
}
/**
* This method is responsible for dispatching the "initPage" handlers
* associated with this LayoutDefinition
(if any).
*
* The source
passed in should be the
* UIViewRoot
. However, it is expected that in most
* cases this will not be available. It is reasonable for this to be
* null
.
*
* If the FacesContext
provided is null, this method
* will simply return.
*/
public void dispatchInitPageHandlers(FacesContext ctx, Object source) {
// Sanity check (this may happen if invoked outside JSF)...
if (ctx == null) {
// Do nothing...
return;
}
// Check to see if we've already done this...
if (isInitPageExecuted(ctx)) {
// We've already init'd this request, do nothing
return;
}
// Dispatch Handlers
dispatchHandlers(ctx, INIT_PAGE, new InitPageEvent(source));
// Flag request as having processed the initPage handlers
setInitPageExecuted(ctx, Boolean.TRUE);
}
/**
* This method checks to see if the initPage event has fired yet
* for this request.
*/
public boolean isInitPageExecuted(FacesContext ctx) {
Map reqAtts = ctx.getExternalContext().getRequestMap();
String key = INIT_PAGE_PREFIX + getId(ctx, (UIComponent) null);
return Boolean.TRUE.equals(reqAtts.get(key));
}
/**
* This method marks the initPage event as fired for this request.
*/
public void setInitPageExecuted(FacesContext ctx, boolean value) {
String key = INIT_PAGE_PREFIX + getId(ctx, (UIComponent) null);
ctx.getExternalContext().getRequestMap().put(key, value);
}
/**
*
*/
private static final String INIT_PAGE_PREFIX = "__ip";
/**
* This is the "type" for handlers to be invoked to handle "decode"
* functionality for this element.
*/
public static final String DECODE = "decode";
/**
* This is the "type" for handlers to be invoked to handle "initPage"
* functionality for this element.
*/
public static final String INIT_PAGE = "initPage";
/**
* This is a hard-coded LayoutComponent type. By default it
* corresponds to
* {@link com.sun.jsftemplating.component.factory.basic.StaticTextFactory}.
*/
public static final String STATIC_TEXT_TYPE =
"staticText";
/**
* This is the full classname of the default StaticTextFactory.
*/
public static final String STATIC_TEXT_FACTORY_CLASS_NAME =
"com.sun.jsftemplating.component.factory.basic.StaticTextFactory";
/**
* This is a list of Resource objects. These resources are to be
* added to the Request scope when this LayoutDefinition
* is used.
*/
private List _resources = new ArrayList();
/**
* Map of types. This information is needed to instantiate
* UIComponents.
*/
private Map _types = null;
/**
* Map of attributes. Attributes can be used to store extra
* information about the LayoutDefinition
.
*/
private Map _attributes = new HashMap();
}