
org.apache.myfaces.custom.tree.HtmlTreeNode Maven / Gradle / Ivy
/*
* 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 expandCollapse
.
*
* @JSFComponent
* @author Oliver Rossmueller
* @version $Revision: 659874 $ $Date: 2008-05-24 15:59:15 -0500 (Sat, 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));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy