001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.base.data.util;
039:
040: import java.util.Collection;
041: import java.util.LinkedList;
042: import java.util.Collections;
043: import java.util.Hashtable;
044: import java.util.HashSet;
045: import org.millstone.base.data.Container;
046: import org.millstone.base.data.Item;
047:
048: /** A specialized Container whose contents can be accessed like it was a
049: * tree-like structure.
050: *
051: * @author IT Mill Ltd.
052: * @version 3.1.1
053: * @since 3.0
054: */
055: public class HierarchicalContainer extends IndexedContainer implements
056: Container.Hierarchical {
057:
058: /** Set of IDs of those contained Items that can't have children. */
059: private HashSet noChildrenAllowed = new HashSet();
060:
061: /** Mapping from Item ID to parent Item */
062: private Hashtable parent = new Hashtable();
063:
064: /** Mapping from Item ID to a list of child IDs */
065: private Hashtable children = new Hashtable();
066:
067: /** List that contains all root elements of the container. */
068: private LinkedList roots = new LinkedList();
069:
070: /* Can the specified Item have any children?
071: * Don't add a JavaDoc comment here, we use the default documentation
072: * from implemented interface.
073: */
074: public boolean areChildrenAllowed(Object itemId) {
075: return !noChildrenAllowed.contains(itemId);
076: }
077:
078: /* Get the IDs of the children of the specified Item.
079: * Don't add a JavaDoc comment here, we use the default documentation
080: * from implemented interface.
081: */
082: public Collection getChildren(Object itemId) {
083: Collection c = (Collection) children.get(itemId);
084: if (c == null)
085: return null;
086: return Collections.unmodifiableCollection(c);
087: }
088:
089: /* Get the ID of the parent of the specified Item.
090: * Don't add a JavaDoc comment here, we use the default documentation
091: * from implemented interface.
092: */
093: public Object getParent(Object itemId) {
094: return parent.get(itemId);
095: }
096:
097: /* Is the Item corresponding to the given ID a leaf node?
098: * Don't add a JavaDoc comment here, we use the default documentation
099: * from implemented interface.
100: */
101: public boolean hasChildren(Object itemId) {
102: return children.get(itemId) != null;
103: }
104:
105: /* Is the Item corresponding to the given ID a root node?
106: * Don't add a JavaDoc comment here, we use the default documentation
107: * from implemented interface.
108: */
109: public boolean isRoot(Object itemId) {
110: return parent.get(itemId) == null;
111: }
112:
113: /* Get the IDs of the root elements in the container.
114: * Don't add a JavaDoc comment here, we use the default documentation
115: * from implemented interface.
116: */
117: public Collection rootItemIds() {
118: return Collections.unmodifiableCollection(roots);
119: }
120:
121: /** <p>Sets the given Item's capability to have children. If the Item
122: * identified with <code>itemId</code> already has children and
123: * <code>areChildrenAllowed</code> is false this method fails and
124: * <code>false</code> is returned; the children must be first explicitly
125: * removed with {@link #setParent(Object itemId, Object newParentId)} or
126: * {@link org.millstone.base.data.Container#removeItem(Object itemId)}.</p>
127: *
128: * @param itemId ID of the Item in the container whose child
129: * capability is to be set
130: * @param childrenAllowed boolean value specifying if the Item
131: * can have children or not
132: * @return <code>true</code> if the operation succeeded,
133: * <code>false</code> if not
134: */
135: public boolean setChildrenAllowed(Object itemId,
136: boolean childrenAllowed) {
137:
138: // Check that the item is in the container
139: if (!containsId(itemId))
140: return false;
141:
142: // Update status
143: if (childrenAllowed)
144: noChildrenAllowed.remove(itemId);
145: else
146: noChildrenAllowed.add(itemId);
147:
148: return true;
149: }
150:
151: /** <p>Sets the parent of an Item. The new parent item must exist and be
152: * able to have children.
153: * (<code>canHaveChildren(newParentId) == true</code>). It is also
154: * possible to detach a node from the hierarchy (and thus make it root)
155: * by setting the parent <code>null</code>.</p>
156: *
157: * @param itemId ID of the item to be set as the child of the Item
158: * identified with <code>newParentId</code>
159: * @param newParentId ID of the Item that's to be the new parent
160: * of the Item identified with <code>itemId</code>
161: * @return <code>true</code> if the operation succeeded,
162: * <code>false</code> if not
163: */
164: public boolean setParent(Object itemId, Object newParentId) {
165:
166: // Check that the item is in the container
167: if (!containsId(itemId))
168: return false;
169:
170: // Get the old parent
171: Object oldParentId = parent.get(itemId);
172:
173: // Check if no change is necessary
174: if ((newParentId == null && oldParentId == null)
175: || newParentId.equals(oldParentId))
176: return true;
177:
178: // Making root
179: if (newParentId == null) {
180:
181: // Remove from old parents children list
182: LinkedList l = (LinkedList) children.get(itemId);
183: if (l != null) {
184: l.remove(itemId);
185: if (l.isEmpty())
186: children.remove(itemId);
187: }
188:
189: // Add to be a root
190: roots.add(itemId);
191:
192: // Update parent
193: parent.remove(itemId);
194:
195: return true;
196: }
197:
198: // Check that the new parent exists in container and can have
199: // children
200: if (!containsId(newParentId)
201: || noChildrenAllowed.contains(newParentId))
202: return false;
203:
204: // Check that setting parent doesn't result to a loop
205: Object o = newParentId;
206: while (o != null && !o.equals(itemId))
207: o = parent.get(o);
208: if (o != null)
209: return false;
210:
211: // Update parent
212: parent.put(itemId, newParentId);
213: LinkedList pcl = (LinkedList) children.get(newParentId);
214: if (pcl == null) {
215: pcl = new LinkedList();
216: children.put(newParentId, pcl);
217: }
218: pcl.add(itemId);
219:
220: // Remove from old parent or root
221: if (oldParentId == null)
222: roots.remove(itemId);
223: else {
224: LinkedList l = (LinkedList) children.get(oldParentId);
225: if (l != null) {
226: l.remove(itemId);
227: if (l.isEmpty())
228: children.remove(oldParentId);
229: }
230: }
231:
232: return true;
233: }
234:
235: /**
236: * @see org.millstone.base.data.Container#addItem()
237: */
238: public Object addItem() {
239: Object id = super .addItem();
240: if (id != null && !roots.contains(id))
241: roots.add(id);
242: return id;
243:
244: }
245:
246: /**
247: * @see org.millstone.base.data.Container#addItem(Object)
248: */
249: public Item addItem(Object itemId) {
250: Item item = super .addItem(itemId);
251: if (item != null)
252: roots.add(itemId);
253: return item;
254: }
255:
256: /**
257: * @see org.millstone.base.data.Container#removeAllItems()
258: */
259: public boolean removeAllItems() {
260: boolean success = super .removeAllItems();
261:
262: if (success) {
263: roots.clear();
264: parent.clear();
265: children.clear();
266: noChildrenAllowed.clear();
267: }
268: return success;
269: }
270:
271: /**
272: * @see org.millstone.base.data.Container#removeItem(Object)
273: */
274: public boolean removeItem(Object itemId) {
275: boolean success = super .removeItem(itemId);
276:
277: if (success) {
278: if (isRoot(itemId))
279: roots.remove(itemId);
280: children.remove(itemId);
281: Object p = parent.get(itemId);
282: if (p != null) {
283: LinkedList c = (LinkedList) children.get(p);
284: if (c != null)
285: c.remove(itemId);
286: }
287: parent.remove(itemId);
288: noChildrenAllowed.remove(itemId);
289: }
290:
291: return success;
292: }
293:
294: }
|