You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2010/01/29 00:13:18 UTC
svn commit: r904296 [3/3] - in
/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces:
component/html/ component/html/ext/ custom/aliasbean/ custom/datascroller/
custom/document/ custom/inputHtml/ custom/schedule/ custom/tabbedpane/
custom/tre...
Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTree.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTree.java?rev=904296&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTree.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTree.java Thu Jan 28 23:13:17 2010
@@ -0,0 +1,1071 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.custom.tree;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.html.HtmlPanelGroup;
+import javax.faces.context.FacesContext;
+import javax.faces.el.ValueBinding;
+
+import org.apache.myfaces.custom.tree.event.TreeSelectionEvent;
+import org.apache.myfaces.custom.tree.event.TreeSelectionListener;
+import org.apache.myfaces.custom.tree.model.TreeModel;
+import org.apache.myfaces.custom.tree.model.TreeModelEvent;
+import org.apache.myfaces.custom.tree.model.TreeModelListener;
+import org.apache.myfaces.custom.tree.model.TreePath;
+
+
+/**
+ * A tree data component.
+ * Unless otherwise specified, all attributes accept static values or EL expressions.
+ * <p>
+ * Tree implementation based on javax.swing.JTree.
+ * </p>
+ * <p>
+ * The tree model is assigned by using a value binding named <code>model</code>
+ * and is not stored in view state.
+ * </p>
+ * <p>
+ * A hierarchy of {@link HtmlTreeNode}objects is used to represent the current
+ * expanded state of the tree. The root node is held as a faces named *
+ * <code>rootNode</code>.
+ * </p>
+ *
+ * @JSFComponent
+ * name = "t:tree"
+ * tagClass = "org.apache.myfaces.custom.tree.taglib.TreeTag"
+ * tagSuperclass = "org.apache.myfaces.custom.tree.taglib.AbstractTreeTag"
+ * type = "org.apache.myfaces.HtmlTree"
+ * tagHandler = "org.apache.myfaces.custom.tree.taglib.TreeTagHandler"
+ *
+ * @JSFJspProperty name = "headerClass" returnType = "java.lang.String"
+ * @JSFJspProperty name = "footerClass" returnType = "java.lang.String"
+ * @JSFJspProperty name = "expandRoot" returnType = "boolean" literalOnly="true" inheritedTag="true"
+ * @JSFJspProperty name = "style" tagExcluded = "true"
+ * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller </a>
+ * @version $Revision: 672986 $ $Date: 2008-06-30 23:13:55 -0500 (lun, 30 jun 2008) $
+ */
+public class HtmlTree extends HtmlPanelGroup implements TreeModelListener
+{
+ public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlTree";
+ public static final String COMPONENT_FAMILY = "org.apache.myfaces.HtmlTree";
+ private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.HtmlTree";
+
+ public static final long DEFAULT_EXPIRE_LISTENERS = 8 * 60 * 60 * 1000; // 8 hours
+ private static final String FACET_ROOTNODE = "rootNode";
+ private static final String PREVIOUS_VIEW_ROOT = HtmlTree.class.getName() + ".PREVIOUS_VIEW_ROOT";
+ private static final int EVENT_CHANGED = 0;
+ private static final int EVENT_INSERTED = 1;
+ private static final int EVENT_REMOVED = 2;
+ private static final int EVENT_STRUCTURE_CHANGED = 3;
+ private static int counter = 0;
+
+ private IconProvider iconProvider;
+ private boolean itemStatesRestored = false;
+ private String var;
+ private String nodeClass;
+ private String rowClasses;
+ private String columnClasses;
+ private String selectedNodeClass;
+ private String iconClass;
+ private String iconLine;
+ private String iconNoline;
+ private String iconChildFirst;
+ private String iconChildMiddle;
+ private String iconChildLast;
+ private String iconNodeOpen;
+ private String iconNodeOpenFirst;
+ private String iconNodeOpenMiddle;
+ private String iconNodeOpenLast;
+ private String iconNodeClose;
+ private String iconNodeCloseFirst;
+ private String iconNodeCloseMiddle;
+ private String iconNodeCloseLast;
+ private int uniqueIdCounter = 0;
+ private int[] selectedPath;
+ private int internalId;
+ private Long expireListeners;
+
+
+ /**
+ * <p/>
+ * Default constructor.
+ * </p>
+ */
+ public HtmlTree()
+ {
+ internalId = counter++;
+ }
+
+
+ /**
+ * @JSFProperty
+ * jspName = "value"
+ * required = "true"
+ * inheritedTag = "true"
+ */
+ public TreeModel getModel(FacesContext context)
+ {
+ ValueBinding binding = getValueBinding("model");
+
+ if (binding != null)
+ {
+ TreeModel model = (TreeModel) binding.getValue(context);
+ if (model != null)
+ {
+ return model;
+ }
+ }
+ return null;
+ }
+
+
+ public String createUniqueId(FacesContext context)
+ {
+ return getClientId(context).replaceAll(":", "_") + "_node_" + uniqueIdCounter++;
+ }
+
+
+ public void addTreeSelectionListener(TreeSelectionListener listener)
+ {
+ addFacesListener(listener);
+ }
+
+
+ public IconProvider getIconProvider()
+ {
+ return iconProvider;
+ }
+
+
+ public void setIconProvider(IconProvider iconProvider)
+ {
+ this.iconProvider = iconProvider;
+ }
+
+
+ /**
+ * @JSFProperty
+ * @return Returns the var.
+ */
+ public String getVar()
+ {
+ return getStringValue(var, "var");
+ }
+
+
+ /**
+ * @param var The var to set.
+ */
+ public void setVar(String var)
+ {
+ this.var = var;
+ }
+
+ protected String getStringValue(String value, String vbName)
+ {
+ if(value != null)
+ {
+ return value;
+ }
+ ValueBinding vb = getValueBinding(vbName);
+ if(vb != null)
+ {
+ Object obj = vb.getValue(getFacesContext());
+ if(obj != null)
+ {
+ return obj.toString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconLine()
+ {
+ return getStringValue(iconLine, "iconLine");
+ }
+
+ public void setIconLine(String iconLine)
+ {
+ this.iconLine = iconLine;
+ }
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNoline()
+ {
+ return getStringValue(iconNoline, "iconNoline");
+ }
+
+
+ public void setIconNoline(String iconNoline)
+ {
+ this.iconNoline = iconNoline;
+ }
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconChildFirst()
+ {
+ return getStringValue(iconChildFirst, "iconChildFirst");
+ }
+
+
+ public void setIconChildFirst(String iconChildFirst)
+ {
+ this.iconChildFirst = iconChildFirst;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconChildMiddle()
+ {
+ return getStringValue(iconChildMiddle, "iconChildMiddle");
+ }
+
+
+ public void setIconChildMiddle(String iconChildMiddle)
+ {
+ this.iconChildMiddle = iconChildMiddle;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconChildLast()
+ {
+ return getStringValue(iconChildLast, "iconChildLast");
+ }
+
+
+ public void setIconChildLast(String iconChildLast)
+ {
+ this.iconChildLast = iconChildLast;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeOpen()
+ {
+ return getStringValue(iconNodeOpen, "iconNodeOpen");
+ }
+
+
+ public void setIconNodeOpen(String iconNodeOpen)
+ {
+ this.iconNodeOpen = iconNodeOpen;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeOpenFirst()
+ {
+ return getStringValue(iconNodeOpenFirst, "iconNodeOpenFirst");
+ }
+
+
+ public void setIconNodeOpenFirst(String iconNodeOpenFirst)
+ {
+ this.iconNodeOpenFirst = iconNodeOpenFirst;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeOpenMiddle()
+ {
+ return getStringValue(iconNodeOpenMiddle, "iconNodeOpenMiddle");
+ }
+
+
+ public void setIconNodeOpenMiddle(String iconNodeOpenMiddle)
+ {
+ this.iconNodeOpenMiddle = iconNodeOpenMiddle;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeOpenLast()
+ {
+ return getStringValue(iconNodeOpenLast, "iconNodeOpenLast");
+ }
+
+
+ public void setIconNodeOpenLast(String iconNodeOpenLast)
+ {
+ this.iconNodeOpenLast = iconNodeOpenLast;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeClose()
+ {
+ return getStringValue(iconNodeClose, "iconNodeClose");
+ }
+
+
+ public void setIconNodeClose(String iconNodeClose)
+ {
+ this.iconNodeClose = iconNodeClose;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeCloseFirst()
+ {
+ return getStringValue(iconNodeCloseFirst, "iconNodeCloseFirst");
+ }
+
+
+ public void setIconNodeCloseFirst(String iconNodeCloseFirst)
+ {
+ this.iconNodeCloseFirst = iconNodeCloseFirst;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeCloseMiddle()
+ {
+ return getStringValue(iconNodeCloseMiddle, "iconNodeCloseMiddle");
+ }
+
+
+ public void setIconNodeCloseMiddle(String iconNodeCloseMiddle)
+ {
+ this.iconNodeCloseMiddle = iconNodeCloseMiddle;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconNodeCloseLast()
+ {
+ return getStringValue(iconNodeCloseLast, "iconNodeCloseLast");
+ }
+
+
+ public void setIconNodeCloseLast(String iconNodeCloseLast)
+ {
+ this.iconNodeCloseLast = iconNodeCloseLast;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getNodeClass()
+ {
+ return getStringValue(nodeClass, "nodeClass");
+ }
+
+
+ public void setNodeClass(String nodeClass)
+ {
+ this.nodeClass = nodeClass;
+ }
+
+
+ /**
+ * @JSFProperty
+ * @return Returns the rowClasses.
+ */
+ public String getRowClasses()
+ {
+ return getStringValue(rowClasses, "rowClasses");
+ }
+
+
+ /**
+ * @param rowClasses The rowClasses to set.
+ */
+ public void setRowClasses(String rowClasses)
+ {
+ this.rowClasses = rowClasses;
+ }
+
+
+ /**
+ * @JSFProperty
+ * @return Returns the columnClasses.
+ */
+ public String getColumnClasses()
+ {
+ return getStringValue(columnClasses, "columnClasses");
+ }
+
+
+ /**
+ * @param columnClasses The columnClasses to set.
+ */
+ public void setColumnClasses(String columnClasses)
+ {
+ this.columnClasses = columnClasses;
+ }
+
+
+ /**
+ * @JSFProperty
+ * @return Returns the selectedNodeClass.
+ */
+ public String getSelectedNodeClass()
+ {
+ return getStringValue(selectedNodeClass, "selectedNodeClass");
+ }
+
+
+ /**
+ * @param selectedNodeClass The selectedNodeClass to set.
+ */
+ public void setSelectedNodeClass(String selectedNodeClass)
+ {
+ this.selectedNodeClass = selectedNodeClass;
+ }
+
+
+ /**
+ * @JSFProperty
+ */
+ public String getIconClass()
+ {
+ return getStringValue(iconClass, "iconClass");
+ }
+
+
+ public void setIconClass(String iconClass)
+ {
+ this.iconClass = iconClass;
+ }
+
+
+ /**
+ * Time interval the tree will remain registered as a TreeModelListener
+ * without being accessed
+ *
+ * @JSFProperty
+ */
+ public long getExpireListeners()
+ {
+ if(expireListeners != null)
+ {
+ return expireListeners.longValue();
+ }
+ ValueBinding vb = getValueBinding("expireListeners");
+ if(vb != null)
+ {
+ Object obj = vb.getValue(getFacesContext());
+ if(obj instanceof java.lang.Number)
+ {
+ return ((java.lang.Number)obj).longValue();
+ }
+ }
+ return DEFAULT_EXPIRE_LISTENERS;
+ }
+
+
+ public void setExpireListeners(long expireListeners)
+ {
+ this.expireListeners = new Long(expireListeners);
+ }
+
+
+ public String getFamily()
+ {
+ return "org.apache.myfaces.HtmlTree";
+ }
+
+
+ /**
+ * Ensures that the node identified by the specified path is expanded and
+ * viewable. If the last item in the path is a leaf, this will have no
+ * effect.
+ *
+ * @param path the <code>TreePath</code> identifying a node
+ */
+ public void expandPath(TreePath path, FacesContext context)
+ {
+ // Only expand if not leaf!
+ TreeModel model = getModel(context);
+
+ if (path != null && model != null && !model.isLeaf(path.getLastPathComponent()))
+ {
+ int[] translatedPath = HtmlTreeNode.translatePath(path, getModel(context));
+ HtmlTreeNode rootNode = getRootNode();
+ if (rootNode == null)
+ {
+ createRootNode(context);
+ rootNode = getRootNode();
+ }
+ if (!rootNode.isExpanded())
+ {
+ rootNode.setExpanded(true);
+ }
+ rootNode.expandPath(translatedPath, 0);
+
+ }
+ }
+
+
+ /**
+ * Ensures that the node identified by the specified path is collapsed and
+ * viewable.
+ *
+ * @param path the <code>TreePath</code> identifying a node
+ */
+ public void collapsePath(TreePath path, FacesContext context)
+ {
+ HtmlTreeNode node = findNode(path, context);
+
+ if (node != null)
+ {
+ node.setExpanded(false);
+ }
+ }
+
+
+ public boolean isExpanded(TreePath path, FacesContext context)
+ {
+ if (path == null)
+ {
+ return false;
+ }
+
+ return findNode(path, context) != null;
+ }
+
+
+ private HtmlTreeNode findNode(TreePath path, FacesContext context)
+ {
+ HtmlTreeNode node = getRootNode();
+ int[] translatedPath = HtmlTreeNode.translatePath(path, getModel(context));
+
+ for (int i = 0; i < translatedPath.length; i++)
+ {
+ if (!node.isExpanded())
+ {
+ return null;
+ }
+ int index = translatedPath[i];
+ node = (HtmlTreeNode) node.getChildren().get(index);
+ }
+ return node;
+ }
+
+
+ public TreePath getSelectionPath()
+ {
+ if (selectedPath == null)
+ {
+ return null;
+ }
+ return HtmlTreeNode.translatePath(selectedPath, getModel(FacesContext.getCurrentInstance()));
+ }
+
+
+ public void selectionChanged(HtmlTreeNode node)
+ {
+ TreePath oldPath = null;
+
+ if (selectedPath != null)
+ {
+ oldPath = HtmlTreeNode.translatePath(selectedPath, getModel(FacesContext.getCurrentInstance()));
+ }
+ selectedPath = node.getTranslatedPath();
+ if (node.isSelected())
+ {
+ queueEvent(new TreeSelectionEvent(this, oldPath, node.getPath()));
+ } else
+ {
+ queueEvent(new TreeSelectionEvent(this, oldPath, null));
+ }
+ }
+
+
+ private void createRootNode(FacesContext context)
+ {
+ HtmlTreeNode node;
+ TreeModel model = getModel(context);
+ Object root = model.getRoot();
+ node = (HtmlTreeNode) context.getApplication().createComponent(HtmlTreeNode.COMPONENT_TYPE);
+ String id = createUniqueId(context);
+ node.setId(id);
+
+ node.setPath(new TreePath(new Object[]{root}));
+ node.setUserObject(root);
+ node.setLayout(new int[]{HtmlTreeNode.CLOSED_SINGLE});
+ getFacets().put(FACET_ROOTNODE, node);
+ }
+
+
+ public HtmlTreeNode getRootNode()
+ {
+ return (HtmlTreeNode) getFacet(FACET_ROOTNODE);
+ }
+
+
+ public Object saveState(FacesContext context)
+ {
+ Object values[] = new Object[24];
+ values[0] = super.saveState(context);
+ values[1] = iconChildFirst;
+ values[2] = iconChildMiddle;
+ values[3] = iconChildLast;
+ values[4] = iconLine;
+ values[5] = iconNodeClose;
+ values[6] = iconNodeCloseFirst;
+ values[7] = iconNodeCloseLast;
+ values[8] = iconNodeCloseMiddle;
+ values[9] = iconNodeOpen;
+ values[10] = iconNodeOpenFirst;
+ values[11] = iconNodeOpenLast;
+ values[12] = iconNodeOpenMiddle;
+ values[13] = iconNoline;
+ values[14] = var;
+ values[15] = nodeClass;
+ values[16] = selectedNodeClass;
+ values[17] = new Integer(uniqueIdCounter);
+ values[18] = selectedPath;
+ values[19] = iconClass;
+ values[20] = new Integer(internalId);
+ values[21] = expireListeners;
+ values[22] = rowClasses;
+ values[23] = columnClasses;
+ return ((Object) (values));
+ }
+
+
+ public void restoreState(FacesContext context, Object state)
+ {
+ Object values[] = (Object[]) state;
+ super.restoreState(context, values[0]);
+ iconChildFirst = (String) values[1];
+ iconChildMiddle = (String) values[2];
+ iconChildLast = (String) values[3];
+ iconLine = (String) values[4];
+ iconNodeClose = (String) values[5];
+ iconNodeCloseFirst = (String) values[6];
+ iconNodeCloseLast = (String) values[7];
+ iconNodeCloseMiddle = (String) values[8];
+ iconNodeOpen = (String) values[9];
+ iconNodeOpenFirst = (String) values[10];
+ iconNodeOpenLast = (String) values[11];
+ iconNodeOpenMiddle = (String) values[12];
+ iconNoline = (String) values[13];
+ var = (String) values[14];
+ nodeClass = (String) values[15];
+ selectedNodeClass = (String) values[16];
+ uniqueIdCounter = ((Integer) values[17]).intValue();
+ selectedPath = (int[]) values[18];
+ iconClass = (String) values[19];
+ internalId = ((Integer) values[20]).intValue();
+ expireListeners = (Long) values[21];
+ rowClasses = (String) values[22];
+ columnClasses = (String) values[23];
+ addToModelListeners();
+ }
+
+
+ public void decode(FacesContext context)
+ {
+ super.decode(context);
+
+ //Save the current view root for later reference...
+ context.getExternalContext().getRequestMap().put(PREVIOUS_VIEW_ROOT, context.getViewRoot());
+ //...and remember that this instance needs NO special treatment on
+ // rendering:
+ itemStatesRestored = true;
+ }
+
+
+ public void processDecodes(FacesContext context)
+ {
+ addToModelListeners();
+ super.processDecodes(context);
+ }
+
+
+ public void processValidators(FacesContext context)
+ {
+ addToModelListeners();
+ super.processValidators(context);
+ }
+
+
+ public void processUpdates(FacesContext context)
+ {
+ addToModelListeners();
+ super.processUpdates(context);
+ }
+
+
+ public void encodeBegin(FacesContext context) throws IOException
+ {
+ addToModelListeners();
+ processModelEvents();
+
+ HtmlTreeNode node = getRootNode();
+
+ if (node == null)
+ {
+ createRootNode(context);
+ }
+
+ if (!itemStatesRestored)
+ {
+ UIViewRoot previousRoot = (UIViewRoot) context.getExternalContext().getRequestMap().get(PREVIOUS_VIEW_ROOT);
+ if (previousRoot != null)
+ {
+ restoreItemStates(context, previousRoot);
+ } else
+ {
+ //no previous root, means no decode was done
+ //--> a new request
+ }
+ }
+
+ super.encodeBegin(context);
+ }
+
+
+ public void encodeEnd(FacesContext context) throws IOException
+ {
+ super.encodeEnd(context);
+ }
+
+
+ public void restoreItemStates(FacesContext facesContext, UIViewRoot previousRoot)
+ {
+ HtmlTree previousTree = (HtmlTree) previousRoot.findComponent(getClientId(facesContext));
+
+ if (previousTree != null)
+ {
+ HtmlTreeNode node = previousTree.getRootNode();
+
+ if (node != null)
+ {
+ getRootNode().restoreItemState(node);
+ }
+
+ selectedPath = previousTree.selectedPath;
+ }
+ }
+
+
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ TreePath path = e.getTreePath();
+ FacesContext context = FacesContext.getCurrentInstance();
+ HtmlTreeNode node = findNode(path, context);
+
+ if (node != null)
+ {
+ node.childrenChanged(e.getChildIndices(), context);
+ }
+ }
+
+
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ TreePath path = e.getTreePath();
+ FacesContext context = FacesContext.getCurrentInstance();
+ HtmlTreeNode node = findNode(path, context);
+
+ if (node != null)
+ {
+ node.childrenAdded(e.getChildIndices(), context);
+ }
+ }
+
+
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ TreePath path = e.getTreePath();
+ FacesContext context = FacesContext.getCurrentInstance();
+ HtmlTreeNode node = findNode(path, context);
+
+ if (node != null)
+ {
+ node.childrenRemoved(e.getChildIndices());
+ }
+ }
+
+
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ TreePath path = e.getTreePath();
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ if (isExpanded(path, context))
+ {
+ collapsePath(path, context);
+ expandPath(path, context);
+ }
+ }
+
+
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof HtmlTree))
+ {
+ return false;
+ }
+ HtmlTree other = (HtmlTree) obj;
+
+ return other.getId().equals(getId());
+ }
+
+
+ public int hashCode()
+ {
+ return getClientId(FacesContext.getCurrentInstance()).hashCode();
+ }
+
+
+ public void addToModelListeners()
+ {
+ Collection listeners = getModel(FacesContext.getCurrentInstance()).getTreeModelListeners();
+ long currentTime = System.currentTimeMillis();
+ boolean found = false;
+
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();)
+ {
+ ModelListener listener = (ModelListener) iterator.next();
+
+ if (listener.getId() == internalId)
+ {
+ found = true;
+ } else if (currentTime - listener.getLastAccessTime() > getExpireListeners())
+ {
+ iterator.remove();
+ }
+ }
+ if (!found)
+ {
+ listeners.add(new ModelListener(internalId));
+ }
+ }
+
+
+ private void processModelEvents()
+ {
+ Collection listeners = getModel(FacesContext.getCurrentInstance()).getTreeModelListeners();
+
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();)
+ {
+ ModelListener listener = (ModelListener) iterator.next();
+
+ if (listener.getId() == internalId)
+ {
+ for (Iterator events = listener.getEvents().iterator(); events.hasNext();)
+ {
+ Event event = (Event) events.next();
+ event.process(this);
+ events.remove();
+ }
+ break;
+ }
+ }
+ }
+
+
+ public void collapseAll()
+ {
+ HtmlTreeNode root = getRootNode();
+ FacesContext context = FacesContext.getCurrentInstance();
+ collapsePath(root.getPath(), context);
+ for (int i = 0; i < root.getChildren().size(); i++)
+ {
+ HtmlTreeNode child = (HtmlTreeNode) (root.getChildren().get(i));
+ collapsePath(child.getPath(), context);
+ if (!child.isLeaf(context))
+ {
+ collapseChildren(context, child);
+ }
+ }
+ }
+
+
+ private void collapseChildren(FacesContext context, HtmlTreeNode parent)
+ {
+ for (int i = 0; i < parent.getChildren().size(); i++)
+ {
+ HtmlTreeNode child = (HtmlTreeNode) (parent.getChildren().get(i));
+ collapsePath(child.getPath(), context);
+ if (!child.isLeaf(context))
+ {
+ collapseChildren(context, child);
+ }
+ }
+
+ }
+
+
+ public void expandAll()
+ {
+ HtmlTreeNode root = getRootNode();
+ FacesContext context = FacesContext.getCurrentInstance();
+ expandPath(root.getPath(), context);
+ for (int i = 0; i < root.getChildren().size(); i++)
+ {
+ HtmlTreeNode child = (HtmlTreeNode) (root.getChildren().get(i));
+ expandPath(child.getPath(), context);
+ if (!child.isLeaf(context))
+ {
+ expandChildren(context, child);
+ }
+ }
+ }
+
+
+ private void expandChildren(FacesContext context, HtmlTreeNode parent)
+ {
+ for (int i = 0; i < parent.getChildren().size(); i++)
+ {
+ HtmlTreeNode child = (HtmlTreeNode) (parent.getChildren().get(i));
+ expandPath(child.getPath(), context);
+ if (!child.isLeaf(context))
+ {
+ expandChildren(context, child);
+ }
+ }
+ }
+
+ private static class ModelListener implements TreeModelListener, Serializable
+ {
+
+ private long lastAccessTime = System.currentTimeMillis();
+
+ private LinkedList events = new LinkedList();
+
+ int id;
+
+
+ public ModelListener(int id)
+ {
+ this.id = id;
+ }
+
+
+ public List getEvents()
+ {
+ lastAccessTime = System.currentTimeMillis();
+ return events;
+ }
+
+
+ public int getId()
+ {
+ return id;
+ }
+
+
+ public long getLastAccessTime()
+ {
+ return lastAccessTime;
+ }
+
+
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ events.addLast(new Event(EVENT_CHANGED, e));
+ }
+
+
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ events.addLast(new Event(EVENT_INSERTED, e));
+ }
+
+
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ events.addLast(new Event(EVENT_REMOVED, e));
+ }
+
+
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ events.addLast(new Event(EVENT_STRUCTURE_CHANGED, e));
+ }
+ }
+
+
+ private static class Event
+ {
+
+ private int kind;
+
+ private TreeModelEvent event;
+
+
+ public Event(int kind, TreeModelEvent event)
+ {
+ this.kind = kind;
+ this.event = event;
+ }
+
+
+ public void process(HtmlTree tree)
+ {
+ switch (kind)
+ {
+ case EVENT_CHANGED:
+ tree.treeNodesChanged(event);
+ break;
+ case EVENT_INSERTED:
+ tree.treeNodesInserted(event);
+ break;
+ case EVENT_REMOVED:
+ tree.treeNodesRemoved(event);
+ break;
+ case EVENT_STRUCTURE_CHANGED:
+ tree.treeStructureChanged(event);
+ break;
+ }
+ }
+ }
+}
Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTreeNode.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTreeNode.java?rev=904296&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTreeNode.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTreeNode.java Thu Jan 28 23:13:17 2010
@@ -0,0 +1,591 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.custom.tree;
+
+import org.apache.myfaces.custom.tree.model.TreeModel;
+import org.apache.myfaces.custom.tree.model.TreePath;
+
+import javax.faces.component.html.HtmlCommandLink;
+import javax.faces.context.FacesContext;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Represents a single node of a three. A custom html link component representing the expand/collapse icon
+ * is held as a facet named <code>expandCollapse</code>.
+ *
+ * @JSFComponent
+ * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller</a>
+ * @version $Revision: 659874 $ $Date: 2008-05-24 15:59:15 -0500 (sáb, 24 may 2008) $
+ */
+public class HtmlTreeNode
+ extends HtmlCommandLink
+{
+
+ private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.HtmlTreeNode";
+
+ public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlTreeNode";
+ public static final String EXPAND_COLLAPSE_FACET = "expandCollapse";
+
+ public static final int OPEN = 0;
+ public static final int OPEN_FIRST = 1;
+ public static final int OPEN_LAST = 2;
+ public static final int OPEN_SINGLE = 3;
+ public static final int CLOSED = 10;
+ public static final int CLOSED_FIRST = 11;
+ public static final int CLOSED_LAST = 12;
+ public static final int CLOSED_SINGLE = 13;
+ public static final int CHILD = 20;
+ public static final int CHILD_FIRST = 21;
+ public static final int CHILD_LAST = 22;
+ public static final int LINE = 30;
+ public static final int EMPTY = 40;
+ private static final int OFFSET = 10;
+ private static final int MOD_FIRST = 1;
+ private static final int MOD_LAST = 2;
+
+ private transient TreePath path;
+ private int[] translatedPath;
+ private transient Object userObject;
+ private boolean expanded = false;
+ private boolean selected = false;
+ private int[] layout;
+
+
+ public HtmlTreeNode()
+ {
+ setRendererType(DEFAULT_RENDERER_TYPE);
+ }
+
+
+ public int getLevel()
+ {
+ return layout.length - 1;
+ }
+
+
+ public int getMaxChildLevel()
+ {
+ if (getChildCount() == 0)
+ {
+ return getLevel();
+ }
+
+ int max = getLevel();
+ for (Iterator iterator = getChildren().iterator(); iterator.hasNext();)
+ {
+ HtmlTreeNode child = (HtmlTreeNode) iterator.next();
+ max = Math.max(max, child.getMaxChildLevel());
+ }
+ return max;
+ }
+
+
+ public TreePath getPath()
+ {
+ if (path == null)
+ {
+ if (translatedPath == null)
+ {
+ throw new IllegalStateException("No path and no translated path available");
+ }
+ path = translatePath(translatedPath, getTreeModel(FacesContext.getCurrentInstance()));
+ }
+ return path;
+ }
+
+
+ public void setPath(TreePath path)
+ {
+ this.path = path;
+ }
+
+
+ int[] getTranslatedPath()
+ {
+ if (translatedPath != null)
+ {
+ return translatedPath;
+ }
+
+ return translatePath(getPath(), getTreeModel(FacesContext.getCurrentInstance()));
+ }
+
+
+ public Object getUserObject()
+ {
+ if (userObject == null)
+ {
+ userObject = getPath().getLastPathComponent();
+ }
+ return userObject;
+ }
+
+
+ public void setUserObject(Object userObject)
+ {
+ this.userObject = userObject;
+ setValue(userObject.toString());
+ }
+
+
+ public boolean isExpanded()
+ {
+ return expanded;
+ }
+
+
+ void childrenAdded(int[] children, FacesContext context)
+ {
+ if (getChildCount() == 0 && children.length > 0)
+ {
+ // change to CLOSED_*
+ layout[layout.length - 1] -= OFFSET;
+ }
+
+ if (!expanded)
+ {
+ // nothing to do
+ return;
+ }
+
+ TreeModel model = getTreeModel(context);
+ int childCount = model.getChildCount(getUserObject());
+ int pathUpdateIndex = getTranslatedPath().length;
+
+ for (int i = 0; i < children.length; i++)
+ {
+ int index = children[i];
+ translateChildrenPath(pathUpdateIndex, index, 1);
+ Object userObject = model.getChild(getUserObject(), index);
+ addNode(model, userObject, index, childCount, context);
+ }
+ }
+
+
+ void childrenChanged(int[] children, FacesContext context)
+ {
+ if (!expanded)
+ {
+ // nothing to do
+ return;
+ }
+
+ TreeModel model = getTreeModel(context);
+
+ for (int i = 0; i < children.length; i++)
+ {
+ int index = children[i];
+ Object userObject = model.getChild(getUserObject(), index);
+ HtmlTreeNode node = (HtmlTreeNode) getChildren().get(index);
+ node.setUserObject(userObject);
+ // todo: modify path????
+ }
+ }
+
+
+ private void addNode(TreeModel model, Object userObject, int index, int childCount, FacesContext context)
+ {
+ HtmlTreeNode node = createNode(model, userObject, childCount, index, context);
+ List children = getChildren();
+
+ if (!children.isEmpty())
+ {
+ if (index == 0)
+ {
+ HtmlTreeNode first = (HtmlTreeNode) getChildren().get(0);
+ int[] layout = first.getLayout();
+ if (layout[layout.length - 1] % OFFSET == MOD_FIRST)
+ {
+ // change from *_FIRST to *
+ layout[layout.length - 1]--;
+ }
+ } else if (index == childCount - 1)
+ {
+ HtmlTreeNode last = (HtmlTreeNode) getChildren().get(childCount - 2);
+ int[] layout = last.getLayout();
+ if (layout[layout.length - 1] % OFFSET == MOD_LAST)
+ {
+ // change from *_LAST to *
+ layout[layout.length - 1] -= 2;
+ }
+ }
+ }
+
+ children.add(index, node);
+ }
+
+
+ void childrenRemoved(int[] children)
+ {
+ if (!expanded)
+ {
+ // nothing to do
+ return;
+ }
+ List childNodes = getChildren();
+ int pathUpdateIndex = getTranslatedPath().length;
+
+ for (int i = children.length - 1; i >= 0; i--)
+ {
+ translateChildrenPath(pathUpdateIndex, children[i], -1);
+ HtmlTreeNode child = (HtmlTreeNode) childNodes.get(children[i]);
+
+ if (child.isSelected()) {
+ child.setSelected(false);
+ if (children[i] < childNodes.size() - 1) {
+ ((HtmlTreeNode) childNodes.get(children[i] + 1)).setSelected(true);
+ } else {
+ if (children[i] > 0) {
+ ((HtmlTreeNode) childNodes.get(children[i] - 1)).setSelected(true);
+ } else {
+ setSelected(true);
+ }
+ }
+ }
+ childNodes.remove(children[i]);
+ }
+ }
+
+
+ /**
+ * Update the translatedPath of all child nodes so the path points to the same object in the model
+ * after adding/removing a node
+ *
+ * @param pathUpdateIndex
+ * @param startIndex
+ * @param offset
+ */
+ private void translateChildrenPath(int pathUpdateIndex, int startIndex, int offset) {
+ List children = getChildren();
+ int childrenSize = children.size();
+
+ for (int i = startIndex; i < childrenSize; i++) {
+ HtmlTreeNode node = (HtmlTreeNode) children.get(i);
+ node.updateTranslatedPath(pathUpdateIndex, offset);
+ }
+ }
+
+
+ private void updateTranslatedPath(int pathUpdateIndex, int offset)
+ {
+ translatedPath[pathUpdateIndex] += offset;
+ // reset path and userObject so the values are acquired from the model
+ path = null;
+ userObject = null;
+
+ if (isExpanded()) {
+ // continue with the children of this node
+ translateChildrenPath(pathUpdateIndex, 0, offset);
+ }
+ }
+
+
+ public void setExpanded(boolean expanded)
+ {
+ if (this.expanded == expanded)
+ {
+ // no change
+ return;
+ }
+ this.expanded = expanded;
+
+ if (expanded)
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ TreeModel model = getTreeModel(context);
+ int childCount = model.getChildCount(getUserObject());
+
+ for (int i = 0; i < childCount; i++)
+ {
+ Object child = model.getChild(getUserObject(), i);
+ HtmlTreeNode node = createNode(model, child, childCount, i, context);
+ getChildren().add(node);
+ }
+ layout[layout.length - 1] -= OFFSET;
+ } else
+ {
+ if (clearSelection())
+ {
+ setSelected(true);
+ }
+ getChildren().clear();
+ layout[layout.length - 1] += OFFSET;
+ }
+
+ }
+
+
+ private HtmlTreeNode createNode(TreeModel model, Object child, int childCount, int index, FacesContext context)
+ {
+ HtmlTreeNode node = (HtmlTreeNode) context.getApplication().createComponent(HtmlTreeNode.COMPONENT_TYPE);
+ String id = getTree().createUniqueId(context);
+ node.setId(id);
+
+ node.setPath(getPath().pathByAddingChild(child));
+ node.setUserObject(child);
+ int state = CHILD;
+
+ if (model.isLeaf(child))
+ {
+
+ if (childCount > 1)
+ {
+ if (index == 0)
+ {
+ state = CHILD;
+ } else if (index == childCount - 1)
+ {
+ state = CHILD_LAST;
+ }
+ } else
+ {
+ state = CHILD_LAST;
+ }
+ } else
+ {
+ if (childCount > 1)
+ {
+ if (index == 0)
+ {
+ state = CLOSED;
+ } else if (index == childCount - 1)
+ {
+ state = CLOSED_LAST;
+ } else
+ {
+ state = CLOSED;
+ }
+ } else
+ {
+ state = CLOSED_LAST;
+ }
+
+ }
+ node.setLayout(layout, state);
+
+ return node;
+ }
+
+
+ public void toggleExpanded()
+ {
+ setExpanded(!expanded);
+ }
+
+
+ public boolean isSelected()
+ {
+ return selected;
+ }
+
+
+ public void setSelected(boolean selected)
+ {
+ if (selected)
+ {
+ getTree().getRootNode().clearSelection();
+ }
+ this.selected = selected;
+ getTree().selectionChanged(this);
+ }
+
+
+ public void toggleSelected()
+ {
+ setSelected(!selected);
+ }
+
+
+ private boolean clearSelection()
+ {
+ if (isSelected())
+ {
+ selected = false;
+ return true;
+ }
+ for (Iterator iterator = getChildren().iterator(); iterator.hasNext();)
+ {
+ HtmlTreeNode node = (HtmlTreeNode) iterator.next();
+ if (node.clearSelection())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public int[] getLayout()
+ {
+ return layout;
+ }
+
+
+ public void setLayout(int[] layout)
+ {
+ this.layout = layout;
+ }
+
+
+ public void setLayout(int[] parentLayout, int layout)
+ {
+ this.layout = new int[parentLayout.length + 1];
+ this.layout[parentLayout.length] = layout;
+
+ for (int i = 0; i < parentLayout.length; i++)
+ {
+ int state = parentLayout[i];
+ if (state == OPEN || state == OPEN_FIRST || state == CLOSED || state == CLOSED_FIRST || state == CHILD || state == CHILD_FIRST || state == LINE)
+ {
+ this.layout[i] = LINE;
+ } else
+ {
+ this.layout[i] = EMPTY;
+ }
+ }
+ }
+
+
+ public HtmlTreeImageCommandLink getExpandCollapseCommand(FacesContext context)
+ {
+ HtmlTreeImageCommandLink command = (HtmlTreeImageCommandLink) getFacet(EXPAND_COLLAPSE_FACET);
+
+ if (command == null)
+ {
+ command = (HtmlTreeImageCommandLink) context.getApplication().createComponent(HtmlTreeImageCommandLink.COMPONENT_TYPE);
+ String id = getTree().createUniqueId(context);
+ command.setId(id);
+ getFacets().put(EXPAND_COLLAPSE_FACET, command);
+ }
+ return command;
+ }
+
+
+ public Object saveState(FacesContext context)
+ {
+ Object values[] = new Object[5];
+ values[0] = super.saveState(context);
+ values[1] = Boolean.valueOf(expanded);
+ values[2] = layout;
+ values[3] = translatePath(getPath(), getTreeModel(context));
+ values[4] = Boolean.valueOf(selected);
+ return values;
+ }
+
+
+ public void restoreState(FacesContext context, Object state)
+ {
+ Object values[] = (Object[]) state;
+ super.restoreState(context, values[0]);
+ expanded = ((Boolean) values[1]).booleanValue();
+ layout = (int[]) values[2];
+ translatedPath = (int[]) values[3];
+ selected = ((Boolean) values[4]).booleanValue();
+ }
+
+
+ protected static int[] translatePath(TreePath treePath, TreeModel model)
+ {
+ Object[] path = treePath.getPath();
+ int[] translated = new int[path.length - 1];
+
+ Object parent = path[0];
+
+ for (int i = 1; i < path.length; i++)
+ {
+ Object element = path[i];
+ translated[i - 1] = model.getIndexOfChild(parent, element);
+ parent = element;
+ }
+ return translated;
+ }
+
+
+ protected static TreePath translatePath(int[] path, TreeModel model)
+ {
+ Object[] translated = new Object[path.length + 1];
+ Object parent = model.getRoot();
+
+ translated[0] = parent;
+
+ for (int i = 0; i < path.length; i++)
+ {
+ int index = path[i];
+ translated[i + 1] = model.getChild(parent, index);
+ parent = translated[i + 1];
+ }
+ return new TreePath(translated);
+ }
+
+
+ protected TreeModel getTreeModel(FacesContext context)
+ {
+ return getTree().getModel(context);
+ }
+
+
+ protected HtmlTree getTree()
+ {
+ if (getParent() instanceof HtmlTree)
+ {
+ return (HtmlTree) getParent();
+ }
+ return ((HtmlTreeNode) getParent()).getTree();
+ }
+
+
+ public boolean isLeaf(FacesContext context)
+ {
+ return getTreeModel(context).isLeaf(getUserObject());
+ }
+
+
+ public void expandPath(int[] translatedPath, int current)
+ {
+ if (current >= translatedPath.length)
+ {
+ return;
+ }
+
+ HtmlTreeNode child = (HtmlTreeNode) getChildren().get(translatedPath[current]);
+
+ if (!child.isExpanded())
+ {
+ child.setExpanded(true);
+ }
+
+ child.expandPath(translatedPath, current + 1);
+ }
+
+
+ public void restoreItemState(HtmlTreeNode node)
+ {
+ setExpanded(node.isExpanded());
+ selected = node.isSelected();
+ List children = getChildren();
+ List otherChildren = node.getChildren();
+ for (int i = 0; i < children.size(); i++)
+ {
+ HtmlTreeNode child = (HtmlTreeNode) children.get(i);
+ child.restoreItemState((HtmlTreeNode) otherChildren.get(i));
+ }
+ }
+}
Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/AbstractHtmlTree.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/AbstractHtmlTree.java?rev=904296&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/AbstractHtmlTree.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/AbstractHtmlTree.java Thu Jan 28 23:13:17 2010
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.custom.tree2;
+
+import java.util.Map;
+
+import javax.faces.component.UICommand;
+import javax.faces.component.html.HtmlCommandLink;
+import javax.faces.context.FacesContext;
+import javax.faces.el.MethodBinding;
+
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
+import org.apache.myfaces.component.LocationAware;
+
+/**
+ * Represents "tree data" in an HTML format. Also provides a mechanism for maintaining expand/collapse
+ * state of the nodes in the tree.
+ *
+ * A component that provides an HTML-based tree from data supplied by a
+ * backing bean. The tree is highly customizable and allows for
+ * fine-grained control over the appearance of each of the nodes
+ * depending on their type.
+ *
+ * Almost any type of JSF component (text, image, checkbox, etc.) can
+ * be rendered inside the nodes and there is an option for client-side
+ * or server-side toggling of the expand/collapse state.
+ *
+ * Unless otherwise specified, all attributes accept static values or EL expressions.
+ *
+ * @since 1.1.7
+ * @author Sean Schofield
+ */
+@JSFComponent(
+ name = "t:tree2",
+ clazz = "org.apache.myfaces.custom.tree2.HtmlTree",
+ tagClass = "org.apache.myfaces.custom.tree2.TreeTag")
+public abstract class AbstractHtmlTree extends UITreeData
+ implements LocationAware
+{
+ public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlTree2";
+ private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.HtmlTree2";
+
+ private UICommand _expandControl = null;
+
+ /**
+ * Perform client-side toggling of expand/collapse state via javascript (default is true.)
+ *
+ * @return the new clientSideToggle value
+ */
+ @JSFProperty(defaultValue = "true")
+ public boolean isClientSideToggle()
+ {
+ return (Boolean) getStateHelper().eval(PropertyKeys.clientSideToggle, true);
+ }
+
+ /**
+ * Sets
+ *
+ * @param clientSideToggle the new clientSideToggle value
+ */
+ public void setClientSideToggle(boolean clientSideToggle)
+ {
+ getStateHelper().put(PropertyKeys.clientSideToggle, clientSideToggle );
+ }
+
+ /**
+ * @see org.apache.myfaces.custom.tree2.UITreeData#processNodes(javax.faces.context.FacesContext, int, org.apache.myfaces.custom.tree2.TreeWalker)
+ */
+ protected void processNodes(FacesContext context, int processAction, TreeWalker walker)
+ {
+ if (isClientSideToggle()) {
+ walker.setCheckState(false);
+ }
+ super.processNodes(context, processAction, walker);
+ }
+
+ //public abstract String getLocalVarNodeToggler();
+
+ // see superclass for documentation
+ public void setNodeId(String nodeId)
+ {
+ super.setNodeId(nodeId);
+
+ String varNodeToggler = (String) getStateHelper().get(PropertyKeys.varNodeToggler);
+ if (varNodeToggler != null)
+ {
+ Map requestMap = getFacesContext().getExternalContext().getRequestMap();
+ requestMap.put(varNodeToggler, this);
+ }
+ }
+
+ /**
+ * Gets the expand/collapse control that can be used to handle expand/collapse nodes. This is only used in server-side
+ * mode. It allows the nagivation controls (if any) to be clickable as well as any commandLinks the user has set up in
+ * their JSP.
+ *
+ * @return UICommand
+ */
+ public UICommand getExpandControl()
+ {
+ if (_expandControl == null){
+ _expandControl = new HtmlCommandLink();
+ _expandControl.setParent(this);
+ }
+ return _expandControl;
+ }
+
+ /**
+ * Gets
+ *
+ * @return the new varNodeToggler value
+ */
+ @JSFProperty(literalOnly=true)
+ public String getVarNodeToggler()
+ {
+ return (String) getStateHelper().get(PropertyKeys.varNodeToggler);
+ }
+
+ public void setVarNodeToggler(String varNodeToggler)
+ {
+ getStateHelper().put(PropertyKeys.varNodeToggler, varNodeToggler );
+
+ // create a method binding for the expand control
+ String bindingString = "#{" + varNodeToggler + ".toggleExpanded}";
+ MethodBinding actionBinding = FacesContext.getCurrentInstance().getApplication().createMethodBinding(bindingString, null);
+ getExpandControl().setAction(actionBinding);
+ }
+
+ /**
+ * Show the "plus" and "minus" navigation icons (default is true.)
+ * Value is ignored if clientSideToggle is true.
+ *
+ */
+ @JSFProperty(defaultValue="true")
+ public abstract boolean isShowNav();
+
+ /**
+ * Show the connecting lines (default is true.)
+ *
+ */
+ @JSFProperty(defaultValue="true")
+ public abstract boolean isShowLines();
+
+ /**
+ * Include the root node when rendering the tree (default is true.)
+ *
+ */
+ @JSFProperty(defaultValue="true")
+ public abstract boolean isShowRootNode();
+
+ /**
+ * Preserve changes in client-side toggle information between
+ * requests (default is true.)
+ *
+ */
+ @JSFProperty(defaultValue="true")
+ public abstract boolean isPreserveToggle();
+
+ protected enum PropertyKeys
+ {
+ clientSideToggle
+ , varNodeToggler
+ }
+}
Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/UITreeData.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/UITreeData.java?rev=904296&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/UITreeData.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/UITreeData.java Thu Jan 28 23:13:17 2010
@@ -0,0 +1,852 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.custom.tree2;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.component.EditableValueHolder;
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIComponentBase;
+import javax.faces.context.FacesContext;
+import javax.faces.el.ValueBinding;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.FacesListener;
+import javax.faces.event.PhaseId;
+
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
+import org.apache.myfaces.shared_tomahawk.util.MessageUtils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * TreeData is a {@link UIComponent} that supports binding data stored in a tree represented
+ * by a {@link TreeNode} instance. During iterative processing over the tree nodes in the
+ * data model, the object for the current node is exposed as a request attribute under the key
+ * specified by the <code>var</code> property. {@link javax.faces.render.Renderer}s of this
+ * component should use the appropriate facet to assist in rendering.
+ *
+ * @author Sean Schofield
+ * @author Hans Bergsten (Some code taken from an example in his O'Reilly JavaServer Faces book. Copied with permission)
+ * @version $Revision: 703742 $ $Date: 2008-10-11 17:10:36 -0500 (sáb, 11 oct 2008) $
+ */
+@JSFComponent
+public class UITreeData extends UIComponentBase implements NamingContainer, Tree {
+ private Log log = LogFactory.getLog(UITreeData.class);
+
+ public static final String COMPONENT_TYPE = "org.apache.myfaces.UITree2";
+ public static final String COMPONENT_FAMILY = "org.apache.myfaces.HtmlTree2";
+ //private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.Tree2";
+ private static final String MISSING_NODE = "org.apache.myfaces.tree2.MISSING_NODE";
+ private static final int PROCESS_DECODES = 1;
+ private static final int PROCESS_VALIDATORS = 2;
+ private static final int PROCESS_UPDATES = 3;
+
+ private TreeModel _cachedModel;
+ private String _nodeId;
+ private TreeNode _node;
+
+ private Object _value;
+ private String _var;
+ private Map _saved = new HashMap();
+
+ private TreeState _restoredState = null;
+
+ /**
+ * Constructor
+ */
+ public UITreeData()
+ {
+ //setRendererType(DEFAULT_RENDERER_TYPE);
+ }
+
+
+ // see superclass for documentation
+ public String getFamily()
+ {
+ return COMPONENT_FAMILY;
+ }
+
+ // see superclass for documentation
+ public Object saveState(FacesContext context)
+ {
+ Object values[] = new Object[3];
+ values[0] = super.saveState(context);
+ values[1] = _var;
+ values[2] = _restoredState;
+ return ((Object) (values));
+ }
+
+
+ // see superclass for documentation
+ public void restoreState(FacesContext context, Object state)
+ {
+ Object values[] = (Object[]) state;
+ super.restoreState(context, values[0]);
+
+ _var = (String)values[1];
+ _restoredState = (TreeState) values[2];
+ }
+
+ public void encodeEnd(FacesContext context) throws IOException {
+ super.encodeEnd(context);
+
+ // prepare to save the tree state -- fix for MYFACES-618
+ // should be done in saveState() but Sun RI does not call saveState() and restoreState()
+ // with javax.faces.STATE_SAVING_METHOD = server
+ TreeState state = getDataModel().getTreeState();
+ if ( state == null)
+ {
+ // the model supplier has forgotten to return a valid state manager, but we need one
+ state = new TreeStateBase();
+ }
+ // save the state with the component, unless it should explicitly not saved eg. session-scoped model and state
+ _restoredState = (state.isTransient()) ? null : state;
+
+ }
+
+ public void queueEvent(FacesEvent event)
+ {
+ super.queueEvent(new FacesEventWrapper(event, getNodeId(), this));
+ }
+
+
+ public void broadcast(FacesEvent event) throws AbortProcessingException
+ {
+ if (event instanceof FacesEventWrapper)
+ {
+ FacesEventWrapper childEvent = (FacesEventWrapper) event;
+ String currNodeId = getNodeId();
+ setNodeId(childEvent.getNodeId());
+ FacesEvent nodeEvent = childEvent.getFacesEvent();
+ nodeEvent.getComponent().broadcast(nodeEvent);
+ setNodeId(currNodeId);
+ return;
+ }
+ else if(event instanceof ToggleExpandedEvent)
+ {
+ ToggleExpandedEvent toggleEvent = (ToggleExpandedEvent) event;
+ String currentNodeId = getNodeId();
+ setNodeId(toggleEvent.getNodeId());
+ toggleExpanded();
+ setNodeId(currentNodeId);
+ }
+ else
+ {
+ super.broadcast(event);
+ return;
+ }
+ }
+
+
+ // see superclass for documentation
+ public void processDecodes(FacesContext context)
+ {
+ if (context == null) throw new NullPointerException("context");
+ if (!isRendered()) return;
+
+ _cachedModel = null;
+ _saved = new HashMap();
+
+ setNodeId(null);
+ decode(context);
+
+ processNodes(context, PROCESS_DECODES, getDataModel().getTreeWalker());
+ // After processNodes is executed, the node active is the last one
+ // we have to set it to null again to avoid inconsistency on outsider
+ // code (just like UIData components does)
+ setNodeId(null);
+
+ }
+
+ // see superclass for documentation
+ public void processValidators(FacesContext context)
+ {
+ if (context == null) throw new NullPointerException("context");
+ if (!isRendered()) return;
+
+ processNodes(context, PROCESS_VALIDATORS, getDataModel().getTreeWalker());
+
+ setNodeId(null);
+ }
+
+
+ // see superclass for documentation
+ public void processUpdates(FacesContext context)
+ {
+ if (context == null) throw new NullPointerException("context");
+ if (!isRendered()) return;
+
+ processNodes(context, PROCESS_UPDATES, getDataModel().getTreeWalker());
+
+ setNodeId(null);
+ }
+
+ // see superclass for documentation
+ public String getClientId(FacesContext context)
+ {
+ String ownClientId = super.getClientId(context);
+ if (_nodeId != null)
+ {
+ return ownClientId + NamingContainer.SEPARATOR_CHAR + _nodeId;
+ } else
+ {
+ return ownClientId;
+ }
+ }
+
+ // see superclass for documentation
+ public void setValueBinding(String name, ValueBinding binding)
+ {
+ if ("value".equals(name))
+ {
+ _cachedModel = null;
+ } else if ("nodeVar".equals(name) || "nodeId".equals(name) || "treeVar".equals(name))
+ {
+ throw new IllegalArgumentException("name " + name);
+ }
+ super.setValueBinding(name, binding);
+ }
+
+ // see superclass for documentation
+ public void encodeBegin(FacesContext context) throws IOException
+ {
+ /**
+ * The renderer will handle most of the encoding, but if there are any
+ * error messages queued for the components (validation errors), we
+ * do want to keep the saved state so that we can render the node with
+ * the invalid value.
+ */
+
+ if (!keepSaved(context))
+ {
+ _saved = new HashMap();
+ }
+
+ // FIX for MYFACES-404
+ // do not use the cached model the render phase
+ _cachedModel = null;
+
+ super.encodeBegin(context);
+ }
+
+ /**
+ * Sets the value of the TreeData.
+ *
+ * @param value The new value
+ *
+ * @deprecated
+ */
+ public void setValue(Object value)
+ {
+ _cachedModel = null;
+ _value = value;
+ }
+
+
+ /**
+ * Gets the model of the TreeData -
+ * due to backwards-compatibility, this can also be retrieved by getValue.
+ *
+ * @return The value
+ */
+ public Object getModel()
+ {
+ return getValue();
+ }
+
+ /**
+ * Sets the model of the TreeData -
+ * due to backwards-compatibility, this can also be set by calling setValue.
+ *
+ * @param model The new model
+ */
+ public void setModel(Object model)
+ {
+ setValue(model);
+ }
+
+
+ /**
+ * Gets the value of the TreeData.
+ *
+ * @JSFProperty
+ * required="true"
+ * @return The value
+ *
+ * @deprecated
+ */
+ public Object getValue()
+ {
+ if (_value != null) return _value;
+ ValueBinding vb = getValueBinding("value");
+ return vb != null ? vb.getValue(getFacesContext()) : null;
+ }
+
+ /**
+ * Set the request-scope attribute under which the data object for the current node wil be exposed
+ * when iterating.
+ *
+ * @param var The new request-scope attribute name
+ */
+ public void setVar(String var)
+ {
+ _var = var;
+ }
+
+
+ /**
+ * Return the request-scope attribute under which the data object for the current node will be exposed
+ * when iterating. This property is not enabled for value binding expressions.
+ *
+ * @JSFProperty
+ * @return The iterator attribute
+ */
+ public String getVar()
+ {
+ return _var;
+ }
+
+ /**
+ * Calls through to the {@link TreeModel} and returns the current {@link TreeNode} or <code>null</code>.
+ *
+ * @return The current node
+ */
+ public TreeNode getNode()
+ {
+ return _node;
+ }
+
+
+ public String getNodeId()
+ {
+ return _nodeId;
+ }
+
+
+ public void setNodeId(String nodeId)
+ {
+ saveDescendantState();
+
+ _nodeId = nodeId;
+
+ TreeModel model = getDataModel();
+ if (model == null)
+ {
+ return;
+ }
+
+ try
+ {
+ _node = model.getNodeById(nodeId);
+ }
+ //TODO: change to an own exception
+ catch (IndexOutOfBoundsException aob)
+ {
+ /**
+ * This might happen if we are trying to process a commandLink for a node that node that no longer
+ * exists. Instead of allowing a RuntimeException to crash the application, we will add a warning
+ * message so the user can optionally display the warning. Also, we will allow the user to provide
+ * their own value binding method to be called so they can handle it how they see fit.
+ */
+ FacesMessage message = MessageUtils.getMessage(MISSING_NODE, new String[] {nodeId});
+ message.setSeverity(FacesMessage.SEVERITY_WARN);
+ FacesContext.getCurrentInstance().addMessage(getId(), message);
+
+ /** @todo call hook */
+ /** @todo figure out whether or not to abort this method gracefully */
+ }
+
+ restoreDescendantState();
+
+ if (_var != null)
+ {
+ Map requestMap = getFacesContext().getExternalContext().getRequestMap();
+
+ if (nodeId == null)
+ {
+ requestMap.remove(_var);
+ } else
+ {
+ requestMap.put(_var, getNode());
+ }
+ }
+ }
+
+ /**
+ * Gets an array of String containing the ID's of all of the {@link TreeNode}s in the path to
+ * the specified node. The path information will be an array of <code>String</code> objects
+ * representing node ID's. The array will starting with the ID of the root node and end with
+ * the ID of the specified node.
+ *
+ * @param nodeId The id of the node for whom the path information is needed.
+ * @return String[]
+ */
+ public String[] getPathInformation(String nodeId)
+ {
+ return getDataModel().getPathInformation(nodeId);
+ }
+
+ /**
+ * Indicates whether or not the specified {@link TreeNode} is the last child in the <code>List</code>
+ * of children. If the node id provided corresponds to the root node, this returns <code>true</code>.
+ *
+ * @param nodeId The ID of the node to check
+ * @return boolean
+ */
+ public boolean isLastChild(String nodeId)
+ {
+ return getDataModel().isLastChild(nodeId);
+ }
+
+ /**
+ * Returns a previously cached {@link TreeModel}, if any, or sets the cache variable to either the
+ * current value (if its a {@link TreeModel}) or to a new instance of {@link TreeModel} (if it's a
+ * {@link TreeNode}) with the provided value object as the root node.
+ *
+ * @return TreeModel
+ */
+ public TreeModel getDataModel()
+ {
+ if (_cachedModel != null)
+ {
+ return _cachedModel;
+ }
+
+ Object value = getValue();
+ if (value != null)
+ {
+ if (value instanceof TreeModel)
+ {
+ _cachedModel = (TreeModel) value;
+ }
+ else if (value instanceof TreeNode)
+ {
+ _cachedModel = new TreeModelBase((TreeNode) value);
+ } else
+ {
+ throw new IllegalArgumentException("Value must be a TreeModel or TreeNode");
+ }
+ }
+
+ if (_restoredState != null)
+ _cachedModel.setTreeState(_restoredState); // set the restored state (if there is one) on the model
+
+ return _cachedModel;
+ }
+
+ /**
+ * Epands all nodes by default.
+ */
+ public void expandAll()
+ {
+ toggleAll(true);
+ }
+
+ /**
+ * Collapse all nodes by default.
+ */
+ public void collapseAll()
+ {
+ toggleAll(false);
+ }
+
+ /**
+ * Toggles all of the nodes to either expanded or collapsed depending on the
+ * parameter supplied.
+ *
+ * @param expanded Expand all of the nodes (a value of false indicates collapse
+ * all nodes)
+ */
+ private void toggleAll(boolean expanded)
+ {
+ TreeWalker walker = getDataModel().getTreeWalker();
+ walker.reset();
+
+ TreeState state = getDataModel().getTreeState();
+ walker.setCheckState(false);
+ walker.setTree(this);
+
+ while(walker.next())
+ {
+ String id = getNodeId();
+ if ((expanded && !state.isNodeExpanded(id)) || (!expanded && state.isNodeExpanded(id)))
+ {
+ state.toggleExpanded(id);
+ }
+ }
+ }
+
+ /**
+ * Expands all of the nodes in the specfied path.
+ * @param nodePath The path to expand.
+ */
+ public void expandPath(String[] nodePath)
+ {
+ getDataModel().getTreeState().expandPath(nodePath);
+ }
+
+ /**
+ * Expands all of the nodes in the specfied path.
+ * @param nodePath The path to expand.
+ */
+ public void collapsePath(String[] nodePath)
+ {
+ getDataModel().getTreeState().collapsePath(nodePath);
+ }
+
+
+ protected void processNodes(FacesContext context, int processAction, TreeWalker walker)
+ {
+ UIComponent facet = null;
+ walker.reset();
+ walker.setTree(this);
+
+ while(walker.next())
+ {
+ TreeNode node = getNode();
+ facet = getFacet(node.getType());
+
+ if (facet == null)
+ {
+ log.warn("Unable to locate facet with the name: " + node.getType());
+ continue;
+ //throw new IllegalArgumentException("Unable to locate facet with the name: " + node.getType());
+ }
+
+ switch (processAction)
+ {
+ case PROCESS_DECODES:
+
+ facet.processDecodes(context);
+ break;
+
+ case PROCESS_VALIDATORS:
+
+ facet.processValidators(context);
+ break;
+
+ case PROCESS_UPDATES:
+
+ facet.processUpdates(context);
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * To support using input components for the nodes (e.g., input fields, checkboxes, and selection
+ * lists) while still only using one set of components for all nodes, the state held by the components
+ * for the current node must be saved for a new node is selected.
+ */
+ private void saveDescendantState()
+ {
+ FacesContext context = getFacesContext();
+ Iterator i = getFacets().values().iterator();
+ while (i.hasNext())
+ {
+ UIComponent facet = (UIComponent) i.next();
+ saveDescendantState(facet, context);
+ }
+ }
+
+ /**
+ * Overloaded helper method for the no argument version of this method.
+ *
+ * @param component The component whose state needs to be saved
+ * @param context FacesContext
+ */
+ private void saveDescendantState(UIComponent component, FacesContext context)
+ {
+ if (component instanceof EditableValueHolder)
+ {
+ EditableValueHolder input = (EditableValueHolder) component;
+ String clientId = component.getClientId(context);
+ SavedState state = (SavedState) _saved.get(clientId);
+ if (state == null)
+ {
+ state = new SavedState();
+ _saved.put(clientId, state);
+ }
+ state.setValue(input.getLocalValue());
+ state.setValid(input.isValid());
+ state.setSubmittedValue(input.getSubmittedValue());
+ state.setLocalValueSet(input.isLocalValueSet());
+ }
+
+ List kids = component.getChildren();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ saveDescendantState((UIComponent) kids.get(i), context);
+ }
+ }
+
+
+ /**
+ * Used to configure a new node with the state stored previously.
+ */
+ private void restoreDescendantState()
+ {
+ FacesContext context = getFacesContext();
+ Iterator i = getFacets().values().iterator();
+ while (i.hasNext())
+ {
+ UIComponent facet = (UIComponent) i.next();
+ restoreDescendantState(facet, context);
+ }
+ }
+
+ /**
+ * Overloaded helper method for the no argument version of this method.
+ *
+ * @param component The component whose state needs to be restored
+ * @param context FacesContext
+ */
+ private void restoreDescendantState(UIComponent component, FacesContext context)
+ {
+ String id = component.getId();
+ component.setId(id); // forces the cilent id to be reset
+
+ if (component instanceof EditableValueHolder)
+ {
+ EditableValueHolder input = (EditableValueHolder) component;
+ String clientId = component.getClientId(context);
+ SavedState state = (SavedState) _saved.get(clientId);
+ if (state == null)
+ {
+ state = new SavedState();
+ }
+ input.setValue(state.getValue());
+ input.setValid(state.isValid());
+ input.setSubmittedValue(state.getSubmittedValue());
+ input.setLocalValueSet(state.isLocalValueSet());
+ }
+
+ List kids = component.getChildren();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ restoreDescendantState((UIComponent)kids.get(i), context);
+ }
+ Map facets = component.getFacets();
+ for(Iterator i = facets.values().iterator(); i.hasNext();)
+ {
+ restoreDescendantState((UIComponent)i.next(), context);
+ }
+ }
+
+ /**
+ * A regular bean with accessor methods for all state variables.
+ *
+ * @author Sean Schofield
+ * @author Hans Bergsten (Some code taken from an example in his O'Reilly JavaServer Faces book. Copied with permission)
+ * @version $Revision: 703742 $ $Date: 2008-10-11 17:10:36 -0500 (sáb, 11 oct 2008) $
+ */
+ private static class SavedState implements Serializable
+ {
+ private static final long serialVersionUID = 273343276957070557L;
+ private Object submittedValue;
+ private boolean valid = true;
+ private Object value;
+ private boolean localValueSet;
+
+ Object getSubmittedValue()
+ {
+ return submittedValue;
+ }
+
+ void setSubmittedValue(Object submittedValue)
+ {
+ this.submittedValue = submittedValue;
+ }
+
+ boolean isValid()
+ {
+ return valid;
+ }
+
+ void setValid(boolean valid)
+ {
+ this.valid = valid;
+ }
+
+ Object getValue()
+ {
+ return value;
+ }
+
+ void setValue(Object value)
+ {
+ this.value = value;
+ }
+
+ boolean isLocalValueSet()
+ {
+ return localValueSet;
+ }
+
+ void setLocalValueSet(boolean localValueSet)
+ {
+ this.localValueSet = localValueSet;
+ }
+ }
+
+ /**
+ * Inner class used to wrap the original events produced by child components in the tree.
+ * This will allow the tree to find the appropriate component later when its time to
+ * broadcast the events to registered listeners. Code is based on a similar private
+ * class for UIData.
+ */
+ private static class FacesEventWrapper extends FacesEvent
+ {
+ private static final long serialVersionUID = -3056153249469828447L;
+ private FacesEvent _wrappedFacesEvent;
+ private String _nodeId;
+
+
+ public FacesEventWrapper(FacesEvent facesEvent, String nodeId, UIComponent component)
+ {
+ super(component);
+ _wrappedFacesEvent = facesEvent;
+ _nodeId = nodeId;
+ }
+
+
+ public PhaseId getPhaseId()
+ {
+ return _wrappedFacesEvent.getPhaseId();
+ }
+
+
+ public void setPhaseId(PhaseId phaseId)
+ {
+ _wrappedFacesEvent.setPhaseId(phaseId);
+ }
+
+
+ public void queue()
+ {
+ _wrappedFacesEvent.queue();
+ }
+
+
+ public String toString()
+ {
+ return _wrappedFacesEvent.toString();
+ }
+
+
+ public boolean isAppropriateListener(FacesListener faceslistener)
+ {
+ // this event type is only intended for wrapping a real event
+ return false;
+ }
+
+
+ public void processListener(FacesListener faceslistener)
+ {
+ throw new UnsupportedOperationException("This event type is only intended for wrapping a real event");
+ }
+
+
+ public FacesEvent getFacesEvent()
+ {
+ return _wrappedFacesEvent;
+ }
+
+
+ public String getNodeId()
+ {
+ return _nodeId;
+ }
+ }
+
+ /**
+ * Returns true if there is an error message queued for at least one of the nodes.
+ *
+ * @param context FacesContext
+ * @return whether an error message is present
+ */
+ private boolean keepSaved(FacesContext context)
+ {
+ Iterator clientIds = _saved.keySet().iterator();
+ while (clientIds.hasNext())
+ {
+ String clientId = (String) clientIds.next();
+ Iterator messages = context.getMessages(clientId);
+ while (messages.hasNext())
+ {
+ FacesMessage message = (FacesMessage) messages.next();
+ if (message.getSeverity().compareTo(FacesMessage.SEVERITY_ERROR) >= 0)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Toggle the expanded state of the current node.
+ */
+ public void toggleExpanded()
+ {
+ getDataModel().getTreeState().toggleExpanded(getNodeId());
+ }
+
+ /**
+ * Indicates whether or not the current {@link TreeNode} is expanded.
+ * @return boolean
+ */
+ public boolean isNodeExpanded()
+ {
+ return getDataModel().getTreeState().isNodeExpanded(getNodeId());
+ }
+
+ /**
+ * Implements the {@link javax.faces.event.ActionListener} interface. Basically, this
+ * method is used to listen for node selection events (when a user has clicked on a
+ * leaf node.)
+ *
+ * @param event ActionEvent
+ */
+ public void setNodeSelected(ActionEvent event)
+ {
+ getDataModel().getTreeState().setSelected(getNodeId());
+ }
+
+ /**
+ * Indicates whether or not the current {@link TreeNode} is selected.
+ * @return boolean
+ */
+ public boolean isNodeSelected()
+ {
+ return (getNodeId() != null) ? getDataModel().getTreeState().isSelected(getNodeId()) : false;
+ }
+}