All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.intarsys.pdf.cos.COSIndirectObject Maven / Gradle / Ivy

/*
 * Copyright (c) 2007, intarsys consulting GmbH
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of intarsys nor the names of its contributors may be used
 *   to endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package de.intarsys.pdf.cos;

import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.logging.Level;

import de.intarsys.pdf.parser.COSLoadException;
import de.intarsys.pdf.st.STDocument;
import de.intarsys.tools.logging.LogTools;
import de.intarsys.tools.resourcetracker.ResourceTracker;

/**
 * An object representing an indirect object within a COSDocument.
 * 

* The indirect object provides transparent lazy access to the serialized * COSObject. Dereferencing this will deserialize the COSObject if not * available. On the other hand, this implementation must support the handling * of very large documents, so transparent garbage collection must be supported. * This is implemented using a two stage reference: A hard reference via * object is held to objects that should be hard wired in the * memory. A soft reference is held via reference to objects that * should be available to garbage collection. An object is "hard wired" when one * of the following conditions is true:
*

    *
  • An explicit call to "harden" is made.
  • *
  • The object or one of its descendants is changed.
  • *
*

* You should not override equals or hash without really knowing what you do. * Part of the implementation depends on indirect objects being real unique. *

*/ public class COSIndirectObject extends COSDocumentElement implements ICOSContainer { /** * A tracker monitoring the disposal of COSObjects. *

* This is a debugging aid. */ private static ResourceTracker tracker = new ResourceTracker() { @Override protected void basicDispose(Object resource) { LogTools.getLogger(COSIndirectObject.class).log(Level.WARNING, "disposed " + resource); } }; private static final byte F_DIRTY = 1; private static final byte F_FIXED = 2; /** * Create an indirection for object. * * @param object * The object that should be indirect. * * @return The new indirect object. */ public static COSIndirectObject create(COSObject object) { COSIndirectObject ref = new COSIndirectObject(); ref.setDirty(true); object.getContainer().register(ref); // set object after registration as it should need no recursive // registration // new dirty object, hard reference always ref.object = object; object.basicSetContainer(ref); return ref; } public static COSIndirectObject create(STDocument stDoc, COSObjectKey key) { COSIndirectObject ref = new COSIndirectObject(); ref.setKey(key); ref.registerWith(stDoc); return ref; } public static COSIndirectObject create(STDocument stDoc, int objectNumber, int generationNumber) { COSIndirectObject ref = new COSIndirectObject(); ref.setKey(objectNumber, generationNumber); ref.registerWith(stDoc); return ref; } private byte flags = 0; /** * The referenced COS object. This may be a {@link Reference} object also, * so this is typed to {@link Object}. * */ private Object object; /** The document hosting the indirect object. */ private COSDocument doc; /** * The storage level document for the indirect object */ private STDocument stDoc; private int objectNumber = -1; private short generationNumber = -1; /** * number of known references. */ private short referenceCount = 0; protected COSIndirectObject() { super(); } protected COSIndirectObject(COSIndirectObject indirectObject) { super(); this.flags = indirectObject.flags; this.doc = indirectObject.doc; this.objectNumber = indirectObject.objectNumber; this.generationNumber = indirectObject.generationNumber; this.stDoc = indirectObject.stDoc; this.referenceCount = indirectObject.referenceCount; } /* * (non-Javadoc) * * @seede.intarsys.pdf.cos.COSDocumentElement#accept(de.intarsys.pdf.cos. * ICOSObjectVisitor) */ @Override public Object accept(ICOSObjectVisitor visitor) throws COSVisitorException { return visitor.visitFromIndirectObject(this); } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.COSDocumentElement#addContainer(de.intarsys.pdf.cos * .ICOSContainer) */ @Override protected ICOSContainer addContainer(ICOSContainer container) { return associate(container, null); } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.ICOSContainer#associate(de.intarsys.pdf.cos.ICOSContainer * , de.intarsys.pdf.cos.COSObject) */ public ICOSContainer associate(ICOSContainer newContainer, COSObject pObject) { newContainer.register(this); referenceCount++; return this; } /** * Swap in the data for the indirect object. *

* ATTENTION: this method must return the newly read object on the stack to * ensure a strong reference at least as long as the caller can access the * result. * * @return The newly read object. */ protected COSObject basicSwapIn() { try { return stGetDoc().load(this); } catch (IOException e) { throw new COSSwapException("io error reading object " + getKey(), e); //$NON-NLS-1$ } catch (COSLoadException e) { throw new COSSwapException( "parse error reading object " + getKey(), e); //$NON-NLS-1$ } } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.COSDocumentElement#containable(de.intarsys.pdf.cos * .COSDocumentElement) */ @Override public COSDocumentElement containable() { return this; } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.ICOSContainer#containable(de.intarsys.pdf.cos.COSObject * ) */ public COSDocumentElement containable(COSObject pObject) { return this; } /** * see copyDeep() * *

* This method keeps track of already copied objects to deal with cyclic * references. *

* * @see de.intarsys.pdf.cos.COSObject#copyDeep() */ @Override protected COSObject copyDeep(Map copied) { COSObject result = (COSObject) copied.get(this); if (result == null) { result = dereference().copyDeep(copied); } return result; } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.COSDocumentElement#copyShallowNested() */ @Override protected COSDocumentElement copyShallowNested() { return this; } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.COSDocumentElement#dereference() */ @Override public COSObject dereference() { // synchronization is needed because of lazy loading in multithreaded // environment. synchronization is made against a read lock first. If // this fails, the read lock is released and the object is re-access // holding the access lock. Do NOT hold the read lock while holding the // access lock to avoid deadlocks arising from accessing indirect // objects and the access lock in opposite order (as is done when // writing the document). COSObject tempObject; synchronized (this) { tempObject = getObject(); } // do NOT hold read lock when accessing !! if (tempObject == null) { Object lock = this; if (stGetDoc() != null) { lock = stGetDoc().getAccessLock(); } synchronized (lock) { // must retry tempObject = getObject(); if (tempObject == null) { try { // now swap with access lock held tempObject = swapIn(); synchronized (this) { // and write with both locks setObject(tempObject); } } catch (COSRuntimeException e) { setObject(COSNull.create()); if (doc != null) { doc.handleException(e); } else { throw e; } } } } } return tempObject; } /* * (non-Javadoc) * * @seede.intarsys.pdf.cos.ICOSContainer#disassociate(de.intarsys.pdf.cos. * ICOSContainer, de.intarsys.pdf.cos.COSObject) */ public ICOSContainer disassociate(ICOSContainer oldContainer, COSObject pObject) { referenceCount--; return this; } public boolean exists() { // synchronization is needed because of lazy loading in multithreaded // environment. synchronization is made against a read lock first. If // this fails, the read lock is released and the object is re-access // holding the access lock. Do NOT hold the read lock while holding the // access lock to avoid deadlocks arising from accessing indirect // objects and the access lock in opposite order (as is done when // writing the document). COSObject tempObject; synchronized (this) { tempObject = getObject(); } if (tempObject != null) { return true; } // do NOT hold read lock when accessing !! Object lock = this; if (stGetDoc() != null) { lock = stGetDoc().getAccessLock(); } synchronized (lock) { // must retry tempObject = getObject(); if (tempObject != null) { return true; } return basicSwapIn() != null; } } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.COSDocumentElement#getDoc() */ @Override public COSDocument getDoc() { return doc; } public int getGenerationNumber() { return generationNumber & 0xffff; } /** * The key for this. * * @return The key for this. */ public COSObjectKey getKey() { COSObjectKey key = null; if (objectNumber == -1) { if (stGetDoc() != null) { key = stGetDoc().createObjectKey(); objectNumber = key.getObjectNumber(); generationNumber = (short) key.getGenerationNumber(); } } else { key = new COSObjectKey(objectNumber, generationNumber); } return key; } protected COSObject getObject() { if (object == null) { return null; } else if (object instanceof Reference) { return (COSObject) ((Reference) object).get(); } else { return (COSObject) object; } } public int getObjectNumber() { if (objectNumber == -1) { if (stGetDoc() != null) { COSObjectKey key = stGetDoc().createObjectKey(); objectNumber = key.getObjectNumber(); generationNumber = (short) key.getGenerationNumber(); } } return objectNumber; } public void harden(COSObject pObject) { if (object instanceof Reference) { flags |= F_FIXED; object = ((Reference) object).get(); } } /** * true if the object graph referenced by this is changed. * * @return true if the object graph referenced by this is * changed. */ public boolean isDirty() { return (flags & F_DIRTY) != 0; } /** * true if this {@link COSIndirectObject} never has been * swapped in. * * @return true if this {@link COSIndirectObject} never has * been swapped in. */ public boolean isInitialState() { synchronized (this) { return object == null; } } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.COSDocumentElement#isReference() */ @Override public boolean isReference() { return true; } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.COSDocumentElement#isSwapped() */ @Override public boolean isSwapped() { synchronized (this) { return getObject() == null; } } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.ICOSContainer#referenceCount() */ public int referenceCount() { return referenceCount; } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.ICOSContainer#referenceIndirect(de.intarsys.pdf.cos * .COSObject) */ public COSIndirectObject referenceIndirect(COSObject pObject) { return this; } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.ICOSContainer#register(de.intarsys.pdf.cos.COSObject) */ public void register(COSDocumentElement pObject) { if (doc != null) { pObject.registerWith(doc); } } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.COSDocumentElement#registerWith(de.intarsys.pdf.cos * .COSDocument) */ @Override protected void registerWith(COSDocument newDoc) { if (doc == null) { doc = newDoc; registerWith(newDoc.stGetDoc()); // register descendents COSObject tempObject = getObject(); if (tempObject != null) { tempObject.registerWith(doc); } } else { if (doc != newDoc) { throw new IllegalStateException( "You can not merge objects from different documents"); //$NON-NLS-1$ } } } /** * @param pSTDoc */ public void registerWith(STDocument pSTDoc) { this.stDoc = pSTDoc; pSTDoc.addObjectReference(this); if ((flags & F_DIRTY) != 0) { pSTDoc.addChangedReference(this); } } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.COSDocumentElement#removeContainer(de.intarsys.pdf * .cos.ICOSContainer) */ @Override protected ICOSContainer removeContainer(ICOSContainer oldContainer) { return disassociate(oldContainer, null); } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.ICOSContainer#restoreStateContainer(de.intarsys.pdf * .cos.ICOSContainer) */ public ICOSContainer restoreStateContainer(ICOSContainer container) { COSIndirectObject indirectObject = (COSIndirectObject) container; this.flags = indirectObject.flags; // this.doc = indirectObject.doc; // this.encryptOnWrite = indirectObject.encryptOnWrite; // this.key = indirectObject.key; // this.stDoc = indirectObject.stDoc; this.referenceCount = indirectObject.referenceCount; return this; } /* * (non-Javadoc) * * @see de.intarsys.pdf.cos.ICOSContainer#storeStateContainer() */ public ICOSContainer saveStateContainer() { return new COSIndirectObject(this); } /** * Set the dirty state of the indirect object */ public void setDirty(boolean pDirty) { if (pDirty) { if ((flags & F_DIRTY) == 0) { harden(null); } if (stGetDoc() != null) { stGetDoc().addChangedReference(this); } flags |= F_DIRTY; } else { if ((flags & F_DIRTY) != 0) { soften(null); } flags ^= F_DIRTY; } } /** * Assign a {@link COSObjectKey} to this. * * @param key * The new key. */ public void setKey(COSObjectKey key) { if (key == null) { objectNumber = -1; generationNumber = -1; } else { objectNumber = key.getObjectNumber(); generationNumber = (short) key.getGenerationNumber(); } } public void setKey(int objectNumber, int generationNumber) { this.objectNumber = objectNumber; this.generationNumber = (short) generationNumber; } /** * Set the object for this reference. The reference is already registered * with the document, the object is provided "lazy" by the storage. * * @param newObject * the new object to set */ protected void setObject(COSObject newObject) { // COSObject oldObject = getObject(); // if (oldObject != null) { // // completely destroy connection to old object - no longer reachable // oldObject.basicSetContainer(COSObject.NULL_CONTAINER); // } if (newObject.mayBeSwapped()) { this.object = new SoftReference(newObject); // debugging aid // this.reference = tracker.trackSoft(newObject, getKey()); } else { this.object = newObject; } // set new container and register newObject newObject.basicSetContainer(this); if (doc != null) { newObject.registerWith(doc); } } public void soften(COSObject pObject) { // is fixed? if ((flags & F_FIXED) != 0 && ((COSObject) object).mayBeSwapped()) { flags ^= F_FIXED; object = new SoftReference(object); } } /** * The ST level document. * * @return The ST level document. */ final public STDocument stGetDoc() { return stDoc; } /** * Swap in the data for the indirect object. *

* ATTENTION: this method must return the newly read object on the stack to * ensure a strong reference at least as long as the caller can access the * result. * * @return The newly read object. */ protected COSObject swapIn() { COSObject loadedObject = basicSwapIn(); if (loadedObject == null) { loadedObject = COSNull.create(); } return loadedObject; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "" + getObjectNumber() + " " + getGenerationNumber() + " R" + "->"; //$NON-NLS-1$ //$NON-NLS-2$ } /* * (non-Javadoc) * * @see * de.intarsys.pdf.cos.ICOSContainer#willChange(de.intarsys.pdf.cos.COSObject * ) */ public void willChange(COSObject change) { if (getDoc() != null) { getDoc().willChange(change); } setDirty(true); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy