001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2007
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.faces.components;
034:
035: import com.flexive.faces.FxJsfUtils;
036: import com.flexive.faces.JsfRelativeUriMapper;
037: import com.flexive.faces.beans.PluginRegistryBean;
038: import com.flexive.faces.javascript.FxJavascriptUtils;
039: import com.flexive.faces.javascript.RelativeUriMapper;
040: import com.flexive.faces.javascript.menu.MenuWriter;
041: import com.flexive.faces.javascript.tree.TreeNodeWriter;
042: import com.flexive.faces.plugin.ExtensionPoint;
043: import com.flexive.faces.plugin.TreePluginExecutor;
044: import org.apache.commons.lang.StringUtils;
045:
046: import javax.faces.component.UIComponent;
047: import javax.faces.context.FacesContext;
048: import javax.faces.context.ResponseWriter;
049: import javax.faces.render.Renderer;
050: import java.io.IOException;
051: import java.util.*;
052:
053: public class TreeRenderer extends Renderer {
054: /**
055: * Property set for dynamic javascript includes - enables embedded tree
056: * renderers to use the main tree nodewriter.
057: */
058: public static final String PROP_NODEWRITER = "treeNodeWriter";
059:
060: /**
061: * Extended tree writer that injects tree nodes generated by plugins
062: * in the rendered tree.
063: */
064: private static class PluginAwareTreeWriter extends TreeNodeWriter {
065: private final Map<String, List<Node>> injectedNodes;
066: private final Stack<String> nodeStack = new Stack<String>();
067:
068: private PluginAwareTreeWriter(java.io.Writer out,
069: RelativeUriMapper uriMapper, NodeFormatter formatter,
070: Map<String, List<Node>> injectedNodes)
071: throws IOException {
072: super (out, uriMapper, formatter);
073: this .injectedNodes = new HashMap<String, List<Node>>(
074: injectedNodes);
075: }
076:
077: @Override
078: public void startNode(Node node) throws IOException {
079: super .startNode(node);
080: nodeStack.push(node.getId());
081: }
082:
083: @Override
084: public void closeChildren() throws IOException {
085: writeInjectedNodes();
086: super .closeChildren();
087: }
088:
089: @Override
090: public void closeNode() throws IOException {
091: if (hasInjectedNodes()) {
092: // if there are nodes available, closeChildren() has not been called on this
093: // node, thus no children have been rendered yet
094: startChildren();
095: writeInjectedNodes();
096: closeChildren();
097: }
098: nodeStack.pop();
099: super .closeNode();
100: }
101:
102: @Override
103: public void finishResponse() throws IOException {
104: writeInjectedNodes();
105: super .finishResponse();
106: }
107:
108: private void writeInjectedNodes() throws IOException {
109: final String parent = getCurrentParent();
110: if (injectedNodes.get(parent) != null) {
111: final List<Node> injected = injectedNodes
112: .remove(parent);
113: for (Node node : injected) {
114: writeNode(node);
115: }
116: }
117: }
118:
119: private boolean hasInjectedNodes() {
120: return injectedNodes.containsKey(getCurrentParent());
121: }
122:
123: private String getCurrentParent() {
124: return nodeStack.isEmpty() ? null : nodeStack.peek();
125: }
126: }
127:
128: /**
129: * {@inheritDoc}
130: */
131: @Override
132: public boolean getRendersChildren() {
133: return true;
134: }
135:
136: /**
137: * {@inheritDoc}
138: */
139: @Override
140: public void encodeBegin(FacesContext context, UIComponent component)
141: throws IOException {
142: Tree tree = (Tree) component;
143: ResponseWriter writer = context.getResponseWriter();
144: FxJavascriptUtils.beginJavascript(writer);
145: final String treeControllerWidget = (tree.isContentTree() ? "flexive.widget.FxTreeRpcController"
146: : "dojo.widget.TreeBasicControllerV3");
147: writer.write("dojo.addOnLoad(function() {\n");
148: FxJavascriptUtils.writeDojoRequires(writer,
149: "dojo.widget.TreeV3", StringUtils.replace(
150: treeControllerWidget, "flexive:", ""),
151: "dojo.widget.TreeSelectorV3",
152: "dojo.widget.TreeEmphasizeOnSelect",
153: "dojo.widget.TreeNodeV3",
154: "dojo.widget.TreeContextMenuV3",
155: "dojo.widget.TreeLinkExtension",
156: "dojo.widget.TreeDndControllerV3",
157: "dojo.widget.TreeEditor",
158: "dojo.widget.TreeDocIconExtension");
159: writer.write("var controller = dojo.widget.createWidget(\""
160: + FxJavascriptUtils.getWidgetName(treeControllerWidget)
161: + "\"" + ",{RpcUrl: \""
162: + FxJsfUtils.getJsonServletUri() + "\"});\n"
163: + "var _listeners = [controller.widgetId];\n");
164: addTreeExtension(writer, "selector", "TreeSelectorV3", true,
165: tree.isSelector());
166: addTreeExtension(writer, "emphasize", "TreeEmphasizeOnSelect",
167: false, tree.isSelector(),
168: "{selector: selector.widgetId}");
169: addTreeExtension(writer, "dnd", "TreeDndControllerV3", true,
170: tree.isDragAndDrop(), "{controller: controller}");
171: addTreeExtension(writer, "docIcons", "TreeDocIconExtension",
172: true, tree.isDocIcons());
173: if (tree.isEditor()) {
174: writer
175: .write("var editor = dojo.widget.createWidget(\"TreeEditor\");\n"
176: + "controller.editor = editor;\n"
177: + "editor.controller = controller;\n");
178:
179: }
180:
181: final Map<String, List<TreeNodeWriter.Node>> injectedNodes = new HashMap<String, List<TreeNodeWriter.Node>>();
182: if (StringUtils.isNotBlank(tree.getExtensionPoint())) {
183: // execute plugins, populate injectedNodes map
184: TreePluginExecutor executor = new TreePluginExecutor() {
185: public void addNode(String parentNode,
186: TreeNodeWriter.Node node) {
187: if (!injectedNodes.containsKey(parentNode)) {
188: injectedNodes.put(parentNode,
189: new ArrayList<TreeNodeWriter.Node>());
190: }
191: injectedNodes.get(parentNode).add(node);
192: }
193: };
194: PluginRegistryBean.getInstance().execute(
195: new ExtensionPoint<TreePluginExecutor>(tree
196: .getExtensionPoint()) {
197: }, executor);
198: }
199:
200: writer.write("var children = ");
201: // Tree JSON representation follows
202: TreeNodeWriter nodeWriter = new PluginAwareTreeWriter(context
203: .getResponseWriter(), new JsfRelativeUriMapper(), tree
204: .isContentTree() ? TreeNodeWriter.FORMAT_CONTENTTREE
205: : tree.isDocIcons() ? TreeNodeWriter.FORMAT_PLAIN
206: : TreeNodeWriter.FORMAT_ADMINTREE,
207: injectedNodes);
208: tree.setNodeWriter(nodeWriter);
209: }
210:
211: /**
212: * {@inheritDoc}
213: */
214: @Override
215: public void encodeChildren(FacesContext context,
216: UIComponent component) throws IOException {
217: Tree tree = ((Tree) component);
218: TreeNodeWriter nodeWriter = tree.getNodeWriter();
219: for (Object child : component.getChildren()) {
220: if (child instanceof TreeNode) {
221: TreeNode treeNode = (TreeNode) child;
222: treeNode.writeTreeNodes(nodeWriter);
223: } else if (child instanceof JsonRpcCall) {
224: // indirect rendering through json-rpc call
225: FxJsfUtils.getRequest().setAttribute(
226: TreeRenderer.PROP_NODEWRITER, nodeWriter);
227: JsonRpcCall rpcCall = (JsonRpcCall) child;
228: rpcCall.encodeBegin(context);
229: rpcCall.encodeEnd(context);
230: } else if (child instanceof UIComponent) {
231: UIComponent childComponent = (UIComponent) child;
232: childComponent.encodeBegin(context);
233: childComponent.encodeChildren(context);
234: childComponent.encodeEnd(context);
235: }
236: }
237: }
238:
239: /**
240: * {@inheritDoc}
241: */
242: @Override
243: public void encodeEnd(FacesContext context, UIComponent component)
244: throws IOException {
245: Tree tree = (Tree) component;
246: ResponseWriter writer = context.getResponseWriter();
247: TreeNodeWriter nodeWriter = ((Tree) component).getNodeWriter();
248:
249: nodeWriter.finishResponse();
250:
251: // close "tree.setChildren"
252: writer.write(";\n");
253:
254: // init tree reporter (ignoring the context menu listener)
255: writer.write("initializeTreeReporter(_listeners, "
256: + StringUtils.defaultString(tree.getClickHandler(),
257: "null") + ");\n");
258: String widgetId = tree.getName();
259:
260: if (tree.getContextMenu() != null) {
261: MenuWriter.writeMenu(writer, widgetId + "Menu",
262: "contextMenu", "TreeContextMenuV3",
263: "TreeMenuItemV3", tree.getContextMenu(),
264: new JsfRelativeUriMapper(), null, tree
265: .getContextMenu().getShowHandler());
266: // add to tree listeners
267: writer.write("_listeners.push(contextMenu.widgetId);\n");
268: }
269:
270: writer
271: .write("var tree = dojo.widget.createWidget(\"TreeV3\", "
272: + "{widgetId: \""
273: + widgetId
274: + "\", listeners: _listeners\n"
275: + (tree.isDragAndDrop() ? ", DndAcceptTypes:\""
276: + widgetId
277: + "\", DndMode:\"between;onto\"" : "")
278: + "});\n"
279: + "tree.fxController = controller;\n"
280: + (tree.isSelector() ? "tree.fxSelector = selector;\n"
281: : "")
282: + (tree.isDragAndDrop() ? "tree.dndController = dnd;\n"
283: : "")
284: + "tree.setChildren(children);\n"
285: + (tree.isExpandFirstNode() ? "if (children.length > 0) tree.children[0].expand();\n"
286: : ""));
287: writer.write("document.getElementById(\"" + tree.getTargetId()
288: + "\").appendChild(tree.domNode);\n");
289: // close "addOnLoad"
290: writer.write("});\n");
291: FxJavascriptUtils.endJavascript(writer);
292:
293: }
294:
295: private void addTreeExtension(ResponseWriter writer,
296: String varName, String widget, boolean addToListeners,
297: boolean rendered, String... args) throws IOException {
298: if (!rendered) {
299: return;
300: }
301: writer.write("var "
302: + varName
303: + " = dojo.widget.createWidget(\""
304: + widget
305: + "\""
306: + (args.length > 0 ? "," + StringUtils.join(args, ',')
307: : "") + ");\n");
308: if (addToListeners) {
309: writer
310: .write("_listeners.push(" + varName
311: + ".widgetId);\n");
312: }
313: }
314: }
|