com.sun.jsftemplating.component.factory.tree.DynamicTreeNodeFactory 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.component.factory.tree;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import com.sun.jsftemplating.annotation.UIComponentFactory;
import com.sun.jsftemplating.component.ComponentUtil;
import com.sun.jsftemplating.component.factory.ComponentFactoryBase;
import com.sun.jsftemplating.layout.descriptors.LayoutComponent;
import com.sun.jsftemplating.layout.descriptors.handler.Handler;
import com.sun.jsftemplating.util.Util;
/**
* Portions of many trees are non-static, or Dynamic. By this it is meant
* that some of the nodes can only be determined at Runtime. The goal of
* this factory is to provide a means for dynamic tree nodes to be
* defined. This implementation allows a portion or an entire tree to be
* defined.
*
* It relies on the supplied {@link TreeAdaptor} to drive the creation
* of the "root" tree node. The "root" node is a single Tree or TreeNode
* that is returned from the {@link #create(FacesContext, LayoutComponent,
* UIComponent)} method. Since this is a factory class for creating a
* single UIComponent, it is expected that a single UIComponent is
* returned from this method. However, the returned UIComponent may
* contain 0 or more children (other TreeNodes). The {@link TreeAdaptor}
* facilitates this.
*
* The {@link TreeAdaptor} implemenation is respsonsible for traversing
* the tree data and providing the necessary information about that data
* to this factory. The tree data can be stored in any format, a specific
* TreeAdaptor must be written to interpret each unique type of data
* format. When this factory interacts with the {@link TreeAdaptor}, it
* passes an Object
that represents a tree node in the
* arbitrary data format. This Object
is obtained originally
* from the {@link TreeAdaptor}, so the developer has control over what
* object is used to identify tree nodes in their own data format.
*
* See {@link TreeAdaptor} to see the necessary methods to implement in
* order for this factory to be capable of populating
* TreeNode
s based on your data.
*
* The {@link com.sun.jsftemplating.layout.descriptors.ComponentType}
* id for this factory is: "dynamicTreeNode".
*
* @author Ken Paulsen ([email protected])
*/
@UIComponentFactory("dynamicTreeNode")
public class DynamicTreeNodeFactory extends ComponentFactoryBase {
/**
* Constructor
*/
public DynamicTreeNodeFactory() {
}
/**
* This is the factory method responsible for creating the
* UIComponent
.
*
* @param context The FacesContext
* @param descriptor The {@link LayoutComponent} descriptor associated
* with the requested UIComponent
.
* @param parent The parent UIComponent
*
* @return The newly created component.
*/
public UIComponent create(FacesContext context, LayoutComponent descriptor, UIComponent parent) {
// Get the TreeAdaptor which should be used
TreeAdaptor treeAdaptor = getTreeAdaptor(context, descriptor, parent);
// Initialize the TreeAdaptor instance
treeAdaptor.init();
// First pull off the root...
Object currentObj = treeAdaptor.getTreeNodeObject();
// Return the root TreeNode
return processNode(context, treeAdaptor, currentObj, parent);
}
/**
* This method gets the TreeAdaptor
by looking at the
* {@link #TREE_ADAPTOR_CLASS} option and invoking
* getInstance
on the specified TreeAdaptor
* implementation.
*/
protected TreeAdaptor getTreeAdaptor(FacesContext ctx, LayoutComponent desc, UIComponent parent) {
TreeAdaptor adaptor = null;
Object cls = desc.getEvaluatedOption(ctx, TREE_ADAPTOR_CLASS, parent);
if (cls == null) {
throw new IllegalArgumentException("'" + TREE_ADAPTOR_CLASS
+ "' must be specified!");
}
try {
Class adaptorClass = Util.getClass(cls);
adaptor = (TreeAdaptor) adaptorClass.getMethod("getInstance",
(Class []) new Class[] {FacesContext.class,
LayoutComponent.class, UIComponent.class}).
invoke((Object) null,
(Object []) new Object[] {ctx,
desc, parent});
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex);
}
// Return the TreeAdaptor
return adaptor;
}
/**
* This method is responsible for creating and configuring a
* TreeNode
given its TreeNode
object. It
* then recurses for each child TreeNode
object.
*/
protected UIComponent processNode(FacesContext ctx, TreeAdaptor adaptor, Object currentObj, UIComponent parent) {
// Pull off the important information...
String id = adaptor.getId(currentObj);
String factoryClass = adaptor.getFactoryClass(currentObj);
// NOTE: Properties specify things such as:
// URL, Text, Image, Action, Expanded, ActionListener, etc.
Map props = adaptor.getFactoryOptions(currentObj);
Properties properties = Util.mapToProperties(props);
// Create TreeNode
UIComponent node = ComponentUtil.getInstance(ctx).getChild(
(UIComponent) parent, id, factoryClass, properties);
// The above util method defaults to using a facet... change to child
// NOTE: The above needs "parent" to correctly evaluate ${}
// NOTE: expressions, in the future find a way to make it store as a
// NOTE: child vs. facet (possible if I create the LayoutComponent).
if (parent != null) {
parent.getFacets().remove(id);
parent.getChildren().add(node);
}
// Configure TreeNode
configureTreeNode(ctx, adaptor, node, currentObj);
// Walk TreeAdaptor and Create child TreeNodes
List