KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > formmodel > tree > Tree


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.forms.formmodel.tree;
17
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.Locale JavaDoc;
22 import java.util.Set JavaDoc;
23
24 import org.apache.cocoon.environment.Request;
25 import org.apache.cocoon.forms.FormContext;
26 import org.apache.cocoon.forms.event.WidgetEventMulticaster;
27 import org.apache.cocoon.forms.formmodel.AbstractWidget;
28 import org.apache.cocoon.forms.formmodel.Widget;
29 import org.apache.cocoon.forms.formmodel.WidgetDefinition;
30 import org.xml.sax.ContentHandler JavaDoc;
31 import org.xml.sax.SAXException JavaDoc;
32
33 /**
34  * A tree widget, heavily inspired by Swing's <code>JTree</code>.
35  *
36  * @version $Id: Tree.java 190962 2005-06-16 17:17:00Z sylvain $
37  */

38 public class Tree extends AbstractWidget {
39     
40     public static final int SINGLE_SELECTION = 0;
41     public static final int MULTIPLE_SELECTION = 1;
42     
43     public interface ActionHandler {
44         public void act(Tree tree, FormContext context);
45     }
46     
47     private TreeDefinition treeDef;
48
49     private TreeModel treeModel;
50     
51     private Set JavaDoc expandedPaths = new HashSet JavaDoc();
52     
53     private Set JavaDoc selectedPaths = new HashSet JavaDoc();
54     
55     private Set JavaDoc changedPaths = new HashSet JavaDoc();
56     
57     private HashMap JavaDoc pathWidgets = new HashMap JavaDoc();
58     
59     private boolean rootVisible = true;
60
61     private boolean expandSelectedPath = false;
62     
63     private TreeSelectionListener selectionListener;
64     
65     private int selectionModel = MULTIPLE_SELECTION;
66     
67     private TreeModelListener modelListener = new TreeModelListener() {
68         public void treeStructureChanged(TreeModelEvent event) {
69             markForRefresh(event.getPath());
70         }
71     };
72
73     protected Tree(TreeDefinition definition) {
74         super(definition);
75         this.treeDef = definition;
76         this.rootVisible = definition.isRootVisible();
77         if (!this.rootVisible) {
78             // Expand it so that first-level children are visible
79
this.expandedPaths.add(TreePath.ROOT_PATH);
80         }
81         this.treeModel = definition.createModel();
82         this.treeModel.addTreeModelListener(modelListener);
83         this.selectionListener = definition.getSelectionListener();
84         this.selectionModel = definition.getSelectionModel();
85     }
86
87     public WidgetDefinition getDefinition() {
88         return this.treeDef;
89     }
90
91     protected String JavaDoc getXMLElementName() {
92         return "tree";
93     }
94
95     protected void generateItemSaxFragment(ContentHandler JavaDoc contentHandler, Locale JavaDoc locale) throws SAXException JavaDoc {
96             throw new UnsupportedOperationException JavaDoc(this + " cannot be rendered using <ft:widget>. Please use <ft:tree>.");
97     }
98
99     public void readFromRequest(FormContext formContext) {
100         //TODO: crawl open nodes, calling their widget's readFromRequest
101

102         Request req = formContext.getRequest();
103         String JavaDoc paramName = getRequestParameterName();
104         
105         //---------------------------------------------------------------------
106
// Handle node selection using checkboxes named <id>$select
107
//---------------------------------------------------------------------
108
String JavaDoc[] selectValues = req.getParameterValues(paramName + ":select");
109         if (selectValues != null) {
110             // Create the set of paths given by the request
111
Set JavaDoc newSelection = new HashSet JavaDoc();
112             for (int i = 0; i < selectValues.length; i++) {
113                 newSelection.add(TreePath.valueOf(selectValues[i]));
114             }
115             
116             // Check if all visible selections are in the new selection
117
TreePath[] currentSelection = (TreePath[])this.selectedPaths.toArray(new TreePath[this.selectedPaths.size()]);
118             for (int i = 0; i < currentSelection.length; i++) {
119                 TreePath p = currentSelection[i];
120                 if (!newSelection.contains(p) && isVisible(p)) {
121                     removeSelectionPath(p);
122                 }
123             }
124
125             // Now add the currently selected items (may be selected already)
126
Iterator JavaDoc iter = newSelection.iterator();
127             while(iter.hasNext()) {
128                 addSelectionPath((TreePath)iter.next());
129             }
130         }
131         
132         //---------------------------------------------------------------------
133
// Handle tree actions:
134
// - action is in <name>$action
135
// - path is in <name>$path
136
//---------------------------------------------------------------------
137
String JavaDoc action = req.getParameter(paramName + ":action");
138         
139         if (action == null || action.length() == 0) {
140             // Nothing more to do here
141
return;
142         }
143         
144         getForm().setSubmitWidget(this);
145         String JavaDoc pathValue = req.getParameter(paramName + ":path");
146         
147         if (pathValue == null || pathValue.length() == 0) {
148             //this.treeDef.getLogger().warn("No tree path given");
149
return;
150         }
151         
152         // Parse the path
153
TreePath path = TreePath.valueOf(pathValue);
154         
155         if ("expand".equals(action)) {
156             this.expandPath(path);
157         } else if ("collapse".equals(action)) {
158             this.collapsePath(path);
159         } else if ("toggle-collapse".equals(action)) {
160             if (this.isExpanded(path)) {
161                 this.collapsePath(path);
162             } else {
163                 this.expandPath(path);
164             }
165         } else if ("select".equals(action)) {
166             this.addSelectionPath(path);
167         } else if ("unselect".equals(action)) {
168             this.removeSelectionPath(path);
169         } else if ("toggle-select".equals(action)) {
170             if (this.isPathSelected(path)) {
171                 this.removeSelectionPath(path);
172             } else {
173                 this.addSelectionPath(path);
174             }
175         } else {
176             // Unknown action
177
//this.treeDef.getLogger().warn("Unknown action " + action);
178
}
179     }
180
181     public TreeModel getModel() {
182         return this.treeModel;
183     }
184     
185     public void setModel(TreeModel model) {
186         if (model == null) {
187             model = DefaultTreeModel.UNSPECIFIED_MODEL;
188         }
189         this.treeModel.removeTreeModelListener(this.modelListener);
190         this.treeModel = model;
191         model.addTreeModelListener(this.modelListener);
192     }
193     
194     private void markForRefresh(TreePath path) {
195         this.changedPaths.add(path);
196         getForm().addWidgetUpdate(this);
197     }
198     
199     //---------------------------------------------------------------------------------------------
200
// Selection
201
//---------------------------------------------------------------------------------------------
202

203     public void setSelectionModel(int model) {
204         if (model < 0 || model > MULTIPLE_SELECTION) {
205             throw new IllegalArgumentException JavaDoc("Illegal selection model " + model);
206         }
207         
208         if (model == this.selectionModel) {
209             return;
210         }
211         
212         this.selectionModel = model;
213         if (model == SINGLE_SELECTION && getSelectionCount() > 1) {
214             clearSelection();
215         }
216     }
217     
218     public int getSelectionCount() {
219         return this.selectedPaths.size();
220     }
221
222     public TreePath getSelectionPath() {
223         if (this.selectedPaths.isEmpty()) {
224             return null;
225         } else {
226             return (TreePath)this.selectedPaths.iterator().next();
227         }
228     }
229     
230     public TreePath[] getSelectionPaths() {
231         return (TreePath[])this.selectedPaths.toArray(new TreePath[this.selectedPaths.size()]);
232     }
233     
234     public boolean isPathSelected(TreePath path) {
235         return this.selectedPaths.contains(path);
236     }
237     
238     public boolean isSelectionEmpty() {
239         return this.selectedPaths.isEmpty();
240     }
241     
242     public void setSelectionPath(TreePath path) {
243         clearSelection();
244         addSelectionPath(path);
245     }
246     
247     public void setSelectionPaths(TreePath paths[]) {
248         clearSelection();
249         addSelectionPaths(paths);
250     }
251     
252     public void addSelectionPath(TreePath path) {
253         if (this.selectionModel == SINGLE_SELECTION) {
254             clearSelection();
255         }
256
257         if (this.selectedPaths.add(path)) {
258             markForRefresh(path);
259             if (this.expandSelectedPath) {
260                 expandPath(path);
261             }
262             if (this.selectionListener != null) {
263                 this.selectionListener.selectionChanged(new TreeSelectionEvent(this, path, true));
264             }
265         }
266     }
267     
268     public void addSelectionPaths(TreePath paths[]) {
269         if (this.selectionModel == SINGLE_SELECTION) {
270             setSelectionPath(paths[0]);
271         } else {
272             for (int i = 0; i < paths.length; i++) {
273                 addSelectionPath(paths[i]);
274                 // FIXME: use array-based constructors of TreeSelectionEvent
275
}
276         }
277     }
278
279     public void removeSelectionPath(TreePath path) {
280         if (this.selectedPaths.remove(path)) {
281             // Need to redisplay the parent
282
markForRefresh(path.getParentPath());
283             if (this.selectionListener != null) {
284                 this.selectionListener.selectionChanged(new TreeSelectionEvent(this, path, false));
285             }
286         }
287     }
288     
289     public void removeSelectionPaths(TreePath paths[]) {
290         for (int i = 0; i < paths.length; i++) {
291             removeSelectionPath(paths[i]);
292             // FIXME: use array-based constructors of TreeSelectionEvent
293
}
294     }
295     
296     public void clearSelection() {
297         if (this.isSelectionEmpty()) {
298             return;
299         }
300
301         TreePath[] paths = (TreePath[])this.selectedPaths.toArray(new TreePath[this.selectedPaths.size()]);
302         for (int i = 0; i < paths.length; i++) {
303             // Need to redisplay the parent
304
markForRefresh(paths[i].getParentPath());
305         }
306         this.selectedPaths.clear();
307         if (this.selectionListener != null) {
308             this.selectionListener.selectionChanged(new TreeSelectionEvent(this, paths, false));
309         }
310     }
311     
312     public void addTreeSelectionListener(TreeSelectionListener listener) {
313         this.selectionListener = WidgetEventMulticaster.add(this.selectionListener, listener);
314     }
315     
316     public void removeTreeSelectionListener(TreeSelectionListener listener) {
317         this.selectionListener = WidgetEventMulticaster.remove(this.selectionListener, listener);
318     }
319     
320     //---------------------------------------------------------------------------------------------
321
// Visibility, expand & collapse
322
//---------------------------------------------------------------------------------------------
323

324     public boolean isCollapsed(TreePath path) {
325         return !isExpanded(path);
326     }
327     
328     public boolean isExpanded(TreePath path) {
329         if (this.expandedPaths.contains(path)) {
330             // Ensure all parents are expanded
331
TreePath parent = path.getParentPath();
332             return parent == null ? true : isExpanded(parent);
333         } else {
334             return false;
335         }
336     }
337     
338     /**
339      * Returns true if the value identified by path is currently viewable,
340      * which means it is either the root or all of its parents are expanded.
341      * Otherwise, this method returns false.
342      *
343      * @return true if the node is viewable, otherwise false
344      */

345     public boolean isVisible(TreePath path) {
346         if (path == TreePath.ROOT_PATH) {
347             return true;
348         }
349         if (path != null) {
350             TreePath parent = path.getParentPath();
351             if (parent != null) {
352                 return isExpanded(parent);
353             } else {
354                 // root node
355
return true;
356             }
357         } else {
358             return false;
359         }
360     }
361     
362     public void makeVisible(TreePath path) {
363         if (path != null) {
364             TreePath parent = path.getParentPath();
365             if (parent != null) {
366                 expandPath(parent);
367             }
368         }
369     }
370     
371     public boolean isRootVisible() {
372         return this.rootVisible;
373     }
374     
375     public void setRootVisible(boolean visible) {
376         if (this.rootVisible != visible) {
377             this.markForRefresh(TreePath.ROOT_PATH);
378             this.rootVisible = visible;
379             if (!visible) {
380                 // Expand it so that first-level children are visible
381
this.expandPath(TreePath.ROOT_PATH);
382             }
383         }
384     }
385     
386     public void collapsePath(TreePath path) {
387         if (path != null) {
388             if (this.expandedPaths.remove(path)) {
389                 markForRefresh(path);
390             }
391         }
392     }
393
394     public void expandPath(TreePath path) {
395         if (path != null) {
396             if (this.expandedPaths.add(path)) {
397                 markForRefresh(path);
398             }
399         }
400     }
401     
402     public void setExpandsSelectedPath(boolean value) {
403         this.expandSelectedPath = value;
404     }
405     
406     //---------------------------------------------------------------------------------------------
407
// Widget management
408
//---------------------------------------------------------------------------------------------
409

410     public Widget getWidgetForPath(TreePath path) {
411         Widget result = (Widget)this.pathWidgets.get(path);
412         if (result == null && !this.pathWidgets.containsKey(path)) {
413             result = createWidgetForPath(path);
414             if (result != null) {
415                 result.setAttribute("TreePath", path);
416             }
417             this.pathWidgets.put(path, result);
418         }
419         
420         return result;
421     }
422     
423     private Widget createWidgetForPath(TreePath path) {
424         //TODO
425
return null;
426     }
427     
428     //---------------------------------------------------------------------------------------------
429
// TreeNode widget, which is the actual parent of widgets contained in a node
430
//---------------------------------------------------------------------------------------------
431
// TODO
432
}
433
Popular Tags