com.sun.jsftemplating.handlers.ComponentHandlers 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.
*/
/*
* ComponentHandlers.java
*
* Created on December 6, 2004, 11:06 PM
*/
package com.sun.jsftemplating.handlers;
import java.io.IOException;
import java.util.Iterator;
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.annotation.Handler;
import com.sun.jsftemplating.annotation.HandlerInput;
import com.sun.jsftemplating.annotation.HandlerOutput;
import com.sun.jsftemplating.component.ComponentUtil;
import com.sun.jsftemplating.layout.LayoutDefinitionManager;
import com.sun.jsftemplating.layout.LayoutViewHandler;
import com.sun.jsftemplating.layout.descriptors.LayoutComponent;
import com.sun.jsftemplating.layout.descriptors.LayoutElement;
import com.sun.jsftemplating.layout.descriptors.LayoutElementBase;
import com.sun.jsftemplating.layout.descriptors.handler.HandlerContext;
import com.sun.jsftemplating.util.LayoutElementUtil;
/**
* This class contains
* {@link com.sun.jsftemplating.layout.descriptors.handler.Handler}
* methods that perform component functions.
*
* @author Ken Paulsen ([email protected])
*/
public class ComponentHandlers {
/**
* Default Constructor.
*/
public ComponentHandlers() {
}
/**
* This handler returns the children of the given
* UIComponent
.
*
* Input value: "parent" -- Type: UIComponent
*
* Output value: "children" -- Type: java.util.List
* Output value: "size" -- Type: java.lang.Integer
*
* @param context The HandlerContext.
*/
@Handler(id="getUIComponentChildren",
input={
@HandlerInput(name="parent", type=UIComponent.class, required=true)},
output={
@HandlerOutput(name="children", type=List.class),
@HandlerOutput(name="size", type=Integer.class)})
public static void getChildren(HandlerContext context) {
UIComponent parent = (UIComponent) context.getInputValue("parent");
List list = parent.getChildren();
context.setOutputValue("children", list);
context.setOutputValue("size", new Integer(list.size()));
}
/**
* This handler replaces the given old UIComponent
in the
* UIComponent
tree with the given new
* UIComponent
. If the new UIComponent
is not
* specified or is null
, the old UIComponent will simply
* be removed.
*
* Input value: "old" -- Type: UIComponent
* Input value: "new" -- Type: UIComponent
*
* @param context The HandlerContext
.
*/
@Handler(id="replaceUIComponent",
input={
@HandlerInput(name="old", type=UIComponent.class, required=true),
@HandlerInput(name="new", type=UIComponent.class, required=false)}
)
public static void replaceUIComponent(HandlerContext context) {
// Get the old component which is to be replaced
UIComponent oldComp = (UIComponent) context.getInputValue("old");
if (oldComp == null) {
throw new IllegalArgumentException(
"You must provide a non-null value for 'component'.");
}
// Check for a replacement UIComponent
UIComponent newComp = (UIComponent) context.getInputValue("new");
// Get the child UIComponent list...
List list = oldComp.getParent().getChildren();
if (newComp == null) {
// Nothing to replace it with, just do a remove...
list.remove(oldComp);
} else {
// Find the index to put the new UIComponent in the right place
int index = list.indexOf(oldComp);
list.set(index, newComp);
}
}
/**
* This will build a UIComponent
tree from a
* {@link LayoutElement}. You must pass in the {@link LayoutElement}
* that will be used to create the UIComponent
tree.
* You may optionally pass in the parent UIComponent
* which will serve as the parent for the newly created
* UIComponent
tree. The resulting
* UIComponent
tree will be returned via the
* result
output value. If more than 1 root node exists
* for the given LayoutElement
, the last added to the
* parent
will be returned. Typically, you will pass in
* a {@link LayoutComponent} as the layoutElement
so
* there will only be 1.
*
* It is recommended that you *do* supply the parent since EL
* expressions may depend on this when creating the
* UIComponent
tree.
*
* One possible use case for calling this method would be to have a
* dynamic "id" property of a {@link LayoutComponent}, call this
* method multiple times with different values set in the "id"
* property. Remember, that you should not change a
* {@link LayoutComponent} (or any {@link LayoutElement}) directly.
* It is only safe to have dynamic values through EL bindings #{}.
*
* Another reason to use this handler is to cause a portion of a
* UIComponent
tree to be recreated. This of often
* desirable during Ajax requests so that factory options can be
* reevaluated.
*
* Input value: "layoutElement" -- Type: LayoutElement
* Input value: "parent" -- Type: UIComponent
*
* Output value: "result" -- Type: UIComponent
*
* @param context The HandlerContext.
*/
@Handler(id="buildUIComponentTree",
input={
@HandlerInput(name="layoutElement", type=LayoutElement.class, required=true),
@HandlerInput(name="parent", type=UIComponent.class, required=false)},
output={
@HandlerOutput(name="result", type=UIComponent.class)}
)
public static void buildUIComponentTree(HandlerContext context) {
LayoutElement desc = (LayoutElement) context.getInputValue("layoutElement");
UIComponent parent = (UIComponent) context.getInputValue("parent");
// If they didn't give us a parent, make one one up...
if (parent == null) {
parent = new UIViewRoot();
((UIViewRoot) parent).setViewId("fake");
}
// Build it...
FacesContext facesCtx = context.getFacesContext();
if (desc instanceof LayoutComponent) {
// Special case if LayoutComponent is the root node element
// The LayoutViewHandler assumes that the root node is not used
// because it was written assuming that the LayoutDefinition was
// always going to be passed in. The result is that it ignores
// the LayoutElement that is passed in and goes striaight to the
// children. This isn't what we want. Process the child here,
// then continue as normal.
UIComponent tmpParent =
((LayoutComponent) desc).getChild(facesCtx, parent);
LayoutViewHandler.buildUIComponentTree(facesCtx, tmpParent, desc);
} else {
// Process normally (which in this path is probably not normal)
LayoutViewHandler.buildUIComponentTree(facesCtx, parent, desc);
}
// Get the result to return...
String id = desc.getId(facesCtx, parent);
UIComponent result = parent.findComponent(id);
if (result == null) {
// First see if we can find it in the facet Map
if (desc instanceof LayoutComponent) {
result = parent.getFacets().get(id);
} else {
// FIXME: find a way to find a "facet child" if the root LayoutElement is not a LayoutComponent
}
if (result == null) {
// Still not found, check children...
List children = parent.getChildren();
if (children.size() > 0) {
// Return the last child added b/c we want to make sure
// we're returning something that we added, not something
// that already existed. While not perfect, this is
// reasonable.
result = children.get(children.size() -1);
}
}
}
// Set the output...
context.setOutputValue("result", result);
}
/**
* This handler creates a UIComponent
. It requires you
* to pass in the componentType (type
) and returns the
* new component via the output parameter component
.
*
* Input value: "type" -- Type: String
* Input value: "parent" -- Type: UIComponent
*
* Output value: "component" -- Type: UIComponent
*
* @param context The {@link HandlerContext}.
*/
@Handler(id="createComponent",
input={
@HandlerInput(name="type", type=String.class, required=true),
@HandlerInput(name="id", type=String.class, required=false),
@HandlerInput(name="parent", type=UIComponent.class, required=false)},
output={
@HandlerOutput(name="component", type=UIComponent.class)}
)
public static void createComponent(HandlerContext context) {
// Get the input...
String type = (String) context.getInputValue("type");
UIComponent parent = (UIComponent) context.getInputValue("parent");
String id = (String) context.getInputValue("id");
if (id == null) {
id = LayoutElementUtil.getGeneratedId(type);
}
// Create a LayoutComponent...
FacesContext ctx = context.getFacesContext();
LayoutComponent desc = new LayoutComponent((LayoutComponent) null, id,
LayoutDefinitionManager.getGlobalComponentType(ctx, type));
// Create the component...
UIComponent component = ComponentUtil.getInstance(ctx).createChildComponent(
ctx, desc, parent);
// Return the result...
context.setOutputValue("component", component);
}
/**
* This handler sets a UIComponent
attribute /
* property.
*
* Input value: "component" -- Type: UIComponent
* Input value: "property" -- Type: String
* Input value: "value" -- Type: Object
*
* @param context The HandlerContext.
*/
@Handler(id="setUIComponentProperty",
input={
@HandlerInput(name="component", type=UIComponent.class, required=true),
@HandlerInput(name="property", type=String.class, required=true),
@HandlerInput(name="value")})
public static void setComponentProperty(HandlerContext context) {
UIComponent component =
(UIComponent) context.getInputValue("component");
String propName = (String) context.getInputValue("property");
Object value = context.getInputValue("value");
// Set the attribute or property value
component.getAttributes().put(propName, value);
}
/**
* This handler finds the requested UIComponent
by
* clientId
. It takes clientId
as an input
* parameter, and returns component
as an output
* parameter.
*/
@Handler(id="getUIComponent",
input={
@HandlerInput(name="clientId", type=String.class, required=true)},
output={
@HandlerOutput(name="component", type=UIComponent.class)})
public static void getUIComponent(HandlerContext context) {
UIComponent viewRoot = context.getFacesContext().getViewRoot();
String clientId = (String) context.getInputValue("clientId");
context.setOutputValue("component", viewRoot.findComponent(clientId));
}
/**
* This handler retrieves a property from the given
* UIComponent
. It expects component
and
* name
as an input parameters, and returns
* value
as an output parameter containing the value of
* the property.
*/
@Handler(id="getUIComponentProperty",
input={
@HandlerInput(name="component", type=UIComponent.class, required=true),
@HandlerInput(name="name", type=String.class, required=true)},
output={
@HandlerOutput(name="value", type=Object.class)})
public static void getUIComponentProperty(HandlerContext context) {
UIComponent comp = (UIComponent) context.getInputValue("component");
String name = (String) context.getInputValue("name");
if ((comp == null) || (name == null)) {
throw new IllegalArgumentException("This Handler requires non-null"
+ " values for 'component' and 'name'. 'component' was"
+ " specified as '"
+ context.getHandler().getInputValue("component")
+ "' and evaluated to '" + comp + "'. 'name' was"
+ " specified as '"
+ context.getHandler().getInputValue("name")
+ "' and evaluated to '" + name + "'.");
}
Object value = comp.getAttributes().get(name);
context.setOutputValue("value", value);
}
/**
* This handler retrieves the requested the "facet" from the given
* UIComponent
. component
or
* clientId
for the component must be passed in. The
* facet name
must also be specified. It will return
* the UIComponent
found (or null
) in the
* value
output parameter.
*
* @param context The {@link HandlerContext}.
*/
@Handler(id="getFacet",
input={
@HandlerInput(name="clientId", type=String.class, required=false),
@HandlerInput(name="component", type=UIComponent.class, required=false),
@HandlerInput(name="name", type=String.class, required=true)},
output={
@HandlerOutput(name="value", type=UIComponent.class)})
public static void getFacet(HandlerContext context) {
// Get the UIComponent to use
UIComponent comp = getUIComponentFromInput(context);
// Get the facet name
String clientId = "" + (String) context.getInputValue("name");
// Look for the facet
UIComponent value = null;
if (comp != null) {
value = comp.getFacets().get(clientId);
}
// Return the UIComponent (or null)
context.setOutputValue("value", value);
}
/**
* This handler encodes the given UIComponent
. You can
* specify the UIComponent
by clientId
, or
* pass it in directly via the component
input
* parameter.
*
* @param context The {@link HandlerContext}.
*/
@Handler(id="encodeUIComponent",
input={
@HandlerInput(name="clientId", type=String.class, required=false),
@HandlerInput(name="component", type=UIComponent.class, required=false)}
)
public static void encode(HandlerContext context) throws IOException {
// Get the UIComponent to use
UIComponent comp = getUIComponentFromInput(context);
// Encode the component
LayoutElementBase.encodeChild(context.getFacesContext(), comp);
}
/**
* This method simply helps resolve a UIComponent
for
* handlers that allow them to be specified via "component" or
* "clientId". "component" takes precedence. If neither are
* supplied, an IllegalArgumentException
is thrown.
*
* @param context The {@link HandlerContext}.
*
* @return The UIComponent
or null
(if clientId
* did not resolve it).
*
* @throws IllegalArgumentException If neither "clientId" or
* "component" are provided.
*/
private static UIComponent getUIComponentFromInput(HandlerContext context) {
UIComponent comp = (UIComponent) context.getInputValue("component");
if (comp == null) {
String clientId = (String) context.getInputValue("clientId");
if (clientId != null) {
UIComponent viewRoot = context.getFacesContext().getViewRoot();
comp = viewRoot.findComponent(clientId);
} else {
throw new IllegalArgumentException(
"You must specify the component to use, or a clientId to "
+ "locate the UIComponent to use.");
}
}
// Return the UIComponent (may be null if clientId didn't resolve it)
return comp;
}
/**
* This handler will print out the structure of a
* UIComponent
tree from the given UIComponent.
*/
@Handler(id="dumpUIComponentTree",
input={
@HandlerInput(name="component", type=UIComponent.class, required=false)},
output={
@HandlerOutput(name="value", type=String.class)})
public static void dumpUIComponentTree(HandlerContext context) {
// FIXME: Add flag to dump attributes also, perhaps facets should be optional as well?
// Find the root UIComponent to use...
UIComponent comp = (UIComponent) context.getInputValue("component");
if (comp == null) {
Object eventObject = context.getEventObject();
if (eventObject instanceof UIComponent) {
comp = (UIComponent) eventObject;
} else {
comp = context.getFacesContext().getViewRoot();
if (comp == null) {
throw new IllegalArgumentException(
"Unable to determine UIComponent to dump!");
}
}
}
// Create the buffer and populate it...
StringBuffer buf = new StringBuffer("UIComponent Tree:\n");
dumpTree(comp, buf, " ");
context.setOutputValue("value", buf.toString());
}
/**
* This method recurses through the UIComponent
tree to
* generate a String representation of its structure.
*/
private static void dumpTree(UIComponent comp, StringBuffer buf, String indent) {
// First add the current UIComponent
buf.append(indent + comp.getId() + " (" + comp.getClass().getName() + ") = (" + comp.getAttributes().get("value") + ")\n");
// Children...
Iterator it = comp.getChildren().iterator();
if (it.hasNext()) {
buf.append(indent + " Children:\n");
while (it.hasNext()) {
dumpTree(it.next(), buf, indent + " ");
}
}
// Facets...
Map facetMap = comp.getFacets();
Iterator facetNames = facetMap.keySet().iterator();
if (facetNames.hasNext()) {
while (facetNames.hasNext()) {
String name = facetNames.next();
buf.append(indent + " Facet (" + name + "):\n");
dumpTree(facetMap.get(name), buf, indent + " ");
}
}
}
/**
* This handler will print out the structure of a
* {@link LayoutElement} tree from the given LayoutElement.
*/
@Handler(id="dumpLayoutElementTree",
input={
@HandlerInput(name="layoutElement", type=LayoutElement.class, required=false)},
output={
@HandlerOutput(name="value", type=String.class)})
public static void dumpLayoutElementTree(HandlerContext context) {
// FIXME: Add flag to dump attributes also, perhaps facets should be optional as well?
// Find the root UIComponent to use...
LayoutElement elt = (LayoutElement) context.getInputValue("layoutElement");
if (elt == null) {
elt = context.getLayoutElement();
if (elt == null) {
throw new IllegalArgumentException(
"Unable to determine LayoutElement to dump!");
}
}
// Create the buffer and populate it...
StringBuffer buf = new StringBuffer("LayoutElement Tree:\n");
LayoutElementUtil.dumpTree(elt, buf, " ");
context.setOutputValue("value", buf.toString());
}
}