You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by gb...@apache.org on 2013/03/06 16:57:52 UTC

svn commit: r1453395 [7/11] - in /pdfbox/trunk/xmpbox: ./ src/main/java/org/apache/xmpbox/ src/main/java/org/apache/xmpbox/schema/ src/main/java/org/apache/xmpbox/type/ src/main/java/org/apache/xmpbox/xml/ src/test/java/org/apache/xmpbox/ src/test/java...

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URIType.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URIType.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URIType.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URIType.java Wed Mar  6 15:57:44 2013
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * 
+ *
  * 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
@@ -23,11 +23,12 @@ package org.apache.xmpbox.type;
 
 import org.apache.xmpbox.XMPMetadata;
 
-public class URIType extends TextType {
+public class URIType extends TextType
+{
 
-	public URIType(XMPMetadata metadata, String namespaceURI,
-			String prefix, String propertyName, Object value) {
-		super(metadata, namespaceURI, prefix, propertyName, value);
-	}
+    public URIType(XMPMetadata metadata, String namespaceURI, String prefix, String propertyName, Object value)
+    {
+        super(metadata, namespaceURI, prefix, propertyName, value);
+    }
 
 }

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URLType.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URLType.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URLType.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/URLType.java Wed Mar  6 15:57:44 2013
@@ -23,11 +23,12 @@ package org.apache.xmpbox.type;
 
 import org.apache.xmpbox.XMPMetadata;
 
-public class URLType extends TextType {
+public class URLType extends TextType
+{
 
-	public URLType(XMPMetadata metadata, String namespaceURI,
-			String prefix, String propertyName, Object value) {
-		super(metadata, namespaceURI, prefix, propertyName, value);
-	}
+    public URLType(XMPMetadata metadata, String namespaceURI, String prefix, String propertyName, Object value)
+    {
+        super(metadata, namespaceURI, prefix, propertyName, value);
+    }
 
 }

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/VersionType.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/VersionType.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/VersionType.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/VersionType.java Wed Mar  6 15:57:44 2013
@@ -25,80 +25,90 @@ import java.util.Calendar;
 
 import org.apache.xmpbox.XMPMetadata;
 
-@StructuredType(preferedPrefix="stVer",namespace="http://ns.adobe.com/xap/1.0/sType/Version#")
-public class VersionType extends AbstractStructuredType {
-
-	@PropertyType(type = Types.Text, card = Cardinality.Simple)
-	public static final String COMMENTS = "comments";
-
-	@PropertyType(type = Types.ResourceEvent, card = Cardinality.Simple)
-	public static final String EVENT = "event";
-
-	@PropertyType(type = Types.ProperName, card = Cardinality.Simple) 
-	public static final String MODIFIER = "modifier";
-	
-	@PropertyType(type = Types.Date, card = Cardinality.Simple)
-	public static final String MODIFY_DATE = "modifyDate";
-	
-	@PropertyType(type = Types.Text, card = Cardinality.Simple)
-	public static final String VERSION = "version";
-
-
-	/**
-	 * 
-	 * @param metadata
-	 *            The metadata to attach to this property
-	 * @param namespace
-	 *            the namespace URI to associate to this property
-	 * @param prefix
-	 *            The prefix to set for this property
-	 * @param propertyName
-	 *            The local Name of this thumbnail type
-	 */
-	public VersionType(XMPMetadata metadata) {
-		super(metadata);
-		addNamespace(getNamespace(),getPreferedPrefix());
-	}
-	
-	
-	public String getComments() {
-		return getPropertyValueAsString(COMMENTS);
-	}
-
-	public void setComments (String value) {
-		addSimpleProperty(COMMENTS, value);
-	}
-
-	public ResourceEventType getEvent () {
-		return (ResourceEventType)getFirstEquivalentProperty(EVENT,ResourceEventType.class);
-	}
-
-	public void setEvent (ResourceEventType value) {
-		this.addProperty(value);
-	}
-
-	public Calendar getModifyDate () {
-		return getDatePropertyAsCalendar(MODIFY_DATE);
-	}
-
-	public void setModifyDate (Calendar value) {
-		addSimpleProperty(MODIFY_DATE, value);
-	}
-
-	public String getVersion () {
-		return getPropertyValueAsString(VERSION);
-	}
-
-	public void setVersion (String value) {
-		addSimpleProperty(VERSION, value);
-	}
-
-	public String getModifier () {
-		return getPropertyValueAsString(MODIFIER);
-	}
-
-	public void setModifier (String value) {
-		addSimpleProperty(MODIFIER, value);
-	}
+@StructuredType(preferedPrefix = "stVer", namespace = "http://ns.adobe.com/xap/1.0/sType/Version#")
+public class VersionType extends AbstractStructuredType
+{
+
+    @PropertyType(type = Types.Text, card = Cardinality.Simple)
+    public static final String COMMENTS = "comments";
+
+    @PropertyType(type = Types.ResourceEvent, card = Cardinality.Simple)
+    public static final String EVENT = "event";
+
+    @PropertyType(type = Types.ProperName, card = Cardinality.Simple)
+    public static final String MODIFIER = "modifier";
+
+    @PropertyType(type = Types.Date, card = Cardinality.Simple)
+    public static final String MODIFY_DATE = "modifyDate";
+
+    @PropertyType(type = Types.Text, card = Cardinality.Simple)
+    public static final String VERSION = "version";
+
+    /**
+     * 
+     * @param metadata
+     *            The metadata to attach to this property
+     * @param namespace
+     *            the namespace URI to associate to this property
+     * @param prefix
+     *            The prefix to set for this property
+     * @param propertyName
+     *            The local Name of this thumbnail type
+     */
+    public VersionType(XMPMetadata metadata)
+    {
+        super(metadata);
+        addNamespace(getNamespace(), getPreferedPrefix());
+    }
+
+    public String getComments()
+    {
+        return getPropertyValueAsString(COMMENTS);
+    }
+
+    public void setComments(String value)
+    {
+        addSimpleProperty(COMMENTS, value);
+    }
+
+    public ResourceEventType getEvent()
+    {
+        return (ResourceEventType) getFirstEquivalentProperty(EVENT, ResourceEventType.class);
+    }
+
+    public void setEvent(ResourceEventType value)
+    {
+        this.addProperty(value);
+    }
+
+    public Calendar getModifyDate()
+    {
+        return getDatePropertyAsCalendar(MODIFY_DATE);
+    }
+
+    public void setModifyDate(Calendar value)
+    {
+        addSimpleProperty(MODIFY_DATE, value);
+    }
+
+    public String getVersion()
+    {
+        return getPropertyValueAsString(VERSION);
+    }
+
+    public void setVersion(String value)
+    {
+        addSimpleProperty(VERSION, value);
+    }
+
+    public String getModifier()
+    {
+        return getPropertyValueAsString(MODIFIER);
+    }
+
+    public void setModifier(String value)
+    {
+        addSimpleProperty(MODIFIER, value);
+    }
 
 }

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/XPathType.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/XPathType.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/XPathType.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/type/XPathType.java Wed Mar  6 15:57:44 2013
@@ -23,11 +23,12 @@ package org.apache.xmpbox.type;
 
 import org.apache.xmpbox.XMPMetadata;
 
-public class XPathType extends TextType {
+public class XPathType extends TextType
+{
 
-	public XPathType(XMPMetadata metadata, String namespaceURI,
-			String prefix, String propertyName, Object value) {
-		super(metadata, namespaceURI, prefix, propertyName, value);
-	}
+    public XPathType(XMPMetadata metadata, String namespaceURI, String prefix, String propertyName, Object value)
+    {
+        super(metadata, namespaceURI, prefix, propertyName, value);
+    }
 
 }

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomHelper.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomHelper.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomHelper.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomHelper.java Wed Mar  6 15:57:44 2013
@@ -32,72 +32,90 @@ import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
-public final class DomHelper {
-
-	private DomHelper () {}
-
-	public static Element getUniqueElementChild (Element description) throws XmpParsingException {
-		NodeList nl = description.getChildNodes();
-		int pos = -1;
-		for (int i=0; i < nl.getLength(); i++) {
-			if (nl.item(i) instanceof Element) {
-				if (pos>=0) {
-					// invalid : found two child elements
-					throw new XmpParsingException(ErrorType.Undefined,"Found two child elements in "+description);
-				} else {
-					pos = i;
-				}
-			}
-		}
-		return (Element)nl.item(pos);
-	}
-
-	/**
-	 * Return the first child element of the element parameter.
-	 * If there is no child, null is returned
-	 * @param description
-	 * @return
-	 * @throws XmpParsingException
-	 */
-	public static Element getFirstChildElement (Element description) throws XmpParsingException {
-		NodeList nl = description.getChildNodes();
-		for (int i=0; i < nl.getLength(); i++) {
-			if (nl.item(i) instanceof Element) {
-				return (Element)nl.item(i);
-			}
-		}
-		return null;
-	}
-
-
-	public static List<Element> getElementChildren (Element description) throws XmpParsingException {
-		NodeList nl = description.getChildNodes();
-		List<Element> ret = new ArrayList<Element>(nl.getLength());
-		for (int i=0; i < nl.getLength(); i++) {
-			if (nl.item(i) instanceof Element) {
-				ret.add((Element)nl.item(i));
-			}
-		}
-		return ret;
-	}
-
-	public static QName getQName (Element element) {
-		return new QName(element.getNamespaceURI(), element.getLocalName(), element.getPrefix());
-	}
-
-	public static boolean isRdfDescription (Element element) {
-		return (XmpConstants.DEFAULT_RDF_PREFIX.equals(element.getPrefix()) && XmpConstants.DESCRIPTION_NAME.equals(element.getLocalName()));
-	}
-
-	public static boolean isParseTypeResource (Element element) {
-		Attr parseType = element.getAttributeNodeNS(XmpConstants.RDF_NAMESPACE, XmpConstants.PARSE_TYPE);
-		if (parseType!=null && XmpConstants.RESOURCE_NAME.equals(parseType.getValue())) {
-			// parseType resourc
-			return true;
-		}
-		// else
-		return false;
-	}
+public final class DomHelper
+{
 
+    private DomHelper()
+    {
+    }
+
+    public static Element getUniqueElementChild(Element description) throws XmpParsingException
+    {
+        NodeList nl = description.getChildNodes();
+        int pos = -1;
+        for (int i = 0; i < nl.getLength(); i++)
+        {
+            if (nl.item(i) instanceof Element)
+            {
+                if (pos >= 0)
+                {
+                    // invalid : found two child elements
+                    throw new XmpParsingException(ErrorType.Undefined, "Found two child elements in " + description);
+                }
+                else
+                {
+                    pos = i;
+                }
+            }
+        }
+        return (Element) nl.item(pos);
+    }
+
+    /**
+     * Return the first child element of the element parameter. If there is no child, null is returned
+     * 
+     * @param description
+     * @return
+     * @throws XmpParsingException
+     */
+    public static Element getFirstChildElement(Element description) throws XmpParsingException
+    {
+        NodeList nl = description.getChildNodes();
+        for (int i = 0; i < nl.getLength(); i++)
+        {
+            if (nl.item(i) instanceof Element)
+            {
+                return (Element) nl.item(i);
+            }
+        }
+        return null;
+    }
+
+    public static List<Element> getElementChildren(Element description) throws XmpParsingException
+    {
+        NodeList nl = description.getChildNodes();
+        List<Element> ret = new ArrayList<Element>(nl.getLength());
+        for (int i = 0; i < nl.getLength(); i++)
+        {
+            if (nl.item(i) instanceof Element)
+            {
+                ret.add((Element) nl.item(i));
+            }
+        }
+        return ret;
+    }
+
+    public static QName getQName(Element element)
+    {
+        return new QName(element.getNamespaceURI(), element.getLocalName(), element.getPrefix());
+    }
+
+    public static boolean isRdfDescription(Element element)
+    {
+        return (XmpConstants.DEFAULT_RDF_PREFIX.equals(element.getPrefix()) && XmpConstants.DESCRIPTION_NAME
+                .equals(element.getLocalName()));
+    }
+
+    public static boolean isParseTypeResource(Element element)
+    {
+        Attr parseType = element.getAttributeNodeNS(XmpConstants.RDF_NAMESPACE, XmpConstants.PARSE_TYPE);
+        if (parseType != null && XmpConstants.RESOURCE_NAME.equals(parseType.getValue()))
+        {
+            // parseType resourc
+            return true;
+        }
+        // else
+        return false;
+    }
 
 }

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomXmpParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomXmpParser.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomXmpParser.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/DomXmpParser.java Wed Mar  6 15:57:44 2013
@@ -65,596 +65,780 @@ import org.w3c.dom.ProcessingInstruction
 import org.w3c.dom.Text;
 import org.xml.sax.SAXException;
 
-public class DomXmpParser {
+public class DomXmpParser
+{
 
-	private DocumentBuilder dBuilder;
+    private DocumentBuilder dBuilder;
 
-	private NamespaceFinder nsFinder;
+    private NamespaceFinder nsFinder;
 
-	private boolean strictParsing = true;
-
-	public DomXmpParser () throws XmpParsingException {
-		try {
-			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
-			dbFactory.setNamespaceAware(true);
-			dBuilder = dbFactory.newDocumentBuilder();
-			nsFinder = new NamespaceFinder();
-		} catch (ParserConfigurationException e) {
-			throw new XmpParsingException(ErrorType.Configuration,"Failed to initilalize",e);
-		}
-
-	}
-
-	public boolean isStrictParsing() {
-		return strictParsing;
-	}
-
-	public void setStrictParsing(boolean strictParsing) {
-		this.strictParsing = strictParsing;
-	}
-
-
-
-	public XMPMetadata parse(byte[] xmp) throws XmpParsingException {
-		ByteArrayInputStream input = new ByteArrayInputStream(xmp);
-		return parse(input);
-	}
-
-	public XMPMetadata parse(InputStream input ) throws XmpParsingException {
-		Document document = null;
-		try {
-			document = dBuilder.parse(input);
-		} catch (SAXException e) {
-			throw new XmpParsingException(ErrorType.Undefined,"Failed to parse", e);
-		} catch (IOException e) {
-			throw new XmpParsingException(ErrorType.Undefined,"Failed to parse", e);
-		}
-		//		document.normalizeDocument();
-		XMPMetadata xmp = null; 
-
-		// Start reading
-		removeComments(document.getFirstChild());
-		Node node = document.getFirstChild();
-
-		// expect xpacket processing instruction
-		if (!(node instanceof ProcessingInstruction)) {
-			throw new XmpParsingException(ErrorType.XpacketBadStart,"xmp should start with a processing instruction");
-		} else {
-			xmp = parseInitialXpacket((ProcessingInstruction)node);
-			node = node.getNextSibling();
-		}
-		// forget other processing instruction
-		while (node instanceof ProcessingInstruction) {
-			node = node.getNextSibling();
-		}
-		// expect root element
-		Element root = null;
-		if (!(node instanceof Element)) {
-			throw new XmpParsingException(ErrorType.NoRootElement,"xmp should contain a root element");
-		} else {
-			// use this element as root
-			root = (Element)node;
-			node = node.getNextSibling();
-		}
-		// expect xpacket end
-		if (!(node instanceof ProcessingInstruction)) {
-			throw new XmpParsingException(ErrorType.XpacketBadEnd,"xmp should end with a processing instruction");
-		} else {
-			parseEndPacket(xmp, (ProcessingInstruction)node);
-			node = node.getNextSibling();
-		}
-		// should be null
-		if (node!=null) {
-			throw new XmpParsingException(ErrorType.XpacketBadEnd,"xmp should end after xpacket end processing instruction");
-		}
-		// xpacket is OK and the is no more nodes
-		// Now, parse the content of root
-		Element rdfRdf = findDescriptionsParent(root);
-		List<Element> descriptions = DomHelper.getElementChildren(rdfRdf);
-		List<Element> dataDescriptions = new ArrayList<Element>(descriptions.size());
-		for (Element description : descriptions) {
-			Element first = DomHelper.getFirstChildElement(description);
-			if (first!=null && "pdfaExtension".equals(first.getPrefix())) {
-				PdfaExtensionHelper.validateNaming(xmp, description);
-				parseDescriptionRoot(xmp, description);
-			} else {
-				dataDescriptions.add(description);
-			}
-		}
-		// find schema description
-		PdfaExtensionHelper.populateSchemaMapping(xmp);
-		// parse data description
-		for (Element description : dataDescriptions) {
-			parseDescriptionRoot(xmp, description);
-		}
-
-
-		return xmp;
-	}
-
-	private void parseDescriptionRoot (XMPMetadata xmp, Element description) throws XmpParsingException {
-		nsFinder.push(description);
-		TypeMapping tm = xmp.getTypeMapping();
-		try {
-			List<Element> properties = DomHelper.getElementChildren(description);
-			// parse attributes as properties
-			NamedNodeMap nnm = description.getAttributes();
-			for (int i=0; i < nnm.getLength(); i++) {
-				Attr attr = (Attr)nnm.item(i);
-				if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.getPrefix())) {
-					// do nothing
-				} else if (XmpConstants.DEFAULT_RDF_PREFIX.equals(attr.getPrefix()) && XmpConstants.ABOUT_NAME.equals(attr.getLocalName())) {
-					// do nothing
-				} else if (attr.getPrefix()==null && XmpConstants.ABOUT_NAME.equals(attr.getLocalName())) {
-					// do nothing
-				} else {
-					String namespace = attr.getNamespaceURI();
-					XMPSchema schema = xmp.getSchema(namespace);
-					if (schema==null) {
-						schema = tm.getSchemaFactory(namespace).createXMPSchema(xmp, attr.getPrefix());
-						loadAttributes(schema, description);
-					}
-					ComplexPropertyContainer container = schema.getContainer();
-					PropertyType type = checkPropertyDefinition(xmp, new QName(attr.getNamespaceURI(), attr.getLocalName()));
-					AbstractSimpleProperty sp = tm.instanciateSimpleProperty(
-							namespace, 
-							schema.getPrefix(), 
-							attr.getLocalName(), 
-							attr.getValue(),
-							type.type());
-					container.addProperty(sp);
-				}
-				
-			}
-			// parse children elements as properties
-			for (Element property : properties) {
-				String namespace = property.getNamespaceURI();
-				PropertyType type = checkPropertyDefinition(xmp, DomHelper.getQName(property));
-				// create the container 
-				if (!tm.isDefinedSchema(namespace)) {
-					throw new XmpParsingException(ErrorType.NoSchema,"This namespace is not a schema or a structured type : "+namespace);
-				} 
-				XMPSchema schema = xmp.getSchema(namespace);
-				if (schema==null) {
-					schema = tm.getSchemaFactory(namespace).createXMPSchema(xmp, property.getPrefix());
-					loadAttributes(schema, description);
-				}
-				ComplexPropertyContainer container = schema.getContainer();
-				// create property
-				createProperty(xmp, property, type, container);
-			}
-		} catch (XmpSchemaException e) {
-			throw new XmpParsingException(ErrorType.Undefined,"Parsing failed", e);
-		} finally {
-			nsFinder.pop();
-		}
-	}
-
-
-	private void createProperty (XMPMetadata xmp, Element property, PropertyType type, ComplexPropertyContainer container) throws XmpParsingException {
-		String prefix = property.getPrefix();
-		String name = property.getLocalName();
-		String namespace = property.getNamespaceURI();
-		// create property
-		nsFinder.push(property);
-		try {
-			if (type==null) {
-				if (strictParsing) {
-					throw new XmpParsingException(ErrorType.InvalidType, "No type defined for {"+namespace+"}"+name);
-				} else {
-					// use it as string
-					manageSimpleType (xmp, property, Types.Text, container);
-				}
-			} else if (type.type()==Types.LangAlt) {
-				manageLangAlt (xmp, property, container);
-			} else if (type.card().isArray()) {
-				manageArray(xmp,property,type,container);
-			} else if (type.type().isSimple()) {
-				manageSimpleType (xmp, property, type.type(), container);
-			} else if (type.type().isStructured()) {
-				if (DomHelper.isParseTypeResource(property)) {
-					AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), property);
-					ast.setPrefix(prefix);
-					container.addProperty(ast);
-				} else {
-					Element inner = DomHelper.getFirstChildElement(property);
-					if (inner!=null) {
-						AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), inner);
-						ast.setPrefix(prefix);
-						container.addProperty(ast);
-					}
-				}
-			} else if (type.type()==Types.DefinedType) {
-				if (DomHelper.isParseTypeResource(property)) {
-					AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), property);
-					ast.setPrefix(prefix);
-					container.addProperty(ast);
-				} else {
-					Element inner = DomHelper.getFirstChildElement(property);
-					if (inner==null) {
-						throw new XmpParsingException(ErrorType.Format, "property should contain child element : "+property);
-					}
-					AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), inner);
-					ast.setPrefix(prefix);
-					container.addProperty(ast);
-				}
-			}
-		} finally {
-			nsFinder.pop();
-		}
-
-	}
-
-	private void manageSimpleType (XMPMetadata xmp, Element property, Types type, ComplexPropertyContainer container) throws XmpParsingException {
-		TypeMapping tm = xmp.getTypeMapping();
-		String prefix = property.getPrefix();
-		String name = property.getLocalName();
-		String namespace = property.getNamespaceURI();
-		AbstractSimpleProperty sp = tm.instanciateSimpleProperty(
-				namespace, 
-				prefix, 
-				name, 
-				property.getTextContent(),
-				type);
-		loadAttributes(sp, property);
-		container.addProperty(sp);
-	}
-
-	private void manageArray (XMPMetadata xmp, Element property, PropertyType type, ComplexPropertyContainer container) throws XmpParsingException {
-		TypeMapping tm = xmp.getTypeMapping();
-		String prefix = property.getPrefix();
-		String name = property.getLocalName();
-		String namespace = property.getNamespaceURI();
-		Element bagOrSeq = DomHelper.getUniqueElementChild(property);
-		// ensure this is the good type of array
-		if (bagOrSeq==null) {
-			// not an array
-			throw new XmpParsingException(ErrorType.Format,"Invalid array definition, expecting "+type.card()+" and found nothing");
-		}
-		if (!bagOrSeq.getLocalName().equals(type.card().name())) {
-			// not the good array type
-			throw new XmpParsingException(ErrorType.Format,"Invalid array type, expecting "+type.card()+" and found "+bagOrSeq.getLocalName());
-		}
-		ArrayProperty array = tm.createArrayProperty(namespace, prefix, name, type.card());
-		container.addProperty(array);
-		List<Element> lis=  DomHelper.getElementChildren(bagOrSeq);
-
-		for (Element element : lis) {
-			QName propertyQName = DomHelper.getQName(property);
-			AbstractField ast = parseLiElement(xmp, propertyQName, element);
-			if (ast!=null) {
-				array.addProperty(ast);
-			}
-		}
-	}
-
-	private void manageLangAlt (XMPMetadata xmp, Element property, ComplexPropertyContainer container) throws XmpParsingException {
-		manageArray(xmp,property,TypeMapping.createPropertyType(Types.LangAlt, Cardinality.Alt),container);
-	}
-
-
-	private void parseDescriptionInner (XMPMetadata xmp, Element description, ComplexPropertyContainer parentContainer) throws XmpParsingException {
-		nsFinder.push(description);
-		TypeMapping tm = xmp.getTypeMapping();
-		try {
-			List<Element> properties = DomHelper.getElementChildren(description);
-			for (Element property : properties) {
-				String name = property.getLocalName();
-				PropertyType dtype = checkPropertyDefinition(xmp, DomHelper.getQName(property));
-				PropertyType ptype = tm.getStructuredPropMapping(dtype.type()).getPropertyType(name);
-				// create property
-				createProperty(xmp, property, ptype, parentContainer);
-			}
-		} finally {
-			nsFinder.pop();
-		}
-	}
-
-
-
-	private AbstractField parseLiElement (XMPMetadata xmp, QName descriptor, Element liElement) throws XmpParsingException {
-		if (DomHelper.isParseTypeResource(liElement)) {
-			return parseLiDescription(xmp, descriptor,liElement);
-		} 
-		// will find rdf:Description
-		Element liChild = DomHelper.getUniqueElementChild(liElement);
-		if (liChild!=null) {
-			return parseLiDescription(xmp, descriptor, liChild);
-		} else {
-			// no child, so consider as simple text
-			String text = liElement.getTextContent();
-			TypeMapping tm = xmp.getTypeMapping();
-			AbstractSimpleProperty sp = tm.instanciateSimpleProperty(
-					descriptor.getNamespaceURI(), 
-					descriptor.getPrefix(), 
-					descriptor.getLocalPart(), 
-					text,
-					Types.Text);
-			loadAttributes(sp, liElement);
-			return sp;
-		}
-	}
-
-	private void loadAttributes (AbstractField sp, Element element) {
-		NamedNodeMap nnm = element.getAttributes();
-		for (int i=0; i < nnm.getLength() ; i++) {
-			Attr attr = (Attr)nnm.item(i);
-			if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.getPrefix())) {
-				// do nothing
-			} else if (XmpConstants.DEFAULT_RDF_PREFIX.equals(attr.getPrefix()) && XmpConstants.ABOUT_NAME.equals(attr.getLocalName())) {
-				// set about
-				if (sp instanceof XMPSchema) {
-					((XMPSchema)sp).setAboutAsSimple(attr.getValue());
-				}
-			} else {
-				Attribute attribute = new Attribute(XMLConstants.XML_NS_URI,attr.getLocalName(), attr.getValue());
-				sp.setAttribute(attribute);
-			}
-		}
-	}
-
-	private AbstractStructuredType parseLiDescription (XMPMetadata xmp, QName descriptor, Element liElement) throws XmpParsingException {
-		TypeMapping tm = xmp.getTypeMapping();
-		List<Element> elements = DomHelper.getElementChildren(liElement);
-		if (elements.size()==0) {
-			// The list is empty
-			return null;
-		}
-		// Instantiate abstract structured type with hint from first element
-		Element first = elements.get(0);
-		PropertyType ctype = checkPropertyDefinition(xmp, DomHelper.getQName(first));
-		Types tt = ctype.type();
-		AbstractStructuredType ast = instanciateStructured(tm, tt, descriptor.getLocalPart(),first.getNamespaceURI());
-		
-		ast.setNamespace(descriptor.getNamespaceURI());
-		ast.setPrefix(descriptor.getPrefix());
-
-		PropertiesDescription pm;
-		if (tt.isStructured()) {
-			pm = tm.getStructuredPropMapping(tt);
-		} else {
-			pm = tm.getDefinedDescriptionByNamespace(first.getNamespaceURI());
-		}
-		for (Element element : elements) {
-			String prefix = element.getPrefix();
-			String name = element.getLocalName();
-			String namespace = element.getNamespaceURI();
-			PropertyType type = pm.getPropertyType(name);
-			if (type==null) {
-				// not defined
-				throw new XmpParsingException(ErrorType.NoType, "Type '"+name+"' not defined in "+element.getNamespaceURI());
-			} else if (type.card().isArray()) {
-				ArrayProperty array = tm.createArrayProperty(namespace, prefix, name, type.card());
-				ast.getContainer().addProperty(array);
-				Element bagOrSeq = DomHelper.getUniqueElementChild(element);
-				List<Element> lis=  DomHelper.getElementChildren(bagOrSeq);
-				for (Element element2 : lis) {
-					AbstractField ast2 = parseLiElement(xmp, descriptor, element2);
-					if (ast2!=null) {
-						array.addProperty(ast2);
-					}
-				}
-			} else if (type.type().isSimple()) {
-				AbstractSimpleProperty sp = tm.instanciateSimpleProperty(
-						namespace, 
-						prefix, 
-						name, 
-						element.getTextContent(),
-						type.type());
-				loadAttributes(sp,element);
-				ast.getContainer().addProperty(sp);
-			} else if (type.type().isStructured()) {
-				// create a new structured type
-				AbstractStructuredType inner = instanciateStructured(tm, type.type(), name,null);
-				inner.setNamespace(namespace);
-				inner.setPrefix(prefix);
-				ast.getContainer().addProperty(inner);
-				ComplexPropertyContainer cpc = inner.getContainer();
-				if (DomHelper.isParseTypeResource(element)) {
-					parseDescriptionInner(xmp, element, cpc);
-				} else {
-					Element descElement = DomHelper.getFirstChildElement(element);
-					if (descElement!=null) {
-						parseDescriptionInner(xmp,descElement,cpc);
-					}
-				}
-			} else {
-				throw new XmpParsingException(ErrorType.NoType, "Unidentified element to parse "+element+" (type="+type+")");
-			}
-
-		}
-		return ast;
-	}
-
-
-	private XMPMetadata parseInitialXpacket(ProcessingInstruction pi)
-			throws XmpParsingException {
-		if (!"xpacket".equals(pi.getNodeName())) {
-			throw new XmpParsingException(ErrorType.XpacketBadStart, "Bad processing instruction name : "+pi.getNodeName());
-		}
-		String data = pi.getData();
-		StringTokenizer tokens = new StringTokenizer(data, " ");
-		String id = null;
-		String begin = null;
-		String bytes = null;
-		String encoding = null;
-		while (tokens.hasMoreTokens()) {
-			String token = tokens.nextToken();
-			if (!token.endsWith("\"") && !token.endsWith("\'")) {
-				throw new XmpParsingException(ErrorType.XpacketBadStart,
-						"Cannot understand PI data part : '" + token + "'");
-			}
-			String quote = token.substring(token.length()-1);
-			int pos = token.indexOf("="+quote);
-			if (pos <= 0) {
-				throw new XmpParsingException(ErrorType.XpacketBadStart,
-						"Cannot understand PI data part : '" + token + "'");
-			}
-			String name = token.substring(0, pos);
-			String value = token.substring(pos + 2, token.length() - 1);
-			if ("id".equals(name)) {
-				id = value;
-			} else if ("begin".equals(name)) {
-				begin = value;
-			} else if ("bytes".equals(name)) {
-				bytes = value;
-			} else if ("encoding".equals(name)) {
-				encoding = value;
-			} else {
-				throw new XmpParsingException(ErrorType.XpacketBadStart,
-						"Unknown attribute in xpacket PI : '" + token + "'");
-			}
-		}
-		return XMPMetadata.createXMPMetadata(begin, id, bytes, encoding);
-	}
-
-	private void parseEndPacket (XMPMetadata metadata, ProcessingInstruction pi) throws XmpParsingException {
-		String xpackData = pi.getData();
-		// end attribute must be present and placed in first
-		// xmp spec says Other unrecognized attributes can follow, but
-		// should be ignored
-		if (xpackData.startsWith("end=")) {
-			char end = xpackData.charAt(5);
-			// check value (5 for end='X')
-			if (end!='r' && end!='w') {
-				throw new XmpParsingException(ErrorType.XpacketBadEnd,
-						"Excepted xpacket 'end' attribute with value 'r' or 'w' ");
-			} else {
-				metadata.setEndXPacket(Character.toString(end));
-			}
-		} else {
-			// should find end='r/w'
-			throw new XmpParsingException(ErrorType.XpacketBadEnd,
-					"Excepted xpacket 'end' attribute (must be present and placed in first)");
-		}
-	}
-
-	private Element findDescriptionsParent (Element root) throws XmpParsingException {
-		// always <x:xmpmeta xmlns:x="adobe:ns:meta/">
-		expectNaming(root,"adobe:ns:meta/","x","xmpmeta");
-		// should only have one child
-		NodeList nl = root.getChildNodes();
-		if (nl.getLength()==0) {
-			// empty description 
-			throw new XmpParsingException(ErrorType.Format, "No rdf description found in xmp");
-		} else if (nl.getLength()>1) {
-			// only expect one element
-			throw new XmpParsingException(ErrorType.Format, "More than one element found in x:xmpmeta");
-		} else if (!(root.getFirstChild() instanceof Element)) {
-			// should be an element
-			throw new XmpParsingException(ErrorType.Format, "x:xmpmeta does not contains rdf:RDF element");
-		} // else let's parse
-		Element rdfRdf = (Element)root.getFirstChild();
-		// always <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-		expectNaming(rdfRdf,XmpConstants.RDF_NAMESPACE,XmpConstants.DEFAULT_RDF_PREFIX,XmpConstants.DEFAULT_RDF_LOCAL_NAME);
-		// return description parent
-		return rdfRdf;
-	}
-
-	private void expectNaming (Element element, String ns, String prefix, String ln) throws XmpParsingException {
-		if ((ns!=null) && !(ns.equals(element.getNamespaceURI()))) {
-			throw new XmpParsingException(ErrorType.Format, "Expecting namespace '"+ns+"' and found '"+element.getNamespaceURI()+"'");
-		} else if ((prefix!=null) && !(prefix.equals(element.getPrefix()))) {
-			throw new XmpParsingException(ErrorType.Format, "Expecting prefix '"+prefix+"' and found '"+element.getPrefix()+"'");
-		} else if ((ln!=null) && !(ln.equals(element.getLocalName()))) {
-			throw new XmpParsingException(ErrorType.Format, "Expecting local name '"+ln+"' and found '"+element.getLocalName()+"'");
-		} // else OK
-	}
-
-	/**
-	 * Remove all the comments node in the parent element of the parameter
-	 * @param node the first node of an element or document to clear
-	 */
-	private void removeComments (Node root) {
-		Node node = root;
-		while (node!=null) {
-			Node next = node.getNextSibling();
-			if (node instanceof Comment) {
-				// remove the comment
-				node.getParentNode().removeChild(node);
-			} else if (node instanceof Text) {
-				Text t = (Text)node;
-				if (t.getTextContent().trim().length()==0) {
-					// XXX is there a better way to remove useless Text ?
-					node.getParentNode().removeChild(node);
-				}
-			} else if (node instanceof Element) {
-				// clean child
-				removeComments(node.getFirstChild());
-			} // else do nothing
-			node = next;
-		}
-		// end of document
-	}
-
-	private AbstractStructuredType instanciateStructured (TypeMapping tm, Types type, String name, String structuredNamespace) throws XmpParsingException {
-		try {
-			if (type.isStructured()) {
-				return tm.instanciateStructuredType(type,name);
-			} else if (type.isDefined()) {
-				return tm.instanciateDefinedType(name, structuredNamespace);
-			} else {
-				throw new XmpParsingException(ErrorType.InvalidType, "Type not structured : "+type);
-			}
-		} catch (BadFieldValueException e) {
-			throw new XmpParsingException(ErrorType.InvalidType, "Parsing failed", e);
-		}
-	}
-
-	private PropertyType checkPropertyDefinition (XMPMetadata xmp, QName prop) throws XmpParsingException {
-		TypeMapping tm = xmp.getTypeMapping();
-		// test if namespace is set in xml
-		if (!nsFinder.containsNamespace(prop.getNamespaceURI())) {
-			throw new XmpParsingException(ErrorType.NoSchema, "Schema is not set in this document : "+prop.getNamespaceURI());
-		}
-		// test if namespace is defined
-		String nsuri = prop.getNamespaceURI();
-		if (!tm.isDefinedNamespace(nsuri)) {
-			throw new XmpParsingException(ErrorType.NoSchema, "Cannot find a definition for the namespace "+prop.getNamespaceURI());
-		}
-		try {
-			return tm.getSpecifiedPropertyType(prop);
-		} catch (BadFieldValueException e) {
-			throw new XmpParsingException(ErrorType.InvalidType,"Failed to retreive property definition",e);
-		}
-	}
-
-
-
-	protected class NamespaceFinder {
-
-		private Stack<Map<String, String>> stack = new Stack<Map<String,String>>();
-
-		protected void push (Element description) {
-			NamedNodeMap nnm = description.getAttributes();
-			Map<String, String> map = new HashMap<String, String>(nnm.getLength());
-			for (int j=0; j < nnm.getLength() ; j++) {
-				Attr no = (Attr)nnm.item(j);
-				// if ns definition add it
-				if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(no.getNamespaceURI())) {
-					map.put(no.getLocalName(),no.getValue());
-				}
-			}
-			stack.push(map);
-		}
-
-		protected Map<String, String> pop () {
-			return stack.pop();
-		}
-
-		protected boolean containsNamespace (String namespace) {
-			for (int i=stack.size()-1; i>=0; i--) {
-				Map<String,String> map = stack.get(i);
-				if (map.containsValue(namespace)) {
-					return true;
-				}
-			}
-			// else namespace not found
-			return false;
-		}
-
-	}
+    private boolean strictParsing = true;
 
+    public DomXmpParser() throws XmpParsingException
+    {
+        try
+        {
+            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+            dbFactory.setNamespaceAware(true);
+            dBuilder = dbFactory.newDocumentBuilder();
+            nsFinder = new NamespaceFinder();
+        }
+        catch (ParserConfigurationException e)
+        {
+            throw new XmpParsingException(ErrorType.Configuration, "Failed to initilalize", e);
+        }
+
+    }
+
+    public boolean isStrictParsing()
+    {
+        return strictParsing;
+    }
+
+    public void setStrictParsing(boolean strictParsing)
+    {
+        this.strictParsing = strictParsing;
+    }
+
+    public XMPMetadata parse(byte[] xmp) throws XmpParsingException
+    {
+        ByteArrayInputStream input = new ByteArrayInputStream(xmp);
+        return parse(input);
+    }
+
+    public XMPMetadata parse(InputStream input) throws XmpParsingException
+    {
+        Document document = null;
+        try
+        {
+            document = dBuilder.parse(input);
+        }
+        catch (SAXException e)
+        {
+            throw new XmpParsingException(ErrorType.Undefined, "Failed to parse", e);
+        }
+        catch (IOException e)
+        {
+            throw new XmpParsingException(ErrorType.Undefined, "Failed to parse", e);
+        }
+        // document.normalizeDocument();
+        XMPMetadata xmp = null;
+
+        // Start reading
+        removeComments(document.getFirstChild());
+        Node node = document.getFirstChild();
+
+        // expect xpacket processing instruction
+        if (!(node instanceof ProcessingInstruction))
+        {
+            throw new XmpParsingException(ErrorType.XpacketBadStart, "xmp should start with a processing instruction");
+        }
+        else
+        {
+            xmp = parseInitialXpacket((ProcessingInstruction) node);
+            node = node.getNextSibling();
+        }
+        // forget other processing instruction
+        while (node instanceof ProcessingInstruction)
+        {
+            node = node.getNextSibling();
+        }
+        // expect root element
+        Element root = null;
+        if (!(node instanceof Element))
+        {
+            throw new XmpParsingException(ErrorType.NoRootElement, "xmp should contain a root element");
+        }
+        else
+        {
+            // use this element as root
+            root = (Element) node;
+            node = node.getNextSibling();
+        }
+        // expect xpacket end
+        if (!(node instanceof ProcessingInstruction))
+        {
+            throw new XmpParsingException(ErrorType.XpacketBadEnd, "xmp should end with a processing instruction");
+        }
+        else
+        {
+            parseEndPacket(xmp, (ProcessingInstruction) node);
+            node = node.getNextSibling();
+        }
+        // should be null
+        if (node != null)
+        {
+            throw new XmpParsingException(ErrorType.XpacketBadEnd,
+                    "xmp should end after xpacket end processing instruction");
+        }
+        // xpacket is OK and the is no more nodes
+        // Now, parse the content of root
+        Element rdfRdf = findDescriptionsParent(root);
+        List<Element> descriptions = DomHelper.getElementChildren(rdfRdf);
+        List<Element> dataDescriptions = new ArrayList<Element>(descriptions.size());
+        for (Element description : descriptions)
+        {
+            Element first = DomHelper.getFirstChildElement(description);
+            if (first != null && "pdfaExtension".equals(first.getPrefix()))
+            {
+                PdfaExtensionHelper.validateNaming(xmp, description);
+                parseDescriptionRoot(xmp, description);
+            }
+            else
+            {
+                dataDescriptions.add(description);
+            }
+        }
+        // find schema description
+        PdfaExtensionHelper.populateSchemaMapping(xmp);
+        // parse data description
+        for (Element description : dataDescriptions)
+        {
+            parseDescriptionRoot(xmp, description);
+        }
+
+        return xmp;
+    }
+
+    private void parseDescriptionRoot(XMPMetadata xmp, Element description) throws XmpParsingException
+    {
+        nsFinder.push(description);
+        TypeMapping tm = xmp.getTypeMapping();
+        try
+        {
+            List<Element> properties = DomHelper.getElementChildren(description);
+            // parse attributes as properties
+            NamedNodeMap nnm = description.getAttributes();
+            for (int i = 0; i < nnm.getLength(); i++)
+            {
+                Attr attr = (Attr) nnm.item(i);
+                if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.getPrefix()))
+                {
+                    // do nothing
+                }
+                else if (XmpConstants.DEFAULT_RDF_PREFIX.equals(attr.getPrefix())
+                        && XmpConstants.ABOUT_NAME.equals(attr.getLocalName()))
+                {
+                    // do nothing
+                }
+                else if (attr.getPrefix() == null && XmpConstants.ABOUT_NAME.equals(attr.getLocalName()))
+                {
+                    // do nothing
+                }
+                else
+                {
+                    String namespace = attr.getNamespaceURI();
+                    XMPSchema schema = xmp.getSchema(namespace);
+                    if (schema == null)
+                    {
+                        schema = tm.getSchemaFactory(namespace).createXMPSchema(xmp, attr.getPrefix());
+                        loadAttributes(schema, description);
+                    }
+                    ComplexPropertyContainer container = schema.getContainer();
+                    PropertyType type = checkPropertyDefinition(xmp,
+                            new QName(attr.getNamespaceURI(), attr.getLocalName()));
+                    AbstractSimpleProperty sp = tm.instanciateSimpleProperty(namespace, schema.getPrefix(),
+                            attr.getLocalName(), attr.getValue(), type.type());
+                    container.addProperty(sp);
+                }
+
+            }
+            // parse children elements as properties
+            for (Element property : properties)
+            {
+                String namespace = property.getNamespaceURI();
+                PropertyType type = checkPropertyDefinition(xmp, DomHelper.getQName(property));
+                // create the container
+                if (!tm.isDefinedSchema(namespace))
+                {
+                    throw new XmpParsingException(ErrorType.NoSchema,
+                            "This namespace is not a schema or a structured type : " + namespace);
+                }
+                XMPSchema schema = xmp.getSchema(namespace);
+                if (schema == null)
+                {
+                    schema = tm.getSchemaFactory(namespace).createXMPSchema(xmp, property.getPrefix());
+                    loadAttributes(schema, description);
+                }
+                ComplexPropertyContainer container = schema.getContainer();
+                // create property
+                createProperty(xmp, property, type, container);
+            }
+        }
+        catch (XmpSchemaException e)
+        {
+            throw new XmpParsingException(ErrorType.Undefined, "Parsing failed", e);
+        }
+        finally
+        {
+            nsFinder.pop();
+        }
+    }
+
+    private void createProperty(XMPMetadata xmp, Element property, PropertyType type, ComplexPropertyContainer container)
+            throws XmpParsingException
+    {
+        String prefix = property.getPrefix();
+        String name = property.getLocalName();
+        String namespace = property.getNamespaceURI();
+        // create property
+        nsFinder.push(property);
+        try
+        {
+            if (type == null)
+            {
+                if (strictParsing)
+                {
+                    throw new XmpParsingException(ErrorType.InvalidType, "No type defined for {" + namespace + "}"
+                            + name);
+                }
+                else
+                {
+                    // use it as string
+                    manageSimpleType(xmp, property, Types.Text, container);
+                }
+            }
+            else if (type.type() == Types.LangAlt)
+            {
+                manageLangAlt(xmp, property, container);
+            }
+            else if (type.card().isArray())
+            {
+                manageArray(xmp, property, type, container);
+            }
+            else if (type.type().isSimple())
+            {
+                manageSimpleType(xmp, property, type.type(), container);
+            }
+            else if (type.type().isStructured())
+            {
+                if (DomHelper.isParseTypeResource(property))
+                {
+                    AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), property);
+                    ast.setPrefix(prefix);
+                    container.addProperty(ast);
+                }
+                else
+                {
+                    Element inner = DomHelper.getFirstChildElement(property);
+                    if (inner != null)
+                    {
+                        AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), inner);
+                        ast.setPrefix(prefix);
+                        container.addProperty(ast);
+                    }
+                }
+            }
+            else if (type.type() == Types.DefinedType)
+            {
+                if (DomHelper.isParseTypeResource(property))
+                {
+                    AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), property);
+                    ast.setPrefix(prefix);
+                    container.addProperty(ast);
+                }
+                else
+                {
+                    Element inner = DomHelper.getFirstChildElement(property);
+                    if (inner == null)
+                    {
+                        throw new XmpParsingException(ErrorType.Format, "property should contain child element : "
+                                + property);
+                    }
+                    AbstractStructuredType ast = parseLiDescription(xmp, DomHelper.getQName(property), inner);
+                    ast.setPrefix(prefix);
+                    container.addProperty(ast);
+                }
+            }
+        }
+        finally
+        {
+            nsFinder.pop();
+        }
+
+    }
+
+    private void manageSimpleType(XMPMetadata xmp, Element property, Types type, ComplexPropertyContainer container)
+            throws XmpParsingException
+    {
+        TypeMapping tm = xmp.getTypeMapping();
+        String prefix = property.getPrefix();
+        String name = property.getLocalName();
+        String namespace = property.getNamespaceURI();
+        AbstractSimpleProperty sp = tm.instanciateSimpleProperty(namespace, prefix, name, property.getTextContent(),
+                type);
+        loadAttributes(sp, property);
+        container.addProperty(sp);
+    }
+
+    private void manageArray(XMPMetadata xmp, Element property, PropertyType type, ComplexPropertyContainer container)
+            throws XmpParsingException
+    {
+        TypeMapping tm = xmp.getTypeMapping();
+        String prefix = property.getPrefix();
+        String name = property.getLocalName();
+        String namespace = property.getNamespaceURI();
+        Element bagOrSeq = DomHelper.getUniqueElementChild(property);
+        // ensure this is the good type of array
+        if (bagOrSeq == null)
+        {
+            // not an array
+            throw new XmpParsingException(ErrorType.Format, "Invalid array definition, expecting " + type.card()
+                    + " and found nothing");
+        }
+        if (!bagOrSeq.getLocalName().equals(type.card().name()))
+        {
+            // not the good array type
+            throw new XmpParsingException(ErrorType.Format, "Invalid array type, expecting " + type.card()
+                    + " and found " + bagOrSeq.getLocalName());
+        }
+        ArrayProperty array = tm.createArrayProperty(namespace, prefix, name, type.card());
+        container.addProperty(array);
+        List<Element> lis = DomHelper.getElementChildren(bagOrSeq);
+
+        for (Element element : lis)
+        {
+            QName propertyQName = DomHelper.getQName(property);
+            AbstractField ast = parseLiElement(xmp, propertyQName, element);
+            if (ast != null)
+            {
+                array.addProperty(ast);
+            }
+        }
+    }
+
+    private void manageLangAlt(XMPMetadata xmp, Element property, ComplexPropertyContainer container)
+            throws XmpParsingException
+    {
+        manageArray(xmp, property, TypeMapping.createPropertyType(Types.LangAlt, Cardinality.Alt), container);
+    }
+
+    private void parseDescriptionInner(XMPMetadata xmp, Element description, ComplexPropertyContainer parentContainer)
+            throws XmpParsingException
+    {
+        nsFinder.push(description);
+        TypeMapping tm = xmp.getTypeMapping();
+        try
+        {
+            List<Element> properties = DomHelper.getElementChildren(description);
+            for (Element property : properties)
+            {
+                String name = property.getLocalName();
+                PropertyType dtype = checkPropertyDefinition(xmp, DomHelper.getQName(property));
+                PropertyType ptype = tm.getStructuredPropMapping(dtype.type()).getPropertyType(name);
+                // create property
+                createProperty(xmp, property, ptype, parentContainer);
+            }
+        }
+        finally
+        {
+            nsFinder.pop();
+        }
+    }
+
+    private AbstractField parseLiElement(XMPMetadata xmp, QName descriptor, Element liElement)
+            throws XmpParsingException
+    {
+        if (DomHelper.isParseTypeResource(liElement))
+        {
+            return parseLiDescription(xmp, descriptor, liElement);
+        }
+        // will find rdf:Description
+        Element liChild = DomHelper.getUniqueElementChild(liElement);
+        if (liChild != null)
+        {
+            return parseLiDescription(xmp, descriptor, liChild);
+        }
+        else
+        {
+            // no child, so consider as simple text
+            String text = liElement.getTextContent();
+            TypeMapping tm = xmp.getTypeMapping();
+            AbstractSimpleProperty sp = tm.instanciateSimpleProperty(descriptor.getNamespaceURI(),
+                    descriptor.getPrefix(), descriptor.getLocalPart(), text, Types.Text);
+            loadAttributes(sp, liElement);
+            return sp;
+        }
+    }
+
+    private void loadAttributes(AbstractField sp, Element element)
+    {
+        NamedNodeMap nnm = element.getAttributes();
+        for (int i = 0; i < nnm.getLength(); i++)
+        {
+            Attr attr = (Attr) nnm.item(i);
+            if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.getPrefix()))
+            {
+                // do nothing
+            }
+            else if (XmpConstants.DEFAULT_RDF_PREFIX.equals(attr.getPrefix())
+                    && XmpConstants.ABOUT_NAME.equals(attr.getLocalName()))
+            {
+                // set about
+                if (sp instanceof XMPSchema)
+                {
+                    ((XMPSchema) sp).setAboutAsSimple(attr.getValue());
+                }
+            }
+            else
+            {
+                Attribute attribute = new Attribute(XMLConstants.XML_NS_URI, attr.getLocalName(), attr.getValue());
+                sp.setAttribute(attribute);
+            }
+        }
+    }
+
+    private AbstractStructuredType parseLiDescription(XMPMetadata xmp, QName descriptor, Element liElement)
+            throws XmpParsingException
+    {
+        TypeMapping tm = xmp.getTypeMapping();
+        List<Element> elements = DomHelper.getElementChildren(liElement);
+        if (elements.size() == 0)
+        {
+            // The list is empty
+            return null;
+        }
+        // Instantiate abstract structured type with hint from first element
+        Element first = elements.get(0);
+        PropertyType ctype = checkPropertyDefinition(xmp, DomHelper.getQName(first));
+        Types tt = ctype.type();
+        AbstractStructuredType ast = instanciateStructured(tm, tt, descriptor.getLocalPart(), first.getNamespaceURI());
+
+        ast.setNamespace(descriptor.getNamespaceURI());
+        ast.setPrefix(descriptor.getPrefix());
+
+        PropertiesDescription pm;
+        if (tt.isStructured())
+        {
+            pm = tm.getStructuredPropMapping(tt);
+        }
+        else
+        {
+            pm = tm.getDefinedDescriptionByNamespace(first.getNamespaceURI());
+        }
+        for (Element element : elements)
+        {
+            String prefix = element.getPrefix();
+            String name = element.getLocalName();
+            String namespace = element.getNamespaceURI();
+            PropertyType type = pm.getPropertyType(name);
+            if (type == null)
+            {
+                // not defined
+                throw new XmpParsingException(ErrorType.NoType, "Type '" + name + "' not defined in "
+                        + element.getNamespaceURI());
+            }
+            else if (type.card().isArray())
+            {
+                ArrayProperty array = tm.createArrayProperty(namespace, prefix, name, type.card());
+                ast.getContainer().addProperty(array);
+                Element bagOrSeq = DomHelper.getUniqueElementChild(element);
+                List<Element> lis = DomHelper.getElementChildren(bagOrSeq);
+                for (Element element2 : lis)
+                {
+                    AbstractField ast2 = parseLiElement(xmp, descriptor, element2);
+                    if (ast2 != null)
+                    {
+                        array.addProperty(ast2);
+                    }
+                }
+            }
+            else if (type.type().isSimple())
+            {
+                AbstractSimpleProperty sp = tm.instanciateSimpleProperty(namespace, prefix, name,
+                        element.getTextContent(), type.type());
+                loadAttributes(sp, element);
+                ast.getContainer().addProperty(sp);
+            }
+            else if (type.type().isStructured())
+            {
+                // create a new structured type
+                AbstractStructuredType inner = instanciateStructured(tm, type.type(), name, null);
+                inner.setNamespace(namespace);
+                inner.setPrefix(prefix);
+                ast.getContainer().addProperty(inner);
+                ComplexPropertyContainer cpc = inner.getContainer();
+                if (DomHelper.isParseTypeResource(element))
+                {
+                    parseDescriptionInner(xmp, element, cpc);
+                }
+                else
+                {
+                    Element descElement = DomHelper.getFirstChildElement(element);
+                    if (descElement != null)
+                    {
+                        parseDescriptionInner(xmp, descElement, cpc);
+                    }
+                }
+            }
+            else
+            {
+                throw new XmpParsingException(ErrorType.NoType, "Unidentified element to parse " + element + " (type="
+                        + type + ")");
+            }
+
+        }
+        return ast;
+    }
+
+    private XMPMetadata parseInitialXpacket(ProcessingInstruction pi) throws XmpParsingException
+    {
+        if (!"xpacket".equals(pi.getNodeName()))
+        {
+            throw new XmpParsingException(ErrorType.XpacketBadStart, "Bad processing instruction name : "
+                    + pi.getNodeName());
+        }
+        String data = pi.getData();
+        StringTokenizer tokens = new StringTokenizer(data, " ");
+        String id = null;
+        String begin = null;
+        String bytes = null;
+        String encoding = null;
+        while (tokens.hasMoreTokens())
+        {
+            String token = tokens.nextToken();
+            if (!token.endsWith("\"") && !token.endsWith("\'"))
+            {
+                throw new XmpParsingException(ErrorType.XpacketBadStart, "Cannot understand PI data part : '" + token
+                        + "'");
+            }
+            String quote = token.substring(token.length() - 1);
+            int pos = token.indexOf("=" + quote);
+            if (pos <= 0)
+            {
+                throw new XmpParsingException(ErrorType.XpacketBadStart, "Cannot understand PI data part : '" + token
+                        + "'");
+            }
+            String name = token.substring(0, pos);
+            String value = token.substring(pos + 2, token.length() - 1);
+            if ("id".equals(name))
+            {
+                id = value;
+            }
+            else if ("begin".equals(name))
+            {
+                begin = value;
+            }
+            else if ("bytes".equals(name))
+            {
+                bytes = value;
+            }
+            else if ("encoding".equals(name))
+            {
+                encoding = value;
+            }
+            else
+            {
+                throw new XmpParsingException(ErrorType.XpacketBadStart, "Unknown attribute in xpacket PI : '" + token
+                        + "'");
+            }
+        }
+        return XMPMetadata.createXMPMetadata(begin, id, bytes, encoding);
+    }
+
+    private void parseEndPacket(XMPMetadata metadata, ProcessingInstruction pi) throws XmpParsingException
+    {
+        String xpackData = pi.getData();
+        // end attribute must be present and placed in first
+        // xmp spec says Other unrecognized attributes can follow, but
+        // should be ignored
+        if (xpackData.startsWith("end="))
+        {
+            char end = xpackData.charAt(5);
+            // check value (5 for end='X')
+            if (end != 'r' && end != 'w')
+            {
+                throw new XmpParsingException(ErrorType.XpacketBadEnd,
+                        "Excepted xpacket 'end' attribute with value 'r' or 'w' ");
+            }
+            else
+            {
+                metadata.setEndXPacket(Character.toString(end));
+            }
+        }
+        else
+        {
+            // should find end='r/w'
+            throw new XmpParsingException(ErrorType.XpacketBadEnd,
+                    "Excepted xpacket 'end' attribute (must be present and placed in first)");
+        }
+    }
+
+    private Element findDescriptionsParent(Element root) throws XmpParsingException
+    {
+        // always <x:xmpmeta xmlns:x="adobe:ns:meta/">
+        expectNaming(root, "adobe:ns:meta/", "x", "xmpmeta");
+        // should only have one child
+        NodeList nl = root.getChildNodes();
+        if (nl.getLength() == 0)
+        {
+            // empty description
+            throw new XmpParsingException(ErrorType.Format, "No rdf description found in xmp");
+        }
+        else if (nl.getLength() > 1)
+        {
+            // only expect one element
+            throw new XmpParsingException(ErrorType.Format, "More than one element found in x:xmpmeta");
+        }
+        else if (!(root.getFirstChild() instanceof Element))
+        {
+            // should be an element
+            throw new XmpParsingException(ErrorType.Format, "x:xmpmeta does not contains rdf:RDF element");
+        } // else let's parse
+        Element rdfRdf = (Element) root.getFirstChild();
+        // always <rdf:RDF
+        // xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+        expectNaming(rdfRdf, XmpConstants.RDF_NAMESPACE, XmpConstants.DEFAULT_RDF_PREFIX,
+                XmpConstants.DEFAULT_RDF_LOCAL_NAME);
+        // return description parent
+        return rdfRdf;
+    }
+
+    private void expectNaming(Element element, String ns, String prefix, String ln) throws XmpParsingException
+    {
+        if ((ns != null) && !(ns.equals(element.getNamespaceURI())))
+        {
+            throw new XmpParsingException(ErrorType.Format, "Expecting namespace '" + ns + "' and found '"
+                    + element.getNamespaceURI() + "'");
+        }
+        else if ((prefix != null) && !(prefix.equals(element.getPrefix())))
+        {
+            throw new XmpParsingException(ErrorType.Format, "Expecting prefix '" + prefix + "' and found '"
+                    + element.getPrefix() + "'");
+        }
+        else if ((ln != null) && !(ln.equals(element.getLocalName())))
+        {
+            throw new XmpParsingException(ErrorType.Format, "Expecting local name '" + ln + "' and found '"
+                    + element.getLocalName() + "'");
+        } // else OK
+    }
+
+    /**
+     * Remove all the comments node in the parent element of the parameter
+     * 
+     * @param node
+     *            the first node of an element or document to clear
+     */
+    private void removeComments(Node root)
+    {
+        Node node = root;
+        while (node != null)
+        {
+            Node next = node.getNextSibling();
+            if (node instanceof Comment)
+            {
+                // remove the comment
+                node.getParentNode().removeChild(node);
+            }
+            else if (node instanceof Text)
+            {
+                Text t = (Text) node;
+                if (t.getTextContent().trim().length() == 0)
+                {
+                    // XXX is there a better way to remove useless Text ?
+                    node.getParentNode().removeChild(node);
+                }
+            }
+            else if (node instanceof Element)
+            {
+                // clean child
+                removeComments(node.getFirstChild());
+            } // else do nothing
+            node = next;
+        }
+        // end of document
+    }
+
+    private AbstractStructuredType instanciateStructured(TypeMapping tm, Types type, String name,
+            String structuredNamespace) throws XmpParsingException
+    {
+        try
+        {
+            if (type.isStructured())
+            {
+                return tm.instanciateStructuredType(type, name);
+            }
+            else if (type.isDefined())
+            {
+                return tm.instanciateDefinedType(name, structuredNamespace);
+            }
+            else
+            {
+                throw new XmpParsingException(ErrorType.InvalidType, "Type not structured : " + type);
+            }
+        }
+        catch (BadFieldValueException e)
+        {
+            throw new XmpParsingException(ErrorType.InvalidType, "Parsing failed", e);
+        }
+    }
+
+    private PropertyType checkPropertyDefinition(XMPMetadata xmp, QName prop) throws XmpParsingException
+    {
+        TypeMapping tm = xmp.getTypeMapping();
+        // test if namespace is set in xml
+        if (!nsFinder.containsNamespace(prop.getNamespaceURI()))
+        {
+            throw new XmpParsingException(ErrorType.NoSchema, "Schema is not set in this document : "
+                    + prop.getNamespaceURI());
+        }
+        // test if namespace is defined
+        String nsuri = prop.getNamespaceURI();
+        if (!tm.isDefinedNamespace(nsuri))
+        {
+            throw new XmpParsingException(ErrorType.NoSchema, "Cannot find a definition for the namespace "
+                    + prop.getNamespaceURI());
+        }
+        try
+        {
+            return tm.getSpecifiedPropertyType(prop);
+        }
+        catch (BadFieldValueException e)
+        {
+            throw new XmpParsingException(ErrorType.InvalidType, "Failed to retreive property definition", e);
+        }
+    }
+
+    protected class NamespaceFinder
+    {
+
+        private Stack<Map<String, String>> stack = new Stack<Map<String, String>>();
+
+        protected void push(Element description)
+        {
+            NamedNodeMap nnm = description.getAttributes();
+            Map<String, String> map = new HashMap<String, String>(nnm.getLength());
+            for (int j = 0; j < nnm.getLength(); j++)
+            {
+                Attr no = (Attr) nnm.item(j);
+                // if ns definition add it
+                if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(no.getNamespaceURI()))
+                {
+                    map.put(no.getLocalName(), no.getValue());
+                }
+            }
+            stack.push(map);
+        }
+
+        protected Map<String, String> pop()
+        {
+            return stack.pop();
+        }
+
+        protected boolean containsNamespace(String namespace)
+        {
+            for (int i = stack.size() - 1; i >= 0; i--)
+            {
+                Map<String, String> map = stack.get(i);
+                if (map.containsValue(namespace))
+                {
+                    return true;
+                }
+            }
+            // else namespace not found
+            return false;
+        }
 
+    }
 
 }

Modified: pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/PdfaExtensionHelper.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/PdfaExtensionHelper.java?rev=1453395&r1=1453394&r2=1453395&view=diff
==============================================================================
--- pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/PdfaExtensionHelper.java (original)
+++ pdfbox/trunk/xmpbox/src/main/java/org/apache/xmpbox/xml/PdfaExtensionHelper.java Wed Mar  6 15:57:44 2013
@@ -47,175 +47,235 @@ import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 
-public final class PdfaExtensionHelper {
+public final class PdfaExtensionHelper
+{
 
-	private PdfaExtensionHelper() {}
-
-	public static void validateNaming (XMPMetadata meta, Element description) throws XmpParsingException {
-		NamedNodeMap nnm = description.getAttributes();
-		for (int i=0; i < nnm.getLength(); i++) {
-			Attr attr = (Attr)nnm.item(i);
-			checkNamespaceDeclaration(attr,PDFAExtensionSchema.class);
-			checkNamespaceDeclaration(attr,PDFAFieldType.class);
-			checkNamespaceDeclaration(attr,PDFAPropertyType.class);
-			checkNamespaceDeclaration(attr,PDFASchemaType.class);
-			checkNamespaceDeclaration(attr,PDFATypeType.class);
-		}
-	}
-
-	private static void checkNamespaceDeclaration (Attr attr, Class<? extends AbstractStructuredType> clz) throws XmpParsingException {
-		String prefix = attr.getLocalName();
-		String namespace = attr.getValue();
-		String cprefix = clz.getAnnotation(StructuredType.class).preferedPrefix();
-		String cnamespace = clz.getAnnotation(StructuredType.class).namespace();
-		// check extension 
-		if (cprefix.equals(prefix) && !cnamespace.equals(namespace)) {
-			throw new XmpParsingException(ErrorType.InvalidPdfaSchema, "Invalid PDF/A namespace definition");
-		} // else good match
-		if (cnamespace.equals(namespace) && !cprefix.equals(prefix)) {
-			throw new XmpParsingException(ErrorType.InvalidPdfaSchema, "Invalid PDF/A namespace definition");
-		} // else good match
-
-	}
-
-
-
-	public static  void populateSchemaMapping (XMPMetadata meta) 
-			throws XmpParsingException {
-		List<XMPSchema> schems = meta.getAllSchemas();
-		TypeMapping tm = meta.getTypeMapping();
-		StructuredType stPdfaExt = PDFAExtensionSchema.class.getAnnotation(StructuredType.class);
-		for (XMPSchema xmpSchema : schems) {
-			if (xmpSchema.getNamespace().equals(stPdfaExt.namespace())) {
-				// ensure the prefix is the preferred one (cannot use other definition)
-				if (!xmpSchema.getPrefix().equals(stPdfaExt.preferedPrefix())) {
-					throw new XmpParsingException(ErrorType.InvalidPrefix, "Found invalid prefix for PDF/A extension, found '"+
-							xmpSchema.getPrefix()+"', should be '"+stPdfaExt.preferedPrefix()+"'"
-							);
-				}
-				// create schema and types
-				PDFAExtensionSchema pes = (PDFAExtensionSchema)xmpSchema;
-				ArrayProperty sp = pes.getSchemasProperty();
-				for (AbstractField af: sp.getAllProperties()) {
-					if (af instanceof PDFASchemaType) {
-						PDFASchemaType st = (PDFASchemaType)af;
-						String namespaceUri = st.getNamespaceURI();
-						String prefix = st.getPrefixValue();
-						ArrayProperty properties = st.getProperty();
-						ArrayProperty valueTypes = st.getValueType();
-						XMPSchemaFactory xsf = tm.getSchemaFactory(namespaceUri);
-						// retrieve namespaces
-						if (xsf==null) {
-							// create namespace with no field
-							tm.addNewNameSpace(namespaceUri,prefix);
-							xsf = tm.getSchemaFactory(namespaceUri);
-						}
-						// populate value type
-						if (valueTypes!=null) {
-							for (AbstractField af2 : valueTypes.getAllProperties()) {
-								if (af2 instanceof PDFATypeType) {
-									PDFATypeType type = (PDFATypeType)af2;
-									String ttype= type.getType();
-									String tns = type.getNamespaceURI();
-									String tprefix = type.getPrefixValue();
-									String tdescription = type.getDescription();
-									ArrayProperty fields = type.getFields();
-									if (ttype==null || tns==null || tprefix==null || tdescription==null) {
-										// all fields are mandatory
-										throw new XmpParsingException(ErrorType.RequiredProperty,"Missing field in type definition");
-									}
-									// create the structured type
-									DefinedStructuredType structuredType = new DefinedStructuredType(meta, tns, tprefix,null); // TODO maybe a name exists
-									if (fields!=null) {
-										List<AbstractField> definedFields = fields.getAllProperties();
-										for (AbstractField af3 : definedFields) {
-											if (af3 instanceof PDFAFieldType) {
-												PDFAFieldType field = (PDFAFieldType)af3;
-												String fName = field.getName();
-												String fDescription = field.getDescription();
-												String fValueType = field.getValueType();
-												if (fName==null || fDescription==null || fValueType==null) {
-													throw new XmpParsingException(ErrorType.RequiredProperty,"Missing field in field definition");
-												}
-												try {
-													Types fValue = Types.valueOf(fValueType);
-													structuredType.addProperty(fName, TypeMapping.createPropertyType(fValue,Cardinality.Simple));
-												} catch (IllegalArgumentException e) {
-													throw new XmpParsingException(ErrorType.NoValueType, "Type not defined : "+fValueType,e);
-													// TODO could fValueType be a structured type ?
-												}
-											} // else TODO
-										}
-									}
-									// add the structured type to list
-									PropertiesDescription pm = new PropertiesDescription();
-									for (Map.Entry<String, PropertyType> entry : structuredType.getDefinedProperties().entrySet()) {
-										pm.addNewProperty(entry.getKey(), entry.getValue());
-									}
-									tm.addToDefinedStructuredTypes(ttype, tns,pm);
-								}
-							}	
-						}
-						// populate properties
-						for (AbstractField af2 : properties.getAllProperties()) {
-							if (af2 instanceof PDFAPropertyType) {
-								PDFAPropertyType property = (PDFAPropertyType)af2;
-								String pname = property.getName();
-								String ptype = property.getValueType();
-								String pdescription = property.getDescription();
-								String pCategory = property.getCategory();
-								// check all mandatory fields are OK
-								if (pname==null || ptype==null || pdescription==null || pCategory==null) {
-									// all fields are mandatory
-									throw new XmpParsingException(ErrorType.RequiredProperty,"Missing field in property definition");
-								}
-								// check ptype existance
-								PropertyType pt = transformValueType(tm,ptype);
-								if (pt.type()==null) {
-									throw new XmpParsingException(ErrorType.NoValueType, "Type not defined : "+ptype);
-								} else if (pt.type().isSimple() || pt.type().isStructured() || pt.type()==Types.DefinedType) {
-									xsf.getPropertyDefinition().addNewProperty(pname, pt);
-								} else {
-									throw new XmpParsingException(ErrorType.NoValueType, "Type not defined : "+ptype);
-								}
-
-							} // TODO unmanaged ?
-						}
-					} // TODO unmanaged ?
-				}
-			}
-		}
-	}
-
-	private static PropertyType transformValueType (TypeMapping tm, String valueType) throws XmpParsingException {
-		if ("Lang Alt".equals(valueType)) {
-			return TypeMapping.createPropertyType(Types.LangAlt, Cardinality.Simple);
-		}
-		// else all other cases
-		int pos = valueType.indexOf(' ');
-		Cardinality card = Cardinality.Simple;
-		if (pos>0) {
-			String scard = valueType.substring(0,pos);
-			if ("seq".equals(scard)) {
-				card = Cardinality.Seq;
-			} else if ("bag".equals(scard)) {
-				card = Cardinality.Bag;
-			} else if ("alt".equals(scard)) {
-				card = Cardinality.Alt;
-			} else {
-				return null;
-			}
-		}
-		String vt = valueType.substring(pos+1);
-		Types type = null; 
-		try {
-			type = pos<0?Types.valueOf(valueType):Types.valueOf(vt);
-		} catch (IllegalArgumentException e) {
-			if (tm.isDefinedType(vt)) {
-				type = Types.DefinedType;
-			}
-		}
-		return TypeMapping.createPropertyType(type, card);
-	}
+    private PdfaExtensionHelper()
+    {
+    }
+
+    public static void validateNaming(XMPMetadata meta, Element description) throws XmpParsingException
+    {
+        NamedNodeMap nnm = description.getAttributes();
+        for (int i = 0; i < nnm.getLength(); i++)
+        {
+            Attr attr = (Attr) nnm.item(i);
+            checkNamespaceDeclaration(attr, PDFAExtensionSchema.class);
+            checkNamespaceDeclaration(attr, PDFAFieldType.class);
+            checkNamespaceDeclaration(attr, PDFAPropertyType.class);
+            checkNamespaceDeclaration(attr, PDFASchemaType.class);
+            checkNamespaceDeclaration(attr, PDFATypeType.class);
+        }
+    }
+
+    private static void checkNamespaceDeclaration(Attr attr, Class<? extends AbstractStructuredType> clz)
+            throws XmpParsingException
+    {
+        String prefix = attr.getLocalName();
+        String namespace = attr.getValue();
+        String cprefix = clz.getAnnotation(StructuredType.class).preferedPrefix();
+        String cnamespace = clz.getAnnotation(StructuredType.class).namespace();
+        // check extension
+        if (cprefix.equals(prefix) && !cnamespace.equals(namespace))
+        {
+            throw new XmpParsingException(ErrorType.InvalidPdfaSchema, "Invalid PDF/A namespace definition");
+        } // else good match
+        if (cnamespace.equals(namespace) && !cprefix.equals(prefix))
+        {
+            throw new XmpParsingException(ErrorType.InvalidPdfaSchema, "Invalid PDF/A namespace definition");
+        } // else good match
+
+    }
+
+    public static void populateSchemaMapping(XMPMetadata meta) throws XmpParsingException
+    {
+        List<XMPSchema> schems = meta.getAllSchemas();
+        TypeMapping tm = meta.getTypeMapping();
+        StructuredType stPdfaExt = PDFAExtensionSchema.class.getAnnotation(StructuredType.class);
+        for (XMPSchema xmpSchema : schems)
+        {
+            if (xmpSchema.getNamespace().equals(stPdfaExt.namespace()))
+            {
+                // ensure the prefix is the preferred one (cannot use other
+                // definition)
+                if (!xmpSchema.getPrefix().equals(stPdfaExt.preferedPrefix()))
+                {
+                    throw new XmpParsingException(ErrorType.InvalidPrefix,
+                            "Found invalid prefix for PDF/A extension, found '" + xmpSchema.getPrefix()
+                                    + "', should be '" + stPdfaExt.preferedPrefix() + "'");
+                }
+                // create schema and types
+                PDFAExtensionSchema pes = (PDFAExtensionSchema) xmpSchema;
+                ArrayProperty sp = pes.getSchemasProperty();
+                for (AbstractField af : sp.getAllProperties())
+                {
+                    if (af instanceof PDFASchemaType)
+                    {
+                        PDFASchemaType st = (PDFASchemaType) af;
+                        String namespaceUri = st.getNamespaceURI();
+                        String prefix = st.getPrefixValue();
+                        ArrayProperty properties = st.getProperty();
+                        ArrayProperty valueTypes = st.getValueType();
+                        XMPSchemaFactory xsf = tm.getSchemaFactory(namespaceUri);
+                        // retrieve namespaces
+                        if (xsf == null)
+                        {
+                            // create namespace with no field
+                            tm.addNewNameSpace(namespaceUri, prefix);
+                            xsf = tm.getSchemaFactory(namespaceUri);
+                        }
+                        // populate value type
+                        if (valueTypes != null)
+                        {
+                            for (AbstractField af2 : valueTypes.getAllProperties())
+                            {
+                                if (af2 instanceof PDFATypeType)
+                                {
+                                    PDFATypeType type = (PDFATypeType) af2;
+                                    String ttype = type.getType();
+                                    String tns = type.getNamespaceURI();
+                                    String tprefix = type.getPrefixValue();
+                                    String tdescription = type.getDescription();
+                                    ArrayProperty fields = type.getFields();
+                                    if (ttype == null || tns == null || tprefix == null || tdescription == null)
+                                    {
+                                        // all fields are mandatory
+                                        throw new XmpParsingException(ErrorType.RequiredProperty,
+                                                "Missing field in type definition");
+                                    }
+                                    // create the structured type
+                                    DefinedStructuredType structuredType = new DefinedStructuredType(meta, tns,
+                                            tprefix, null); // TODO
+                                                            // maybe
+                                                            // a name
+                                                            // exists
+                                    if (fields != null)
+                                    {
+                                        List<AbstractField> definedFields = fields.getAllProperties();
+                                        for (AbstractField af3 : definedFields)
+                                        {
+                                            if (af3 instanceof PDFAFieldType)
+                                            {
+                                                PDFAFieldType field = (PDFAFieldType) af3;
+                                                String fName = field.getName();
+                                                String fDescription = field.getDescription();
+                                                String fValueType = field.getValueType();
+                                                if (fName == null || fDescription == null || fValueType == null)
+                                                {
+                                                    throw new XmpParsingException(ErrorType.RequiredProperty,
+                                                            "Missing field in field definition");
+                                                }
+                                                try
+                                                {
+                                                    Types fValue = Types.valueOf(fValueType);
+                                                    structuredType.addProperty(fName,
+                                                            TypeMapping.createPropertyType(fValue, Cardinality.Simple));
+                                                }
+                                                catch (IllegalArgumentException e)
+                                                {
+                                                    throw new XmpParsingException(ErrorType.NoValueType,
+                                                            "Type not defined : " + fValueType, e);
+                                                    // TODO could fValueType be
+                                                    // a structured type ?
+                                                }
+                                            } // else TODO
+                                        }
+                                    }
+                                    // add the structured type to list
+                                    PropertiesDescription pm = new PropertiesDescription();
+                                    for (Map.Entry<String, PropertyType> entry : structuredType.getDefinedProperties()
+                                            .entrySet())
+                                    {
+                                        pm.addNewProperty(entry.getKey(), entry.getValue());
+                                    }
+                                    tm.addToDefinedStructuredTypes(ttype, tns, pm);
+                                }
+                            }
+                        }
+                        // populate properties
+                        for (AbstractField af2 : properties.getAllProperties())
+                        {
+                            if (af2 instanceof PDFAPropertyType)
+                            {
+                                PDFAPropertyType property = (PDFAPropertyType) af2;
+                                String pname = property.getName();
+                                String ptype = property.getValueType();
+                                String pdescription = property.getDescription();
+                                String pCategory = property.getCategory();
+                                // check all mandatory fields are OK
+                                if (pname == null || ptype == null || pdescription == null || pCategory == null)
+                                {
+                                    // all fields are mandatory
+                                    throw new XmpParsingException(ErrorType.RequiredProperty,
+                                            "Missing field in property definition");
+                                }
+                                // check ptype existance
+                                PropertyType pt = transformValueType(tm, ptype);
+                                if (pt.type() == null)
+                                {
+                                    throw new XmpParsingException(ErrorType.NoValueType, "Type not defined : " + ptype);
+                                }
+                                else if (pt.type().isSimple() || pt.type().isStructured()
+                                        || pt.type() == Types.DefinedType)
+                                {
+                                    xsf.getPropertyDefinition().addNewProperty(pname, pt);
+                                }
+                                else
+                                {
+                                    throw new XmpParsingException(ErrorType.NoValueType, "Type not defined : " + ptype);
+                                }
+
+                            } // TODO unmanaged ?
+                        }
+                    } // TODO unmanaged ?
+                }
+            }
+        }
+    }
+
+    private static PropertyType transformValueType(TypeMapping tm, String valueType) throws XmpParsingException
+    {
+        if ("Lang Alt".equals(valueType))
+        {
+            return TypeMapping.createPropertyType(Types.LangAlt, Cardinality.Simple);
+        }
+        // else all other cases
+        int pos = valueType.indexOf(' ');
+        Cardinality card = Cardinality.Simple;
+        if (pos > 0)
+        {
+            String scard = valueType.substring(0, pos);
+            if ("seq".equals(scard))
+            {
+                card = Cardinality.Seq;
+            }
+            else if ("bag".equals(scard))
+            {
+                card = Cardinality.Bag;
+            }
+            else if ("alt".equals(scard))
+            {
+                card = Cardinality.Alt;
+            }
+            else
+            {
+                return null;
+            }
+        }
+        String vt = valueType.substring(pos + 1);
+        Types type = null;
+        try
+        {
+            type = pos < 0 ? Types.valueOf(valueType) : Types.valueOf(vt);
+        }
+        catch (IllegalArgumentException e)
+        {
+            if (tm.isDefinedType(vt))
+            {
+                type = Types.DefinedType;
+            }
+        }
+        return TypeMapping.createPropertyType(type, card);
+    }
 
 }