/**
* License Agreement.
*
* JBoss RichFaces - Ajax4jsf Component Library
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.richfaces.component.state;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.faces.application.FacesMessage;
import javax.faces.component.StateHolder;
import javax.faces.context.FacesContext;
import org.ajax4jsf.model.DataComponentState;
import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.Range;
import org.richfaces.component.UITree;
import org.richfaces.component.state.events.TreeStateCommandsListener;
import org.richfaces.model.ListRowKey;
import org.richfaces.model.TreeRange;
import org.richfaces.model.TreeRowKey;
/**
* @author Nick Belaevski - nbelaevski@exadel.com created 23.11.2006
*
*/
public class TreeState implements DataComponentState, TreeStateCommandsListener, StateHolder, Serializable {
/**
*
*/
private static final long serialVersionUID = 9083705369340888171L;
enum NodeState {
EXPANDED, COLLAPSED;
}
private class ExpansionVisitor implements DataVisitor {
private UITree tree;
public ExpansionVisitor(UITree tree) {
super();
this.tree = tree;
}
public void process(FacesContext context, Object rowKey, Object argument)
throws IOException {
tree.setRowKey(context, rowKey);
if (!tree.isLeaf()) {
addQueuedState((TreeRowKey) rowKey, NodeState.EXPANDED);
}
}
};
private boolean stopInCollapsed = false;
private TreeRowKey selectedNode = null;
private Set<TreeRowKey> expandedNodes = new HashSet<TreeRowKey>();
private Map<TreeRowKey, NodeState> queuedNodeStates = new HashMap<TreeRowKey, NodeState>();
public TreeState() {
super();
}
public TreeState(boolean stopInCollapsed) {
super();
this.stopInCollapsed = stopInCollapsed;
}
public boolean isExpanded(TreeRowKey rowKey) {
if (rowKey == null) {
return true;
}
return expandedNodes.contains(rowKey) ||
NodeState.EXPANDED.equals(queuedNodeStates.get(rowKey));
}
public boolean isSelected(TreeRowKey rowKey) {
return (rowKey == null && selectedNode == null) || (selectedNode != null && selectedNode.equals(rowKey));
}
public TreeRowKey getSelectedNode() {
return selectedNode;
}
public void setSelected(TreeRowKey rowKey) {
selectedNode = rowKey;
}
private boolean _transient;
public Range getRange() {
if (stopInCollapsed) {
return new TreeRange() {
public boolean processChildren(TreeRowKey rowKey) {
if (rowKey == null) {
return true;
}
return expandedNodes.contains(rowKey);
}
public boolean processNode(TreeRowKey rowKey) {
return true;
}
};
} else {
return TreeRange.RANGE_UNCONSTRAINED;
}
}
public boolean isTransient() {
return _transient;
}
public void restoreState(FacesContext context, Object state) {
Object[] _state = (Object[]) state;
expandedNodes = (Set) _state[0];
queuedNodeStates = (Map) _state[1];
_transient = ((Boolean) _state[2]).booleanValue();
stopInCollapsed = ((Boolean) _state[3]).booleanValue();
selectedNode = (TreeRowKey) _state[4];
}
public Object saveState(FacesContext context) {
Object[] state = new Object[5];
state[0] = expandedNodes;
state[1] = queuedNodeStates;
state[2] = Boolean.valueOf(_transient);
state[3] = Boolean.valueOf(stopInCollapsed);
state[4] = selectedNode;
return state;
}
public void setTransient(boolean newTransientValue) {
this._transient = newTransientValue;
}
public boolean isStopInCollapsed() {
return stopInCollapsed;
}
public void setStopInCollapsed(boolean stopInCollapsed) {
this.stopInCollapsed = stopInCollapsed;
}
public void expandAll(UITree tree) throws IOException {
queuedNodeStates = new HashMap<TreeRowKey, NodeState>();
tree.walk(FacesContext.getCurrentInstance(),
new ExpansionVisitor(tree), TreeRange.RANGE_UNCONSTRAINED, null, null);
}
public void collapseAll(UITree tree) throws IOException {
queuedNodeStates = new HashMap<TreeRowKey, NodeState>();
for (TreeRowKey key : expandedNodes) {
addQueuedState(key, NodeState.COLLAPSED);
}
}
public void addQueuedState(TreeRowKey rowKey, NodeState state) {
if (NodeState.EXPANDED.equals(state)) {
if (!expandedNodes.contains(rowKey)) {
queuedNodeStates.put(rowKey, NodeState.EXPANDED);
}
} else {
queuedNodeStates.put(rowKey, NodeState.COLLAPSED);
}
}
public void collapseNode(UITree tree, TreeRowKey rowKey) throws IOException {
addQueuedState(rowKey, NodeState.COLLAPSED);
}
public void expandNode(UITree tree, TreeRowKey rowKey) throws IOException {
Object oldRowKey = tree.getRowKey();
tree.setRowKey(rowKey);
if (tree.isRowAvailable()) {
if (!tree.isLeaf()) {
TreeRowKey key = rowKey;
while (key != null && key.depth() != 0) {
addQueuedState(key, NodeState.EXPANDED);
key = (TreeRowKey) tree.getParentRowKey(key);
};
} else {
//TODO debug log
}
} else {
FacesMessage message = new FacesMessage("Row key: " + rowKey
+ " isn't available!");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(tree.getBaseClientId(context), message);
}
tree.setRowKey(oldRowKey);
}
private void transferState(TreeRowKey key, NodeState state) {
if (state != null) {
if (NodeState.EXPANDED.equals(state)) {
expandedNodes.add(key);
} else {
expandedNodes.remove(key);
}
}
}
public void transferQueuedNodes(TreeRowKey rootKey) {
Iterator<Entry<TreeRowKey, NodeState>> itr = queuedNodeStates.entrySet().iterator();
while (itr.hasNext()) {
Entry<TreeRowKey, NodeState> entry = itr.next();
TreeRowKey rowKey = entry.getKey();
if (rootKey == null || rootKey.depth() == 0 || rootKey.isSubKey(rowKey)) {
transferState(rowKey, entry.getValue());
itr.remove();
}
}
if (queuedNodeStates.isEmpty()) {
queuedNodeStates = new HashMap<TreeRowKey, NodeState>();
}
}
public void makeExpanded(TreeRowKey key) {
queuedNodeStates.remove(key);
expandedNodes.add(key);
}
public void makeCollapsed(TreeRowKey key) {
queuedNodeStates.remove(key);
expandedNodes.remove(key);
}
public TreeState getSubState(TreeRowKey rowKey) {
TreeState subTreeState = new TreeState(this.stopInCollapsed);
if (getSelectedNode() != null && rowKey.equals(getSelectedNode())) {
subTreeState.setSelected(rowKey);
}
// FIXME: whether it is needed?
subTreeState._transient = _transient;
int subKeyDepth = rowKey.depth() - 1;
for (Iterator<TreeRowKey> iter = this.expandedNodes.iterator(); iter.hasNext(); ) {
TreeRowKey nextKey = iter.next();
if (nextKey != null && rowKey.isSubKey(nextKey)) {
subTreeState.expandedNodes.add(nextKey.getSubKey(subKeyDepth));
}
}
for (Iterator<Entry<TreeRowKey, NodeState>> iter = this.queuedNodeStates.entrySet().iterator();
iter.hasNext(); ) {
Entry<TreeRowKey, NodeState> entry = iter.next();
TreeRowKey nextKey = entry.getKey();
if (nextKey != null && rowKey.isSubKey(nextKey)) {
subTreeState.queuedNodeStates.put(nextKey.getSubKey(subKeyDepth),
entry.getValue());
}
}
return subTreeState;
}
public void clearSubState(TreeRowKey rowKey) {
if (rowKey.equals(getSelectedNode())) {
setSelected(null);
}
if (rowKey.getPath().equals("null")) { // root node
this.expandedNodes.clear();
this.queuedNodeStates = new HashMap<TreeRowKey, NodeState>();
} else {
// collect nodes to clean up
for (Iterator<TreeRowKey> itr = this.expandedNodes.iterator(); itr.hasNext(); ) {
TreeRowKey nextKey = itr.next();
if (nextKey != null && rowKey.isSubKey(nextKey)) {
itr.remove();
}
}
for (Iterator<Entry<TreeRowKey, NodeState>> itr =
this.queuedNodeStates.entrySet().iterator(); itr.hasNext(); ) {
TreeRowKey nextKey = itr.next().getKey();
if (nextKey != null && rowKey.isSubKey(nextKey)) {
itr.remove();
}
}
}
}
public void mergeSubState(TreeRowKey rowKey, TreeState subState) {
Iterator<TreeRowKey> iter = subState.expandedNodes.iterator();
while (iter != null && iter.hasNext()) {
TreeRowKey key = iter.next().getSubKey(1);
if (key.depth() > 0) {
expandedNodes.add(new ListRowKey((ListRowKey)rowKey, (ListRowKey)key));
} else if (!expandedNodes.contains(rowKey)) {
expandedNodes.add(rowKey);
}
}
Iterator<Entry<TreeRowKey, NodeState>> sItr = subState.queuedNodeStates.entrySet().iterator();
while (sItr.hasNext()) {
Entry<TreeRowKey, NodeState> entry = sItr.next();
TreeRowKey key = entry.getKey().getSubKey(1);
TreeRowKey newKey;
if (key.depth() > 0) {
newKey = new ListRowKey((ListRowKey)rowKey, (ListRowKey)key);
} else {
newKey = rowKey;
}
addQueuedState(newKey, entry.getValue());
}
}
}