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.Iterator;
042: import java.util.LinkedList;
043: import java.util.Hashtable;
044: import org.millstone.base.data.Container;
045: import org.millstone.base.data.Item;
046: import org.millstone.base.data.Property;
047:
048: /** <p>A wrapper class for adding external ordering to containers not
049: * implementing the {@link org.millstone.base.data.Container.Ordered}
050: * interface.</p>
051: *
052: * <p>If the wrapped container is changed directly (that is, not through
053: * the wrapper), the ordering must be updated with the
054: * {@link #updateOrderWrapper()} method.</p>
055: *
056: * @author IT Mill Ltd.
057: * @version 3.1.1
058: * @since 3.0
059: */
060: public class ContainerOrderedWrapper implements Container.Ordered,
061: Container.ItemSetChangeNotifier,
062: Container.PropertySetChangeNotifier {
063:
064: /** The wrapped container */
065: private Container container;
066:
067: /** Ordering information, ie. the mapping from Item ID to the next
068: * item ID
069: */
070: private Hashtable next;
071:
072: /** Reverse ordering information for convenience and performance
073: * reasons.
074: */
075: private Hashtable prev;
076:
077: /** ID of the first Item in the container. */
078: private Object first;
079:
080: /** ID of the last Item in the container. */
081: private Object last;
082:
083: /** Is the wrapped container ordered by itself, ie. does it implement
084: * the Container.Ordered interface by itself? If it does, this class
085: * will use the methods of the underlying container directly.
086: */
087: private boolean ordered = false;
088:
089: /** Constructs a new ordered wrapper for an existing Container. Works
090: * even if the to-be-wrapped container already implements the
091: * Container.Ordered interface.
092: *
093: * @param toBeWrapped the container whose contents need to be ordered
094: */
095: public ContainerOrderedWrapper(Container toBeWrapped) {
096:
097: container = toBeWrapped;
098: ordered = container instanceof Container.Ordered;
099:
100: // Check arguments
101: if (container == null)
102: throw new NullPointerException("Null can not be wrapped");
103:
104: // Create initial order if needed
105: updateOrderWrapper();
106: }
107:
108: /** Removes the specified Item from the wrapper's internal hierarchy
109: * structure. Note that the Item is not removed from the underlying
110: * Container.
111: *
112: * @param id ID of the Item to be removed from the ordering
113: */
114: private void removeFromOrderWrapper(Object id) {
115: if (id != null) {
116: Object pid = prev.get(id);
117: Object nid = next.get(id);
118: if (first.equals(id))
119: first = nid;
120: if (last.equals(id))
121: first = pid;
122: if (nid != null)
123: prev.put(nid, pid);
124: if (pid != null)
125: next.put(pid, nid);
126: next.remove(id);
127: prev.remove(id);
128: }
129: }
130:
131: /** Adds the specified Item to the last position in the wrapper's
132: * internal ordering. The underlying container is not modified.
133: *
134: * @param id ID of the Item to be added to the ordering
135: */
136: private void addToOrderWrapper(Object id) {
137:
138: // Add the if to tail
139: if (last != null) {
140: next.put(last, id);
141: prev.put(id, last);
142: last = id;
143: } else {
144: first = last = id;
145: }
146: }
147:
148: /** Adds the specified Item after the specified itemId in the wrapper's
149: * internal ordering. The underlying container is not modified.
150: * Given item id must be in the container, or must be null.
151: *
152: * @param id ID of the Item to be added to the ordering
153: */
154: private void addToOrderWrapper(Object id, Object previousItemId) {
155:
156: if (last == previousItemId || last == null)
157: addToOrderWrapper(id);
158: else {
159: if (previousItemId == null) {
160: next.put(id, first);
161: prev.put(first, id);
162: first = id;
163: } else {
164: prev.put(id, previousItemId);
165: next.put(id, next.get(previousItemId));
166: prev.put(next.get(previousItemId), id);
167: next.put(previousItemId, id);
168: }
169: }
170: }
171:
172: /** Updates the wrapper's internal ordering information to include all
173: * Items in the underlying container. If the contents of the wrapped
174: * container change without the wrapper's knowledge, this method needs
175: * to be called to update the ordering information of the Items.
176: */
177: public void updateOrderWrapper() {
178:
179: if (!ordered) {
180:
181: Collection ids = container.getItemIds();
182:
183: // Recreate ordering if some parts of it are missing
184: if (next == null || first == null || last == null
185: || prev != null) {
186: first = null;
187: last = null;
188: next = new Hashtable();
189: prev = new Hashtable();
190: }
191:
192: // Filter out all the missing items
193: LinkedList l = new LinkedList(next.keySet());
194: for (Iterator i = l.iterator(); i.hasNext();) {
195: Object id = i.next();
196: if (!container.containsId(id))
197: removeFromOrderWrapper(id);
198: }
199:
200: // Add missing items
201: for (Iterator i = ids.iterator(); i.hasNext();) {
202: Object id = i.next();
203: if (!next.containsKey(id))
204: addToOrderWrapper(id);
205: }
206: }
207: }
208:
209: /* Gets the first item stored in the ordered container
210: * Don't add a JavaDoc comment here, we use the default documentation
211: * from implemented interface.
212: */
213: public Object firstItemId() {
214: if (ordered)
215: return ((Container.Ordered) container).firstItemId();
216: return first;
217: }
218:
219: /* Test if the given item is the first item in the container
220: * Don't add a JavaDoc comment here, we use the default documentation
221: * from implemented interface.
222: */
223: public boolean isFirstId(Object itemId) {
224: if (ordered)
225: return ((Container.Ordered) container).isFirstId(itemId);
226: return first != null && first.equals(itemId);
227: }
228:
229: /* Test if the given item is the last item in the container
230: * Don't add a JavaDoc comment here, we use the default documentation
231: * from implemented interface.
232: */
233: public boolean isLastId(Object itemId) {
234: if (ordered)
235: return ((Container.Ordered) container).isLastId(itemId);
236: return last != null && last.equals(itemId);
237: }
238:
239: /* Gets the last item stored in the ordered container
240: * Don't add a JavaDoc comment here, we use the default documentation
241: * from implemented interface.
242: */
243: public Object lastItemId() {
244: if (ordered)
245: return ((Container.Ordered) container).lastItemId();
246: return last;
247: }
248:
249: /* Get the item that is next from the specified item.
250: * Don't add a JavaDoc comment here, we use the default documentation
251: * from implemented interface.
252: */
253: public Object nextItemId(Object itemId) {
254: if (ordered)
255: return ((Container.Ordered) container).nextItemId(itemId);
256: return next.get(itemId);
257: }
258:
259: /* Get the item that is previous from the specified item.
260: * Don't add a JavaDoc comment here, we use the default documentation
261: * from implemented interface.
262: */
263: public Object prevItemId(Object itemId) {
264: if (ordered)
265: return ((Container.Ordered) container).prevItemId(itemId);
266: return prev.get(itemId);
267: }
268:
269: /** Adds a new Property to all Items in the Container.
270: *
271: * @param propertyId ID of the new Property
272: * @param type Data type of the new Property
273: * @param defaultValue The value all created Properties are
274: * initialized to
275: * @return <code>true</code> if the operation succeeded,
276: * <code>false</code> if not
277: */
278: public boolean addContainerProperty(Object propertyId, Class type,
279: Object defaultValue) throws UnsupportedOperationException {
280:
281: return container.addContainerProperty(propertyId, type,
282: defaultValue);
283: }
284:
285: /** Creates a new Item into the Container, assigns it an
286: * automatic ID, and adds it to the ordering.
287: *
288: * @return the autogenerated ID of the new Item or <code>null</code>
289: * if the operation failed
290: */
291: public Object addItem() throws UnsupportedOperationException {
292:
293: Object id = container.addItem();
294: if (id != null)
295: addToOrderWrapper(id);
296: return id;
297: }
298:
299: /** Adds a new Item by its ID to the underlying container and to the
300: * ordering.
301: *
302: * @return the added Item or <code>null</code> if the operation failed
303: */
304: public Item addItem(Object itemId)
305: throws UnsupportedOperationException {
306: Item item = container.addItem(itemId);
307: if (item != null)
308: addToOrderWrapper(itemId);
309: return item;
310: }
311:
312: /** Removes all items from the underlying container and from the
313: * ordering.
314: *
315: * @return <code>true</code> if the operation succeeded,
316: * <code>false</code> if not
317: */
318: public boolean removeAllItems()
319: throws UnsupportedOperationException {
320: boolean success = container.removeAllItems();
321: if (success) {
322: first = last = null;
323: next.clear();
324: prev.clear();
325: }
326: return success;
327: }
328:
329: /** Removes an Item specified by <code>itemId</code> from the underlying
330: * container and from the ordering.
331: *
332: * @return <code>true</code> if the operation succeeded,
333: * <code>false</code> if not
334: */
335: public boolean removeItem(Object itemId)
336: throws UnsupportedOperationException {
337:
338: boolean success = container.removeItem(itemId);
339: if (success)
340: removeFromOrderWrapper(itemId);
341: return success;
342: }
343:
344: /** Removes the specified Property from the underlying container and
345: * from the ordering. Note that the Property will be removed from all
346: * Items in the Container.
347: *
348: * @param propertyId ID of the Property to remove
349: * @return <code>true</code> if the operation succeeded,
350: * <code>false</code> if not
351: */
352: public boolean removeContainerProperty(Object propertyId)
353: throws UnsupportedOperationException {
354: return container.removeContainerProperty(propertyId);
355: }
356:
357: /* Does the container contain the specified Item?
358: * Don't add a JavaDoc comment here, we use the default documentation
359: * from implemented interface.
360: */
361: public boolean containsId(Object itemId) {
362: return container.containsId(itemId);
363: }
364:
365: /* Gets the specified Item from the container.
366: * Don't add a JavaDoc comment here, we use the default documentation
367: * from implemented interface.
368: */
369: public Item getItem(Object itemId) {
370: return container.getItem(itemId);
371: }
372:
373: /* Gets the ID's of all Items stored in the Container
374: * Don't add a JavaDoc comment here, we use the default documentation
375: * from implemented interface.
376: */
377: public Collection getItemIds() {
378: return container.getItemIds();
379: }
380:
381: /* Gets the Property identified by the given itemId and propertyId from
382: * the Container
383: * Don't add a JavaDoc comment here, we use the default documentation
384: * from implemented interface.
385: */
386: public Property getContainerProperty(Object itemId,
387: Object propertyId) {
388: return container.getContainerProperty(itemId, propertyId);
389: }
390:
391: /* Gets the ID's of all Properties stored in the Container
392: * Don't add a JavaDoc comment here, we use the default documentation
393: * from implemented interface.
394: */
395: public Collection getContainerPropertyIds() {
396: return container.getContainerPropertyIds();
397: }
398:
399: /* Gets the data type of all Properties identified by the given Property
400: * ID.
401: * Don't add a JavaDoc comment here, we use the default documentation
402: * from implemented interface.
403: */
404: public Class getType(Object propertyId) {
405: return container.getType(propertyId);
406: }
407:
408: /* Gets the number of Items in the Container.
409: * Don't add a JavaDoc comment here, we use the default documentation
410: * from implemented interface.
411: */
412: public int size() {
413: return container.size();
414: }
415:
416: /* Registers a new Item set change listener for this Container.
417: * Don't add a JavaDoc comment here, we use the default documentation
418: * from implemented interface.
419: */
420: public void addListener(Container.ItemSetChangeListener listener) {
421: if (container instanceof Container.ItemSetChangeNotifier)
422: ((Container.ItemSetChangeNotifier) container)
423: .addListener(listener);
424: }
425:
426: /* Removes a Item set change listener from the object.
427: * Don't add a JavaDoc comment here, we use the default documentation
428: * from implemented interface.
429: */
430: public void removeListener(Container.ItemSetChangeListener listener) {
431: if (container instanceof Container.ItemSetChangeNotifier)
432: ((Container.ItemSetChangeNotifier) container)
433: .removeListener(listener);
434: }
435:
436: /* Registers a new Property set change listener for this Container.
437: * Don't add a JavaDoc comment here, we use the default documentation
438: * from implemented interface.
439: */
440: public void addListener(Container.PropertySetChangeListener listener) {
441: if (container instanceof Container.PropertySetChangeNotifier)
442: ((Container.PropertySetChangeNotifier) container)
443: .addListener(listener);
444: }
445:
446: /* Removes a Property set change listener from the object.
447: * Don't add a JavaDoc comment here, we use the default documentation
448: * from implemented interface.
449: */
450: public void removeListener(
451: Container.PropertySetChangeListener listener) {
452: if (container instanceof Container.PropertySetChangeNotifier)
453: ((Container.PropertySetChangeNotifier) container)
454: .removeListener(listener);
455: }
456:
457: /**
458: * @see org.millstone.base.data.Container.Ordered#addItemAfter(Object, Object)
459: */
460: public Item addItemAfter(Object previousItemId, Object newItemId)
461: throws UnsupportedOperationException {
462:
463: // If the previous item is not in the container, fail
464: if (previousItemId != null && !containsId(previousItemId))
465: return null;
466:
467: // Add the item to container
468: Item item = container.addItem(newItemId);
469:
470: // Put the new item to its correct place
471: if (item != null)
472: addToOrderWrapper(newItemId, previousItemId);
473:
474: return item;
475: }
476:
477: /**
478: * @see org.millstone.base.data.Container.Ordered#addItemAfter(Object)
479: */
480: public Object addItemAfter(Object previousItemId)
481: throws UnsupportedOperationException {
482:
483: // If the previous item is not in the container, fail
484: if (previousItemId != null && !containsId(previousItemId))
485: return null;
486:
487: // Add the item to container
488: Object id = container.addItem();
489:
490: // Put the new item to its correct place
491: if (id != null)
492: addToOrderWrapper(id, previousItemId);
493:
494: return id;
495: }
496:
497: }
|