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.HashMap;
033: import java.util.Iterator;
034: import java.util.Map;
035:
036: import de.intarsys.tools.component.ISaveStateSupport;
037:
038: /**
039: * Abstract superclass for all COS level object types
040: */
041: abstract public class COSObject extends COSDocumentElement implements
042: ISaveStateSupport {
043: /**
044: * This is the container for template objects. Template objects can be
045: * created static in the application and are copied behind the scenes when
046: * integrated in a document.
047: */
048: public static final ICOSContainer CONSTANT_CONTAINER = new ICOSContainer() {
049: public ICOSContainer associate(ICOSContainer newContainer,
050: COSObject object) {
051: throw new IllegalStateException(
052: "constants can not be contained"); //$NON-NLS-1$
053: }
054:
055: public ICOSContainer disassociate(ICOSContainer oldContainer,
056: COSObject object) {
057: throw new IllegalStateException(
058: "constants can not be contained"); //$NON-NLS-1$
059: }
060:
061: public COSDocumentElement containable(COSObject object) {
062: return object.copyDeep().containable();
063: }
064:
065: public COSDocument getDoc() {
066: return null;
067: }
068:
069: public void willChange(COSObject object) {
070: // do nothing
071: }
072:
073: /**
074: * reference count for template objects is 0.
075: *
076: * @return 0
077: * @see de.intarsys.pdf.cos.ICOSContainer#referenceCount()
078: */
079: public int referenceCount() {
080: return 0;
081: }
082:
083: public COSIndirectObject referenceIndirect(COSObject object) {
084: throw new IllegalStateException(
085: "constants can not be indirect"); //$NON-NLS-1$
086: }
087:
088: public void register(COSDocumentElement object) {
089: // do nothing
090: }
091:
092: /*
093: * (non-Javadoc)
094: *
095: * @see de.intarsys.pdf.cos.ICOSContainer#restoreStateContainer(de.intarsys.pdf.cos.ICOSContainer)
096: */
097: public ICOSContainer restoreStateContainer(
098: ICOSContainer container) {
099: return container;
100: }
101:
102: /*
103: * (non-Javadoc)
104: *
105: * @see de.intarsys.pdf.cos.ICOSContainer#storeStateContainer()
106: */
107: public ICOSContainer saveStateContainer() {
108: return this ;
109: }
110: };
111:
112: /**
113: * This is the default container for non - contained objects.
114: *
115: */
116: public static final ICOSContainer NULL_CONTAINER = new ICOSContainer() {
117: public ICOSContainer associate(ICOSContainer newContainer,
118: COSObject object) {
119: object.basicSetContainer(newContainer);
120: newContainer.register(object);
121: return newContainer;
122: }
123:
124: public ICOSContainer disassociate(ICOSContainer oldContainer,
125: COSObject object) {
126: throw new IllegalStateException("association inconsistent"); //$NON-NLS-1$
127: }
128:
129: public COSDocumentElement containable(COSObject object) {
130: return object;
131: }
132:
133: public COSDocument getDoc() {
134: return null;
135: }
136:
137: public void willChange(COSObject object) {
138: // do nothing
139: }
140:
141: /**
142: * reference count for new objects is 0.
143: *
144: * @return 0
145: * @see de.intarsys.pdf.cos.ICOSContainer#referenceCount()
146: */
147: public int referenceCount() {
148: return 0;
149: }
150:
151: public COSIndirectObject referenceIndirect(COSObject object) {
152: return COSIndirectObject.create(object);
153: }
154:
155: public void register(COSDocumentElement object) {
156: // do nothing
157: }
158:
159: /*
160: * (non-Javadoc)
161: *
162: * @see de.intarsys.pdf.cos.ICOSContainer#restoreStateContainer(de.intarsys.pdf.cos.ICOSContainer)
163: */
164: public ICOSContainer restoreStateContainer(
165: ICOSContainer container) {
166: return container;
167: }
168:
169: /*
170: * (non-Javadoc)
171: *
172: * @see de.intarsys.pdf.cos.ICOSContainer#storeStateContainer()
173: */
174: public ICOSContainer saveStateContainer() {
175: return this ;
176: }
177: };
178:
179: public static final Object SLOT_CONTAINER = new Object();
180:
181: /**
182: * The optional back-reference to the object "containing" this if any (for
183: * example an array).
184: *
185: * <p>
186: * A literal object may only be contained once, an indirect referenced
187: * object is contained by the indirect reference
188: * </p>
189: */
190: protected ICOSContainer container;
191:
192: protected COSObject() {
193: container = NULL_CONTAINER;
194: }
195:
196: /**
197: * Copy constructor for creating save state.
198: *
199: * @param object
200: * The original object.
201: */
202: protected COSObject(COSObject object) {
203: super ();
204: this .container = object.container.saveStateContainer();
205: }
206:
207: /**
208: * Add a listener for object changes.
209: *
210: * @param listener
211: * The listener to be informed about changes.
212: */
213: abstract public void addObjectListener(ICOSObjectListener listener);
214:
215: /**
216: * <code>true</code> if an {@link ICOSObjectListener} is registered. This
217: * is for test purposes.
218: */
219: abstract public boolean isObjectListenerAvailable();
220:
221: /**
222: * An iterator over contained objects and references. The iterator is an
223: * empty iterator if this is not a container.
224: *
225: * <p>
226: * This iterator returns COSDocumentElements, leaving references alone.
227: * </p>
228: *
229: * @return Iterator over contained objects and references.
230: */
231: abstract public Iterator basicIterator();
232:
233: protected void basicSetContainer(ICOSContainer newContainer) {
234: container = newContainer;
235: }
236:
237: /**
238: * A string representation for the receiver.
239: *
240: * @return A string representation for the receiver.
241: */
242: abstract protected String basicToString();
243:
244: /**
245: * Declare this to be a constant. This declaration ensures, that when using
246: * this in a document context a copy will be made.
247: *
248: * @return The receiver.
249: */
250: public COSObject beConstant() {
251: container = CONSTANT_CONTAINER;
252: return this ;
253: }
254:
255: /**
256: * Make an indirect object out of a direct one. An object can always be
257: * changed to an indirect one.
258: * <p>
259: * It is possible to morph existing objects into indirect ones, the objects
260: * in the hierarchy (container/document) are informed and will reflect the
261: * change.
262: * </p>
263: */
264: public COSIndirectObject beIndirect() {
265: return container.referenceIndirect(this );
266: }
267:
268: /*
269: * (non-Javadoc)
270: *
271: * @see de.intarsys.pdf.cos.COSDocumentElement#containable(de.intarsys.pdf.cos.COSDocumentElement)
272: */
273: public COSDocumentElement containable() {
274: return container.containable(this );
275: }
276:
277: /**
278: * Create a new instance of the receiver that may be used as the base object
279: * for a copy. The result is an uninitialized instance of the receiver. The
280: * attributes are copied depending on the strategy (deep, shallow).
281: *
282: * @return The new instance of a COSObject
283: */
284: abstract protected COSObject copyBasic();
285:
286: /**
287: * Make a deep copy of the receiver within the same document. The result is
288: * a "PDF semantic" deep copy, implementation artifacts as "attributes" and
289: * listeners are NOT copied.
290: *
291: * <p>
292: * The algorithm copies <code>this</code> along with all outgoing
293: * references (recursively).
294: * </p>
295: *
296: * <p>
297: * Object identity is preserved.
298: * </p>
299: *
300: * <p>
301: * Be careful when copying objects, as there are semantics that may NOT be
302: * recognized by this method.
303: * </p>
304: *
305: * @return the object copied recursively
306: */
307: abstract public COSObject copyDeep();
308:
309: /**
310: * Make a deep copy of the receiver within the same document. The result is
311: * a "PDF semantic" deep copy, implementation artifacts as "attributes" and
312: * listeners are NOT copied.
313: * <p>
314: * The <code>copied</code> map is used to identify objects copied in
315: * earlier runs of this method to avoid duplicating resources used in
316: * different copy targets (for example the pages of a document).
317: * <code>copied</code> is modified while executing <code>copyDeep</code>
318: * and contains a mapping from indirect objects in the original document to
319: * copied objects.
320: * <p>
321: * The algorithm copies <code>this</code> along with all outgoing
322: * references (recursively).
323: * </p>
324: *
325: * <p>
326: * Object identity is preserved.
327: * </p>
328: *
329: * <p>
330: * Be careful when copying objects, as there are semantics that may NOT be
331: * recognized by this method.
332: * </p>
333: *
334: * @return the object copied recursively
335: */
336: abstract public COSObject copyDeep(Map copied);
337:
338: /**
339: * Make a copy of the receiver within the same document. A copy is made only
340: * if we have an object that may not be inserted in multiple containers.
341: * This means all direct objects are (recursively) copied, all indirect
342: * objects return the receiver.
343: *
344: * <p>
345: * Be careful when copying objects, as there are semantics that may NOT be
346: * recognized by this method.
347: * </p>
348: *
349: * @return The optional copy.
350: */
351: public final COSObject copyOptional() {
352: if (isIndirect()) {
353: return this ;
354: }
355: return copyShallow();
356: }
357:
358: /**
359: * Make a copy of the receiver.
360: *
361: * <p>
362: * A copy is made of the receiver and after this recursively of all not
363: * indirect objects.
364: * </p>
365: *
366: * <p>
367: * Be careful when copying objects, as there are semantics that may NOT be
368: * recognized by this method.
369: * </p>
370: *
371: * @return The object copied
372: */
373: public COSObject copyShallow() {
374: COSObject result = copyBasic();
375: if (isIndirect()) {
376: result.beIndirect();
377: }
378: return result;
379: }
380:
381: /*
382: * (non-Javadoc)
383: *
384: * @see de.intarsys.pdf.cos.COSDocumentElement#copyShallowNested()
385: */
386: protected COSDocumentElement copyShallowNested() {
387: return copyShallow();
388: }
389:
390: /**
391: * Make a copy of the receiver within the same document.
392: *
393: * <p>
394: * The algorithm copies <code>this</code> along with all outgoing
395: * references (recursively) that themselve have a navigation path to
396: * <code>this</code>. The result is a new subgraph extending from the
397: * copy of <code>this</code> where no navigation path leads back to
398: * <code>this</code>.
399: * </p>
400: *
401: * <p>
402: * Object identity is preserved.
403: * </p>
404: *
405: * <p>
406: * Be careful when copying objects, as there are semantics that may NOT be
407: * recognized by this method.
408: * </p>
409: *
410: * @return the object copied recursively
411: */
412: public final COSObject copySubGraph() {
413: return copySubGraph(new HashMap());
414: }
415:
416: /**
417: * The implementation of {@link
418: * de.intarsys.pdf.cos.COSObject#copySubGraph()}. The parameters
419: * <code>copied</code>keeps track of already copied objects to deal with
420: * cyclic references.
421: *
422: * @see de.intarsys.pdf.cos.COSObject#copySubGraph()
423: */
424: protected COSObject copySubGraph(Map copied) {
425: COSObject result = copyBasic();
426: if (isIndirect()) {
427: result.beIndirect();
428: copied.put(getIndirectObject(), result);
429: }
430: return result;
431: }
432:
433: /**
434: * return the real object. this is needed for polymorphic handling of
435: * document elements. at application programming level only COSObject, never
436: * COSReference is seen.
437: *
438: * @return de.intarsys.pdf.cos.COSObject
439: */
440: public COSObject dereference() {
441: return this ;
442: }
443:
444: /**
445: * Answer the object that contains this. The container is never null.
446: *
447: * @return Answer the object that contains this.
448: */
449: public ICOSContainer getContainer() {
450: return container;
451: }
452:
453: /**
454: * The document that contains this.
455: *
456: * <p>
457: * This may return null, as COSObject graphs may be created "offline" and
458: * add to the document as a whole.
459: * </p>
460: *
461: * <p>
462: * The document is evaluated via the COSObject graph hierarchy that finally
463: * must be contained within a document.
464: * </p>
465: *
466: * @return The document that contains this.
467: */
468: public COSDocument getDoc() {
469: return container.getDoc();
470: }
471:
472: /**
473: * return the indirect object for the receiver. application level
474: * programmers should not use this method. this is needed for creating a
475: * physical representation of the document (serializing)
476: *
477: * @return the indirect object for the receiver
478: */
479: public COSIndirectObject getIndirectObject() {
480: if (container instanceof COSIndirectObject) {
481: return (COSIndirectObject) container;
482: } else {
483: return null;
484: }
485: }
486:
487: /**
488: * Answer <code>true</code> if the receiver has a navigation path to
489: * <code>other</code>.
490: *
491: * @param other
492: * The object we search a path to from <code>this</code>.
493: *
494: * @return Answer <code>true</code> if the receiver has a navigation path
495: * to <code>other</code>.
496: */
497: protected boolean hasNavigationPathTo(COSObject other) {
498: return false;
499: }
500:
501: /**
502: * <code>true</code> if this object is not contained in a document
503: * directly or indirectly. This is especially true when an object is new (or
504: * reset to this state when an undo happend). Remember that an object can
505: * still be garbage, even if it is not dangling.
506: *
507: * @return <code>true</code> if this object is not contained in a document
508: * directly or indirectly.
509: */
510: public boolean isDangling() {
511: return container.referenceCount() == 0;
512: }
513:
514: /**
515: * Answer <code>true</code> if this object is an indirect one.
516: *
517: * @return <code>true</code> if this object is an indirect one.
518: */
519: public boolean isIndirect() {
520: return container instanceof COSIndirectObject;
521: }
522:
523: /**
524: * answer true if receiver is the null object
525: *
526: * @return true if receiver is the null object
527: */
528: public boolean isNull() {
529: return false;
530: }
531:
532: /**
533: * answer true if receiver is a number
534: *
535: * @return answer true if receiver is a number
536: */
537: public boolean isNumber() {
538: return false;
539: }
540:
541: /**
542: * Answer true if this object is of a primitive type
543: *
544: * @return Answer true if this object is of a primitive type
545: */
546: abstract public boolean isPrimitive();
547:
548: /*
549: * (non-Javadoc)
550: *
551: * @see de.intarsys.pdf.cos.COSDocumentElement#isSwapped()
552: */
553: public boolean isSwapped() {
554: return isIndirect() && getIndirectObject().isSwapped();
555: }
556:
557: /**
558: * An iterator over contained objects. The iterator is an empty iterator if
559: * this is not a container.
560: *
561: * <p>
562: * This iterator returns only COSObject instances, references are
563: * dereferenced.
564: * </p>
565: *
566: * @return Iterator over contained objects.
567: */
568: abstract public Iterator iterator();
569:
570: /**
571: * Remove a listener for object changes.
572: *
573: * @param listener
574: * The listener to be removed.
575: */
576: abstract public void removeObjectListener(
577: ICOSObjectListener listener);
578:
579: public void restoreState(Object saveState) {
580: container = container
581: .restoreStateContainer(((COSObject) saveState).container);
582: }
583:
584: /*
585: * (non-Javadoc)
586: *
587: * @see de.intarsys.pdf.cos.COSDocumentElement#addContainer(de.intarsys.pdf.cos.ICOSContainer)
588: */
589: protected ICOSContainer addContainer(ICOSContainer newContainer) {
590: return container.associate(newContainer, this );
591: }
592:
593: /*
594: * (non-Javadoc)
595: *
596: * @see de.intarsys.pdf.cos.COSDocumentElement#removeContainer(de.intarsys.pdf.cos.ICOSContainer)
597: */
598: protected ICOSContainer removeContainer(ICOSContainer oldContainer) {
599: return container.disassociate(oldContainer, this );
600: }
601:
602: /**
603: * A string representation for the receiver.
604: *
605: * @return A string representation for the receiver.
606: */
607: public String stringValue() {
608: return basicToString();
609: }
610:
611: /*
612: * (non-Javadoc)
613: *
614: * @see java.lang.Object#toString()
615: */
616: public String toString() {
617: StringBuilder sb = new StringBuilder();
618: sb.append(basicToString());
619: return sb.toString();
620: }
621:
622: abstract protected void triggerChanged(Object slot,
623: Object oldValue, Object newValue);
624:
625: /**
626: * @return a COSBoolean or <code>null</code>
627: */
628: public COSBoolean asBoolean() {
629: return null;
630: }
631:
632: /**
633: * @return a COSName or <code>null</code>
634: */
635: public COSName asName() {
636: return null;
637: }
638:
639: /**
640: * @return a COSNull or <code>null</code>
641: */
642: public COSNull asNull() {
643: return null;
644: }
645:
646: /**
647: * @return a COSString or <code>null</code>
648: */
649: public COSString asString() {
650: return null;
651: }
652:
653: /**
654: * @return a COSFixed or <code>null</code>
655: */
656: public COSFixed asFixed() {
657: return null;
658: }
659:
660: /**
661: * @return a COSInteger or <code>null</code>
662: */
663: public COSInteger asInteger() {
664: return null;
665: }
666:
667: /**
668: * @return a COSNumber or <code>null</code>
669: */
670: public COSNumber asNumber() {
671: return null;
672: }
673:
674: /**
675: * <code>this</code> as a COSArray or <code>null</code>
676: *
677: * @return a COSArray or <code>null</code>
678: */
679: public COSArray asArray() {
680: return null;
681: }
682:
683: /**
684: * @return a COSDictionary or <code>null</code>
685: */
686: public COSDictionary asDictionary() {
687: return null;
688: }
689:
690: /**
691: * @return a COSStream or <code>null</code>
692: */
693: public COSStream asStream() {
694: return null;
695: }
696: }
|