View Javadoc

1   /*
2    * $Id: Tree.java 651946 2008-04-27 13:41:38Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts2.dojo.components;
23  
24  import java.io.Writer;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Random;
28  
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.apache.struts2.components.ClosingUIBean;
33  import org.apache.struts2.views.annotations.StrutsTag;
34  import org.apache.struts2.views.annotations.StrutsTagAttribute;
35  import org.apache.struts2.views.annotations.StrutsTagSkipInheritance;
36  
37  import com.opensymphony.xwork2.util.ValueStack;
38  
39  /***
40   * <!-- START SNIPPET: javadoc -->
41   *
42   * Renders a tree widget with AJAX support.<p/>
43   *
44   * The "id "attribute is normally specified(recommended), such that it could be looked up using
45   * javascript if necessary. The "id" attribute is required if the "selectedNotifyTopic" or the 
46   * "href" attributes are going to be used.<p/>
47   *
48   * <!-- END SNIPPET: javadoc -->
49   *
50   * <!-- START SNIPPET: example1 -->
51   * &lt;s:tree id="..." label="..."&gt;
52   *    &lt;s:treenode id="..." label="..." /&gt;
53   *    &lt;s:treenode id="..." label="..."&gt;
54   *        &lt;s:treenode id="..." label="..." /&gt;
55   *        &lt;s:treenode id="..." label="..." /&gt;
56   *    &lt;/s:treenode&gt;
57   *    &lt;s:treenode id="..." label="..." /&gt;
58   * &lt;/s:tree&gt;
59   * <!-- END SNIPPET: example1 -->
60   * 
61   * <!-- START SNIPPET: example2 -->
62   * &lt;s:tree
63   *          id="..."
64   *          rootNode="..."
65   *          nodeIdProperty="..."
66   *          nodeTitleProperty="..."
67   *          childCollectionProperty="..." /&gt;
68   * <!-- END SNIPPET: example2 -->
69   * 
70   * <!-- START SNIPPET: example3 -->
71   * &lt;s:url id="nodesUrl" namespace="/nodecorate" action="getNodes" /&gt;
72   * &lt;div style="float:left; margin-right: 50px;"&gt;
73   *     &lt;sx:tree id="tree" href="%{#nodesUrl}" /&gt;
74   * &lt;/div&gt;
75   * 
76   * On this example the url specified on the "href" attibute will be called to load
77   * the elements on the root. The response is expected to be a JSON array of objects like:
78   * [
79   *      {
80   *           label: "Node 1",
81   *           hasChildren: false,
82   *           id: "Node1"
83   *      },
84   *      {
85   *           label: "Node 2",
86   *           hasChildren: true,
87   *           id: "Node2"
88   *      },
89   * ]
90   * 
91   * "label" is the text that will be displayed for the node. "hasChildren" marks the node has
92   * having children or not (if true, a plus icon will be assigned to the node so it can be
93   * expanded). The "id" attribute will be used to load the children of the node, when the node
94   * is expanded. When a node is expanded a request will be made to the url in the "href" attribute
95   * and the node's "id" will be passed in the parameter "nodeId".
96   * 
97   * The children collection for a node will be loaded only once, to reload the children of a 
98   * node, use the "reload()" function of the treenode widget. To reload the children nodes of "Node1"
99   * from the example above use the following javascript:
100  * 
101  * dojo.widget.byId("Node1").reload();
102  * <!-- END SNIPPET: example3 -->
103  */
104 @StrutsTag(name="tree", tldTagClass="org.apache.struts2.dojo.views.jsp.ui.TreeTag", description="Render a tree widget.")
105 public class Tree extends ClosingUIBean {
106 
107     private static final String TEMPLATE = "tree-close";
108     private static final String OPEN_TEMPLATE = "tree";
109     private final static transient Random RANDOM = new Random();    
110 
111     protected String toggle;
112     protected String selectedNotifyTopics;
113     protected String expandedNotifyTopics;
114     protected String collapsedNotifyTopics;
115     protected String rootNodeAttr;
116     protected String childCollectionProperty;
117     protected String nodeTitleProperty;
118     protected String nodeIdProperty;
119     protected String showRootGrid;
120 
121     protected String showGrid;
122     protected String blankIconSrc;
123     protected String gridIconSrcL;
124     protected String gridIconSrcV;
125     protected String gridIconSrcP;
126     protected String gridIconSrcC;
127     protected String gridIconSrcX;
128     protected String gridIconSrcY;
129     protected String expandIconSrcPlus;
130     protected String expandIconSrcMinus;
131     protected String iconWidth;
132     protected String iconHeight;
133     protected String toggleDuration;
134     protected String templateCssPath;
135     protected String href;
136     protected String errorNotifyTopics;
137     
138     private List<String> childrenIds;
139     
140     public Tree(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
141         super(stack, request, response);
142     }
143 
144     public boolean start(Writer writer) {
145         boolean result = super.start(writer);
146 
147         if (this.label == null && (href == null)) {
148             if ((rootNodeAttr == null)
149                     || (childCollectionProperty == null)
150                     || (nodeTitleProperty == null)
151                     || (nodeIdProperty == null)) {
152                 fieldError("label","The TreeTag requires either a value for 'label' or 'href' or ALL of 'rootNode', " +
153                         "'childCollectionProperty', 'nodeTitleProperty', and 'nodeIdProperty'", null);
154             }
155         }
156         return result;
157     }
158 
159     protected void evaluateExtraParams() {
160         super.evaluateExtraParams();
161 
162         if (toggle != null) {
163             addParameter("toggle", findString(toggle));
164         } else {
165             addParameter("toggle", "fade");
166         }
167 
168         if (selectedNotifyTopics != null) {
169             addParameter("selectedNotifyTopics", findString(selectedNotifyTopics));
170         }
171 
172         if (expandedNotifyTopics != null) {
173             addParameter("expandedNotifyTopics", findString(expandedNotifyTopics));
174         }
175 
176         if (collapsedNotifyTopics != null) {
177             addParameter("collapsedNotifyTopics", findString(collapsedNotifyTopics));
178         }
179 
180         if (rootNodeAttr != null) {
181             addParameter("rootNode", findValue(rootNodeAttr));
182         }
183 
184         if (childCollectionProperty != null) {
185             addParameter("childCollectionProperty", findString(childCollectionProperty));
186         }
187 
188         if (nodeTitleProperty != null) {
189             addParameter("nodeTitleProperty", findString(nodeTitleProperty));
190         }
191 
192         if (nodeIdProperty != null) {
193             addParameter("nodeIdProperty", findString(nodeIdProperty));
194         }
195 
196         if (showRootGrid != null) {
197             addParameter("showRootGrid", findValue(showRootGrid, Boolean.class));
198         }
199 
200 
201         if (showGrid != null) {
202             addParameter("showGrid", findValue(showGrid, Boolean.class));
203         }
204 
205         if (blankIconSrc != null) {
206             addParameter("blankIconSrc", findString(blankIconSrc));
207         }
208 
209         if (gridIconSrcL != null) {
210             addParameter("gridIconSrcL", findString(gridIconSrcL));
211         }
212 
213         if (gridIconSrcV != null) {
214             addParameter("gridIconSrcV", findString(gridIconSrcV));
215         }
216 
217         if (gridIconSrcP != null)  {
218             addParameter("gridIconSrcP", findString(gridIconSrcP));
219         }
220 
221         if (gridIconSrcC != null) {
222             addParameter("gridIconSrcC", findString(gridIconSrcC));
223         }
224 
225         if (gridIconSrcX != null) {
226             addParameter("gridIconSrcX", findString(gridIconSrcX));
227         }
228 
229         if (gridIconSrcY != null) {
230             addParameter("gridIconSrcY", findString(gridIconSrcY));
231         }
232 
233         if (expandIconSrcPlus != null) {
234             addParameter("expandIconSrcPlus", findString(expandIconSrcPlus));
235         }
236 
237         if (expandIconSrcMinus != null) {
238             addParameter("expandIconSrcMinus", findString(expandIconSrcMinus));
239         }
240 
241         if (iconWidth != null) {
242             addParameter("iconWidth", findValue(iconWidth, Integer.class));
243         }
244         if (iconHeight != null) {
245             addParameter("iconHeight", findValue(iconHeight, Integer.class));
246         }
247         if (toggleDuration != null) {
248             addParameter("toggleDuration", findValue(toggleDuration, Integer.class));
249         }
250         if (templateCssPath != null) {
251             addParameter("templateCssPath", findString(templateCssPath));
252         }
253         if (href != null) 
254             addParameter("href", findString(href));
255         if (errorNotifyTopics != null)
256             addParameter("errorNotifyTopics", findString(errorNotifyTopics));
257                 
258         // generate a random ID if not explicitly set and not parsing the content
259         Boolean parseContent = (Boolean)stack.getContext().get(Head.PARSE_CONTENT);
260         boolean generateId = (parseContent != null ? !parseContent : true);
261 
262         addParameter("pushId", generateId);
263         if ((this.id == null || this.id.length() == 0) && generateId) {
264             // resolves Math.abs(Integer.MIN_VALUE) issue reported by FindBugs 
265             // http://findbugs.sourceforge.net/bugDescriptions.html#RV_ABSOLUTE_VALUE_OF_RANDOM_INT
266             int nextInt = RANDOM.nextInt();
267             nextInt = nextInt == Integer.MIN_VALUE ? Integer.MAX_VALUE : Math.abs(nextInt);  
268             this.id = "widget_" + String.valueOf(nextInt);
269             addParameter("id", this.id);
270         }
271         
272         if (this.childrenIds != null)
273             addParameter("childrenIds", this.childrenIds);
274     }
275     
276     public void addChildrenId(String id) {
277         if (this.childrenIds == null)
278             this.childrenIds = new ArrayList<String>();
279         this.childrenIds.add(id);
280     }
281 
282     @Override
283     @StrutsTagSkipInheritance
284     public void setTheme(String theme) {
285         super.setTheme(theme);
286     }
287     
288     @Override
289     public String getTheme() {
290         return "ajax";
291     }
292 
293     public String getDefaultOpenTemplate() {
294         return OPEN_TEMPLATE;
295     }
296 
297     protected String getDefaultTemplate() {
298         return TEMPLATE;
299     }
300 
301     public String getToggle() {
302         return toggle;
303     }
304 
305     @StrutsTagAttribute(description="The toggle property (either 'explode' or 'fade')", defaultValue="fade")
306     public void setToggle(String toggle) {
307         this.toggle = toggle;
308     }
309 
310     @StrutsTagAttribute(description="Deprecated. Use 'selectedNotifyTopics' instead.")
311     public void setTreeSelectedTopic(String selectedNotifyTopic) {
312         this.selectedNotifyTopics = selectedNotifyTopic;
313     }
314 
315     @StrutsTagAttribute(description="Deprecated. Use 'expandedNotifyTopics' instead.")
316     public void setTreeExpandedTopics(String expandedNotifyTopic) {
317         this.expandedNotifyTopics = expandedNotifyTopic;
318     }
319 
320     @StrutsTagAttribute(description="Deprecated. Use 'collapsedNotifyTopics' instead.")
321     public void setTreeCollapsedTopics(String collapsedNotifyTopic) {
322         this.collapsedNotifyTopics = collapsedNotifyTopic;
323     }
324 
325     public String getRootNode() {
326         return rootNodeAttr;
327     }
328 
329     @StrutsTagAttribute(description="The rootNode property.")
330     public void setRootNode(String rootNode) {
331         this.rootNodeAttr = rootNode;
332     }
333 
334     public String getChildCollectionProperty() {
335         return childCollectionProperty;
336     }
337 
338     @StrutsTagAttribute(description="The childCollectionProperty property.")
339     public void setChildCollectionProperty(String childCollectionProperty) {
340         this.childCollectionProperty = childCollectionProperty;
341     }
342 
343     public String getNodeTitleProperty() {
344         return nodeTitleProperty;
345     }
346 
347     @StrutsTagAttribute(description="The nodeTitleProperty property.")
348     public void setNodeTitleProperty(String nodeTitleProperty) {
349         this.nodeTitleProperty = nodeTitleProperty;
350     }
351 
352     public String getNodeIdProperty() {
353         return nodeIdProperty;
354     }
355 
356     @StrutsTagAttribute(description="The nodeIdProperty property.")
357     public void setNodeIdProperty(String nodeIdProperty) {
358         this.nodeIdProperty = nodeIdProperty;
359     }
360 
361     @StrutsTagAttribute(description="The showRootGrid property (default true).")
362     public void setShowRootGrid(String showRootGrid) {
363         this.showRootGrid = showRootGrid;
364     }
365 
366     public String getShowRootGrid() {
367         return showRootGrid;
368     }
369 
370     public String getBlankIconSrc() {
371         return blankIconSrc;
372     }
373 
374     @StrutsTagAttribute(description="Blank icon image source.")
375     public void setBlankIconSrc(String blankIconSrc) {
376         this.blankIconSrc = blankIconSrc;
377     }
378 
379     public String getExpandIconSrcMinus() {
380         return expandIconSrcMinus;
381     }
382 
383     @StrutsTagAttribute(description="Expand icon (-) image source.")
384     public void setExpandIconSrcMinus(String expandIconSrcMinus) {
385         this.expandIconSrcMinus = expandIconSrcMinus;
386     }
387 
388     public String getExpandIconSrcPlus() {
389         return expandIconSrcPlus;
390     }
391 
392     @StrutsTagAttribute(description="Expand Icon (+) image source.")
393     public void setExpandIconSrcPlus(String expandIconSrcPlus) {
394         this.expandIconSrcPlus = expandIconSrcPlus;
395     }
396 
397     public String getGridIconSrcC() {
398         return gridIconSrcC;
399     }
400 
401     @StrutsTagAttribute(description="Image source for under child item child icons.")
402     public void setGridIconSrcC(String gridIconSrcC) {
403         this.gridIconSrcC = gridIconSrcC;
404     }
405 
406     public String getGridIconSrcL() {
407         return gridIconSrcL;
408     }
409 
410 
411     @StrutsTagAttribute(description=" Image source for last child grid.")
412     public void setGridIconSrcL(String gridIconSrcL) {
413         this.gridIconSrcL = gridIconSrcL;
414     }
415 
416     public String getGridIconSrcP() {
417         return gridIconSrcP;
418     }
419 
420     @StrutsTagAttribute(description="Image source for under parent item child icons.")
421     public void setGridIconSrcP(String gridIconSrcP) {
422         this.gridIconSrcP = gridIconSrcP;
423     }
424 
425     public String getGridIconSrcV() {
426         return gridIconSrcV;
427     }
428 
429     @StrutsTagAttribute(description="Image source for vertical line.")
430     public void setGridIconSrcV(String gridIconSrcV) {
431         this.gridIconSrcV = gridIconSrcV;
432     }
433 
434     public String getGridIconSrcX() {
435         return gridIconSrcX;
436     }
437 
438     @StrutsTagAttribute(description="Image source for grid for sole root item.")
439     public void setGridIconSrcX(String gridIconSrcX) {
440         this.gridIconSrcX = gridIconSrcX;
441     }
442 
443     public String getGridIconSrcY() {
444         return gridIconSrcY;
445     }
446 
447     @StrutsTagAttribute(description="Image source for grid for last root item.")
448     public void setGridIconSrcY(String gridIconSrcY) {
449         this.gridIconSrcY = gridIconSrcY;
450     }
451 
452     public String getIconHeight() {
453         return iconHeight;
454     }
455 
456 
457     @StrutsTagAttribute(description="Icon height", defaultValue="18px")
458     public void setIconHeight(String iconHeight) {
459         this.iconHeight = iconHeight;
460     }
461 
462     public String getIconWidth() {
463         return iconWidth;
464     }
465 
466     @StrutsTagAttribute(description="Icon width", defaultValue="19px")
467     public void setIconWidth(String iconWidth) {
468         this.iconWidth = iconWidth;
469     }
470 
471 
472 
473     public String getTemplateCssPath() {
474         return templateCssPath;
475     }
476 
477     @StrutsTagAttribute(description="Template css path", defaultValue="{contextPath}/struts/tree.css.")
478     public void setTemplateCssPath(String templateCssPath) {
479         this.templateCssPath = templateCssPath;
480     }
481 
482     public String getToggleDuration() {
483         return toggleDuration;
484     }
485 
486     @StrutsTagAttribute(description="Toggle duration in milliseconds", defaultValue="150")
487     public void setToggleDuration(String toggleDuration) {
488         this.toggleDuration = toggleDuration;
489     }
490 
491     public String getShowGrid() {
492         return showGrid;
493     }
494 
495     @StrutsTagAttribute(description="Show grid", type="Boolean", defaultValue="true")
496     public void setShowGrid(String showGrid) {
497         this.showGrid = showGrid;
498     }
499     
500     @StrutsTagAttribute(description="The css class to use for element")
501     public void setCssClass(String cssClass) {
502         super.setCssClass(cssClass);
503     }
504 
505     @StrutsTagAttribute(description="The css style to use for element")
506     public void setCssStyle(String cssStyle) {
507         super.setCssStyle(cssStyle);
508     }
509 
510     @StrutsTagAttribute(description="The id to use for the element")
511     public void setId(String id) {
512         super.setId(id);
513     }
514 
515     @StrutsTagAttribute(description="The name to set for element")
516     public void setName(String name) {
517         super.setName(name);
518     }
519 
520     @StrutsTagAttribute(description="Comma separated lis of topics to be published when a node" +
521                 " is collapsed. An object with a 'node' property will be passed as parameter to the topics.")
522     public void setCollapsedNotifyTopics(String collapsedNotifyTopics) {
523         this.collapsedNotifyTopics = collapsedNotifyTopics;
524     }
525 
526     @StrutsTagAttribute(description="Comma separated lis of topics to be published when a node" +
527                 " is expanded. An object with a 'node' property will be passed as parameter to the topics.")
528     public void setExpandedNotifyTopics(String expandedNotifyTopics) {
529         this.expandedNotifyTopics= expandedNotifyTopics;
530     }
531 
532     @StrutsTagAttribute(description="Comma separated lis of topics to be published when a node" +
533                 " is selected. An object with a 'node' property will be passed as parameter to the topics.")
534     public void setSelectedNotifyTopics(String selectedNotifyTopics) {
535         this.selectedNotifyTopics = selectedNotifyTopics;
536     }
537 
538     @StrutsTagAttribute(description="Url used to load the list of children nodes for an specific node, whose id will be " +
539     		"passed as a parameter named 'nodeId' (empty for root)")
540     public void setHref(String href) {
541         this.href = href;
542     }
543     
544     @StrutsTagAttribute(description="Comma delimmited list of topics that will published after the request(if the request fails)." +
545     		"Only valid if 'href' is set")
546     public void setErrorNotifyTopics(String errorNotifyTopics) {
547         this.errorNotifyTopics = errorNotifyTopics;
548     }
549 }
550 
551