001: /*
002: * $Header$
003: * $Revision: 3530 $
004: * $Date: 2003-10-16 12:17:28 +0000 (Thu, 16 Oct 2003) $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 2001 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: */
061:
062: package org.objectweb.jonas.webapp.taglib;
063:
064: import java.io.Serializable;
065: import java.util.HashMap;
066:
067: /**
068: * <p>The overall data structure representing a <em>tree control</em>
069: * that can be rendered by the <code>TreeControlTag</code> custom tag.
070: * Each node of the tree is represented by an instance of
071: * <code>TreeControlNode</code>.</p>
072: *
073: * @author Jazmin Jonson
074: * @author Craig R. McClanahan
075: * @version $Revision: 1.4
076: */
077:
078: public class TreeControl implements Serializable {
079: public static final String ID_PREFIX = "treenode";
080:
081: // ----------------------------------------------------------- Constructors
082:
083: /**
084: * Construct a new instance with no predefined root node.
085: */
086: public TreeControl() {
087: super ();
088: setRoot(null);
089: }
090:
091: /**
092: * Construct a new instance with the specified root node.
093: *
094: * @param root The new root node
095: */
096: public TreeControl(TreeControlNode root) {
097: super ();
098: setRoot(root);
099: }
100:
101: // ----------------------------------------------------- Instance Variables
102:
103: /**
104: * The collection of nodes that represent this tree, keyed by name.
105: */
106: protected HashMap registry = new HashMap();
107:
108: /**
109: * The most recently selected node.
110: */
111: protected TreeControlNode selected = null;
112:
113: // Id
114: protected int mi_Id = 0;
115:
116: // ------------------------------------------------------------- Properties
117:
118: /**
119: * The root node of the entire tree.
120: */
121: protected TreeControlNode root = null;
122:
123: public TreeControlNode getRoot() {
124: return (this .root);
125: }
126:
127: protected void setRoot(TreeControlNode root) {
128: if (this .root != null) {
129: removeNode(this .root);
130: }
131: if (root != null) {
132: addNode(root);
133: }
134: root.setLast(true);
135: this .root = root;
136: }
137:
138: /**
139: * The current displayable "width" of this tree (that is, the maximum
140: * depth of the visible part of the tree).
141: */
142: public int getWidth() {
143: if (root == null) {
144: return (0);
145: } else {
146: return (getWidth(root));
147: }
148: }
149:
150: // --------------------------------------------------------- Public Methods
151:
152: /**
153: * Find and return the <code>TreeControlNode</code> for the specified
154: * node name, if it exists; otherwise, return <code>null</code>.
155: *
156: * @param name Name of the <code>TreeControlNode</code> to be returned
157: */
158: public TreeControlNode findNode(String name) {
159: synchronized (registry) {
160: return ((TreeControlNode) registry.get(name));
161: }
162: }
163:
164: /**
165: * Mark the specified node as the one-and-only currently selected one,
166: * deselecting any previous node that was so marked.
167: *
168: * @param node Name of the node to mark as selected, or <code>null</code>
169: * if there should be no currently selected node
170: */
171: public void selectNode(String name) {
172: if (selected != null) {
173: selected.setSelected(false);
174: selected = null;
175: }
176: selected = findNode(name);
177: if (selected != null) {
178: selected.setSelected(true);
179: }
180: }
181:
182: /**
183: * Get the last node selected.
184: *
185: * @return the current node selected
186: */
187: public TreeControlNode getSelected() {
188: return selected;
189: }
190:
191: /**
192: * Expand a branch in the tree of the selected node.
193: */
194: public void expandSelectedParents() {
195: TreeControlNode oCur = getSelected();
196: while (oCur != null) {
197: oCur = oCur.getParent();
198: if (oCur != null) {
199: oCur.setExpanded(true);
200: }
201: }
202: }
203:
204: public String newId() {
205: StringBuffer sbRet = new StringBuffer(ID_PREFIX);
206: sbRet.append(mi_Id);
207: mi_Id++;
208: return sbRet.toString();
209: }
210:
211: // -------------------------------------------------------- Package Methods
212:
213: /**
214: * Register the specified node in our registry of the complete tree.
215: *
216: * @param node The <code>TreeControlNode</code> to be registered
217: *
218: * @exception IllegalArgumentException if the name of this node
219: * is not unique
220: */
221: void addNode(TreeControlNode node) throws IllegalArgumentException {
222: synchronized (registry) {
223: String name = node.getName();
224: if (registry.containsKey(name)) {
225: throw new IllegalArgumentException("Name '" + name
226: + "' is not unique");
227: }
228: node.setTree(this );
229: registry.put(name, node);
230: // Refresh expand info
231: autoRefresh(node);
232: }
233: }
234:
235: /**
236: * Calculate the width of the subtree below the specified node.
237: *
238: * @param node The node for which to calculate the width
239: */
240: int getWidth(TreeControlNode node) {
241: int width = node.getWidth();
242: if (!node.isExpanded()) {
243: return (width);
244: }
245: TreeControlNode children[] = node.findChildren();
246: for (int i = 0; i < children.length; i++) {
247: int current = getWidth(children[i]);
248: if (current > width) {
249: width = current;
250: }
251: }
252: return (width);
253: }
254:
255: /**
256: * Deregister the specified node, as well as all child nodes of this
257: * node, from our registry of the complete tree. If this node is not
258: * present, no action is taken.
259: *
260: * @param node The <code>TreeControlNode</code> to be deregistered
261: */
262: void removeNode(TreeControlNode node) {
263: synchronized (registry) {
264: TreeControlNode children[] = node.findChildren();
265: for (int i = 0; i < children.length; i++) {
266: removeNode(children[i]);
267: }
268: TreeControlNode parent = node.getParent();
269: if (parent != null) {
270: parent.removeChild(node);
271: }
272: node.setParent(null);
273: node.setTree(null);
274: if (node == this .root) {
275: this .root = null;
276: }
277: registry.remove(node.getName());
278: // Save removed node in list
279: addRemovedList(node);
280: }
281: }
282:
283: /**
284: * List to save removed node used by the auto-refresh mode.
285: *
286: */
287: private HashMap m_RemovedList = null;
288:
289: /**
290: * Disable auto-refresh mode.
291: */
292: public void disableAutoRefresh() {
293: if (m_RemovedList != null) {
294: m_RemovedList.clear();
295: }
296: m_RemovedList = null;
297: }
298:
299: /**
300: * Enable auto-refresh mode.
301: * When a set of children are refreshed (removed then added),
302: * the expanded info is copied of the removed node to the added node.
303: * The name is used to retreive the good node.
304: * By default, the auto-refresh mode is disabled.
305: * Be careful, enabled this mode before the remove of all nodes and
306: * disabled it after the add.
307: */
308: public void enableAutoRefresh() {
309: m_RemovedList = new HashMap();
310: }
311:
312: /**
313: * Add the removed node in the removed list.
314: *
315: * @param p_RemovedNode The removed node
316: */
317: void addRemovedList(TreeControlNode p_RemovedNode) {
318: if (m_RemovedList != null) {
319: m_RemovedList.put(p_RemovedNode.getName(), p_RemovedNode);
320: }
321: }
322:
323: /**
324: * Search the added node in the removed list and if it's found, copy the expanded info.
325: *
326: * @param p_AddedNode The added node
327: */
328: protected void autoRefresh(TreeControlNode p_AddedNode) {
329: if (m_RemovedList != null) {
330: TreeControlNode oRemove = (TreeControlNode) m_RemovedList
331: .get(p_AddedNode.getName());
332: if (oRemove != null) {
333: p_AddedNode.setExpanded(oRemove.isExpanded());
334: }
335: }
336: }
337: }
|