You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xmlgraphics.apache.org by je...@apache.org on 2007/12/23 16:42:30 UTC
svn commit: r606564 - in /xmlgraphics/commons/trunk: ./
src/java/org/apache/xmlgraphics/xmp/ test/java/org/apache/xmlgraphics/xmp/
Author: jeremias
Date: Sun Dec 23 07:42:28 2007
New Revision: 606564
URL: http://svn.apache.org/viewvc?rev=606564&view=rev
Log:
XMP: Added support for structured properties.
Bugfix for XMP serialization: arrays with only one entry were serialized as simple property which could lead to information loss for merge operations.
Added:
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java (with props)
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java (with props)
xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/
xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java (with props)
xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-basics.xmp
xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-structures.xmp
xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/unknown-schema.xmp
Modified:
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/Metadata.java
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPArray.java
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPConstants.java
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPHandler.java
xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPProperty.java
xmlgraphics/commons/trunk/status.xml
Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/Metadata.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/Metadata.java?rev=606564&r1=606563&r2=606564&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/Metadata.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/Metadata.java Sun Dec 23 07:42:28 2007
@@ -23,55 +23,54 @@
import java.util.Map;
import java.util.Set;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
import org.apache.xmlgraphics.util.QName;
import org.apache.xmlgraphics.util.XMLizable;
import org.apache.xmlgraphics.xmp.merge.MergeRuleSet;
import org.apache.xmlgraphics.xmp.merge.PropertyMerger;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
/**
* This class represents the root of an XMP metadata tree. It's more or less equivalent to the
* x:xmpmeta element together with its nested rdf:RDF element.
*/
-public class Metadata implements XMLizable {
+public class Metadata implements XMLizable, PropertyAccess {
private Map properties = new java.util.HashMap();
- /**
- * Sets a property.
- * @param prop the property
- */
+ /** {@inheritDoc} */
public void setProperty(XMPProperty prop) {
properties.put(prop.getName(), prop);
}
- /**
- * Returns a property
- * @param uri the namespace URI of the property
- * @param localName the local name of the property
- * @return the requested property or null if it's not available
- */
+ /** {@inheritDoc} */
public XMPProperty getProperty(String uri, String localName) {
return getProperty(new QName(uri, localName));
}
- /**
- * Returns a property.
- * @param name the name of the property
- * @return the requested property or null if it's not available
- */
+ /** {@inheritDoc} */
public XMPProperty getProperty(QName name) {
XMPProperty prop = (XMPProperty)properties.get(name);
return prop;
}
- /** @return the number of properties in this metadata object. */
+ /** {@inheritDoc} */
+ public XMPProperty getValueProperty() {
+ return getProperty(XMPConstants.RDF_VALUE);
+ }
+
+ /** {@inheritDoc} */
public int getPropertyCount() {
return this.properties.size();
}
+ /** {@inheritDoc} */
+ public Iterator iterator() {
+ return this.properties.keySet().iterator();
+ }
+
/**
* Merges this metadata object into a given target metadata object. The merge rule set provided
* by each schema is used for the merge.
@@ -89,7 +88,7 @@
}
}
- /** @see org.apache.xmlgraphics.util.XMLizable#toSAX(org.xml.sax.ContentHandler) */
+ /** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
AttributesImpl atts = new AttributesImpl();
handler.startElement(XMPConstants.XMP_NAMESPACE, "xmpmeta", "x:xmpmeta", atts);
@@ -98,7 +97,8 @@
Set namespaces = new java.util.HashSet();
Iterator iter = properties.keySet().iterator();
while (iter.hasNext()) {
- namespaces.add(((QName)iter.next()).getNamespaceURI());
+ QName n = ((QName)iter.next());
+ namespaces.add(n.getNamespaceURI());
}
//One Description element per namespace
iter = namespaces.iterator();
@@ -106,28 +106,42 @@
String ns = (String)iter.next();
XMPSchema schema = XMPSchemaRegistry.getInstance().getSchema(ns);
String prefix = (schema != null ? schema.getPreferredPrefix() : null);
- if (prefix != null) {
- handler.startPrefixMapping(prefix, ns);
- }
-
- atts.clear();
- atts.addAttribute(XMPConstants.RDF_NAMESPACE, "about", "rdf:about", "CDATA", "");
- handler.startElement(XMPConstants.RDF_NAMESPACE, "RDF", "rdf:Description", atts);
+
+ boolean first = true;
+ boolean empty = true;
Iterator props = properties.values().iterator();
while (props.hasNext()) {
XMPProperty prop = (XMPProperty)props.next();
if (prop.getName().getNamespaceURI().equals(ns)) {
+ if (first) {
+ if (prefix == null) {
+ prefix = prop.getName().getPrefix();
+ }
+ atts.clear();
+ atts.addAttribute(XMPConstants.RDF_NAMESPACE,
+ "about", "rdf:about", "CDATA", "");
+ if (prefix != null) {
+ handler.startPrefixMapping(prefix, ns);
+ }
+ handler.startElement(XMPConstants.RDF_NAMESPACE,
+ "RDF", "rdf:Description", atts);
+ empty = false;
+ first = false;
+ }
prop.toSAX(handler);
}
}
- handler.endElement(XMPConstants.RDF_NAMESPACE, "RDF", "rdf:Description");
- if (prefix != null) {
- handler.endPrefixMapping(prefix);
+ if (!empty) {
+ handler.endElement(XMPConstants.RDF_NAMESPACE, "RDF", "rdf:Description");
+ if (prefix != null) {
+ handler.endPrefixMapping(prefix);
+ }
}
}
handler.endElement(XMPConstants.RDF_NAMESPACE, "RDF", "rdf:RDF");
handler.endElement(XMPConstants.XMP_NAMESPACE, "xmpmeta", "x:xmpmeta");
}
+
}
Added: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java?rev=606564&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java (added)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java Sun Dec 23 07:42:28 2007
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.xmlgraphics.xmp;
+
+import java.util.Iterator;
+
+import org.apache.xmlgraphics.util.QName;
+
+/**
+ * This interface is implemented by the top-level Metadata class and stuctured properties.
+ */
+public interface PropertyAccess {
+
+ /**
+ * Sets a property.
+ * @param prop the property
+ */
+ void setProperty(XMPProperty prop);
+
+ /**
+ * Returns a property
+ * @param uri the namespace URI of the property
+ * @param localName the local name of the property
+ * @return the requested property or null if it's not available
+ */
+ XMPProperty getProperty(String uri, String localName);
+
+ /**
+ * Returns a property.
+ * @param name the name of the property
+ * @return the requested property or null if it's not available
+ */
+ XMPProperty getProperty(QName name);
+
+ /**
+ * Returns the rdf:value property. This is a shortcut for getProperty(XMPConstants.RDF_VALUE).
+ * @return the rdf:value property or null if it's no available
+ */
+ XMPProperty getValueProperty();
+
+ /**
+ * Returns the number of properties.
+ * @return the number of properties in this metadata object.
+ */
+ int getPropertyCount();
+
+ /**
+ * Returns an Iterator over all properties in this structured property.
+ * @return an Iterator over all properties
+ */
+ Iterator iterator();
+
+}
\ No newline at end of file
Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/PropertyAccess.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPArray.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPArray.java?rev=606564&r1=606563&r2=606564&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPArray.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPArray.java Sun Dec 23 07:42:28 2007
@@ -49,7 +49,7 @@
}
/**
- * Returns the value at a given position
+ * Returns the value at a given position.
* @param idx the index of the requested value
* @return the value at the given position
*/
@@ -57,28 +57,64 @@
return this.values.get(idx);
}
- /** @see org.apache.xmlgraphics.xmp.XMPComplexValue#getSimpleValue() */
+ /**
+ * Returns the structure at a given position. If the value is not a structure a
+ * ClassCastException is thrown.
+ * @param idx the index of the requested value
+ * @return the structure at the given position
+ */
+ public XMPStructure getStructure(int idx) {
+ return (XMPStructure)this.values.get(idx);
+ }
+
+ /** {@inheritDoc} */
public Object getSimpleValue() {
if (values.size() == 1) {
return getValue(0);
+ } else if (values.size() > 1) {
+ return getLangValue(XMPConstants.DEFAULT_LANGUAGE);
} else {
return null;
}
}
+ private String getParentLanguage(String lang) {
+ if (lang == null) {
+ return null;
+ }
+ int pos = lang.indexOf('-');
+ if (pos > 0) {
+ String parent = lang.substring(0, pos);
+ return parent;
+ }
+ return null;
+ }
+
/**
- * Returns a language-dependant values (available for alternative arrays).
+ * Returns a language-dependent values (available for alternative arrays).
* @param lang the language ("x-default" for the default value)
* @return the requested value
*/
public String getLangValue(String lang) {
String v = null;
+ String valueForParentLanguage = null;
for (int i = 0, c = values.size(); i < c; i++) {
String l = (String)xmllang.get(i);
if ((l == null && lang == null) || (l != null && l.equals(lang))) {
v = values.get(i).toString();
break;
}
+ if (l != null && lang != null) {
+ //Check for "parent" language, too ("en" matches "en-GB")
+ String parent = getParentLanguage(l);
+ if (parent != null && parent.equals(lang)) {
+ valueForParentLanguage = values.get(i).toString();
+ }
+ }
+ }
+ if (lang != null & v == null && valueForParentLanguage != null) {
+ //Use value found for parent language
+ v = valueForParentLanguage;
}
if (lang == null && v == null) {
v = getLangValue(XMPConstants.DEFAULT_LANGUAGE);
@@ -90,7 +126,7 @@
}
/**
- * Removes a language-dependant value
+ * Removes a language-dependent value
* @param lang the language ("x-default" for the default value)
*/
public void removeLangValue(String lang) {
@@ -117,7 +153,7 @@
}
/**
- * Adds a language-dependant value to the array. Make sure not to add the same language twice.
+ * Adds a language-dependent value to the array. Make sure not to add the same language twice.
* @param value the value
* @param lang the language ("x-default" for the default value)
*/
@@ -143,13 +179,12 @@
return res;
}
- /** @see org.apache.xmlgraphics.util.XMLizable#toSAX(org.xml.sax.ContentHandler) */
+ /** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
AttributesImpl atts = new AttributesImpl();
handler.startElement(XMPConstants.RDF_NAMESPACE,
type.getName(), "rdf:" + type.getName(), atts);
for (int i = 0, c = values.size(); i < c; i++) {
- String value = (String)values.get(i);
String lang = (String)xmllang.get(i);
atts.clear();
if (lang != null) {
@@ -157,8 +192,14 @@
}
handler.startElement(XMPConstants.RDF_NAMESPACE,
"li", "rdf:li", atts);
- char[] chars = value.toCharArray();
- handler.characters(chars, 0, chars.length);
+ Object v = values.get(i);
+ if (v instanceof XMPComplexValue) {
+ ((XMPComplexValue)v).toSAX(handler);
+ } else {
+ String value = (String)values.get(i);
+ char[] chars = value.toCharArray();
+ handler.characters(chars, 0, chars.length);
+ }
handler.endElement(XMPConstants.RDF_NAMESPACE,
"li", "rdf:li");
}
@@ -166,7 +207,7 @@
type.getName(), "rdf:" + type.getName());
}
- /** @see java.lang.Object#toString() */
+ /** {@inheritDoc} */
public String toString() {
return "XMP array: " + type + ", " + getSize();
}
Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPConstants.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPConstants.java?rev=606564&r1=606563&r2=606564&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPConstants.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPConstants.java Sun Dec 23 07:42:28 2007
@@ -19,6 +19,8 @@
package org.apache.xmlgraphics.xmp;
+import org.apache.xmlgraphics.util.QName;
+
/**
* Constants used in XMP metadata.
*/
@@ -27,6 +29,9 @@
/** Namespace URI for the xml: prefix */
String XML_NS = "http://www.w3.org/XML/1998/namespace";
+ /** Namespace URI for the xmlns: prefix */
+ String XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
+
/** Namespace URI for XMP */
String XMP_NAMESPACE = "adobe:ns:meta/";
@@ -58,5 +63,8 @@
/** Default language for the xml:lang property */
String DEFAULT_LANGUAGE = "x-default";
+
+ /** QName for rdf:value */
+ QName RDF_VALUE = new QName(RDF_NAMESPACE, "value");
}
Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPHandler.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPHandler.java?rev=606564&r1=606563&r2=606564&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPHandler.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPHandler.java Sun Dec 23 07:42:28 2007
@@ -21,12 +21,13 @@
import java.util.Stack;
-import org.apache.xmlgraphics.util.QName;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
+import org.apache.xmlgraphics.util.QName;
+
/**
* Passive XMP parser implemented as a SAX DefaultHandler. After the XML document has been parsed
* the Metadata object can be retrieved.
@@ -38,17 +39,87 @@
private StringBuffer content = new StringBuffer();
//private Attributes lastAttributes;
private Stack attributesStack = new Stack();
- //private Stack contextStack = new Stack();
+ private Stack nestingInfoStack = new Stack();
+ private Stack contextStack = new Stack();
- private QName currentPropertyName;
- private XMPProperty currentProperty;
- private XMPComplexValue currentComplexValue;
+ //private QName currentPropertyName;
+ //private XMPProperty currentProperty;
+ //private XMPComplexValue currentComplexValue;
+ //private PropertyAccess currentProperties;
/** @return the parsed metadata, available after the parsing. */
public Metadata getMetadata() {
return this.meta;
}
+ private boolean hasComplexContent() {
+ Object obj = this.contextStack.peek();
+ if (obj instanceof QName) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private PropertyAccess getCurrentProperties() {
+ Object obj = this.contextStack.peek();
+ if (obj instanceof PropertyAccess) {
+ return (PropertyAccess)obj;
+ } else {
+ return null;
+ }
+ }
+
+ private QName getCurrentPropName() {
+ Object obj = this.contextStack.peek();
+ if (obj instanceof QName) {
+ return (QName)obj;
+ } else {
+ return null;
+ }
+ }
+
+ private QName popCurrentPropName() throws SAXException {
+ Object obj = this.contextStack.pop();
+ this.nestingInfoStack.pop();
+ if (obj instanceof QName) {
+ return (QName)obj;
+ } else {
+ throw new SAXException("Invalid XMP structure. Property name expected");
+ }
+ }
+
+ private XMPComplexValue getCurrentComplexValue() {
+ Object obj = this.contextStack.peek();
+ if (obj instanceof XMPComplexValue) {
+ return (XMPComplexValue)obj;
+ } else {
+ return null;
+ }
+ }
+
+ private XMPStructure getCurrentStructure() {
+ Object obj = this.contextStack.peek();
+ if (obj instanceof XMPStructure) {
+ return (XMPStructure)obj;
+ } else {
+ return null;
+ }
+ }
+
+ private XMPArray getCurrentArray(boolean required) throws SAXException {
+ Object obj = this.contextStack.peek();
+ if (obj instanceof XMPArray) {
+ return (XMPArray)obj;
+ } else {
+ if (required) {
+ throw new SAXException("Invalid XMP structure. Not in array");
+ } else {
+ return null;
+ }
+ }
+ }
+
// --- Overrides ---
/**
@@ -69,28 +140,61 @@
throw new SAXException("Invalid XMP document. Root already received earlier.");
}
this.meta = new Metadata();
+ //this.currentProperties = this.meta;
+ this.contextStack.push(this.meta);
+ this.nestingInfoStack.push("metadata");
} else if (XMPConstants.RDF_NAMESPACE.equals(uri)) {
if ("RDF".equals(localName)) {
if (this.meta == null) {
this.meta = new Metadata();
+ //this.currentProperties = this.meta;
+ this.contextStack.push(this.meta);
+ this.nestingInfoStack.push("metadata");
}
} else if ("Description".equals(localName)) {
- if (currentPropertyName == null) {
+ String about = attributes.getValue(XMPConstants.RDF_NAMESPACE, "about");
+ if (this.contextStack.peek().equals(this.meta)) {
//rdf:RDF is the parent
- String about = attributes.getValue(XMPConstants.RDF_NAMESPACE, "about");
} else {
+ if (about != null) {
+ throw new SAXException(
+ "Nested rdf:Description elements may not have an about property");
+ }
//a structured property is the parent
+ XMPStructure struct = new XMPStructure();
+ //this.currentComplexValue = struct;
+ this.contextStack.push(struct);
+ //this.currentProperties = struct;
+ this.nestingInfoStack.push("struct");
}
} else if ("Seq".equals(localName)) {
- this.currentComplexValue = new XMPArray(XMPArrayType.SEQ);
+ XMPArray array = new XMPArray(XMPArrayType.SEQ);
+ //this.currentComplexValue = array;
+ this.contextStack.push(array);
+ this.nestingInfoStack.push("Seq");
} else if ("Bag".equals(localName)) {
- this.currentComplexValue = new XMPArray(XMPArrayType.BAG);
+ XMPArray array = new XMPArray(XMPArrayType.BAG);
+ //this.currentComplexValue = array;
+ this.contextStack.push(array);
+ this.nestingInfoStack.push("Bag");
} else if ("Alt".equals(localName)) {
- this.currentComplexValue = new XMPArray(XMPArrayType.ALT);
+ XMPArray array = new XMPArray(XMPArrayType.ALT);
+ //this.currentComplexValue = array;
+ this.contextStack.push(array);
+ this.nestingInfoStack.push("Alt");
} else if ("li".equals(localName)) {
+ //nop, handle in endElement()
+ } else if ("value".equals(localName)) {
+ QName name = new QName(uri, qName);
+ this.contextStack.push(name);
+ this.nestingInfoStack.push("prop:" + name);
+ } else {
+ throw new SAXException("Unexpected element in the RDF namespace: " + localName);
}
} else {
- this.currentPropertyName = new QName(uri, qName);
+ QName name = new QName(uri, qName);
+ this.contextStack.push(name);
+ this.nestingInfoStack.push("prop:" + name);
}
}
@@ -102,39 +206,81 @@
Attributes atts = (Attributes)attributesStack.pop();
if (XMPConstants.XMP_NAMESPACE.equals(uri)) {
//nop
- } else if (XMPConstants.RDF_NAMESPACE.equals(uri)) {
+ } else if (XMPConstants.RDF_NAMESPACE.equals(uri) && !"value".equals(localName)) {
if ("li".equals(localName)) {
- String s = content.toString().trim();
- if (s.length() > 0) {
- getCurrentArray().add(s);
+ XMPStructure struct = getCurrentStructure();
+ if (struct != null) {
+ //Pop the structure
+ Object obj = this.contextStack.pop();
+ this.nestingInfoStack.pop();
+ getCurrentArray(true).add(struct);
+ } else {
+ String s = content.toString().trim();
+ if (s.length() > 0) {
+ String lang = atts.getValue(XMPConstants.XML_NS, "lang");
+ if (lang != null) {
+ getCurrentArray(true).add(s, lang);
+ } else {
+ getCurrentArray(true).add(s);
+ }
+ }
}
+ } else if ("Description".equals(localName)) {
+ /*
+ if (isInStructure()) {
+ //Description is indicating a structure
+ //this.currentProperties = (PropertyAccess)propertiesStack.pop();
+ this.nestingInfoStack.pop();
+ }*/
} else {
- //nop
+ //nop, don't pop stack so the parent element has access
+ //this.contextStack.pop();
+ //this.nestingInfoStack.pop();
}
} else {
- if (this.currentComplexValue != null) {
- this.currentProperty = new XMPProperty(this.currentPropertyName,
- this.currentComplexValue);
- this.currentComplexValue = null;
+ XMPProperty prop;
+ QName name;
+ if (hasComplexContent()) {
+ //Pop content of property
+ Object obj = this.contextStack.pop();
+ this.nestingInfoStack.pop();
+
+ name = popCurrentPropName();
+
+ if (obj instanceof XMPComplexValue) {
+ XMPComplexValue complexValue = (XMPComplexValue)obj;
+ prop = new XMPProperty(name, complexValue);
+ } else {
+ throw new UnsupportedOperationException("NYI");
+ }
} else {
+ name = popCurrentPropName();
+
String s = content.toString().trim();
- this.currentProperty = new XMPProperty(this.currentPropertyName, s);
+ prop = new XMPProperty(name, s);
String lang = atts.getValue(XMPConstants.XML_NS, "lang");
if (lang != null) {
- this.currentProperty.setXMLLang(lang);
+ prop.setXMLLang(lang);
}
}
- this.meta.setProperty(this.currentProperty);
- this.currentProperty = null;
- this.currentPropertyName = null;
+ if (prop.getName() == null) {
+ throw new IllegalStateException("No content in XMP property");
+ }
+ getCurrentProperties().setProperty(prop);
+ //this.currentProperties.setProperty(this.currentProperty);
+ //this.currentProperty = null;
+ //this.currentPropertyName = null;
}
+
content.setLength(0); //Reset text buffer (see characters())
super.endElement(uri, localName, qName);
}
- private XMPArray getCurrentArray() {
- return (XMPArray)this.currentComplexValue;
+ /*
+ private boolean isInStructure() {
+ return !propertiesStack.isEmpty();
}
+ */
/** @see org.xml.sax.ContentHandler#characters(char[], int, int) */
public void characters(char[] ch, int start, int length) throws SAXException {
Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPProperty.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPProperty.java?rev=606564&r1=606563&r2=606564&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPProperty.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPProperty.java Sun Dec 23 07:42:28 2007
@@ -19,12 +19,16 @@
package org.apache.xmlgraphics.xmp;
-import org.apache.xmlgraphics.util.QName;
-import org.apache.xmlgraphics.util.XMLizable;
+import java.util.Iterator;
+import java.util.Map;
+
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
+import org.apache.xmlgraphics.util.QName;
+import org.apache.xmlgraphics.util.XMLizable;
+
/**
* This class is the base class for all XMP properties.
*/
@@ -33,6 +37,7 @@
private QName name;
private Object value;
private String xmllang;
+ private Map qualifiers;
/**
* Creates a new XMP property.
@@ -108,6 +113,61 @@
}
}
+ /** @return the XMPStructure for a structure or null if the value is not a structure. */
+ public PropertyAccess getStructureValue() {
+ return (value instanceof XMPStructure ? (XMPStructure)value : null);
+ }
+
+ private boolean hasPropertyQualifiers() {
+ return (this.qualifiers == null) || (this.qualifiers.size() == 0);
+ }
+
+ /**
+ * Indicates whether this property is actually not a structure, but a normal property with
+ * property qualifiers. If this method returns true, this structure can be converted to
+ * an simple XMPProperty using the simplify() method.
+ * @return true if this property is a structure property with property qualifiers
+ */
+ public boolean isQualifiedProperty() {
+ PropertyAccess props = getStructureValue();
+ if (props != null) {
+ XMPProperty rdfValue = props.getProperty(XMPConstants.RDF_VALUE);
+ return (rdfValue != null);
+ } else {
+ return hasPropertyQualifiers();
+ }
+ }
+
+ public void simplify() {
+ PropertyAccess props = getStructureValue();
+ if (props != null) {
+ XMPProperty rdfValue = props.getProperty(XMPConstants.RDF_VALUE);
+ if (rdfValue != null) {
+ if (hasPropertyQualifiers()) {
+ throw new IllegalStateException("Illegal internal state"
+ + " (qualifiers present on non-simplified property)");
+ }
+ Object value = props.getProperty(XMPConstants.RDF_VALUE);
+ XMPProperty prop = new XMPProperty(getName(), value);
+ Iterator iter = props.iterator();
+ while (iter.hasNext()) {
+ QName name = (QName)iter.next();
+ if (!XMPConstants.RDF_VALUE.equals(name)) {
+ prop.setPropertyQualifier(name, props.getProperty(name));
+ }
+ }
+ }
+ }
+ }
+
+
+ private void setPropertyQualifier(QName name, XMPProperty property) {
+ if (this.qualifiers == null) {
+ this.qualifiers = new java.util.HashMap();
+ }
+ this.qualifiers.put(name, property);
+ }
+
private String getEffectiveQName() {
String prefix = getName().getPrefix();
if (prefix == null || "".equals(prefix)) {
@@ -125,13 +185,7 @@
getName().getLocalName(), qName, atts);
if (value instanceof XMPComplexValue) {
XMPComplexValue cv = ((XMPComplexValue)value);
- Object obj = cv.getSimpleValue();
- if (obj != null) {
- char[] chars = obj.toString().toCharArray();
- handler.characters(chars, 0, chars.length);
- } else {
- cv.toSAX(handler);
- }
+ cv.toSAX(handler);
} else {
char[] chars = value.toString().toCharArray();
handler.characters(chars, 0, chars.length);
@@ -139,4 +193,13 @@
handler.endElement(getName().getNamespaceURI(),
getName().getLocalName(), qName);
}
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("XMP Property ");
+ sb.append(getName()).append(": ");
+ sb.append(getValue());
+ return sb.toString();
+ }
+
}
Added: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java?rev=606564&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java (added)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java Sun Dec 23 07:42:28 2007
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.xmlgraphics.xmp;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.xmlgraphics.util.QName;
+
+/**
+ * Represents an XMP structure as defined by the XMP specification.
+ */
+public class XMPStructure extends XMPComplexValue implements PropertyAccess {
+
+ private Map properties = new java.util.HashMap();
+
+ /**
+ * Main constructor
+ */
+ public XMPStructure() {
+ }
+
+ /** {@inheritDoc} */
+ public Object getSimpleValue() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public void setProperty(XMPProperty prop) {
+ properties.put(prop.getName(), prop);
+ }
+
+ /** {@inheritDoc} */
+ public XMPProperty getProperty(String uri, String localName) {
+ return getProperty(new QName(uri, localName));
+ }
+
+ /** {@inheritDoc} */
+ public XMPProperty getValueProperty() {
+ return getProperty(XMPConstants.RDF_VALUE);
+ }
+
+ /** {@inheritDoc} */
+ public XMPProperty getProperty(QName name) {
+ XMPProperty prop = (XMPProperty)properties.get(name);
+ return prop;
+ }
+
+ /** {@inheritDoc} */
+ public int getPropertyCount() {
+ return this.properties.size();
+ }
+
+ /** {@inheritDoc} */
+ public Iterator iterator() {
+ return this.properties.keySet().iterator();
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ atts.clear();
+ handler.startElement(XMPConstants.RDF_NAMESPACE, "RDF", "rdf:Description", atts);
+
+ Iterator props = properties.values().iterator();
+ while (props.hasNext()) {
+ XMPProperty prop = (XMPProperty)props.next();
+ //if (prop.getName().getNamespaceURI().equals(ns)) {
+ prop.toSAX(handler);
+ //}
+ }
+ handler.endElement(XMPConstants.RDF_NAMESPACE, "RDF", "rdf:Description");
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "XMP structure: " + getPropertyCount();
+ }
+
+
+}
Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/xmp/XMPStructure.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: xmlgraphics/commons/trunk/status.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/status.xml?rev=606564&r1=606563&r2=606564&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/status.xml (original)
+++ xmlgraphics/commons/trunk/status.xml Sun Dec 23 07:42:28 2007
@@ -25,6 +25,13 @@
</todo>
<changes>
<release version="Trunk" date="n/a">
+ <action context="Code" dev="JM" type="add">
+ XMP: Added support for structured properties.
+ </action>
+ <action context="Code" dev="JM" type="fix">
+ Bugfix for XMP serialization: arrays with only one entry were serialized as
+ simple property which could lead to information loss for merge operations.
+ </action>
<action context="Code" dev="JM" type="update">
PostScript: Improved PSImageUtils by introducing the ImageEncoder interface and
allowing for streams instead of byte arrays for data transfer which can lower
Added: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java?rev=606564&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java (added)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java Sun Dec 23 07:42:28 2007
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.xmlgraphics.xmp;
+
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+import org.apache.xmlgraphics.xmp.schemas.DublinCoreAdapter;
+import org.apache.xmlgraphics.xmp.schemas.DublinCoreSchema;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
+import org.apache.xmlgraphics.xmp.schemas.pdf.AdobePDFAdapter;
+import org.apache.xmlgraphics.xmp.schemas.pdf.AdobePDFSchema;
+
+/**
+ * Tests for the XMP parser.
+ */
+public class XMPParserTest extends TestCase {
+
+ public void testParseBasics() throws Exception {
+ URL url = getClass().getResource("test-basics.xmp");
+ Metadata meta = XMPParser.parseXMP(url);
+
+ DublinCoreAdapter dcAdapter = DublinCoreSchema.getAdapter(meta);
+ XMPBasicAdapter basicAdapter = XMPBasicSchema.getAdapter(meta);
+ AdobePDFAdapter pdfAdapter = AdobePDFSchema.getAdapter(meta);
+
+ XMPProperty prop;
+ prop = meta.getProperty(XMPConstants.DUBLIN_CORE_NAMESPACE, "creator");
+ XMPArray array;
+ array = prop.getArrayValue();
+ assertEquals(1, array.getSize());
+ assertEquals("John Doe", array.getValue(0).toString());
+ assertEquals("John Doe", dcAdapter.getCreators()[0]);
+
+ prop = meta.getProperty(XMPConstants.DUBLIN_CORE_NAMESPACE, "title");
+ assertEquals("Example document", prop.getValue().toString());
+ assertEquals("Example document", dcAdapter.getTitle());
+ prop = meta.getProperty(XMPConstants.XMP_BASIC_NAMESPACE, "CreateDate");
+ //System.out.println("Creation Date: " + prop.getValue() + " " + prop.getClass().getName());
+ prop = meta.getProperty(XMPConstants.XMP_BASIC_NAMESPACE, "CreatorTool");
+ assertEquals("An XML editor", prop.getValue().toString());
+ assertEquals("An XML editor", basicAdapter.getCreatorTool());
+ prop = meta.getProperty(XMPConstants.ADOBE_PDF_NAMESPACE, "Producer");
+ assertEquals("Apache FOP Version SVN trunk", prop.getValue().toString());
+ assertEquals("Apache FOP Version SVN trunk", pdfAdapter.getProducer());
+ prop = meta.getProperty(XMPConstants.ADOBE_PDF_NAMESPACE, "PDFVersion");
+ assertEquals("1.4", prop.getValue().toString());
+ assertEquals("1.4", pdfAdapter.getPDFVersion());
+ }
+
+ public void testParse1() throws Exception {
+ URL url = getClass().getResource("unknown-schema.xmp");
+ Metadata meta = XMPParser.parseXMP(url);
+
+ DublinCoreAdapter dcAdapter = DublinCoreSchema.getAdapter(meta);
+
+ XMPProperty prop;
+ //Access through the known schema as reference
+ prop = meta.getProperty(XMPConstants.DUBLIN_CORE_NAMESPACE, "title");
+ assertEquals("Unknown Schema", prop.getValue().toString());
+ assertEquals("Unknown Schema", dcAdapter.getTitle());
+
+ //Access through a schema unknown to the XMP framework
+ prop = meta.getProperty("http://unknown.org/something", "dummy");
+ assertEquals("Dummy!", prop.getValue().toString());
+ }
+
+ public void testParseStructures() throws Exception {
+ URL url = getClass().getResource("test-structures.xmp");
+ Metadata meta = XMPParser.parseXMP(url);
+
+ XMPProperty prop;
+
+ String testns = "http://foo.bar/test/";
+ prop = meta.getProperty(testns, "something");
+ assertEquals("blablah", prop.getValue().toString());
+
+ prop = meta.getProperty(testns, "ingredients");
+ XMPArray array = prop.getArrayValue();
+ assertEquals(3, array.getSize());
+ XMPStructure struct = array.getStructure(0);
+ assertEquals(2, struct.getPropertyCount());
+ prop = struct.getValueProperty();
+ assertEquals("Apples", prop.getValue());
+ prop = struct.getProperty(testns, "amount");
+ assertEquals("4", prop.getValue());
+
+ prop = meta.getProperty(testns, "villain");
+ XMPProperty prop1;
+ prop1 = prop.getStructureValue().getProperty(testns, "name");
+ assertEquals("Darth Sidious", prop1.getValue());
+ prop1 = prop.getStructureValue().getProperty(testns, "other-name");
+ assertEquals("Palpatine", prop1.getValue());
+
+ }
+
+}
Propchange: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/XMPParserTest.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-basics.xmp
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-basics.xmp?rev=606564&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-basics.xmp (added)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-basics.xmp Sun Dec 23 07:42:28 2007
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/" rdf:about="">
+ <dc:creator>
+ <rdf:Seq>
+ <rdf:li>John Doe</rdf:li>
+ </rdf:Seq>
+ </dc:creator>
+ <dc:title>Example document</dc:title>
+ <dc:date>2006-06-02T10:36:40+02:00</dc:date>
+ </rdf:Description>
+ <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" rdf:about="">
+ <xmp:CreateDate>2006-06-02T10:36:40+02:00</xmp:CreateDate>
+ <xmp:CreatorTool>An XML editor</xmp:CreatorTool>
+ </rdf:Description>
+ <rdf:Description xmlns:pdf="http://ns.adobe.com/pdf/1.3/" rdf:about="">
+ <pdf:Producer>Apache FOP Version SVN trunk</pdf:Producer>
+ <pdf:PDFVersion>1.4</pdf:PDFVersion>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
Added: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-structures.xmp
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-structures.xmp?rev=606564&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-structures.xmp (added)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/test-structures.xmp Sun Dec 23 07:42:28 2007
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
+ <xmp:CreateDate>2007-12-23T16:22:04+01:00</xmp:CreateDate>
+ </rdf:Description>
+ <rdf:Description rdf:about="" xmlns:test="http://foo.bar/test/">
+
+ <test:something>blablah</test:something>
+ <test:ingredients>
+ <rdf:Bag>
+ <rdf:li>
+ <rdf:Description>
+ <rdf:value>Apples</rdf:value>
+ <test:amount>4</test:amount>
+ </rdf:Description>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description>
+ <rdf:value>Nuts</rdf:value>
+ <test:amount>12</test:amount>
+ </rdf:Description>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description>
+ <rdf:value>Hamburger</rdf:value>
+ <test:amount>1</test:amount>
+ </rdf:Description>
+ </rdf:li>
+ </rdf:Bag>
+ </test:ingredients>
+ <test:villain>
+ <rdf:Description>
+ <test:name>Darth Sidious</test:name>
+ <test:other-name>Palpatine</test:other-name>
+ </rdf:Description>
+ </test:villain>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
Added: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/unknown-schema.xmp
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/unknown-schema.xmp?rev=606564&view=auto
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/unknown-schema.xmp (added)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/xmp/unknown-schema.xmp Sun Dec 23 07:42:28 2007
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/" rdf:about="">
+ <dc:title>Unknown Schema</dc:title>
+ </rdf:Description>
+ <rdf:Description xmlns:some="http://unknown.org/something" rdf:about="">
+ <some:dummy>Dummy!</some:dummy>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
---------------------------------------------------------------------
Apache XML Graphics Project URL: http://xmlgraphics.apache.org/
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org