Package org.pdfclown.objects

Source Code of org.pdfclown.objects.PdfObjectWrapper

/*
  Copyright 2006-2010 Stefano Chizzolini. http://www.pdfclown.org

  Contributors:
    * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it)

  This file should be part of the source code distribution of "PDF Clown library"
  (the Program): see the accompanying README files for more info.

  This Program is free software; you can redistribute it and/or modify it under the terms
  of the GNU Lesser General Public License as published by the Free Software Foundation;
  either version 3 of the License, or (at your option) any later version.

  This Program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY,
  either expressed or implied; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.

  You should have received a copy of the GNU Lesser General Public License along with this
  Program (see README files); if not, go to the GNU website (http://www.gnu.org/licenses/).

  Redistribution and use, with or without modification, are permitted provided that such
  redistributions retain the above copyright notice, license and disclaimer, along with
  this list of conditions.
*/

package org.pdfclown.objects;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.util.Collection;

import org.pdfclown.PDF;
import org.pdfclown.Version;
import org.pdfclown.VersionEnum;
import org.pdfclown.documents.Document;
import org.pdfclown.documents.Document.Configuration.CompatibilityModeEnum;
import org.pdfclown.files.File;
import org.pdfclown.util.NotImplementedException;

/**
  High-level representation of a PDF object.
  <h3>Remarks</h3>
  <p>Specialized objects don't inherit directly from their low-level counterparts
  (e.g. {@link org.pdfclown.documents.contents.Contents Contents} extends {@link org.pdfclown.objects.PdfStream PdfStream},
  {@link org.pdfclown.documents.Pages Pages} extends {@link org.pdfclown.objects.PdfArray PdfArray}
  and so on) because there's no plain one-to-one mapping between primitive PDF types and specialized instances:
  the <code>Content</code> entry of <code>Page</code> dictionaries may be a simple reference to a <code>PdfStream</code>
  or a <code>PdfArray</code> of references to <code>PdfStream</code>-s, <code>Pages</code> collections may be spread
  across a B-tree instead of a flat <code>PdfArray</code> and so on.</p>
  <p>So, <i>in order to hide all these annoying inner workings, I chose to adopt a composition pattern instead of
  the apparently-reasonable (but actually awkward!) inheritance pattern</i>.
  Nonetheless, users can navigate through the low-level structure accessing the {@link #getBaseDataObject()} method.</p>

  @author Stefano Chizzolini (http://www.stefanochizzolini.it)
  @version 0.1.0
*/
public abstract class PdfObjectWrapper<TDataObject extends PdfDataObject>
{
  // <class>
  // <dynamic>
  // <fields>
  private TDataObject baseDataObject;
  private PdfDirectObject baseObject;
  private PdfIndirectObject container;
  // </fields>

  // <constructors>
  protected PdfObjectWrapper(
    File context,
    TDataObject baseDataObject
    )
  {
    this(
      context.register(baseDataObject),
      null
      );
  }

  /**
    @param baseObject Base PDF object. MUST be a {@link PdfReference PdfReference}
    everytime available.
    @param container Indirect object containing the base object.
  */
  protected PdfObjectWrapper(
    PdfDirectObject baseObject,
    PdfIndirectObject container
    )
  {
    setBaseObject(baseObject);
    setContainer(container);
  }
  // </constructors>

  // <interface>
  // <public>
  /**
    Gets a clone of the object, registered inside the given document context.

    @param context Which document the clone has to be registered in.
  */
  public abstract Object clone(
    Document context
    );

  /**
    Removes the object from its document context.
    <h3>Remarks</h3>
    <p>The object is no more usable after this method returns.</p>

    @return Whether the object was actually decontextualized (only indirect objects can be
    decontextualized).
  */
  public boolean delete(
    )
  {
    // Is the object indirect?
    if(baseObject instanceof PdfReference) // Indirect object.
    {
      ((PdfReference)baseObject).delete();
      return true;
    }
    else // Direct object.
    {return false;}
  }

  /**
    Gets the underlying data object.
  */
  public TDataObject getBaseDataObject(
    )
  {return baseDataObject;}

  /**
    Gets the underlying reference object, if available;
    otherwise, behaves like {@link #getBaseDataObject() getBaseDataObject()}.
  */
  public PdfDirectObject getBaseObject(
    )
  {return baseObject;}

  /**
    Gets the indirect object containing the base object.
    <h3>Remarks</h3>
    <p>It's used for update purposes.</p>
  */
  public PdfIndirectObject getContainer(
    )
  {return container;}

  /**
    Gets the document context.
  */
  public Document getDocument(
    )
  {return container.getFile().getDocument();}

  /**
    Gets the file context.
  */
  public File getFile(
    )
  {return container.getFile();}

  /**
    Manually update the underlying indirect object.
  */
  public void update(
    )
  {container.update();}
  // </public>

  // <protected>
//TODO:remove?
//  /**
//    Checks whether the caller context is compatible with the {@link Document#getVersion() document's conformance version}.
//    <p>For performance reasons, the relevant feature should be passed as argument to {@link #checkCompatibility(Object)} whenever possible.</p>
//
//    @throws RuntimeException In case of version conflict (see {@link org.pdfclown.documents.Document.Configuration.CompatibilityModeEnum#Strict Strict compatibility mode}).
//    @see #checkCompatibility(Object)
//    @since 0.1.0
//  */
//  protected void checkCompatibility(
//    )
//  {checkCompatibility(null);}

  /**
    Checks whether the specified feature is compatible with the {@link Document#getVersion() document's conformance version}.

    @param feature Entity whose compatibility has to be checked. Supported types:
      <ul>
        <li>{@link VersionEnum}</li>
        <li>{@link String Property name} resolvable to an {@link AnnotatedElement annotated getter method}</li>
        <li>{@link AnnotatedElement}</li>
      </ul>
      In case of <code>null</code>, this method tries to retrieve the caller method's compatibility version.
    @throws RuntimeException In case of version conflict (see {@link org.pdfclown.documents.Document.Configuration.CompatibilityModeEnum#Strict Strict compatibility mode}).
    @since 0.1.0
  */
  protected void checkCompatibility(
    Object feature
    )
  {
    /*
      TODO: Caching!
    */
    CompatibilityModeEnum compatibilityMode = getDocument().getConfiguration().getCompatibilityMode();
    if(compatibilityMode == CompatibilityModeEnum.Passthrough) // No check required.
      return;

    if(feature instanceof Collection<?>)
    {
      for(Object featureItem : (Collection<?>)feature)
      {checkCompatibility(featureItem);}
      return;
    }

    Version featureVersion;
    if(feature instanceof VersionEnum) // Explicit version.
    {featureVersion = ((VersionEnum)feature).getVersion();}
    else // Implicit version (element annotation).
    {
      PDF annotation;
      {
        if(feature instanceof String) // Property name.
        {
          BeanInfo classInfo;
          try
          {classInfo = Introspector.getBeanInfo(getClass());}
          catch(IntrospectionException e)
          {throw new RuntimeException(e);}
          for(PropertyDescriptor property : classInfo.getPropertyDescriptors())
          {
            if(feature.equals(property.getName()))
            {
              feature = property.getReadMethod();
              break;
            }
          }
        }
        else if(feature instanceof Enum<?>) // Enum constant.
        {
          try
          {feature = feature.getClass().getField(((Enum<?>)feature).name());}
          catch(Exception e)
          {throw new RuntimeException(e);}
        }
//TODO:remove?
//        else if(feature == null) // Implicit feature.
//        {
//          try
//          {
//            /*
//              NOTE: I know this is a somewhat weird (considering both OO design and performance) technique,
//              but at the moment I couldn't figure out a better solution for dynamically retrieving
//              the caller context's annotations in case of no arguments.
//             */
//            StackTraceElement callerStackElement = (StackTraceElement)Thread.currentThread().getStackTrace()[2];
//            Class<?> callerClass = Class.forName(callerStackElement.getClassName());
//            String callerMethodName = callerStackElement.getMethodName();
//            for(Method method : callerClass.getMethods())
//            {
//              if(method.getName().equals(callerMethodName))
//              {
//                feature = method; // NOTE: I assume that any overload of the same method conforms to the same version.
//                break;
//              }
//            }
//          }
//          catch(Exception e)
//          {throw new RuntimeException(e);}
//        }
        if(!(feature instanceof AnnotatedElement))
          throw new IllegalArgumentException("Feature type '" + feature.getClass().getName() + "' not supported.");

        while(true)
        {
           annotation = ((AnnotatedElement)feature).getAnnotation(PDF.class);
           if(annotation != null)
             break;

           if(feature instanceof Member)
           {feature = ((Member)feature).getDeclaringClass();}
           else if(feature instanceof Class<?>)
           {
             Class<?> containerClass = ((Class<?>)feature).getDeclaringClass();
             feature = (containerClass != null ? containerClass : ((Class<?>)feature).getPackage());
           }
           else // Element hierarchy walk complete.
             return; // NOTE: As no annotation is available, we assume the feature has no specific compatibility requirements.
        }
      }
      featureVersion = annotation.value().getVersion();
    }
    // Is the feature version compatible?
    if(getDocument().getVersion().compareTo(featureVersion) >= 0)
      return;

    // The feature version is NOT compatible: how to solve the conflict?
    switch(compatibilityMode)
    {
      case Loose: // Accepts the feature version.
        // Synchronize the document version!
        getDocument().setVersion(featureVersion);
        break;
      case Strict: // Refuses the feature version.
        // Throw a violation to the document version!
        throw new RuntimeException("Incompatible feature (version " + featureVersion + " was required against document version " + getDocument().getVersion());
      default:
        throw new NotImplementedException("Unhandled compatibility mode: " + compatibilityMode.name());
    }
  }

  @SuppressWarnings("unchecked")
  protected void setBaseObject(
    PdfDirectObject value
    )
  {
    baseObject = value;
    baseDataObject = (TDataObject)File.resolve(baseObject);
  }
  // </protected>

  // <internal>
  /**
    For internal use only.
  */
  public void setContainer(
    PdfIndirectObject value
    )
  {
    if(baseObject instanceof PdfReference) // Base object is indirect (self-contained).
    {container = ((PdfReference)baseObject).getIndirectObject();}
    else // Base object is direct (contained).
    {container = value;}
  }
  // </internal>
  // </interface>
  // </dynamic>
  // </class>
}
TOP

Related Classes of org.pdfclown.objects.PdfObjectWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.