KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > wcf > tree > TreeComponent


1 /*
2  * ====================================================================
3  * This software is subject to the terms of the Common Public License
4  * Agreement, available at the following URL:
5  * http://www.opensource.org/licenses/cpl.html .
6  * Copyright (C) 2003-2004 TONBELLER AG.
7  * All Rights Reserved.
8  * You must accept the terms of that agreement to use this software.
9  * ====================================================================
10  *
11  *
12  */

13 package com.tonbeller.wcf.tree;
14
15 import java.util.Comparator JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.Set JavaDoc;
19 import java.util.TreeSet JavaDoc;
20
21 import org.w3c.dom.Document JavaDoc;
22 import org.w3c.dom.Element JavaDoc;
23
24 import com.tonbeller.wcf.changeorder.ChangeOrderMgr;
25 import com.tonbeller.wcf.changeorder.ChangeOrderModel;
26 import com.tonbeller.wcf.component.Component;
27 import com.tonbeller.wcf.component.NestableComponentSupport;
28 import com.tonbeller.wcf.component.RenderListener;
29 import com.tonbeller.wcf.controller.Dispatcher;
30 import com.tonbeller.wcf.controller.DispatcherSupport;
31 import com.tonbeller.wcf.controller.RequestContext;
32 import com.tonbeller.wcf.controller.RequestListener;
33 import com.tonbeller.wcf.convert.BooleanConverter;
34 import com.tonbeller.wcf.convert.CheckBoxConverter;
35 import com.tonbeller.wcf.convert.RadioButtonConverter;
36 import com.tonbeller.wcf.scroller.Scroller;
37 import com.tonbeller.wcf.selection.SelectionMgr;
38 import com.tonbeller.wcf.selection.SelectionModel;
39 import com.tonbeller.wcf.utils.DomUtils;
40
41 /**
42  * Tree Component
43  *
44  * @author av
45  */

46 public class TreeComponent extends NestableComponentSupport {
47
48   String JavaDoc treeElementName = "xtree-component";
49   NodeRenderer nodeRenderer = new DefaultNodeRenderer();
50   SelectionMgr selectionMgr;
51   ChangeOrderMgr changeOrderMgr;
52   ChangeOrderModel changeOrderModel;
53   String JavaDoc border = null;
54   String JavaDoc renderId = null;
55   String JavaDoc width = null;
56   String JavaDoc error = null;
57
58   Set JavaDoc expanded = new HashSet JavaDoc();
59   Dispatcher dispatcher = new DispatcherSupport();
60
61   BooleanConverter radioConv = new RadioButtonConverter();
62   BooleanConverter checkConv = new CheckBoxConverter();
63
64   // initialized once in initialize()
65
TreeModel model;
66   TreeBounding bounding = NO_BOUNDING;
67   private static final TreeBounding NO_BOUNDING = new TreeBounding() {
68     public boolean isBounded(Object JavaDoc parent) {
69       return false;
70     }
71     public void unbound(Object JavaDoc parent) {
72     }
73   };
74
75   // initialized for every render() call
76
RequestContext context;
77   Document JavaDoc factory;
78   int nodeCounter;
79
80   DeleteNodeModel deleteNodeModel;
81
82   TreeModelChangeListener changeListener = new TreeModelChangeListener() {
83     public void treeModelChanged(TreeModelChangeEvent event) {
84       if (event.isIdentityChanged()) {
85         selectionMgr.getSelectionModel().clear();
86         expanded.clear();
87         dispatcher.clear();
88       }
89     }
90   };
91
92   /**
93    * Constructor for TreeComponent.
94    */

95   public TreeComponent(String JavaDoc id, Component parent) {
96     this(id, parent, null);
97   }
98
99   /**
100    * Constructor for TreeComponent.
101    */

102   public TreeComponent(String JavaDoc id, Component parent, TreeModel model) {
103     super(id, parent);
104     selectionMgr = new SelectionMgr(getDispatcher(), this);
105     changeOrderMgr = new ChangeOrderMgr(getDispatcher(), this);
106     super.getDispatcher().addRequestListener(null, null, dispatcher);
107
108     setModel(model);
109   }
110
111   /**
112    * comparator fuer die expanded nodes
113    */

114   public void setComparator(Comparator JavaDoc comp) {
115     expanded = new TreeSet JavaDoc(comp);
116   }
117
118   /**
119    * set the model to use
120    * @param model null or a TreeModel. If the TreeModel implements the ChangeOrderModel,
121    * its used by the ChangeOrderMgr.
122    */

123   public void setModel(TreeModel newModel) {
124     bounding = NO_BOUNDING;
125     if (model != null)
126       model.removeTreeModelChangeListener(changeListener);
127     this.model = newModel;
128     if (model != null)
129       model.addTreeModelChangeListener(changeListener);
130     if (model instanceof ChangeOrderModel)
131       changeOrderMgr.setModel((ChangeOrderModel) model);
132     else
133       changeOrderMgr.setModel(null);
134     expanded.clear();
135     selectionMgr.getSelectionModel().clear();
136     dispatcher.clear();
137   }
138
139   public Element JavaDoc render(RequestContext context, Document JavaDoc factory) throws Exception JavaDoc {
140     return render(context, factory, true);
141   }
142
143   /**
144    * renders the component
145    * @param context the wcf context of the current request
146    * @param clearDispatcher clears the dispatcher so all buttons/hyperlinks from
147    * the previous render() call become invalid. Then renders the component.
148    */

149   private Element JavaDoc render(RequestContext context, Document JavaDoc factory, boolean clearDispatcher)
150     throws Exception JavaDoc {
151     this.factory = factory;
152     this.context = context;
153     if (clearDispatcher)
154       dispatcher.clear();
155
156     Element JavaDoc treeElem = factory.createElement(treeElementName);
157     renderTree(treeElem);
158     return treeElem;
159   }
160
161   Element JavaDoc renderTree(Element JavaDoc treeElem) {
162     selectionMgr.startRendering(context);
163     changeOrderMgr.startRendering(context);
164     if (nodeRenderer instanceof RenderListener)
165        ((RenderListener) nodeRenderer).startRendering(context);
166     nodeCounter = 0;
167     Object JavaDoc[] roots = model.getRoots();
168     for (int i = 0; i < roots.length; i++) {
169       Element JavaDoc nodeElem = renderNode(null, roots, i, 0);
170       if (nodeElem != null)
171         treeElem.appendChild(nodeElem);
172     }
173     if (nodeRenderer instanceof RenderListener)
174        ((RenderListener) nodeRenderer).stopRendering();
175     changeOrderMgr.stopRendering();
176     selectionMgr.stopRendering();
177     if (border != null)
178       treeElem.setAttribute("border", border);
179     if (renderId != null)
180       treeElem.setAttribute("renderId", renderId);
181     if (width != null)
182       treeElem.setAttribute("width", width);
183     if (error != null)
184       treeElem.setAttribute("error", error);
185     return treeElem;
186   }
187
188   /**
189    * returns the Element for nodes[nodeIndex] or null, if this
190    * node was deleted
191    */

192   Element JavaDoc renderNode(Object JavaDoc parent, Object JavaDoc[] nodes, int nodeIndex, int level) {
193     Object JavaDoc node = nodes[nodeIndex];
194     if (deleteNodeModel != null && deleteNodeModel.getDeleted().contains(node))
195       return null;
196
197     Element JavaDoc nodeElem = nodeRenderer.renderNode(context, factory, node);
198     ++nodeCounter;
199     // even / odd row count
200
renderEvenOddAttr(nodeElem);
201     String JavaDoc id = DomUtils.randomId();
202     nodeElem.setAttribute("id", id);
203     nodeElem.setAttribute("level", "" + level);
204     renderExpandedAttr(node, nodeElem, id);
205     // recurse
206
if (expanded.contains(node)) {
207       // node is expanded, so render its children
208
Object JavaDoc[] children = model.getChildren(node);
209       for (int i = 0; i < children.length; i++) {
210         Element JavaDoc childElem = renderNode(node, children, i, level + 1);
211         if (childElem != null)
212           nodeElem.appendChild(childElem);
213       }
214     }
215     // render selection
216
selectionMgr.renderButton(nodeElem, node);
217     changeOrderMgr.renderButton(nodeElem, parent, node, nodeIndex, nodes.length);
218     renderDeleteButton(nodeElem, node);
219     return nodeElem;
220   }
221
222   void renderDeleteButton(Element JavaDoc elem, final Object JavaDoc node) {
223     if (deleteNodeModel == null || !deleteNodeModel.isDeletable(node))
224       return;
225     Element JavaDoc button = DomUtils.appendElement(elem, "delete-button");
226     String JavaDoc id = DomUtils.randomId();
227     button.setAttribute("id", id);
228     RequestListener deleter = new RequestListener() {
229       public void request(RequestContext context) throws Exception JavaDoc {
230         Scroller.enableScroller(context);
231         // recursively delete node and all of its children
232
NodeIterator it = new NodeIterator(getModel(), node, false);
233         while (it.hasNext()) {
234           Object JavaDoc n = it.next();
235           deleteNodeModel.delete(n);
236           getSelectionModel().remove(n);
237           expanded.remove(n);
238         }
239       }
240     };
241     dispatcher.addRequestListener(id, null, deleter);
242   }
243
244   void renderExpandedAttr(Object JavaDoc node, Element JavaDoc nodeElem, String JavaDoc id) {
245     if (bounding.isBounded(node)) {
246       nodeElem.setAttribute("state", "bounded");
247       dispatcher.addRequestListener(id + ".unbound", null, new UnboundHandler(node));
248     } else if (expanded.contains(node)) {
249       nodeElem.setAttribute("state", "expanded");
250       dispatcher.addRequestListener(id + ".collapse", null, new CollapseHandler(node));
251     } else if (model.hasChildren(node)) {
252       nodeElem.setAttribute("state", "collapsed");
253       dispatcher.addRequestListener(id + ".expand", null, new ExpandHandler(node));
254     } else {
255       nodeElem.setAttribute("state", "leaf");
256     }
257   }
258
259   void renderEvenOddAttr(Element JavaDoc nodeElem) {
260     if (nodeCounter % 2 == 0)
261       nodeElem.setAttribute("style", "even");
262     else
263       nodeElem.setAttribute("style", "odd");
264   }
265
266   class CollapseHandler implements RequestListener {
267     Object JavaDoc node;
268     public CollapseHandler(Object JavaDoc node) {
269       this.node = node;
270     }
271     public void request(RequestContext context) throws Exception JavaDoc {
272       Scroller.enableScroller(context);
273       expanded.remove(node);
274       validate(context);
275     }
276   }
277
278   class ExpandHandler implements RequestListener {
279     Object JavaDoc node;
280     public ExpandHandler(Object JavaDoc node) {
281       this.node = node;
282     }
283     public void request(RequestContext context) throws Exception JavaDoc {
284       Scroller.enableScroller(context);
285       expanded.add(node);
286       validate(context);
287     }
288   }
289
290   class UnboundHandler implements RequestListener {
291     Object JavaDoc node;
292     public UnboundHandler(Object JavaDoc node) {
293       this.node = node;
294     }
295     public void request(RequestContext context) throws Exception JavaDoc {
296       Scroller.enableScroller(context);
297       bounding.unbound(node);
298       validate(context);
299     }
300   }
301   
302   /**
303    * returns the current selection
304    */

305   public SelectionModel getSelectionModel() {
306     return selectionMgr.getSelectionModel();
307   }
308
309   /**
310    * changes the selection. Must call initialize() first.
311    */

312   public void setSelectionModel(SelectionModel selectionModel) {
313     selectionMgr.setSelectionModel(selectionModel);
314   }
315
316   /**
317    * collapses a node
318    */

319   public void collapse(Object JavaDoc node) {
320     expanded.remove(node);
321   }
322
323   /**
324    * expands a node
325    */

326   public void expand(Object JavaDoc node) {
327     expanded.add(node);
328   }
329
330   /**
331    * collapses all nodes
332    */

333   public void collapseAll() {
334     expanded.clear();
335   }
336
337   /**
338    * expands the parents of the selected nodes. So all selected nodes will be visible
339    * @deprecated use expandSelected(boolean) instead
340    */

341   public void expandSelected() {
342     expandSelected(false);
343   }
344
345   /**
346    * expands the parents of the selected nodes. So all selected nodes will be visible
347    * @param expandSelected true, if the selected nodes should be expanded too (in case they have children)
348    */

349   public void expandSelected(boolean expandSelected) {
350     expanded.clear();
351     for (Iterator JavaDoc it = selectionMgr.getSelectionModel().getSelection().iterator(); it.hasNext();) {
352       Object JavaDoc node = it.next();
353       if (expandSelected && model.hasChildren(node))
354         expanded.add(node);
355       node = model.getParent(node);
356       while (node != null) {
357         expanded.add(node);
358         node = model.getParent(node);
359       }
360     }
361   }
362
363   /**
364    * sets the selection to the currently visible nodes
365    */

366   public void selectVisible() {
367     SelectionModel sm = selectionMgr.getSelectionModel();
368     sm.clear();
369     Object JavaDoc[] roots = model.getRoots();
370     for (int i = 0; i < roots.length; i++)
371       recurseSelectVisible(sm, roots[i]);
372   }
373
374   private void recurseSelectVisible(SelectionModel sm, Object JavaDoc node) {
375     if (sm.isSelectable(node))
376       sm.add(node);
377     if (expanded.contains(node)) {
378       Object JavaDoc[] children = model.getChildren(node);
379       for (int i = 0; i < children.length; i++)
380         recurseSelectVisible(sm, children[i]);
381     }
382   }
383
384   /**
385    * Returns the model.
386    * @return TreeModel
387    */

388   public TreeModel getModel() {
389     return model;
390   }
391
392   /**
393    * Returns the nodeRenderer.
394    * @return NodeRenderer
395    */

396   public NodeRenderer getNodeRenderer() {
397     return nodeRenderer;
398   }
399
400   /**
401    * Returns the treeElementName.
402    * @return String
403    */

404   public String JavaDoc getTreeElementName() {
405     return treeElementName;
406   }
407
408   /**
409    * Sets the nodeRenderer. If NodeRenderer is a RequestListener it will
410    * be registered as a default listener (i.e. receives all requests).
411    * <p>
412    * If NodeRenderer is a RenderListener, it will be informed when rendering
413    * starts and stops (e.g. to clear RequestListeners that corresponds to buttons)
414    *
415    * @param nodeRenderer The nodeRenderer to set
416    * @see RequestListeningNodeRenderer
417    */

418   public void setNodeRenderer(NodeRenderer nodeRenderer) {
419     if (this.nodeRenderer instanceof RequestListener)
420       super.getDispatcher().removeRequestListener((RequestListener) this.nodeRenderer);
421     this.nodeRenderer = nodeRenderer;
422     if (this.nodeRenderer instanceof RequestListener)
423       super.getDispatcher().addRequestListener(null, null, (RequestListener) this.nodeRenderer);
424   }
425
426   /**
427    * Sets the treeElementName.
428    * @param treeElementName The treeElementName to set
429    */

430   public void setTreeElementName(String JavaDoc treeElementName) {
431     this.treeElementName = treeElementName;
432   }
433
434   /**
435    * Returns the changeOrderModel.
436    * @return ChangeOrderModel
437    */

438   public ChangeOrderModel getChangeOrderModel() {
439     return changeOrderMgr.getModel();
440   }
441
442   /**
443    * Sets the changeOrderModel.
444    * @param changeOrderModel The changeOrderModel to set
445    */

446   public void setChangeOrderModel(ChangeOrderModel changeOrderModel) {
447     changeOrderMgr.setModel(changeOrderModel);
448   }
449
450   /**
451    * sets the UI style for moving nodes (for TreeModels that implement ChangeOrderModel)
452    * @see com.tonbeller.wcf.changeorder.ChangeOrderModel
453    */

454   public void setCutPasteMode(boolean b) {
455     changeOrderMgr.setCutPasteMode(b);
456   }
457
458   /**
459    * gets the border attribute of the generated table.
460    * Overrides the global stylesheet parameter "border".
461    * @return the border attribute or null
462    */

463   public String JavaDoc getBorder() {
464     return border;
465   }
466
467   /**
468    * sets the border attribute of the generated table.
469    * Overrides the global stylesheet parameter "border".
470    * @param border the border attribute or null to use the stylesheet parameter
471    */

472   public void setBorder(String JavaDoc border) {
473     this.border = border;
474   }
475
476   /**
477    * sets the renderId attribute of the generated table.
478    * Overrides the global stylesheet parameter "renderId".
479    * @param renderId the renderId attribute or null to use the stylesheet parameter
480    */

481   public void setRenderId(String JavaDoc renderId) {
482     this.renderId = renderId;
483   }
484
485   /**
486    * gets the renderId attribute of the generated table.
487    * Overrides the global stylesheet parameter "renderId".
488    * @return the renderId attribute or null
489    */

490   public String JavaDoc getRenderId() {
491     return renderId;
492   }
493
494   public String JavaDoc getWidth() {
495     return width;
496   }
497
498   public void setWidth(String JavaDoc string) {
499     width = string;
500   }
501
502   public DeleteNodeModel getDeleteNodeModel() {
503     return deleteNodeModel;
504   }
505
506   public void setDeleteNodeModel(DeleteNodeModel model) {
507     deleteNodeModel = model;
508   }
509
510   public String JavaDoc getError() {
511     return error;
512   }
513
514   public void setError(String JavaDoc string) {
515     error = string;
516   }
517
518   public TreeBounding getBounding() {
519     return bounding;
520   }
521
522   public void setBounding(TreeBounding bounding) {
523     if (bounding == null)
524       bounding = NO_BOUNDING;
525     this.bounding = bounding;
526   }
527 }
528
Popular Tags