001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.cos;
031:
032: import java.util.ArrayList;
033: import java.util.HashMap;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Set;
038:
039: /**
040: * Represents a collection of associations (Map).
041: *
042: * <p>
043: * The keys of the association are COSName objects, the value may be any
044: * COSDocumentElement
045: * </p>
046: *
047: */
048: public class COSDictionary extends COSCompositeObject {
049: public static class Entry implements Map.Entry {
050: private COSName key;
051:
052: private COSDocumentElement value;
053:
054: public Entry(COSName key, COSDocumentElement value) {
055: this .key = key;
056: this .value = value;
057: }
058:
059: public Object getKey() {
060: return key;
061: }
062:
063: public Object getValue() {
064: return value.dereference();
065: }
066:
067: public Object setValue(Object newValue) {
068: return value = (COSDocumentElement) newValue;
069: }
070: }
071:
072: /**
073: * Create an empty {@link COSDictionary}.
074: *
075: * @return Create an empty {@link COSDictionary}.
076: */
077: public static COSDictionary create() {
078: return new COSDictionary();
079: }
080:
081: /**
082: * Create an empty {@link COSDictionary} with an initial capacity.
083: *
084: * @return Create an empty {@link COSDictionary} with an initial capacity.
085: */
086: public static COSDictionary create(int size) {
087: return new COSDictionary(size);
088: }
089:
090: /** the map from a COSName to a COSDocumentElement */
091: private final Map objects;
092:
093: protected COSDictionary() {
094: super ();
095: this .objects = new HashMap();
096: }
097:
098: protected COSDictionary(COSDictionary dict) {
099: super (dict);
100: this .objects = new HashMap(dict.objects);
101: }
102:
103: protected COSDictionary(int size) {
104: super ();
105: this .objects = new HashMap(size);
106: }
107:
108: /*
109: * (non-Javadoc)
110: *
111: * @see de.intarsys.pdf.cos.COSObject#accept(de.intarsys.pdf.cos.ICOSObjectVisitor)
112: */
113: public java.lang.Object accept(ICOSObjectVisitor visitor)
114: throws COSVisitorException {
115: return visitor.visitFromDictionary(this );
116: }
117:
118: /**
119: * Add all objects from <code>dict</code>. Associations already available
120: * in this are replaced with new content .
121: *
122: * @param dict
123: * The collection of associations to add to this.
124: */
125: public void addAll(COSDictionary dict) {
126: willChange(this );
127: for (Iterator i = dict.basicEntryIterator(); i.hasNext();) {
128: Map.Entry entry = (Map.Entry) i.next();
129: COSName key = (COSName) entry.getKey();
130: COSDocumentElement element = (COSDocumentElement) entry
131: .getValue();
132: basicPutPropagate(key, element.copyShallowNested());
133: }
134: if (objectListeners != null) {
135: triggerChanged(null, null, null);
136: }
137: }
138:
139: /**
140: * Add all values from <code>dict</code> that are not yet defined in the
141: * receiver.
142: *
143: * @param dict
144: * The dictionary with the associations to add.
145: */
146: public void addIfAbsent(COSDictionary dict) {
147: boolean changed = false;
148: for (Iterator i = dict.basicEntryIterator(); i.hasNext();) {
149: Map.Entry entry = (Map.Entry) i.next();
150: COSName key = (COSName) entry.getKey();
151: COSDocumentElement element = (COSDocumentElement) entry
152: .getValue();
153: if (!containsKey(key)) {
154: if (!changed) {
155: changed = true;
156: willChange(this );
157: }
158: COSObject newObject = element.copyShallowNested()
159: .dereference();
160: basicPutPropagate(key, newObject);
161: }
162: }
163: if (changed && (objectListeners != null)) {
164: triggerChanged(null, null, null);
165: }
166: }
167:
168: /*
169: * (non-Javadoc)
170: *
171: * @see de.intarsys.pdf.cos.COSObject#getCOSDictionary()
172: */
173: public COSDictionary asDictionary() {
174: return this ;
175: }
176:
177: /**
178: * Remove all associations from this.
179: *
180: */
181: protected void basicClearPropagate() {
182: for (Iterator i = objects.values().iterator(); i.hasNext();) {
183: COSDocumentElement element = (COSDocumentElement) i.next();
184: COSObject dereferenced = element.dereference();
185: willChange(dereferenced);
186: ICOSContainer newContainer = element.removeContainer(this );
187: dereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
188: newContainer);
189: }
190: objects.clear();
191: }
192:
193: /**
194: * An iterator over all entries. The value of the entries returned are the
195: * not - dereferenced elements.
196: *
197: * @return An interator over all entries.
198: */
199: public Iterator basicEntryIterator() {
200: return objects.entrySet().iterator();
201: }
202:
203: /**
204: * The {@link COSDocumentElement} associated with <code>key</code>.
205: *
206: * @param key
207: * The name to lookup
208: *
209: * @return The {@link COSDocumentElement} associated with <code>key</code>.
210: */
211: public COSDocumentElement basicGet(COSName key) {
212: return (COSDocumentElement) objects.get(key);
213: }
214:
215: /**
216: * An iterator over all values. The objects returned are not dereferenced.
217: *
218: * @return An iterator over all values.
219: */
220: public Iterator basicIterator() {
221: return objects.values().iterator();
222: }
223:
224: /**
225: * Add a {@link COSDocumentElement} to the collection.
226: *
227: * @param key
228: * The key where to store the new element.
229: * @param element
230: * The {@link COSDocumentElement} to store.
231: *
232: * @return The {@link COSDocumentElement} associated with <code>key</code>
233: * so far.
234: */
235: protected COSDocumentElement basicPutPropagate(COSName key,
236: COSDocumentElement element) {
237: COSObject dereferenced = element.dereference();
238: COSDocumentElement containable = element.containable();
239: //
240: willChange(dereferenced);
241: ICOSContainer newContainer = containable.addContainer(this );
242: COSDocumentElement oldContainable = (COSDocumentElement) objects
243: .put(key, containable);
244: dereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
245: newContainer);
246: //
247: if (oldContainable != null && oldContainable != containable) {
248: COSObject oldDereferenced = oldContainable.dereference();
249: willChange(oldDereferenced);
250: newContainer = oldContainable.removeContainer(this );
251: oldDereferenced.triggerChanged(COSObject.SLOT_CONTAINER,
252: null, newContainer);
253: }
254: return oldContainable;
255: }
256:
257: /**
258: * Add a document element to the collection.
259: * <p>
260: * The element is inserted without change propagation.
261: * <p>
262: * This should not be used by the application level programmer. It is public
263: * for package visibility reasons.
264: *
265: * @param key
266: * The key where to store the new element.
267: * @param element
268: * The {@link COSDocumentElement} to store.
269: *
270: * @return The {@link COSDocumentElement} associated with <code>key</code>
271: * so far.
272: */
273: public COSDocumentElement basicPutSilent(COSName key,
274: COSDocumentElement element) {
275: COSDocumentElement containable = element.containable();
276: containable.addContainer(this );
277: COSDocumentElement oldContainable = (COSDocumentElement) objects
278: .put(key, containable);
279: if (oldContainable != null && oldContainable != containable) {
280: oldContainable.removeContainer(this );
281: }
282: return oldContainable;
283: }
284:
285: /**
286: * Remove the element associated with <code>key</code> from the collection .
287: *
288: * @param key
289: * The key of the element to be removed
290: *
291: * @return The {@link COSDocumentElement} removed or null.
292: */
293: protected COSDocumentElement basicRemovePropagate(COSName key) {
294: COSDocumentElement element = (COSDocumentElement) objects
295: .remove(key);
296: if (element == null) {
297: return null;
298: }
299: COSObject dereferenced = element.dereference();
300: willChange(dereferenced);
301: ICOSContainer newContainer = element.removeContainer(this );
302: dereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
303: newContainer);
304: return element;
305: }
306:
307: /*
308: * (non-Javadoc)
309: *
310: * @see de.intarsys.pdf.cos.COSObject#basicToString()
311: */
312: protected String basicToString() {
313: return objects.toString();
314: }
315:
316: /**
317: * Remove all associations from the receiver.
318: */
319: public void clear() {
320: willChange(this );
321: basicClearPropagate();
322: if (objectListeners != null) {
323: triggerChanged(null, null, null);
324: }
325: }
326:
327: /**
328: * Answer true if <code>key</code> is a valid key in the collection .
329: *
330: * @param key
331: * The key whose existence is to be checked.
332: *
333: * @return Answer true if <code>key</code> is a valid key in the
334: * collection .
335: */
336: public boolean containsKey(COSName key) {
337: return objects.containsKey(key);
338: }
339:
340: /**
341: * Answer <code>true</code> if <code>obj</code> is contained in the
342: * collection
343: *
344: * @param obj
345: * The object to look up in the collection
346: *
347: * @return Answer <code>true</code> if <code>obj</code> is contained in
348: * the collection
349: */
350: public boolean containsValue(COSObject obj) {
351: for (Iterator i = entryIterator(); i.hasNext();) {
352: Map.Entry entry = (Map.Entry) i.next();
353: COSObject current = (COSObject) entry.getValue();
354: if (current.equals(obj)) {
355: return true;
356: }
357: }
358: return false;
359: }
360:
361: /*
362: * (non-Javadoc)
363: *
364: * @see de.intarsys.pdf.cos.COSObject#copyBasic()
365: */
366: protected COSObject copyBasic() {
367: return create(size());
368: }
369:
370: /*
371: * (non-Javadoc)
372: *
373: * @see de.intarsys.pdf.cos.COSCompositeObject#copyDeep(java.util.Map)
374: */
375: public COSObject copyDeep(Map copied) {
376: COSDictionary result = (COSDictionary) super .copyDeep(copied);
377: for (Iterator i = objects.entrySet().iterator(); i.hasNext();) {
378: Map.Entry entry = (Map.Entry) i.next();
379: COSName key = (COSName) entry.getKey();
380: COSDocumentElement element = (COSDocumentElement) entry
381: .getValue();
382: COSObject copy = element.copyDeep(copied);
383: result.basicPutSilent(key, copy);
384: }
385: return result;
386: }
387:
388: /*
389: * (non-Javadoc)
390: *
391: * @see de.intarsys.pdf.cos.COSObject#copyShallow()
392: */
393: public COSObject copyShallow() {
394: COSDictionary result = (COSDictionary) super .copyShallow();
395: for (Iterator i = basicEntryIterator(); i.hasNext();) {
396: Map.Entry entry = (Map.Entry) i.next();
397: COSName key = (COSName) entry.getKey();
398: COSDocumentElement element = (COSDocumentElement) entry
399: .getValue();
400: result.basicPutSilent(key, element.copyShallowNested());
401: }
402: return result;
403: }
404:
405: /*
406: * (non-Javadoc)
407: *
408: * @see de.intarsys.pdf.cos.COSObject#copySubGraph(java.util.Map)
409: */
410: protected COSObject copySubGraph(Map copied) {
411: COSDictionary result = (COSDictionary) super
412: .copySubGraph(copied);
413:
414: // for (Iterator i = objects.entrySet().iterator(); i.hasNext();) {
415: // Map.Entry entry = (Map.Entry) i.next();
416: // COSName key = (COSName) entry.getKey();
417: // COSDocumentElement element = (COSDocumentElement) entry.getValue();
418: // COSObject copy = null;
419: // if (element.isReference()) {
420: // copy = (COSObject) copied.get(element);
421: // if (copy == null) {
422: // if (value.hasNavigationPathTo(this)) {
423: // copy = value.copySubGraph(copied);
424: // } else {
425: // copy = value;
426: // copied.put(value.getIndirectObject(), copy);
427: // }
428: // }
429: // } else {
430: // copy = value.copySubGraph(copied);
431: // }
432: // result.basicPut(key, copy);
433: // }
434: return result;
435: }
436:
437: /**
438: * An iterator over all entries, returning dereferenced entries (
439: * {@link COSObject}).
440: *
441: * @return An interator over all entries, returning dereferenced entries.
442: *
443: */
444: public Iterator entryIterator() {
445: return new Iterator() {
446: private Iterator it = getObjects().entrySet().iterator();
447:
448: public boolean hasNext() {
449: return it.hasNext();
450: }
451:
452: public Object next() {
453: Map.Entry entry = (Map.Entry) it.next();
454: return new Entry((COSName) entry.getKey(),
455: (COSDocumentElement) entry.getValue());
456: }
457:
458: public void remove() {
459: throw new UnsupportedOperationException();
460: }
461: };
462: }
463:
464: /**
465: * The {@link COSObject} associated with <code>key</code>.
466: *
467: * @param key
468: * The key to lookup
469: *
470: * @return The {@link COSObject} associated with <code>key</code>.
471: */
472: public COSObject get(COSName key) {
473: COSDocumentElement element = basicGet(key);
474: return (element == null) ? COSNull.NULL : element.dereference();
475: }
476:
477: protected Map getObjects() {
478: return objects;
479: }
480:
481: /*
482: * (non-Javadoc)
483: *
484: * @see de.intarsys.pdf.cos.COSObject#iterator()
485: */
486: public java.util.Iterator iterator() {
487: return new Iterator() {
488: private Iterator i = getObjects().values().iterator();
489:
490: public boolean hasNext() {
491: return i.hasNext();
492: }
493:
494: public Object next() {
495: return ((COSDocumentElement) i.next()).dereference();
496: }
497:
498: public void remove() {
499: throw new UnsupportedOperationException();
500: }
501: };
502: }
503:
504: /**
505: * The key of obj when it is contained in this or {@link COSNull}.
506: *
507: * @param obj
508: * The object to look up in the collection
509: *
510: * @return The key of obj when it is contained in this or {@link COSNull}.
511: */
512: public COSObject keyOf(COSObject obj) {
513: for (Iterator i = entryIterator(); i.hasNext();) {
514: Map.Entry entry = (Map.Entry) i.next();
515: COSObject current = (COSObject) entry.getValue();
516: if (current.equals(obj)) {
517: return (COSName) entry.getKey();
518: }
519: }
520: return COSNull.NULL;
521: }
522:
523: /**
524: * The set of keys. Keys are {@link COSName} instances.
525: *
526: * @return The set of keys .
527: */
528: public Set keySet() {
529: return objects.keySet();
530: }
531:
532: /**
533: * Add an association to the collection.
534: *
535: * @param key
536: * The key where to store the object
537: * @param object
538: * The object to store in the collection
539: *
540: * @return The {@link COSObject} associated with <code>key</code> so far.
541: */
542: public COSObject put(COSName key, COSObject object) {
543: if (object == null) {
544: return remove(key);
545: }
546: willChange(this );
547: COSDocumentElement oldElement = basicPutPropagate(key, object);
548: COSObject oldObject = null;
549: if (oldElement == null) {
550: oldObject = COSNull.NULL;
551: } else {
552: oldObject = oldElement.dereference();
553: }
554: if (objectListeners != null) {
555: triggerChanged(key, oldObject, object);
556: }
557: return oldObject;
558: }
559:
560: /*
561: * (non-Javadoc)
562: *
563: * @see de.intarsys.pdf.cos.COSObject#referenceIndirect(de.intarsys.pdf.cos.COSIndirectObject)
564: */
565: public COSIndirectObject referenceIndirect(COSObject object) {
566: COSIndirectObject ref = super .referenceIndirect(object);
567: for (Iterator it = objects.entrySet().iterator(); it.hasNext();) {
568: Map.Entry entry = (Map.Entry) it.next();
569: if (entry.getValue() == object) {
570: entry.setValue(ref);
571: // there can be only one...
572: break;
573: }
574: }
575: return ref;
576: }
577:
578: /**
579: * Remove the element from the collection associated with <code>key</code>.
580: *
581: * @param key
582: * The key of the object to remove
583: *
584: * @return The {@link COSObject} removed or null.
585: */
586: public COSObject remove(COSName key) {
587: willChange(this );
588: COSDocumentElement element = basicRemovePropagate(key);
589: COSObject oldObject;
590: if (element == null) {
591: oldObject = COSNull.NULL;
592: } else {
593: oldObject = element.dereference();
594: if (objectListeners != null) {
595: triggerChanged(key, oldObject, COSNull.NULL);
596: }
597: }
598: return oldObject;
599: }
600:
601: /*
602: * (non-Javadoc)
603: *
604: * @see de.intarsys.pdf.cos.COSObject#restoreState(java.lang.Object)
605: */
606: public void restoreState(Object object) {
607: super .restoreState(object);
608: objects.clear();
609: objects.putAll(((COSDictionary) object).objects);
610: triggerChanged(null, null, null);
611: }
612:
613: /*
614: * (non-Javadoc)
615: *
616: * @see de.intarsys.tools.objectsession.ISaveStateSupport#saveState()
617: */
618: public Object saveState() {
619: return new COSDictionary(this );
620: }
621:
622: /**
623: * The number of elements in this.
624: *
625: * @return The number of elements in this.
626: */
627: public int size() {
628: return objects.size();
629: }
630:
631: /**
632: * A list of {@link COSObject} instances within this.
633: *
634: * @return A list of {@link COSObject} instances within this.
635: */
636: public List values() {
637: List result = new ArrayList();
638: for (Iterator it = iterator(); it.hasNext();) {
639: result.add(it.next());
640: }
641: return result;
642: }
643: }
|