/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.renderkit; import java.io.IOException; import java.util.LinkedList; import javax.faces.FacesException; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import org.ajax4jsf.javascript.JSFunction; import org.richfaces.component.AbstractTree; import org.richfaces.component.AbstractTreeNode; import org.richfaces.component.util.HtmlUtil; import org.richfaces.context.ExtendedPartialViewContext; import org.richfaces.model.TreeDataModelTuple; import org.richfaces.model.TreeDataVisitor; abstract class TreeEncoderBase implements TreeDataVisitor { private static final class QueuedData { private enum State { initial, visited, encoded } private State state = State.initial; private TreeDataModelTuple tuple; public QueuedData(TreeDataModelTuple tuple) { super(); this.tuple = tuple; } public boolean isEncoded() { return state == State.encoded; } public void makeEncoded() { this.state = State.encoded; } public void makeVisited() { this.state = State.visited; } public boolean isVisited() { return state == State.visited; } public TreeDataModelTuple getTuple() { return tuple; } } static final String TREE_NODE_STATE_ATTRIBUTE = "__treeNodeState"; protected final FacesContext context; protected final ResponseWriter responseWriter; protected final AbstractTree tree; private LinkedList<QueuedData> queuedDataList = new LinkedList<QueuedData>(); public TreeEncoderBase(FacesContext context, AbstractTree tree) { super(); this.context = context; this.responseWriter = context.getResponseWriter(); this.tree = tree; } protected void encodeTree() throws IOException { tree.walkModel(context, this); } protected void flushParentNode() throws IOException { if (queuedDataList.isEmpty()) { return; } QueuedData data = queuedDataList.getLast(); if (!data.isEncoded()) { data.makeEncoded(); tree.restoreFromSnapshot(context, data.getTuple()); TreeNodeState nodeState = getNodeState(tree.isLeaf(), false); writeTreeNodeStartElement(nodeState); tree.findTreeNodeComponent().encodeAll(context); } } private TreeNodeState getNodeState(boolean leaf, boolean visited) { TreeNodeState nodeState; if (leaf) { nodeState = TreeNodeState.leaf; } else if (visited) { nodeState = TreeNodeState.expandedNoChildren; } else if (tree.isExpanded()) { nodeState = TreeNodeState.expanded; } else { nodeState = TreeNodeState.collapsed; } return nodeState; } public void beforeChildrenVisit() { if (!queuedDataList.isEmpty()) { queuedDataList.getLast().makeVisited(); } } public void afterChildrenVisit() { } public void enterNode() { TreeDataModelTuple tuple = tree.createSnapshot(); QueuedData queuedData = new QueuedData(tuple); try { flushParentNode(); } catch (IOException e) { throw new FacesException(e.getMessage(), e); } tree.restoreFromSnapshot(context, tuple); queuedDataList.add(queuedData); } public void exitNode() { QueuedData data = queuedDataList.removeLast(); tree.restoreFromSnapshot(context, data.getTuple()); try { if (!data.isEncoded()) { writeTreeNodeStartElement(getNodeState(tree.isLeaf(), data.isVisited())); tree.findTreeNodeComponent().encodeAll(context); } writeTreeNodeEndElement(); } catch (IOException e) { throw new FacesException(e.getMessage(), e); } } protected void writeTreeNodeStartElement(TreeNodeState nodeState) throws IOException { AbstractTreeNode treeNodeComponent = tree.findTreeNodeComponent(); context.getAttributes().put(TREE_NODE_STATE_ATTRIBUTE, nodeState); responseWriter.startElement(HtmlConstants.DIV_ELEM, tree); responseWriter.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, HtmlUtil.concatClasses("rf-tr-nd", nodeState.getNodeClass()), null); responseWriter.writeAttribute(HtmlConstants.ID_ATTRIBUTE, treeNodeComponent.getClientId(context), null); emitClientToggleEvent(treeNodeComponent, nodeState); } protected void writeTreeNodeEndElement() throws IOException { responseWriter.endElement(HtmlConstants.DIV_ELEM); } public abstract void encode() throws IOException; private void emitClientToggleEvent(AbstractTreeNode treeNode, TreeNodeState nodeState) { if (treeNode.getClientId(context).equals(context.getAttributes().get(TreeNodeRendererBase.AJAX_TOGGLED_NODE_ATTRIBUTE))) { TreeNodeState initialState = (TreeNodeState) context.getAttributes().get( TreeNodeRendererBase.AJAX_TOGGLED_NODE_STATE_ATTRIBUTE); if (initialState.isDifferentThan(nodeState)) { ExtendedPartialViewContext partialContext = ExtendedPartialViewContext.getInstance(context); partialContext.appendOncomplete(new JSFunction("RichFaces.ui.TreeNode.emitToggleEvent", treeNode .getClientId(context))); } } } }