You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ju...@apache.org on 2008/06/22 21:09:18 UTC
svn commit: r670399 [3/6] - in /incubator/pdfbox/trunk/jempbox: ./ lib/
licenses/ licenses/checkstyle/ licenses/jempbox/ licenses/junit/ src/
src/org/ src/org/jempbox/ src/org/jempbox/impl/ src/org/jempbox/xmp/
src/org/jempbox/xmp/pdfa/ src/test/ src/t...
Added: incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPMetadata.java
URL: http://svn.apache.org/viewvc/incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPMetadata.java?rev=670399&view=auto
==============================================================================
--- incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPMetadata.java (added)
+++ incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPMetadata.java Sun Jun 22 12:09:15 2008
@@ -0,0 +1,790 @@
+/**
+ * Copyright (c) 2006, www.jempbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.jempbox.org
+ *
+ */
+package org.jempbox.xmp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.TransformerException;
+
+import org.jempbox.impl.XMLUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.xml.sax.InputSource;
+
+/**
+ * This class represents the top level XMP data structure and gives access to
+ * the various schemas that are available as part of the XMP specification.
+ *
+ * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
+ * @version $Revision: 1.10 $
+ */
+public class XMPMetadata
+{
+ /**
+ * Supported encoding for persisted XML.
+ */
+ public static final String ENCODING_UTF8 = "UTF-8";
+
+ /**
+ * Supported encoding for persisted XML.
+ */
+ public static final String ENCODING_UTF16BE = "UTF-16BE";
+
+ /**
+ * Supported encoding for persisted XML.
+ */
+ public static final String ENCODING_UTF16LE = "UTF-16LE";
+
+ /**
+ * The DOM representation of the metadata.
+ */
+ protected Document xmpDocument;
+
+ /**
+ * The encoding of the XMP document. Default is UTF8.
+ */
+ protected String encoding = ENCODING_UTF8;
+
+ /**
+ * A mapping of namespaces.
+ */
+ protected Map nsMappings = new HashMap();
+
+ /**
+ * Default constructor, creates blank XMP doc.
+ *
+ * @throws IOException
+ * If there is an error creating the initial document.
+ */
+ public XMPMetadata() throws IOException
+ {
+ xmpDocument = XMLUtil.newDocument();
+ ProcessingInstruction beginXPacket = xmpDocument
+ .createProcessingInstruction("xpacket",
+ "begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"");
+
+ xmpDocument.appendChild(beginXPacket);
+ Element xmpMeta = xmpDocument.createElementNS("adobe:ns:meta/",
+ "x:xmpmeta");
+ xmpMeta.setAttributeNS(XMPSchema.NS_NAMESPACE, "xmlns:x",
+ "adobe:ns:meta/");
+
+ xmpDocument.appendChild(xmpMeta);
+
+ Element rdf = xmpDocument.createElement("rdf:RDF");
+ rdf.setAttributeNS(XMPSchema.NS_NAMESPACE, "xmlns:rdf",
+ "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+
+ xmpMeta.appendChild(rdf);
+
+ ProcessingInstruction endXPacket = xmpDocument
+ .createProcessingInstruction("xpacket", "end=\"w\"");
+ xmpDocument.appendChild(endXPacket);
+ init();
+ }
+
+ /**
+ * Constructor from an existing XML document.
+ *
+ * @param doc
+ * The root XMP document.
+ */
+ public XMPMetadata(Document doc)
+ {
+ xmpDocument = doc;
+ init();
+ }
+
+ private void init()
+ {
+ nsMappings.put(XMPSchemaPDF.NAMESPACE, XMPSchemaPDF.class);
+ nsMappings.put(XMPSchemaBasic.NAMESPACE, XMPSchemaBasic.class);
+ nsMappings
+ .put(XMPSchemaDublinCore.NAMESPACE, XMPSchemaDublinCore.class);
+ nsMappings.put(XMPSchemaMediaManagement.NAMESPACE,
+ XMPSchemaMediaManagement.class);
+ nsMappings.put(XMPSchemaRightsManagement.NAMESPACE,
+ XMPSchemaRightsManagement.class);
+ nsMappings.put(XMPSchemaBasicJobTicket.NAMESPACE,
+ XMPSchemaBasicJobTicket.class);
+ nsMappings.put(XMPSchemaDynamicMedia.NAMESPACE,
+ XMPSchemaDynamicMedia.class);
+ nsMappings.put(XMPSchemaPagedText.NAMESPACE, XMPSchemaPagedText.class);
+ nsMappings.put(XMPSchemaIptc4xmpCore.NAMESPACE,
+ XMPSchemaIptc4xmpCore.class);
+ nsMappings.put(XMPSchemaPhotoshop.NAMESPACE, XMPSchemaPhotoshop.class);
+ }
+
+ /**
+ * Will add a XMPSchema to the set of identified schemas.
+ *
+ * The class needs to have a constructor with parameter Element
+ *
+ * @param namespace
+ * The namespace URI of the schmema for instance
+ * http://purl.org/dc/elements/1.1/.
+ * @param xmpSchema
+ * The schema to associated this identifier with.
+ */
+ public void addXMLNSMapping(String namespace, Class xmpSchema)
+ {
+
+ if (!(XMPSchema.class.isAssignableFrom(xmpSchema)))
+ {
+ throw new IllegalArgumentException(
+ "Only XMPSchemas can be mapped to.");
+ }
+
+ nsMappings.put(namespace, xmpSchema);
+ }
+
+ /**
+ * Get the PDF Schema.
+ *
+ * @return The first PDF schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaPDF getPDFSchema() throws IOException
+ {
+ return (XMPSchemaPDF) getSchemaByClass(XMPSchemaPDF.class);
+ }
+
+ /**
+ * Get the Basic Schema.
+ *
+ * @return The first Basic schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaBasic getBasicSchema() throws IOException
+ {
+ return (XMPSchemaBasic) getSchemaByClass(XMPSchemaBasic.class);
+ }
+
+ /**
+ * Get the Dublin Core Schema.
+ *
+ * @return The first Dublin schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaDublinCore getDublinCoreSchema() throws IOException
+ {
+ return (XMPSchemaDublinCore) getSchemaByClass(XMPSchemaDublinCore.class);
+ }
+
+ /**
+ * Get the Media Management Schema.
+ *
+ * @return The first Media Management schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaMediaManagement getMediaManagementSchema()
+ throws IOException
+ {
+ return (XMPSchemaMediaManagement) getSchemaByClass(XMPSchemaMediaManagement.class);
+ }
+
+ /**
+ * Get the Schema Rights Schema.
+ *
+ * @return The first Schema Rights schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaRightsManagement getRightsManagementSchema()
+ throws IOException
+ {
+ return (XMPSchemaRightsManagement) getSchemaByClass(XMPSchemaRightsManagement.class);
+ }
+
+ /**
+ * Get the Job Ticket Schema.
+ *
+ * @return The first Job Ticket schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaBasicJobTicket getBasicJobTicketSchema() throws IOException
+ {
+ return (XMPSchemaBasicJobTicket) getSchemaByClass(XMPSchemaBasicJobTicket.class);
+ }
+
+ /**
+ * Get the Dynamic Media Schema.
+ *
+ * @return The first Dynamic Media schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaDynamicMedia getDynamicMediaSchema() throws IOException
+ {
+ return (XMPSchemaDynamicMedia) getSchemaByClass(XMPSchemaDynamicMedia.class);
+ }
+
+ /**
+ * Get the Paged Text Schema.
+ *
+ * @return The first Paged Text schema in the list.
+ *
+ * @throws IOException
+ * If there is an error accessing the schema.
+ */
+ public XMPSchemaPagedText getPagedTextSchema() throws IOException
+ {
+ return (XMPSchemaPagedText) getSchemaByClass(XMPSchemaPagedText.class);
+ }
+
+ /**
+ * Add a new Media Management schema.
+ *
+ * @return The newly added schema.
+ */
+ public XMPSchemaMediaManagement addMediaManagementSchema()
+ {
+ XMPSchemaMediaManagement schema = new XMPSchemaMediaManagement(this);
+ return (XMPSchemaMediaManagement) basicAddSchema(schema);
+ }
+
+ /**
+ * Add a new Rights Managment schema.
+ *
+ * @return The newly added schema.
+ */
+ public XMPSchemaRightsManagement addRightsManagementSchema()
+ {
+ XMPSchemaRightsManagement schema = new XMPSchemaRightsManagement(this);
+ return (XMPSchemaRightsManagement) basicAddSchema(schema);
+ }
+
+ /**
+ * Add a new Job Ticket schema.
+ *
+ * @return The newly added schema.
+ */
+ public XMPSchemaBasicJobTicket addBasicJobTicketSchema()
+ {
+ XMPSchemaBasicJobTicket schema = new XMPSchemaBasicJobTicket(this);
+ return (XMPSchemaBasicJobTicket) basicAddSchema(schema);
+ }
+
+ /**
+ * Add a new Dynamic Media schema.
+ *
+ * @return The newly added schema.
+ */
+ public XMPSchemaDynamicMedia addDynamicMediaSchema()
+ {
+ XMPSchemaDynamicMedia schema = new XMPSchemaDynamicMedia(this);
+ return (XMPSchemaDynamicMedia) basicAddSchema(schema);
+ }
+
+ /**
+ * Add a new Paged Text schema.
+ *
+ * @return The newly added schema.
+ */
+ public XMPSchemaPagedText addPagedTextSchema()
+ {
+ XMPSchemaPagedText schema = new XMPSchemaPagedText(this);
+ return (XMPSchemaPagedText) basicAddSchema(schema);
+ }
+
+ /**
+ * Add a custom schema to the root rdf. The schema has to have been created
+ * as a child of this XMPMetadata.
+ *
+ * @param schema
+ * The schema to add.
+ */
+ public void addSchema(XMPSchema schema)
+ {
+ Element rdf = getRDFElement();
+ rdf.appendChild(schema.getElement());
+ }
+
+ /**
+ * Save the XMP document to a file.
+ *
+ * @param file
+ * The file to save the XMP document to.
+ *
+ * @throws Exception
+ * If there is an error while writing to the stream.
+ */
+ public void save(String file) throws Exception
+ {
+ XMLUtil.save(xmpDocument, file, encoding);
+ }
+
+ /**
+ * Save the XMP document to a stream.
+ *
+ * @param outStream
+ * The stream to save the XMP document to.
+ *
+ * @throws TransformerException
+ * If there is an error while writing to the stream.
+ */
+ public void save(OutputStream outStream) throws TransformerException
+ {
+ XMLUtil.save(xmpDocument, outStream, encoding);
+ }
+
+ /**
+ * Get the XML document as a byte array.
+ *
+ * @return The metadata as an XML byte stream.
+ * @throws Exception
+ * If there is an error creating the stream.
+ */
+ public byte[] asByteArray() throws Exception
+ {
+ return XMLUtil.asByteArray(xmpDocument, encoding);
+ }
+
+ /**
+ * Get the XML document from this object.
+ *
+ * @return This object as an XML document.
+ */
+ public Document getXMPDocument()
+ {
+ return xmpDocument;
+ }
+
+ /**
+ * Generic add schema method.
+ *
+ * @param schema
+ * The schema to add.
+ *
+ * @return The newly added schema.
+ */
+ protected XMPSchema basicAddSchema(XMPSchema schema)
+ {
+ Element rdf = getRDFElement();
+ rdf.appendChild(schema.getElement());
+ return schema;
+ }
+
+ /**
+ * Create and add a new PDF Schema to this metadata. Typically a XMP
+ * document will only have one PDF schema (but multiple are supported) so it
+ * is recommended that you first check the existence of a PDF scheme by
+ * using getPDFSchema()
+ *
+ * @return A new blank PDF schema that is now part of the metadata.
+ */
+ public XMPSchemaPDF addPDFSchema()
+ {
+ XMPSchemaPDF schema = new XMPSchemaPDF(this);
+ return (XMPSchemaPDF) basicAddSchema(schema);
+ }
+
+ /**
+ * Create and add a new Dublin Core Schema to this metadata. Typically a XMP
+ * document will only have one schema for each type (but multiple are
+ * supported) so it is recommended that you first check the existence of a
+ * this scheme by using getDublinCoreSchema()
+ *
+ * @return A new blank PDF schema that is now part of the metadata.
+ */
+ public XMPSchemaDublinCore addDublinCoreSchema()
+ {
+ XMPSchemaDublinCore schema = new XMPSchemaDublinCore(this);
+ return (XMPSchemaDublinCore) basicAddSchema(schema);
+ }
+
+ /**
+ * Create and add a new Basic Schema to this metadata. Typically a XMP
+ * document will only have one schema for each type (but multiple are
+ * supported) so it is recommended that you first check the existence of a
+ * this scheme by using getDublinCoreSchema()
+ *
+ * @return A new blank PDF schema that is now part of the metadata.
+ */
+ public XMPSchemaBasic addBasicSchema()
+ {
+ XMPSchemaBasic schema = new XMPSchemaBasic(this);
+ return (XMPSchemaBasic) basicAddSchema(schema);
+ }
+
+ /**
+ * Create and add a new IPTC schema to this metadata.
+ *
+ * @return A new blank IPTC schema that is now part of the metadata.
+ */
+ public XMPSchemaIptc4xmpCore addIptc4xmpCoreSchema()
+ {
+ XMPSchemaIptc4xmpCore schema = new XMPSchemaIptc4xmpCore(this);
+ return (XMPSchemaIptc4xmpCore) basicAddSchema(schema);
+ }
+
+ /**
+ * Create and add a new Photoshop schema to this metadata.
+ *
+ * @return A new blank Photoshop schema that is now part of the metadata.
+ */
+ public XMPSchemaPhotoshop addPhotoshopSchema()
+ {
+ XMPSchemaPhotoshop schema = new XMPSchemaPhotoshop(this);
+ return (XMPSchemaPhotoshop) basicAddSchema(schema);
+ }
+
+ /**
+ * The encoding used to write the XML. Default value:UTF-8<br/> See the
+ * ENCODING_XXX constants
+ *
+ * @param xmlEncoding
+ * The encoding to write the XML as.
+ */
+ public void setEncoding(String xmlEncoding)
+ {
+ encoding = xmlEncoding;
+ }
+
+ /**
+ * Get the current encoding that will be used to write the XML.
+ *
+ * @return The current encoding to write the XML to.
+ */
+ public String getEncoding()
+ {
+ return encoding;
+ }
+
+ /**
+ * Get the root RDF element.
+ *
+ * @return The root RDF element.
+ */
+ private Element getRDFElement()
+ {
+ Element rdf = null;
+ NodeList nodes = xmpDocument.getElementsByTagName("rdf:RDF");
+ if (nodes.getLength() > 0)
+ {
+ rdf = (Element) nodes.item(0);
+ }
+ return rdf;
+ }
+
+ /**
+ * Load metadata from the filesystem.
+ *
+ * @param file
+ * The file to load the metadata from.
+ *
+ * @return The loaded XMP document.
+ *
+ * @throws IOException
+ * If there is an error reading the data.
+ */
+ public static XMPMetadata load(String file) throws IOException
+ {
+ return new XMPMetadata(XMLUtil.parse(file));
+ }
+
+ /**
+ * Load a schema from an input source.
+ *
+ * @param is
+ * The input source to load the schema from.
+ *
+ * @return The loaded/parsed schema.
+ *
+ * @throws IOException
+ * If there was an error while loading the schema.
+ */
+ public static XMPMetadata load(InputSource is) throws IOException
+ {
+ return new XMPMetadata(XMLUtil.parse(is));
+ }
+
+ /**
+ * Load metadata from the filesystem.
+ *
+ * @param is
+ * The stream to load the data from.
+ *
+ * @return The loaded XMP document.
+ *
+ * @throws IOException
+ * If there is an error reading the data.
+ */
+ public static XMPMetadata load(InputStream is) throws IOException
+ {
+ return new XMPMetadata(XMLUtil.parse(is));
+ }
+
+ /**
+ * Test main program.
+ *
+ * @param args
+ * The command line arguments.
+ * @throws Exception
+ * If there is an error.
+ */
+ public static void main(String[] args) throws Exception
+ {
+ XMPMetadata metadata = new XMPMetadata();
+ XMPSchemaPDF pdf = metadata.addPDFSchema();
+ pdf.setAbout("uuid:b8659d3a-369e-11d9-b951-000393c97fd8");
+ pdf.setKeywords("ben,bob,pdf");
+ pdf.setPDFVersion("1.3");
+ pdf.setProducer("Acrobat Distiller 6.0.1 for Macintosh");
+
+ XMPSchemaDublinCore dc = metadata.addDublinCoreSchema();
+ dc.addContributor("Ben Litchfield");
+ dc.addContributor("Solar Eclipse");
+ dc.addContributor("Some Other Guy");
+
+ XMPSchemaBasic basic = metadata.addBasicSchema();
+ Thumbnail t = new Thumbnail(metadata);
+ t.setFormat(Thumbnail.FORMAT_JPEG);
+ t.setImage("IMAGE_DATA");
+ t.setHeight(new Integer(100));
+ t.setWidth(new Integer(200));
+ basic.setThumbnail(t);
+ basic.setBaseURL("http://www.pdfbox.org/");
+
+ List schemas = metadata.getSchemas();
+ System.out.println("schemas=" + schemas);
+
+ metadata.save("test.xmp");
+ }
+
+ /**
+ * This will get a list of XMPSchema(or subclass) objects.
+ *
+ * @return A non null read-only list of schemas that are part of this
+ * metadata.
+ *
+ * @throws IOException
+ * If there is an error creating a specific schema.
+ */
+ public List getSchemas() throws IOException
+ {
+ NodeList schemaList = xmpDocument
+ .getElementsByTagName("rdf:Description");
+ List retval = new ArrayList(schemaList.getLength());
+ for (int i = 0; i < schemaList.getLength(); i++)
+ {
+ Element schema = (Element) schemaList.item(i);
+ boolean found = false;
+ NamedNodeMap attributes = schema.getAttributes();
+ for (int j = 0; j < attributes.getLength() && !found; j++)
+ {
+ Node attribute = attributes.item(j);
+ String name = attribute.getNodeName();
+ String value = attribute.getNodeValue();
+ if (name.startsWith("xmlns:") && nsMappings.containsKey(value))
+ {
+ Class schemaClass = (Class) nsMappings.get(value);
+ try
+ {
+ Constructor ctor = schemaClass
+ .getConstructor(new Class[] { Element.class,
+ String.class });
+ retval.add(ctor.newInstance(new Object[] { schema,
+ name.substring(6) }));
+ found = true;
+ }
+ catch(NoSuchMethodException e)
+ {
+ throw new IOException(
+ "Error: Class "
+ + schemaClass.getName()
+ + " must have a constructor with the signature of "
+ + schemaClass.getName()
+ + "( org.w3c.dom.Element, java.lang.String )");
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ throw new IOException(e.getMessage());
+ }
+ }
+ }
+ if (!found)
+ {
+ retval.add(new XMPSchema(schema, null));
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Will return all schemas that fit the given namespaceURI. Which is only
+ * done by using the namespace mapping (nsMapping) and not by actually
+ * checking the xmlns property.
+ *
+ * @param namespaceURI
+ * The namespaceURI to filter for.
+ * @return A list containing the found schemas or an empty list if non are
+ * found or the namespaceURI could not be found in the namespace
+ * mapping.
+ * @throws IOException
+ * If an operation on the document fails.
+ */
+ public List getSchemasByNamespaceURI(String namespaceURI)
+ throws IOException
+ {
+
+ List l = getSchemas();
+ List result = new LinkedList();
+
+ Class schemaClass = (Class) nsMappings.get(namespaceURI);
+ if (schemaClass == null)
+ {
+ return result;
+ }
+
+ Iterator i = l.iterator();
+ while (i.hasNext())
+ {
+ XMPSchema schema = (XMPSchema) i.next();
+
+ if (schemaClass.isAssignableFrom(schema.getClass()))
+ {
+ result.add(schema);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This will return true if the XMP contains an unknown schema.
+ *
+ * @return True if an unknown schema is found, false otherwise
+ *
+ * @throws IOException
+ * If there is an error
+ */
+ public boolean hasUnknownSchema() throws IOException
+ {
+ NodeList schemaList = xmpDocument
+ .getElementsByTagName("rdf:Description");
+ for (int i = 0; i < schemaList.getLength(); i++)
+ {
+ Element schema = (Element) schemaList.item(i);
+ NamedNodeMap attributes = schema.getAttributes();
+ for (int j = 0; j < attributes.getLength(); j++)
+ {
+ Node attribute = attributes.item(j);
+ String name = attribute.getNodeName();
+ String value = attribute.getNodeValue();
+ if (name.startsWith("xmlns:") && !nsMappings.containsKey(value)
+ && !value.equals(ResourceEvent.NAMESPACE))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tries to retrieve a schema from this by classname.
+ *
+ * @param targetSchema
+ * Class for targetSchema.
+ *
+ * @return XMPSchema or null if no target is found.
+ *
+ * @throws IOException
+ * if there was an error creating the schemas of this.
+ */
+ public XMPSchema getSchemaByClass(Class targetSchema) throws IOException
+ {
+ Iterator iter = getSchemas().iterator();
+ while (iter.hasNext())
+ {
+ XMPSchema element = (XMPSchema) iter.next();
+ if (element.getClass().getName().equals(targetSchema.getName()))
+ {
+ return element;
+ }
+ }
+ // not found
+ return null;
+ }
+
+ /**
+ * Merge this metadata with the given metadata object.
+ *
+ * @param metadata The metadata to merge with this document.
+ *
+ * @throws IOException If there is an error merging the data.
+ */
+ public void merge(XMPMetadata metadata) throws IOException
+ {
+ List schemas2 = metadata.getSchemas();
+ for (Iterator iterator = schemas2.iterator(); iterator.hasNext();)
+ {
+ XMPSchema schema2 = (XMPSchema) iterator.next();
+ XMPSchema schema1 = getSchemaByClass(schema2.getClass());
+ if (schema1 == null)
+ {
+ Element rdf = getRDFElement();
+ rdf.appendChild(xmpDocument.importNode(schema2.getElement(),
+ true));
+ }
+ else
+ {
+ schema1.merge(schema2);
+ }
+ }
+ }
+}
Added: incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchema.java
URL: http://svn.apache.org/viewvc/incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchema.java?rev=670399&view=auto
==============================================================================
--- incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchema.java (added)
+++ incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchema.java Sun Jun 22 12:09:15 2008
@@ -0,0 +1,1094 @@
+/**
+ * Copyright (c) 2006, www.jempbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.jempbox.org
+ *
+ */
+package org.jempbox.xmp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jempbox.impl.DateConverter;
+import org.jempbox.impl.XMLUtil;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * This class represents a metadata schema that can be stored in an XMP
+ * document. It handles all generic properties that are available. See
+ * subclasses for access to specific properties.
+ *
+ * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
+ * @version $Revision: 1.8 $
+ */
+public class XMPSchema
+{
+ /**
+ * The standard xmlns namespace.
+ */
+ public static final String NS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
+
+ /**
+ * The XML schema prefix.
+ */
+ protected String prefix;
+
+ /**
+ * The DOM representation of this object.
+ */
+ protected Element schema = null;
+
+ /**
+ * Create a new blank schema that can be populated.
+ *
+ * @param parent
+ * The parent XMP document that this schema will be part of.
+ * @param namespaceName
+ * The name of the namespace, ie pdf,dc,...
+ * @param namespaceURI
+ * The URI of the namespace, ie "http://ns.adobe.com/pdf/1.3/"
+ */
+ public XMPSchema(XMPMetadata parent, String namespaceName,
+ String namespaceURI)
+ {
+ schema = parent.xmpDocument.createElementNS(
+ "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ "rdf:Description");
+ prefix = namespaceName;
+ schema.setAttributeNS(NS_NAMESPACE, "xmlns:" + namespaceName,
+ namespaceURI);
+ }
+
+ /**
+ * Create schema from an existing XML element.
+ *
+ * @param element
+ * The existing XML element.
+ * @param aPrefix
+ * The XML prefix.
+ */
+ public XMPSchema(Element element, String aPrefix)
+ {
+ schema = element;
+ if (aPrefix != null)
+ {
+ prefix = aPrefix;
+ }
+ else
+ {
+ prefix = "";
+ }
+ }
+
+ /**
+ * Get the XML element that is represented by this schema.
+ *
+ * @return The root XML element of this schema.
+ */
+ public Element getElement()
+ {
+ return schema;
+ }
+
+ /**
+ * Get the RDF about attribute.
+ *
+ * @return The RDF 'about' attribute.
+ */
+ public String getAbout()
+ {
+ return getTextProperty("rdf:about");
+ }
+
+ /**
+ * Set the RDF 'about' attribute. Passing in null will clear this attribute.
+ *
+ * @param about
+ * The new RFD about value.
+ */
+ public void setAbout(String about)
+ {
+ if (about == null)
+ {
+ schema.removeAttribute("rdf:about");
+ }
+ else
+ {
+ schema.setAttribute("rdf:about", about);
+ }
+ }
+
+ /**
+ * Set a simple text property on the schema.
+ *
+ * @param propertyName
+ * The name of the property, it must contain the namespace
+ * prefix, ie "pdf:Keywords"
+ * @param propertyValue
+ * The value for the property, can be any string. Passing null
+ * will remove the property.
+ */
+ public void setTextProperty(String propertyName, String propertyValue)
+ {
+ if (propertyValue == null)
+ {
+ schema.removeAttribute(propertyName);
+ NodeList keywordList = schema.getElementsByTagName(propertyName);
+ for (int i = 0; i < keywordList.getLength(); i++)
+ {
+ schema.removeChild(keywordList.item(i));
+ }
+
+ }
+ else
+ {
+ if (schema.hasAttribute(propertyName))
+ {
+ schema.setAttribute(propertyName, propertyValue);
+ }
+ else
+ {
+ if (schema.hasChildNodes())
+ {
+ NodeList nodeList = schema
+ .getElementsByTagName(propertyName);
+ if (nodeList.getLength() > 0)
+ {
+ Element node = (Element) nodeList.item(0);
+ node.setNodeValue(propertyValue);
+ }
+ else
+ {
+ Element textNode = schema.getOwnerDocument()
+ .createElement(propertyName);
+ XMLUtil.setStringValue(textNode, propertyValue);
+ schema.appendChild(textNode);
+ }
+ }
+ else
+ {
+ schema.setAttribute(propertyName, propertyValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the value of a simple text property.
+ *
+ * @param propertyName
+ * The name of the property to get, it must include the namespace
+ * prefix. ie "pdf:Keywords".
+ *
+ * @return The value of the text property or the null if there is no value.
+ */
+ public String getTextProperty(String propertyName)
+ {
+ // propertyValue == null does not work, since getAttribute returns the
+ // empty string if the attribute is not found
+
+ if (schema.hasAttribute(propertyName))
+ {
+ return schema.getAttribute(propertyName);
+ }
+ else
+ {
+ NodeList nodes = schema.getElementsByTagName(propertyName);
+ if (nodes.getLength() > 0)
+ {
+ Element node = (Element) nodes.item(0);
+ return XMLUtil.getStringValue(node);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Get the value of the property as a date.
+ *
+ * @param propertyName
+ * The fully qualified property name for the date.
+ *
+ * @return The value of the property as a date.
+ *
+ * @throws IOException
+ * If there is an error converting the value to a date.
+ */
+ public Calendar getDateProperty(String propertyName) throws IOException
+ {
+ return DateConverter.toCalendar(getTextProperty(propertyName));
+ }
+
+ /**
+ * Set the value of the property as a date.
+ *
+ * @param propertyName
+ * The fully qualified property name for the date.
+ * @param date
+ * The date to set, or null to clear.
+ */
+ public void setDateProperty(String propertyName, Calendar date)
+ {
+ setTextProperty(propertyName, DateConverter.toISO8601(date));
+ }
+
+ /**
+ * Get the value of the property as a boolean.
+ *
+ * @param propertyName
+ * The fully qualified property name for the boolean.
+ *
+ * @return The value of the property as a boolean.
+ */
+ public Boolean getBooleanProperty(String propertyName)
+ {
+ Boolean value = null;
+ String stringValue = getTextProperty(propertyName);
+ if (stringValue != null)
+ {
+ value = stringValue.equals("True") ? Boolean.TRUE : Boolean.FALSE;
+ }
+ return value;
+ }
+
+ /**
+ * Set the value of the property as a boolean.
+ *
+ * @param propertyName
+ * The fully qualified property name for the boolean.
+ * @param bool
+ * The boolean to set, or null to clear.
+ */
+ public void setBooleanProperty(String propertyName, Boolean bool)
+ {
+ String value = null;
+ if (bool != null)
+ {
+ value = bool.booleanValue() ? "True" : "False";
+ }
+ setTextProperty(propertyName, value);
+ }
+
+ /**
+ * Get the value of the property as an integer.
+ *
+ * @param propertyName
+ * The fully qualified property name for the integer.
+ *
+ * @return The value of the property as an integer.
+ */
+ public Integer getIntegerProperty(String propertyName)
+ {
+ Integer retval = null;
+ String intProperty = getTextProperty(propertyName);
+ if (intProperty.equals(""))
+ {
+ retval = new Integer(intProperty);
+ }
+ return retval;
+ }
+
+ /**
+ * Set the value of the property as an integer.
+ *
+ * @param propertyName
+ * The fully qualified property name for the integer.
+ * @param intValue
+ * The int to set, or null to clear.
+ */
+ public void setIntegerProperty(String propertyName, Integer intValue)
+ {
+ String textValue = null;
+ if (intValue != null)
+ {
+ textValue = intValue.toString();
+ }
+ setTextProperty(propertyName, textValue);
+ }
+
+ /**
+ * Remove all matching entries with the given value from the bag.
+ *
+ * @param bagName
+ * The name of the bag, it must include the namespace prefix. ie
+ * "pdf:Keywords".
+ * @param bagValue
+ * The value to remove from the bagList.
+ */
+ public void removeBagValue(String bagName, String bagValue)
+ {
+ Element bagElement = null;
+ NodeList nodes = schema.getElementsByTagName(bagName);
+ if (nodes.getLength() > 0)
+ {
+ Element contElement = (Element) nodes.item(0);
+ NodeList bagList = contElement.getElementsByTagName("rdf:Bag");
+ if (bagList.getLength() > 0)
+ {
+ bagElement = (Element) bagList.item(0);
+ NodeList items = bagElement.getElementsByTagName("rdf:li");
+ for (int i = items.getLength() - 1; i >= 0; i--)
+ {
+ Element li = (Element) items.item(i);
+ String value = XMLUtil.getStringValue(li);
+ if (value.equals(bagValue))
+ {
+ bagElement.removeChild(li);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add an entry to a bag property.
+ *
+ * @param bagName
+ * The name of the bag, it must include the namespace prefix. ie
+ * "pdf:Keywords".
+ * @param bagValue
+ * The value to add to the bagList.
+ */
+ public void addBagValue(String bagName, String bagValue)
+ {
+ Element bagElement = null;
+ NodeList nodes = schema.getElementsByTagName(bagName);
+ if (nodes.getLength() > 0)
+ {
+ Element contElement = (Element) nodes.item(0);
+ NodeList bagList = contElement.getElementsByTagName("rdf:Bag");
+ if (bagList.getLength() > 0)
+ {
+ bagElement = (Element) bagList.item(0);
+ }
+ }
+ else
+ {
+ Element contElement = schema.getOwnerDocument().createElement(
+ bagName);
+ schema.appendChild(contElement);
+ bagElement = schema.getOwnerDocument().createElement("rdf:Bag");
+ contElement.appendChild(bagElement);
+ }
+ Element liElement = schema.getOwnerDocument().createElement("rdf:li");
+ XMLUtil.setStringValue(liElement, bagValue);
+ bagElement.appendChild(liElement);
+ }
+
+ /**
+ * Get all the values of the bag property. This will return a list of
+ * java.lang.String objects, this is a read-only list.
+ *
+ * @param bagName
+ * The name of the bag property to get, it must include the
+ * namespace prefix. ie "pdf:Keywords"
+ *
+ * @return All of the values of the bag property in a list.
+ */
+ public List getBagList(String bagName)
+ {
+ List retval = null;
+ NodeList nodes = schema.getElementsByTagName(bagName);
+ if (nodes.getLength() > 0)
+ {
+ Element contributor = (Element) nodes.item(0);
+ NodeList bagList = contributor.getElementsByTagName("rdf:Bag");
+ if (bagList.getLength() > 0)
+ {
+ Element bag = (Element) bagList.item(0);
+ retval = new ArrayList();
+ NodeList items = bag.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ retval.add(XMLUtil.getStringValue(li));
+ }
+ retval = Collections.unmodifiableList(retval);
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * Remove all matching values from a sequence property.
+ *
+ * @param seqName
+ * The name of the sequence property. It must include the
+ * namespace prefix. ie "pdf:Keywords".
+ * @param seqValue
+ * The value to remove from the list.
+ */
+ public void removeSequenceValue(String seqName, String seqValue)
+ {
+ Element bagElement = null;
+ NodeList nodes = schema.getElementsByTagName(seqName);
+ if (nodes.getLength() > 0)
+ {
+ Element contElement = (Element) nodes.item(0);
+ NodeList bagList = contElement.getElementsByTagName("rdf:Seq");
+ if (bagList.getLength() > 0)
+ {
+ bagElement = (Element) bagList.item(0);
+ NodeList items = bagElement.getElementsByTagName("rdf:li");
+ for (int i = items.getLength() - 1; i >= 0; i--)
+ {
+ Element li = (Element) items.item(i);
+ String value = XMLUtil.getStringValue(li);
+ if (value.equals(seqValue))
+ {
+ bagElement.removeChild(li);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove a value from a sequence property. This will remove all entries
+ * from the list.
+ *
+ * @param seqName
+ * The name of the sequence property. It must include the
+ * namespace prefix. ie "pdf:Keywords".
+ * @param seqValue
+ * The value to remove from the list.
+ */
+ public void removeSequenceValue(String seqName, Elementable seqValue)
+ {
+ Element bagElement = null;
+ NodeList nodes = schema.getElementsByTagName(seqName);
+ if (nodes.getLength() > 0)
+ {
+ Element contElement = (Element) nodes.item(0);
+ NodeList bagList = contElement.getElementsByTagName("rdf:Seq");
+ if (bagList.getLength() > 0)
+ {
+ bagElement = (Element) bagList.item(0);
+ NodeList items = bagElement.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ if (li == seqValue.getElement())
+ {
+ bagElement.removeChild(li);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a new value to a sequence property.
+ *
+ * @param seqName
+ * The name of the sequence property, it must include the
+ * namespace prefix. ie "pdf:Keywords"
+ * @param seqValue
+ * The value to add to the sequence.
+ */
+ public void addSequenceValue(String seqName, String seqValue)
+ {
+ Element bagElement = null;
+ NodeList nodes = schema.getElementsByTagName(seqName);
+ if (nodes.getLength() > 0)
+ {
+ Element contElement = (Element) nodes.item(0);
+ NodeList bagList = contElement.getElementsByTagName("rdf:Seq");
+ if (bagList.getLength() > 0)
+ {
+ bagElement = (Element) bagList.item(0);
+ }
+ else
+ {
+ // xml is crap discard it
+ schema.removeChild(nodes.item(0));
+ }
+ }
+ if (bagElement == null)
+ {
+ Element contElement = schema.getOwnerDocument().createElement(
+ seqName);
+ schema.appendChild(contElement);
+ bagElement = schema.getOwnerDocument().createElement("rdf:Seq");
+ contElement.appendChild(bagElement);
+ }
+ Element liElement = schema.getOwnerDocument().createElement("rdf:li");
+ liElement.appendChild(schema.getOwnerDocument()
+ .createTextNode(seqValue));
+ bagElement.appendChild(liElement);
+ }
+
+ /**
+ * Add a new value to a sequence property.
+ *
+ * @param seqName
+ * The name of the sequence property, it must include the
+ * namespace prefix. ie "pdf:Keywords"
+ * @param seqValue
+ * The value to add to the sequence.
+ */
+ public void addSequenceValue(String seqName, Elementable seqValue)
+ {
+ Element bagElement = null;
+ NodeList nodes = schema.getElementsByTagName(seqName);
+ if (nodes.getLength() > 0)
+ {
+ Element contElement = (Element) nodes.item(0);
+ NodeList bagList = contElement.getElementsByTagName("rdf:Seq");
+ if (bagList.getLength() > 0)
+ {
+ bagElement = (Element) bagList.item(0);
+ }
+ }
+ else
+ {
+ Element contElement = schema.getOwnerDocument().createElement(
+ seqName);
+ schema.appendChild(contElement);
+ bagElement = schema.getOwnerDocument().createElement("rdf:Seq");
+ contElement.appendChild(bagElement);
+ }
+ bagElement.appendChild(seqValue.getElement());
+ }
+
+ /**
+ * Get all the values in a sequence property.
+ *
+ * @param seqName
+ * The name of the sequence property, it must include the
+ * namespace prefix. ie "pdf:Keywords".
+ *
+ * @return A read-only list of java.lang.String objects or null if the
+ * property does not exist.
+ */
+ public List getSequenceList(String seqName)
+ {
+ List retval = null;
+ NodeList nodes = schema.getElementsByTagName(seqName);
+ if (nodes.getLength() > 0)
+ {
+ Element contributor = (Element) nodes.item(0);
+ NodeList bagList = contributor.getElementsByTagName("rdf:Seq");
+ if (bagList.getLength() > 0)
+ {
+ Element bag = (Element) bagList.item(0);
+ retval = new ArrayList();
+ NodeList items = bag.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ retval.add(XMLUtil.getStringValue(li));
+ }
+ retval = Collections.unmodifiableList(retval);
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * Get a list of ResourceEvent objects.
+ *
+ * @param seqName
+ * The name of the sequence to retrieve.
+ *
+ * @return A list of ResourceEvent objects or null if they do not exist.
+ */
+ public List getEventSequenceList(String seqName)
+ {
+ List retval = null;
+ NodeList nodes = schema.getElementsByTagName(seqName);
+ if (nodes.getLength() > 0)
+ {
+ Element contributor = (Element) nodes.item(0);
+ NodeList bagList = contributor.getElementsByTagName("rdf:Seq");
+ if (bagList.getLength() > 0)
+ {
+ Element bag = (Element) bagList.item(0);
+ retval = new ArrayList();
+ NodeList items = bag.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ retval.add(new ResourceEvent(li));
+ }
+ retval = Collections.unmodifiableList(retval);
+ }
+ }
+
+ return retval;
+ }
+
+ /**
+ * Remove a date sequence value from the list.
+ *
+ * @param seqName
+ * The name of the sequence property, it must include the
+ * namespace prefix. ie "pdf:Keywords"
+ * @param date
+ * The date to remove from the sequence property.
+ */
+ public void removeSequenceDateValue(String seqName, Calendar date)
+ {
+ String dateAsString = DateConverter.toISO8601(date);
+ removeSequenceValue(seqName, dateAsString);
+ }
+
+ /**
+ * Add a date sequence value to the list.
+ *
+ * @param seqName
+ * The name of the sequence property, it must include the
+ * namespace prefix. ie "pdf:Keywords"
+ * @param date
+ * The date to add to the sequence property.
+ */
+ public void addSequenceDateValue(String seqName, Calendar date)
+ {
+ String dateAsString = DateConverter.toISO8601(date);
+ addSequenceValue(seqName, dateAsString);
+ }
+
+ /**
+ * Get all the date values in a sequence property.
+ *
+ * @param seqName
+ * The name of the sequence property, it must include the
+ * namespace prefix. ie "pdf:Keywords".
+ *
+ * @return A read-only list of java.util.Calendar objects or null if the
+ * property does not exist.
+ *
+ * @throws IOException
+ * If there is an error converting the value to a date.
+ */
+ public List getSequenceDateList(String seqName) throws IOException
+ {
+ List strings = getSequenceList(seqName);
+ List retval = null;
+ if (strings != null)
+ {
+ retval = new ArrayList();
+ for (int i = 0; i < strings.size(); i++)
+ {
+ retval.add(DateConverter.toCalendar((String) strings.get(i)));
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Set the value of a multi-lingual property.
+ *
+ * @param propertyName
+ * The name of the property, it must include the namespace
+ * prefix. ie "pdf:Keywords"
+ * @param language
+ * The language code of the value. If null then "x-default" is
+ * assumed.
+ * @param value
+ * The value of the property in the specified language.
+ */
+ public void setLanguageProperty(String propertyName, String language,
+ String value)
+ {
+ NodeList nodes = schema.getElementsByTagName(propertyName);
+ Element property = null;
+ if (nodes.getLength() == 0)
+ {
+ if (value == null)
+ {
+ // value is null, it doesn't already exist so there
+ // is nothing to do.
+ return;
+ }
+ property = schema.getOwnerDocument().createElement(propertyName);
+ schema.appendChild(property);
+ }
+ else
+ {
+ property = (Element) nodes.item(0);
+ }
+ Element alt = null;
+ NodeList altList = property.getElementsByTagName("rdf:Alt");
+ if (altList.getLength() == 0)
+ {
+ if (value == null)
+ {
+ // value is null, it doesn't already exist so there
+ // is nothing to do.
+ return;
+ }
+ alt = schema.getOwnerDocument().createElement("rdf:Alt");
+ property.appendChild(alt);
+ }
+ else
+ {
+ alt = (Element) altList.item(0);
+ }
+ NodeList items = alt.getElementsByTagName("rdf:li");
+ if (language == null)
+ {
+ language = "x-default";
+ }
+ boolean foundValue = false;
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ if (value == null)
+ {
+ alt.removeChild(li);
+ }
+ else if (language.equals(li.getAttribute("xml:lang")))
+ {
+ foundValue = true;
+ XMLUtil.setStringValue(li, value);
+ }
+ }
+ if (value != null && !foundValue)
+ {
+ Element li = schema.getOwnerDocument().createElement("rdf:li");
+ li.setAttribute("xml:lang", language);
+ XMLUtil.setStringValue(li, value);
+ if (language.equals("x-default"))
+ {
+ // default should be first element, see XMP spec
+ alt.insertBefore(li, alt.getFirstChild());
+ }
+ else
+ {
+ alt.appendChild(li);
+ }
+
+ }
+ }
+
+ /**
+ * Get the value of a multi-lingual property.
+ *
+ * @param propertyName
+ * The name of the property, it must include the namespace
+ * prefix. ie "pdf:Keywords"
+ * @param language
+ * The language code of the value. If null then "x-default" is
+ * assumed.
+ *
+ * @return The value of the language property.
+ */
+ public String getLanguageProperty(String propertyName, String language)
+ {
+ String retval = null;
+ if (language == null)
+ {
+ language = "x-default";
+ }
+
+ NodeList nodes = schema.getElementsByTagName(propertyName);
+ if (nodes.getLength() > 0)
+ {
+ Element property = (Element) nodes.item(0);
+ NodeList altList = property.getElementsByTagName("rdf:Alt");
+ if (altList.getLength() > 0)
+ {
+ Element alt = (Element) altList.item(0);
+ NodeList items = alt.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength() && retval == null; i++)
+ {
+ Element li = (Element) items.item(i);
+ String elementLanguage = li.getAttribute("xml:lang");
+ if (language.equals(elementLanguage))
+ {
+ retval = XMLUtil.getStringValue(li);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Set the value of a multi-lingual property.
+ *
+ * @param propertyName
+ * The name of the property, it must include the namespace
+ * prefix. ie "pdf:Keywords"
+ * @param language
+ * The language code of the value. If null then "x-default" is
+ * assumed.
+ * @param value
+ * The value of the property in the specified language.
+ */
+ public void setThumbnailProperty(String propertyName, String language,
+ Thumbnail value)
+ {
+ NodeList nodes = schema.getElementsByTagName(propertyName);
+ Element property = null;
+ if (nodes.getLength() == 0)
+ {
+ if (value == null)
+ {
+ // value is null, it doesn't already exist so there
+ // is nothing to do.
+ return;
+ }
+ property = schema.getOwnerDocument().createElement(propertyName);
+ schema.appendChild(property);
+ }
+ else
+ {
+ property = (Element) nodes.item(0);
+ }
+ Element alt = null;
+ NodeList altList = property.getElementsByTagName("rdf:Alt");
+ if (altList.getLength() == 0)
+ {
+ if (value == null)
+ {
+ // value is null, it doesn't already exist so there
+ // is nothing to do.
+ return;
+ }
+ alt = schema.getOwnerDocument().createElement("rdf:Alt");
+ property.appendChild(alt);
+ }
+ else
+ {
+ alt = (Element) altList.item(0);
+ }
+ NodeList items = alt.getElementsByTagName("rdf:li");
+ if (language == null)
+ {
+ language = "x-default";
+ }
+ boolean foundValue = false;
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ if (value == null)
+ {
+ alt.removeChild(li);
+ }
+ else if (language.equals(li.getAttribute("xml:lang")))
+ {
+ foundValue = true;
+ alt.replaceChild(li, value.getElement());
+ }
+ }
+ if (value != null && !foundValue)
+ {
+ Element li = value.getElement();
+ li.setAttribute("xml:lang", language);
+ if (language.equals("x-default"))
+ {
+ // default should be first element, see XMP spec
+ alt.insertBefore(li, alt.getFirstChild());
+ }
+ else
+ {
+ alt.appendChild(li);
+ }
+
+ }
+ }
+
+ /**
+ * Get the value of a multi-lingual property.
+ *
+ * @param propertyName
+ * The name of the property, it must include the namespace
+ * prefix. ie "pdf:Keywords"
+ * @param language
+ * The language code of the value. If null then "x-default" is
+ * assumed.
+ *
+ * @return The value of the language property.
+ */
+ public Thumbnail getThumbnailProperty(String propertyName, String language)
+ {
+ Thumbnail retval = null;
+ if (language == null)
+ {
+ language = "x-default";
+ }
+
+ NodeList nodes = schema.getElementsByTagName(propertyName);
+ if (nodes.getLength() > 0)
+ {
+ Element property = (Element) nodes.item(0);
+ NodeList altList = property.getElementsByTagName("rdf:Alt");
+ if (altList.getLength() > 0)
+ {
+ Element alt = (Element) altList.item(0);
+ NodeList items = alt.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength() && retval == null; i++)
+ {
+ Element li = (Element) items.item(i);
+ String elementLanguage = li.getAttribute("xml:lang");
+ if (language.equals(elementLanguage))
+ {
+ retval = new Thumbnail(li);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Get a list of all languages that are currently defined for a specific
+ * property.
+ *
+ * @param propertyName
+ * The name of the property, it must include the namespace
+ * prefix. ie "pdf:Keywords"
+ *
+ * @return A list of all languages, this will return an non-null empty list
+ * if none have been defined.
+ */
+ public List getLanguagePropertyLanguages(String propertyName)
+ {
+ List retval = new ArrayList();
+
+ NodeList nodes = schema.getElementsByTagName(propertyName);
+ if (nodes.getLength() > 0)
+ {
+ Element property = (Element) nodes.item(0);
+ NodeList altList = property.getElementsByTagName("rdf:Alt");
+ if (altList.getLength() > 0)
+ {
+ Element alt = (Element) altList.item(0);
+ NodeList items = alt.getElementsByTagName("rdf:li");
+ for (int i = 0; i < items.getLength(); i++)
+ {
+ Element li = (Element) items.item(i);
+ String elementLanguage = li.getAttribute("xml:lang");
+ if (elementLanguage == null)
+ {
+ retval.add("x-default");
+ }
+ else
+ {
+ retval.add(elementLanguage);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * A basic schema merge, it merges bags and sequences and replace everything
+ * else.
+ *
+ * @param xmpSchema The schema to merge.
+ * @throws IOException If there is an error during the merge.
+ */
+ public void merge(XMPSchema xmpSchema) throws IOException
+ {
+ if (!xmpSchema.getClass().equals(this.getClass()))
+ {
+ throw new IOException("Can only merge schemas of the same type.");
+ }
+
+ NamedNodeMap attributes = xmpSchema.getElement().getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ Node a = attributes.item(i);
+ String name = a.getNodeName();
+ if (name.startsWith(prefix))
+ {
+ String newValue = xmpSchema.getTextProperty(name);
+ setTextProperty(name, newValue);
+ }
+ }
+ NodeList nodes = xmpSchema.getElement().getChildNodes();
+
+ for (int i = 0; i < nodes.getLength(); i++)
+ {
+ Node a = nodes.item(i);
+ String name = a.getNodeName();
+ if (name.startsWith(prefix))
+ {
+ if (a instanceof Element)
+ {
+ Element e = (Element) a;
+ if (nodes.getLength() > 0)
+ {
+ NodeList seqList = e.getElementsByTagName("rdf:Seq");
+ if (seqList.getLength() > 0)
+ {
+ List newList = xmpSchema.getSequenceList(name);
+ List oldList = getSequenceList(name);
+
+ Iterator it = newList.iterator();
+
+ while (it.hasNext())
+ {
+ Object object = it.next();
+ if (oldList == null
+ || !oldList.contains(object))
+ {
+ addSequenceValue(name, (String) object);
+ }
+ }
+ continue;
+ }
+ NodeList bagList = e.getElementsByTagName("rdf:Bag");
+ if (bagList.getLength() > 0)
+ {
+ List newList = xmpSchema.getBagList(name);
+ List oldList = getBagList(name);
+
+ Iterator it = newList.iterator();
+
+ while (it.hasNext())
+ {
+ Object object = it.next();
+ if (oldList == null
+ || !oldList.contains(object))
+ {
+ addBagValue(name, (String) object);
+ }
+ }
+ continue;
+ }
+ }
+ }
+ String newValue = xmpSchema.getTextProperty(name);
+ setTextProperty(name, newValue);
+ }
+ }
+ }
+}
\ No newline at end of file
Added: incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasic.java
URL: http://svn.apache.org/viewvc/incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasic.java?rev=670399&view=auto
==============================================================================
--- incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasic.java (added)
+++ incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasic.java Sun Jun 22 12:09:15 2008
@@ -0,0 +1,388 @@
+/**
+ * Copyright (c) 2006, www.jempbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.jempbox.org
+ *
+ */
+package org.jempbox.xmp;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+/**
+ * Define XMP properties that are common to all schemas.
+ *
+ * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
+ * @version $Revision: 1.4 $
+ */
+public class XMPSchemaBasic extends XMPSchema
+{
+ /**
+ * The namespace of this schema.
+ */
+ public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/";
+
+ /**
+ * Construct a new blank PDF schema.
+ *
+ * @param parent The parent metadata schema that this will be part of.
+ */
+ public XMPSchemaBasic( XMPMetadata parent )
+ {
+ super( parent, "xmp", NAMESPACE );
+ schema.setAttributeNS(
+ NS_NAMESPACE,
+ "xmlns:xapGImg",
+ "http://ns.adobe.com/xap/1.0/g/img/" );
+ }
+
+ /**
+ * Constructor from existing XML element.
+ *
+ * @param element The existing element.
+ * @param prefix The schema prefix.
+ */
+ public XMPSchemaBasic( Element element, String prefix )
+ {
+ super( element, prefix );
+ if( schema.getAttribute( "xmlns:xapGImg" ) == null )
+ {
+ schema.setAttributeNS(
+ NS_NAMESPACE,
+ "xmlns:xapGImg",
+ "http://ns.adobe.com/xap/1.0/g/img/" );
+ }
+ }
+
+ /**
+ * Remove an Advisory xpath expression.
+ *
+ * @param advisory An xpath expression specifying properties that
+ * were edited outside of the authoring application.
+ */
+ public void removeAdvisory( String advisory )
+ {
+ removeBagValue( prefix + ":Advisory", advisory );
+ }
+
+ /**
+ * Add an advisory to the list.
+ *
+ * @param advisory The new advisory xpath expression.
+ */
+ public void addAdvisory( String advisory )
+ {
+ addBagValue( prefix + ":Advisory", advisory );
+ }
+
+ /**
+ * Get the complete list of advisories.
+ *
+ * @return The list of advisories.
+ */
+ public List getAdvisories()
+ {
+ return getBagList( prefix + ":Advisory" );
+ }
+
+ /**
+ * The base URL of the resource, for relative URLs in the document.
+ *
+ * @param url The base URL.
+ */
+ public void setBaseURL( String url )
+ {
+ setTextProperty( prefix + ":BaseURL", url );
+ }
+
+ /**
+ * Get the base URL of the resource.
+ *
+ * @return The base URL.
+ */
+ public String getBaseURL()
+ {
+ return getTextProperty( prefix + ":BaseURL" );
+ }
+
+ /**
+ * Set the creation date of the resource.
+ *
+ * @param date The creation date of the resource.
+ */
+ public void setCreateDate( Calendar date )
+ {
+ setDateProperty( prefix + ":CreateDate", date );
+ }
+
+ /**
+ * Get the creation date of the resource.
+ *
+ * @return The creation date of the resource.
+ *
+ * @throws IOException If there is an error while converting this property to
+ * a date.
+ */
+ public Calendar getCreateDate() throws IOException
+ {
+ return getDateProperty( prefix + ":CreateDate" );
+ }
+
+ /**
+ * The creator tool for the resource. In the form of "vendor app version", ie
+ * "Adobe Acrobat Distiller 5.0"
+ *
+ * @param creator The tool that was used to create the resource.
+ */
+ public void setCreatorTool( String creator )
+ {
+ setTextProperty( prefix + ":CreatorTool", creator );
+ }
+
+ /**
+ * Get the tool that created this resource, in the form of "vendor app version", ie
+ * "Adobe Acrobat Distiller 5.0".
+ *
+ * @return The creator tool.
+ */
+ public String getCreatorTool()
+ {
+ return getTextProperty( prefix + ":CreatorTool" );
+ }
+
+ /**
+ * Remove an identifier to this resource.
+ *
+ * @param id An identifier to this resource.
+ */
+ public void removeIdentifier( String id )
+ {
+ removeBagValue( prefix + ":Identifier", id );
+ }
+
+ /**
+ * Add a new identifier for this resource.
+ *
+ * @param id A new identifier for this resource.
+ */
+ public void addIdentifier( String id )
+ {
+ addBagValue( prefix + ":Identifier", id );
+ }
+
+ /**
+ * Get the complete list of identifiers.
+ *
+ * @return The list of identifiers.
+ */
+ public List getIdentifiers()
+ {
+ return getBagList( prefix + ":Identifier" );
+ }
+
+ /**
+ * Set a short phrase that identifies this resource.
+ *
+ * @param label A short description of this resource.
+ */
+ public void setLabel( String label )
+ {
+ setTextProperty( prefix + ":Label", label );
+ }
+
+ /**
+ * Get the short phrase that describes this resource.
+ *
+ * @return The label for this resource.
+ */
+ public String getLabel()
+ {
+ return getTextProperty( prefix + "p:Label" );
+ }
+
+ /**
+ * Set a Title for this resource.
+ *
+ * @param title A title denoting this resource
+ */
+ public void setTitle( String title )
+ {
+ setTextProperty( prefix + ":Title", title);
+ }
+
+ /**
+ * Get the title for this resource.
+ *
+ * @return The titled denoting this resource.
+ */
+ public String getTitle()
+ {
+ return getTextProperty( prefix + ":Title" );
+ }
+
+ /**
+ * Set the date that any metadata was updated.
+ *
+ * @param date The metadata change date for this resource.
+ */
+ public void setMetadataDate( Calendar date )
+ {
+ setDateProperty( prefix + ":MetadataDate", date );
+ }
+
+ /**
+ * Get the metadata change date for this resource.
+ *
+ * @return The metadata change date of the resource.
+ *
+ * @throws IOException If there is an error while converting this property to
+ * a date.
+ */
+ public Calendar getMetadataDate() throws IOException
+ {
+ return getDateProperty( prefix + ":MetadataDate" );
+ }
+
+ /**
+ * Set the date that the resource was last modified.
+ *
+ * @param date The modify date for this resource.
+ */
+ public void setModifyDate( Calendar date )
+ {
+ setDateProperty( prefix + ":ModifyDate", date );
+ }
+
+ /**
+ * Get the date the resource was last modified.
+ *
+ * @return The modify date of the resource.
+ *
+ * @throws IOException If there is an error while converting this property to
+ * a date.
+ */
+ public Calendar getModifyDate() throws IOException
+ {
+ return getDateProperty( prefix + ":ModifyDate" );
+ }
+
+ /**
+ * Set a short informal name for the resource.
+ *
+ * @param nickname A short name of this resource.
+ */
+ public void setNickname( String nickname )
+ {
+ setTextProperty( prefix + ":Nickname", nickname );
+ }
+
+ /**
+ * Get the short informal name for this resource.
+ *
+ * @return The short name for this resource.
+ */
+ public String getNickname()
+ {
+ return getTextProperty( prefix + ":Nickname" );
+ }
+
+ /**
+ * Get a number that indicates the documents status.
+ *
+ * @return The rating of the document.
+ */
+ public Integer getRating()
+ {
+ return getIntegerProperty( prefix + ":Rating" );
+ }
+
+ /**
+ * Set the document status.
+ *
+ * @param rating A number indicating status relative to other documents.
+ */
+ public void setRating( Integer rating )
+ {
+ setIntegerProperty( prefix + ":Rating", rating );
+ }
+
+ /**
+ * Set the default value for the thumbnail.
+ *
+ * @param thumbnail The thumbnail of this resource.
+ */
+ public void setThumbnail( Thumbnail thumbnail )
+ {
+ setThumbnailProperty( prefix + ":Thumbnails", null, thumbnail );
+ }
+
+ /**
+ * Get the default value for the thumbnail.
+ *
+ * @return The thumbnail of this resource.
+ */
+ public Thumbnail getThumbnail()
+ {
+ return getThumbnailProperty( prefix + ":Thumnails", null );
+ }
+
+ /**
+ * Set the thumbnail of this resource in a specific language.
+ *
+ * @param language The language code.
+ * @param thumbnail The thumbnail in a specific language.
+ */
+ public void setThumbnail( String language, Thumbnail thumbnail )
+ {
+ setThumbnailProperty( prefix + ":Thumbnails", language, thumbnail );
+ }
+
+ /**
+ * Get the thumbnail in a specific language.
+ *
+ * @param language The language code to get the description for.
+ *
+ * @return The thumbnail in the specified language or null if it does not exist.
+ */
+ public Thumbnail getThumbnail( String language )
+ {
+ return getThumbnailProperty( prefix + ":Thumbnails", language );
+ }
+
+ /**
+ * Get a list of all languages that a thumbnail exists for.
+ *
+ * @return A non-null list of languages, potentially an empty list.
+ */
+ public List getThumbnailLanguages()
+ {
+ return getLanguagePropertyLanguages( prefix + ":Thumbnails" );
+ }
+}
\ No newline at end of file
Added: incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasicJobTicket.java
URL: http://svn.apache.org/viewvc/incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasicJobTicket.java?rev=670399&view=auto
==============================================================================
--- incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasicJobTicket.java (added)
+++ incubator/pdfbox/trunk/jempbox/src/org/jempbox/xmp/XMPSchemaBasicJobTicket.java Sun Jun 22 12:09:15 2008
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2006, www.jempbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of pdfbox; 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 REGENTS 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.
+ *
+ * http://www.jempbox.org
+ *
+ */
+package org.jempbox.xmp;
+
+import org.w3c.dom.Element;
+
+/**
+ * Implementation of Basic Job Ticket Schema.
+ *
+ * @author Karsten Krieg (kkrieg@intarsys.de)
+ * @version $Revision: 1.2 $
+ */
+public class XMPSchemaBasicJobTicket extends XMPSchema
+{
+ /**
+ * The namespace for this schema.
+ */
+ public static final String NAMESPACE = "http://ns.adobe.com/xap/1.0/bj/";
+
+ /**
+ *
+ * @param parent The parent metadata schema that this will be part of.
+ */
+ public XMPSchemaBasicJobTicket( XMPMetadata parent )
+ {
+ super( parent, "xmpBJ", NAMESPACE );
+ }
+
+ /**
+ * Constructor from existing XML element.
+ *
+ * @param element The existing element.
+ * @param prefix The schema prefix.
+ */
+ public XMPSchemaBasicJobTicket( Element element, String prefix )
+ {
+ super( element , prefix);
+ }
+}
\ No newline at end of file