com.sun.jsftemplating.layout.descriptors.LayoutComponent 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.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import com.sun.jsftemplating.component.ChildManager;
import com.sun.jsftemplating.component.ComponentUtil;
import com.sun.jsftemplating.component.TemplateComponent;
import com.sun.jsftemplating.el.VariableResolver;
import com.sun.jsftemplating.layout.LayoutViewHandler;
import com.sun.jsftemplating.layout.ViewRootUtil;
import com.sun.jsftemplating.layout.descriptors.handler.Handler;
import com.sun.jsftemplating.layout.event.AfterCreateEvent;
import com.sun.jsftemplating.layout.event.AfterEncodeEvent;
import com.sun.jsftemplating.layout.event.BeforeCreateEvent;
import com.sun.jsftemplating.layout.event.BeforeEncodeEvent;
import com.sun.jsftemplating.util.LayoutElementUtil;
/**
* This class defines a LayoutComponent
. A
* LayoutComponent
describes a UIComponent
to be
* instantiated. The method {@link #getType()} provides a
* {@link ComponentType} descriptor that is capable of providing a
* {@link com.sun.jsftemplating.component.factory.ComponentFactory}
* to perform the actual instantiation. This class also stores properties
* and facets (children) to be set on a newly instantiated instance.
*
* @author Ken Paulsen ([email protected])
*/
public class LayoutComponent extends LayoutElementBase implements LayoutElement {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*/
public LayoutComponent(LayoutElement parent, String id, ComponentType type) {
super(parent, id);
_type = type;
}
/**
* Accessor for type.
*/
public ComponentType getType() {
return _type;
}
/**
* Determines if this component should be created even if there is
* already an existing UIComponent
. It will "overwrite"
* the existing component if this property is true.
*/
public void setOverwrite(boolean value) {
_overwrite = value;
}
/**
* Determines if this component should be created even if there is
* already an existing UIComponent
. It will "overwrite"
* the existing component if this property is true.
*/
public boolean isOverwrite() {
return _overwrite;
}
/**
* This method adds an option to the LayoutComponent. Options may be
* useful in constructing the LayoutComponent.
*
* @param name The name of the option
* @param value The value of the option (may be List or String)
*/
public void addOption(String name, Object value) {
_options.put(name, value);
}
/**
* This method adds all the options in the given Map to the
* {@link LayoutComponent}. Options may be useful in constructing the
* {@link LayoutComponent}.
*
* @param map The map of options to add.
*/
public void addOptions(Map map) {
_options.putAll(map);
}
/**
* Accessor method for an option. This method does not evaluate
* expressions.
*
* @param name The option name to retrieve.
*
* @return The option value (List or String), or null if not found.
*
* @see #getEvaluatedOption(FacesContext, String, UIComponent)
*/
public Object getOption(String name) {
return _options.get(name);
}
/**
* Accessor method for an option. This method evaluates our own
* expressions (not JSF expressions).
*
* @param ctx The FacesContext
.
* @param name The option name to retrieve.
* @param component The UIComponent
(may be null).
*
* @return The option value (List or String), or null if not found.
*
* @see #getOption(String)
*/
public Object getEvaluatedOption(FacesContext ctx, String name, UIComponent component) {
// Get the option value
Object value = getOption(name);
// Invoke our own EL. This is needed b/c JSF's EL is designed for
// Bean getters only. It does not get CONSTANTS or pull data from
// other sources (such as session, request attributes, etc., etc.)
// Resolve our variables now because we cannot depend on the
// individual components to do this. We may want to find a way to
// make this work as a regular ValueExpression... but for
// now, we'll just resolve it here.
return VariableResolver.resolveVariables(ctx, this, component, value);
}
/**
* This method returns true/false based on whether the given option
* name has been set.
*
* @param name The option name to look for.
*
* @return true/false depending on whether the options exists.
*/
public boolean containsOption(String name) {
return _options.containsKey(name);
}
/**
* This method sets the Map of options.
*
* @param options Map
of options.
*/
public void setOptions(Map options) {
_options = options;
}
/**
* This method returns the options as a Map. This method does not
* evaluate expressions.
*
* @return Map of options.
*/
public Map getOptions() {
return _options;
}
/**
* This method is overriden so that the correct UIComponent can be
* passed into the events. This is important so that correct
* component is searched for "instance" handlers.
*
* @param context The FacesContext
.
* @param parent The UIComponent
.
*/
public void encode(FacesContext context, UIComponent parent) throws IOException {
if (!this.getClass().getName().equals(CLASS_NAME)) {
// The sub-classes of this component shouldn't use this method,
// this is a hack to allow them to use LayoutElementBase.encode
super.encode(context, parent);
return;
}
// If overwrite...
if (isOverwrite()) {
// FIXME: shouldn't this do a replace, not a remove? Otherwise the order may change
String id = getId(context, parent);
if (parent.getFacets().remove(id) == null) {
UIComponent child = ComponentUtil.getInstance(context).findChild(parent, id, null);
if (child != null) {
// Not a facet, try child...
parent.getChildren().remove(child);
}
}
}
// Display this UIComponent
// First find the UIComponent
UIComponent childComponent = null;
if (parent instanceof ChildManager) {
// If we have a ChildManager, take advantage of it...
childComponent = ((ChildManager) parent).getChild(context, this);
} else {
// Use local util method for finding / creating child component...
childComponent = getChild(context, parent);
}
dispatchHandlers(context, BEFORE_ENCODE,
new BeforeEncodeEvent(childComponent));
// Add child components... (needs to be done here, LE's can't do it)
// Use check for instance of TC. If present we must instantiate its
// children as they were skipped when the tree was initially created.
if (parent instanceof TemplateComponent) {
// Only do this for TemplateRenderer use-cases (LayoutViewHandler
// does this for pages)
LayoutViewHandler.buildUIComponentTree(
context, childComponent, this);
}
// Render the child UIComponent
encodeChild(context, childComponent);
// Invoke "after" handlers
dispatchHandlers(context, AFTER_ENCODE,
new AfterEncodeEvent(childComponent));
}
/**
* Although this method is part of the interface, it is not used b/c
* I overrode the encode() method which calls this method. This
* method does nothing except satisfy the compiler.
*/
public boolean encodeThis(FacesContext context, UIComponent parent) throws IOException {
return false;
}
/**
* This method will find or create a UIComponent
as
* described by this LayoutComponent
descriptor. If the
* component already exists as a child or facet, it will be returned.
* If it creates a new UIComponent
, it will typically be
* added to the given parent UIComponent
as a facet (this
* actually depends on the factory that instantiates the
* UIComponent
).
*
* @param context The FacesContext
* @param parent The UIComponent
to serve as the parent to
* search and to store the new UIComponent
.
*
* @return The UIComponent
requested (found or newly created)
*/
public UIComponent getChild(FacesContext context, UIComponent parent) {
UIComponent childComponent = null;
// First pull off the id from the descriptor
String id = this.getId(context, parent);
// We have an id, use it to search for an already-created child
ComponentUtil compUtil = ComponentUtil.getInstance(context);
childComponent = compUtil.findChild(parent, id, id);
if (childComponent != null) {
return childComponent;
}
// Invoke "beforeCreate" handlers
this.beforeCreate(context, parent);
// Create UIComponent
childComponent = compUtil.createChildComponent(context, this, parent);
// Invoke "afterCreate" handlers
this.afterCreate(context, childComponent);
// Return the newly created UIComponent
return childComponent;
}
/**
* 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
if (comp != null) {
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 method is invoked before the Component described by this
* LayoutComponent is created. This allows handlers registered for
* "beforeCreate" functionality to be invoked.
*
* @param context The FacesContext
*
* @return The result of invoking the handlers (null by default)
*/
public Object beforeCreate(FacesContext context, UIComponent parent) {
// Invoke "beforeCreate" handlers
return dispatchHandlers(
context, BEFORE_CREATE, new BeforeCreateEvent(parent));
}
/**
* This method is invoked after the Component described by this
* LayoutComponent is created. This allows handlers registered for
* "afterCreate" functionality to be invoked.
*
* @param context The FacesContext
*
* @return The result of invoking the handlers (null by default)
*/
public Object afterCreate(FacesContext context, UIComponent component) {
// Invoke "afterCreate" handlers
return dispatchHandlers(
context, AFTER_CREATE, new AfterCreateEvent(component));
}
/**
* This method returns true if the child should be added to the parent
* component as a facet. Otherwise, it returns false indicating that
* it should exist as a real child.
*
* This value is calculated every time this call is made to allow for
* the context in which the LayoutComponent exists to determine its
* value. If a {@link LayoutFacet} exists as a parent
* {@link LayoutElement}, or a UIViewRoot
or
* {@link TemplateComponent} exists as the immediate parent, it will
* return the facet name that should be used. Otherwise, it will
* return null
.
*
* @param parent This is the parent UIComponent.
*
* @return The facet name if the UIComponent should be added as a facet.
*/
public String getFacetName(UIComponent parent) {
String name = null;
// First check to see if this LC specifies a different facet name...
name = (String) getOption(FACET_NAME);
if ((name != null) && name.equals(getUnevaluatedId())) {
// No special facet name supplied, don't assume this is a facet yet
name = null;
}
// Next check to see if we are inside a LayoutFacet
if (name == null) {
LayoutElement parentElt = getParent();
while (parentElt != null) {
if (parentElt instanceof LayoutFacet) {
// Inside a LayoutFacet, use its name... only if this facet
// is a child of a LayoutComponent (otherwise, it is a
// layout facet used for layout, not for defining a facet
// of a UIComponent)
if (LayoutElementUtil.isLayoutComponentChild(parentElt)) {
name = parentElt.getUnevaluatedId();
} else {
name = getUnevaluatedId();
}
if (name == null) {
name = "_noname";
}
break;
}
if (parentElt instanceof LayoutComponent) {
// No need to process further, this is not a facet child
return null;
}
parentElt = parentElt.getParent();
}
}
// If not found yet, check to see if we're at the top...
if (name == null) {
if (parent instanceof TemplateComponent) {
// We don't know if we are adding a child of a
// TemplateComponent from a page, or if the TemplateComponent
// itself has a child... if the TemplateComponent is driving
// the rendering process, then we want this to be a facet. If
// the page is adding a child to a TemplateComponent, we do
// not want this to be a facet.
// Look to see if the parent LayoutDefinition == the current
// LayoutDefinition. If so, we're "inside" a
// TemplateComponent, not a page.
FacesContext ctx = FacesContext.getCurrentInstance();
if (((TemplateComponent) parent).getLayoutDefinition(ctx)
== getLayoutDefinition()) {
name = getUnevaluatedId();
}
} else if ((parent instanceof UIViewRoot) && (ViewRootUtil.
getLayoutDefinition((UIViewRoot) parent) != null)) {
// NOTE: Only set the name if its a JSFT ViewRoot
name = getUnevaluatedId();
}
}
// Return the result
return name;
}
/**
* This method returns a flag that indicates if this
* LayoutComponent
is nested (directly or indirectly)
* inside another LayoutComponent
. This flag is used
* for such purposes as deciding if "instance" handlers are
* appropriate.
*
* @return true
if component is nested.
*/
public boolean isNested() {
return _nested;
}
/**
* This method sets the nested flag for this
* LayoutComponent
. This method is commonly only called
* from code that constructs the tree of {@link LayoutElement}
* components.
*
* @param value The boolean value.
*/
public void setNested(boolean value) {
_nested = value;
}
/**
* Component type
*/
private ComponentType _type = null;
/**
* Determines if this component should be created even if there is
* already an existing UIComponent
. It will "overwrite"
* the existing component if this property is true. Usually only
* applies when this is used within the context of a
* Renderer
.
*/
private boolean _overwrite = false;
/**
* Map of options.
*/
private Map _options = new HashMap();
/**
* This is the "type" for handlers to be invoked to handle
* "afterCreate" functionality for this element.
*/
public static final String AFTER_CREATE = "afterCreate";
/**
* This is the "type" for handlers to be invoked to handle
* "beforeCreate" functionality for this element.
*/
public static final String BEFORE_CREATE = "beforeCreate";
/**
* This is the "type" for handlers to be invoked to handle
* "command" functionality for this element.
*/
public static final String COMMAND = "command";
/**
* This defines the property key for specifying the facet name in
* which the component should be stored under in its parent
* UIComponent.
*/
public static final String FACET_NAME = "_facetName";
public static final String CLASS_NAME = LayoutComponent.class.getName();
/**
* The value of the nested property.
*/
private boolean _nested = false;
}