org.apache.padaf.xmpbox.parser.XMPDocumentBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.padaf.xmpbox.parser.XMPDocumentBuilder.java

Source

/*****************************************************************************
 * 
 * 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.
 * 
 ****************************************************************************/

package org.apache.padaf.xmpbox.parser;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.io.IOUtils;
import org.apache.padaf.xmpbox.CreateXMPMetadataException;
import org.apache.padaf.xmpbox.XMPMetadata;
import org.apache.padaf.xmpbox.schema.PDFAExtensionSchema;
import org.apache.padaf.xmpbox.schema.PDFAFieldDescription;
import org.apache.padaf.xmpbox.schema.SchemaDescription;
import org.apache.padaf.xmpbox.schema.XMPSchema;
import org.apache.padaf.xmpbox.type.AbstractSimpleProperty;
import org.apache.padaf.xmpbox.type.Attribute;
import org.apache.padaf.xmpbox.type.BadFieldValueException;
import org.apache.padaf.xmpbox.type.BooleanType;
import org.apache.padaf.xmpbox.type.ComplexProperty;
import org.apache.padaf.xmpbox.type.ComplexPropertyContainer;
import org.apache.padaf.xmpbox.type.DateType;
import org.apache.padaf.xmpbox.type.IntegerType;
import org.apache.padaf.xmpbox.type.RealType;
import org.apache.padaf.xmpbox.type.TextType;
import org.apache.padaf.xmpbox.type.ThumbnailType;

/**
 * Parse serialized XMP (in XML/RDF Format) to the XmpBox representation.
 * 
 * @author a183132
 * 
 */
public class XMPDocumentBuilder {

    protected NSMapping nsMap;

    protected ThreadLocal<XMLStreamReader> reader = new ThreadLocal<XMLStreamReader>();

    protected List<XMPDocumentPreprocessor> preprocessors = new ArrayList<XMPDocumentPreprocessor>();

    public static final String BAG_NAME = "Bag";

    public static final String SEQ_NAME = "Seq";

    public static final String ALT_NAME = "Alt";

    public static final String VALUE_TYPE_NAME = "valueType";

    /**
     * Constructor of a XMPDocumentBuilder
     * 
     * @throws XmpSchemaException
     *             When instancing schema object failed or in PDF/A Extension
     *             case, if its namespace miss
     */
    public XMPDocumentBuilder() throws XmpSchemaException {
        nsMap = new NSMapping();
    }

    /**
     * Parsing method. Return a XMPMetadata object with all elements read
     * 
     * @param xmp
     *            serialized XMP
     * @return Metadata with all information read
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XmpSchemaException
     *             When instancing schema object failed or in PDF/A Extension
     *             case, if its namespace miss
     * @throws XmpUnknownValueTypeException
     *             When ValueType found not correspond to basic type and not has
     *             been declared in current schema
     * @throws XmpExpectedRdfAboutAttribute
     *             When rdf:Description not contains rdf:about attribute
     * @throws XmpXpacketEndException
     *             When xpacket end Processing Instruction is missing or is
     *             incorrect
     * @throws BadFieldValueException
     *             When treat a Schema associed to a schema Description in PDF/A
     *             Extension schema
     */

    public XMPMetadata parse(byte[] xmp)
            throws XmpParsingException, XmpSchemaException, XmpUnknownValueTypeException,
            XmpExpectedRdfAboutAttribute, XmpXpacketEndException, BadFieldValueException {

        if (!(this instanceof XMPDocumentPreprocessor)) {
            for (XMPDocumentPreprocessor processor : preprocessors) {
                NSMapping additionalNSMapping = processor.process(xmp);
                this.nsMap.importNSMapping(additionalNSMapping);
            }
        }

        ByteArrayInputStream is = new ByteArrayInputStream(xmp);
        try {
            XMLInputFactory factory = XMLInputFactory.newInstance();
            reader.set(factory.createXMLStreamReader(is));

            // expect xpacket processing instruction
            expectNext(XMLStreamReader.PROCESSING_INSTRUCTION,
                    "Did not find initial xpacket processing instruction");
            XMPMetadata metadata = parseInitialXpacket(reader.get().getPIData());

            // expect x:xmpmeta
            expectNextTag(XMLStreamReader.START_ELEMENT, "Did not find initial x:xmpmeta");
            expectName("adobe:ns:meta/", "xmpmeta");

            // expect rdf:RDF
            expectNextTag(XMLStreamReader.START_ELEMENT, "Did not find initial rdf:RDF");
            expectName("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "RDF");

            nsMap.resetComplexBasicTypesDeclarationInEntireXMPLevel();
            // add all namespaces which could declare nsURI of a basicValueType
            // all others declarations are ignored
            int nsCount = reader.get().getNamespaceCount();
            for (int i = 0; i < nsCount; i++) {
                if (nsMap.isComplexBasicTypes(reader.get().getNamespaceURI(i))) {
                    nsMap.setComplexBasicTypesDeclarationForLevelXMP(reader.get().getNamespaceURI(i),
                            reader.get().getNamespacePrefix(i));
                }
            }

            // now work on each rdf:Description
            int type = reader.get().nextTag();
            while (type == XMLStreamReader.START_ELEMENT) {
                parseDescription(metadata);
                type = reader.get().nextTag();
            }

            // all description are finished
            // expect end of rdf:RDF
            expectType(XMLStreamReader.END_ELEMENT, "Expected end of descriptions");
            expectName("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "RDF");

            // expect ending xmpmeta
            expectNextTag(XMLStreamReader.END_ELEMENT, "Did not find initial x:xmpmeta");
            expectName("adobe:ns:meta/", "xmpmeta");

            // expect final processing instruction
            expectNext(XMLStreamReader.PROCESSING_INSTRUCTION, "Did not find final xpacket processing instruction");
            // treats xpacket end
            if (!reader.get().getPITarget().equals("xpacket")) {
                throw new XmpXpacketEndException("Excepted PI xpacket");
            }
            String xpackData = reader.get().getPIData();
            // 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=")) {
                // check value (5 for end='X')
                if (xpackData.charAt(5) != 'r' && xpackData.charAt(5) != 'w') {
                    throw new XmpXpacketEndException("Excepted xpacket 'end' attribute with value 'r' or 'w' ");
                }
            } else {
                // should find end='r/w'
                throw new XmpXpacketEndException(
                        "Excepted xpacket 'end' attribute (must be present and placed in first)");
            }

            metadata.setEndXPacket(xpackData);
            // return constructed object
            return metadata;
        } catch (XMLStreamException e) {
            throw new XmpParsingException("An error has occured when processing the underlying XMP source", e);
        } finally {
            reader.remove();
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Parsing method using serialized xmp read from a stream
     * 
     * @param is
     *            The stream to read
     * @return Metadata with all information read
     * @throws XmpParsingException
     *             When element expected not found When element expected not
     *             found
     * @throws XmpSchemaException
     *             When instancing schema object failed or in PDF/A Extension
     *             case, if its namespace miss
     * @throws XmpUnknownValueTypeException
     *             When ValueType found not correspond to basic type and not has
     *             been declared in current schema
     * @throws XmpExpectedRdfAboutAttribute
     *             When rdf:Description not contains rdf:about attribute
     * @throws XmpXpacketEndException
     *             When xpacket end Processing Instruction is missing or is
     *             incorrect
     * @throws BadFieldValueException
     *             When treat a Schema associed to a schema Description in PDF/A
     *             Extension schema
     */
    public XMPMetadata parse(InputStream input)
            throws XmpParsingException, XmpSchemaException, XmpUnknownValueTypeException,
            XmpExpectedRdfAboutAttribute, XmpXpacketEndException, BadFieldValueException {

        byte[] bos = getStreamAsByteArray(input);
        return parse(bos);
    }

    private byte[] getStreamAsByteArray(InputStream input) throws XmpParsingException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            IOUtils.copyLarge(input, bos);
        } catch (IOException e) {
            throw new XmpParsingException("An error has occured when processing the underlying XMP source", e);
        } finally {
            IOUtils.closeQuietly(bos);
            IOUtils.closeQuietly(input);
        }
        return bos.toByteArray();
    }

    public void addPreprocessor(XMPDocumentPreprocessor processor) {
        this.preprocessors.add(processor);
    }

    /**
     * Check InitialXPacket and build metadata object with these information
     * 
     * @param data
     *            data corresponding to Initial XPacket Processing Instruction
     *            Processing Information corresponding to Inital XPacket data
     * @return Metadata with specified information
     * @throws XmpInitialXPacketParsingException
     *             When Initial XPacket missing or is incorrect
     * @throws CreateXMPMetadataException
     *             If DOM Document associated could not be created
     */
    protected XMPMetadata parseInitialXpacket(String data)
            throws XmpInitialXPacketParsingException, CreateXMPMetadataException {
        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 XmpInitialXPacketParsingException("Cannot understand PI data part : '" + token + "'");
            }
            String quote = token.substring(token.length() - 1);
            int pos = token.indexOf("=" + quote);
            if (pos <= 0) {
                throw new XmpInitialXPacketParsingException("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 XmpInitialXPacketParsingException("Unknown attribute in xpacket PI : '" + token + "'");
            }
        }
        return new XMPMetadata(begin, id, bytes, encoding);
    }

    /**
     * Check the next element type. all comments are ignored.
     * 
     * @param expectType
     *            Type of xml element expected
     * @param message
     *            Error message if problems occur
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected When DOM Element type
     *             found unexpected
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream When error
     *             during reading the rest of xmp stream
     */
    private void expectNext(int expectType, String message)
            throws XmpParsingException, XmpUnexpectedTypeException, XMLStreamException {
        try {
            int type = reader.get().next();
            while (type == XMLStreamReader.COMMENT || type == XMLStreamReader.SPACE) {
                type = reader.get().next();
            }
            if (type != expectType) {
                throw new XmpUnexpectedTypeException(message);
            }
        } catch (NoSuchElementException e) {
            // unexpected end of stream
            throw new XmpParsingException("XMP Stream did not end in a good way, invalid content");
        }
    }

    /**
     * Check the next element type. White spaces , Comments and Processing
     * Instructions are ignored.
     * 
     * @param type
     *            Type of xml element expected
     * @param message
     *            Error message if problems occur
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     */
    private void expectNextTag(int type, String message)
            throws XmpParsingException, XmpUnexpectedTypeException, XMLStreamException {
        try {
            if (!(reader.get().nextTag() == type)) {
                throw new XmpUnexpectedTypeException(message);
            }
        } catch (NoSuchElementException e) {
            // unexpected end of stream
            throw new XmpParsingException("XMP Stream did not end in a good way, invalid content");
        }
    }

    /**
     * check if qualified name of current element is what is expected
     * 
     * @param namespace
     *            namespace URI
     * @param name
     *            current element name
     * @throws XmpUnexpectedElementQualifiedNameException
     *             When a qualifiedName found and is not that expected
     * 
     */
    private void expectName(String namespace, String name) throws XmpUnexpectedElementQualifiedNameException {
        if (!reader.get().getNamespaceURI().equals(namespace)) {
            throw new XmpUnexpectedElementQualifiedNameException(
                    "Expected '" + namespace + "' and found '" + reader.get().getNamespaceURI() + "'");
        }
        if (!reader.get().getLocalName().equals(name)) {
            throw new XmpUnexpectedElementQualifiedNameException(
                    "Expected '" + name + "' and found '" + reader.get().getLocalName() + "'");
        }
    }

    /**
     * Check the current element type.
     * 
     * @param type
     *            XML element type expected
     * @param message
     *            Error Message if problems occur
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     */
    private void expectType(int type, String message) throws XmpUnexpectedTypeException {
        if (!(type == reader.get().getEventType())) {
            throw new XmpUnexpectedTypeException(
                    "Expected type " + type + " and found " + reader.get().getEventType() + " : " + message);
        }
    }

    /**
     * Check if rdf:about attribute is declared for rdf description and add all
     * attributes to the schema
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param schema
     *            Schema corresponding to the rdf:Description use
     * @throws XmpExpectedRdfAboutAttribute
     *             When rdf:Description not contains rdf:about attribute
     * @throws XmpUnexpectedTypeException if the attribute is known 
     * as an expected property but the property type isn't a Simple type.
     */
    protected final void treatDescriptionAttributes(XMPMetadata metadata, XMPSchema schema)
            throws XmpExpectedRdfAboutAttribute, XmpUnexpectedTypeException {
        int cptAtt = reader.get().getAttributeCount();
        int i = 0;
        boolean rdfAboutFound = false;
        String prefix;
        while (i < cptAtt) {
            if (reader.get().getAttributeLocalName(i).equals("about")) {
                prefix = reader.get().getAttributePrefix(i);
                if (prefix != null) {
                    if (!prefix.equals("") && !prefix.equals("rdf")) {
                        // System.out.println("prefix de l'attribut "+reader.get().getAttributeLocalName(i)+": "+prefix);
                        throw new XmpExpectedRdfAboutAttribute(
                                "An about attribute is present but have an invalid prefix (it must be 'rdf')");
                    }
                }
                rdfAboutFound = true;
            }

            Attribute attr = new Attribute(null, reader.get().getAttributePrefix(i),
                    reader.get().getAttributeLocalName(i), reader.get().getAttributeValue(i));

            if (!addAttributeAsProperty(metadata, schema, attr)) {
                // attribute isn't a property, so add the attribute
                schema.setAttribute(attr);
            }

            i++;
        }
        if (!rdfAboutFound) {
            // create rdf:about if not found
            Attribute attr = new Attribute(null, "rdf", "about", "");
            schema.setAttribute(attr);
        }
    }

    /**
     * If the attribute has same the name as an expected property of the Schema, then the property is created using the attributes fields.
     * 
     * @param metadata Metadata to attach new elements
     * @param schema Schema corresponding to the rdf:Description use
     * @param attr the attribute used to create the property
     * @return true if the attribute has been converted into Property
     */
    private boolean addAttributeAsProperty(XMPMetadata metadata, XMPSchema schema, Attribute attr) {
        boolean added = false;
        String schemaNamespace = schema.getNamespaceValue();
        String prefix = attr.getPrefix() != null ? attr.getPrefix() : schema.getPrefix();
        String type = this.nsMap.getSpecifiedPropertyType(schemaNamespace,
                new QName(schemaNamespace, attr.getLocalName(), prefix));
        if (type != null) {
            if (type.equals("Text")) {
                schema.getContent()
                        .addProperty(new TextType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            } else if (type.equals("Integer")) {
                schema.getContent()
                        .addProperty(new IntegerType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            } else if (type.equals("Boolean")) {
                schema.getContent()
                        .addProperty(new BooleanType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            } else if (type.equals("Real")) {
                schema.getContent()
                        .addProperty(new RealType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            } else if (type.equals("Date")) {
                schema.getContent()
                        .addProperty(new DateType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            } else if (type.equals("URI")) {
                schema.getContent()
                        .addProperty(new TextType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            } else if (type.equals("URL")) {
                schema.getContent()
                        .addProperty(new TextType(metadata, prefix, attr.getLocalName(), attr.getValue()));
                added = true;
            }
        }
        return added;
    }

    /**
     * Treat each rdf:Description (which must represent a schema), instanciate
     * class representation of this schema and add it to metadata
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpSchemaException
     *             When instancing schema object failed or in PDF/A Extension
     *             case, if its namespace miss
     * @throws XmpUnknownValueTypeException
     *             When ValueType found not correspond to basic type and not has
     *             been declared in current schema
     * @throws XmpExpectedRdfAboutAttribute
     *             When rdf:Description not contains rdf:about attribute
     * @throws BadFieldValueException
     *             When a bad value found in Schema description content
     */
    protected void parseDescription(XMPMetadata metadata) throws XmpParsingException, XMLStreamException,
            XmpSchemaException, XmpUnknownValueTypeException, XmpExpectedRdfAboutAttribute, BadFieldValueException {
        nsMap.resetComplexBasicTypesDeclarationInSchemaLevel();
        int cptNS = reader.get().getNamespaceCount();
        HashMap<String, String> namespaces = new HashMap<String, String>();
        for (int i = 0; i < cptNS; i++) {
            namespaces.put(reader.get().getNamespacePrefix(i), reader.get().getNamespaceURI(i));
            if (nsMap.isComplexBasicTypes(reader.get().getNamespaceURI(i))) {
                // System.out.println("in parseDesc method: prefix:"+reader.get().getNamespacePrefix(i)+", nsURI:"+reader.get().getNamespaceURI(i));
                nsMap.setComplexBasicTypesDeclarationForLevelSchema(reader.get().getNamespaceURI(i),
                        reader.get().getNamespacePrefix(i));
            }
        }
        // Different treatment for PDF/A Extension schema
        // System.out.println(PDFAExtensionSchema.PDFAEXTENSION+";"+PDFAExtensionSchema.PDFAPROPERTY+";"+PDFAExtensionSchema.PDFASCHEMA);
        if (namespaces.containsKey(PDFAExtensionSchema.PDFAEXTENSION)) {
            if (namespaces.containsKey(PDFAExtensionSchema.PDFAPROPERTY)
                    && namespaces.containsKey(PDFAExtensionSchema.PDFASCHEMA)) {
                if (namespaces.containsValue(PDFAExtensionSchema.PDFAEXTENSIONURI)
                        && namespaces.containsValue(PDFAExtensionSchema.PDFAPROPERTYURI)
                        && namespaces.containsValue(PDFAExtensionSchema.PDFASCHEMAURI)) {
                    PDFAExtensionSchema schema = metadata.createAndAddPDFAExtensionSchemaWithNS(namespaces);
                    treatDescriptionAttributes(metadata, schema);
                    parseExtensionSchema(schema, metadata);

                } else {
                    throw new XmpUnexpectedNamespaceURIException(
                            "Unexpected namespaceURI in PDFA Extension Schema encountered");
                }
            } else {
                throw new XmpUnexpectedNamespacePrefixException(
                        "Unexpected namespace Prefix in PDFA Extension Schema");
            }

        } else {
            int c = 0;
            String namespaceUri = reader.get().getNamespaceURI(c);
            String namespacePrefix = reader.get().getNamespacePrefix(c);
            c++;
            XMPSchema schema = nsMap.getAssociatedSchemaObject(metadata, namespaceUri, namespacePrefix);
            while (c < reader.get().getNamespaceCount() && schema == null) {
                // try next
                namespaceUri = reader.get().getNamespaceURI(c);
                namespacePrefix = reader.get().getNamespacePrefix(c);
                schema = nsMap.getAssociatedSchemaObject(metadata, namespaceUri, namespacePrefix);
                c++;
            }

            if (schema != null) {
                namespaces.remove(namespacePrefix);
            } else {
                schema = metadata.createAndAddDefaultSchema(namespacePrefix, namespaceUri);
            }

            for (int i = 1; i < cptNS; i++) {
                schema.setAttribute(new Attribute(XMPSchema.NS_NAMESPACE, "xmlns",
                        reader.get().getNamespacePrefix(i), reader.get().getNamespaceURI(i)));
            }
            treatDescriptionAttributes(metadata, schema);
            while (reader.get().nextTag() == XMLStreamReader.START_ELEMENT) {
                parseProperty(schema, metadata);
            }
        }

    }

    /**
     * Check the next element type and its expected value
     * 
     * @param type
     *            expected type of xml element
     * @param localNameExpected
     *            The property name (local) expected
     * @param message
     *            Error message if problems occur
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     */
    private void expectNextSpecificTag(int type, String localNameExpected, String message)
            throws XmpUnexpectedTypeException, XmpParsingException, XMLStreamException {
        expectNextTag(type, message);
        expectCurrentLocalName(localNameExpected);
    }

    /**
     * check that the current local name is that expected
     * 
     * @param localNameExpected
     *            The name expected
     * @throws XmpUnexpectedElementException
     *             When Element is not that expected
     */
    protected void expectCurrentLocalName(String localNameExpected) throws XmpUnexpectedElementException {
        if (!reader.get().getLocalName().equals(localNameExpected)) {
            throw new XmpUnexpectedElementException("'" + localNameExpected + "' expected and '"
                    + reader.get().getLocalName() + "' found at " + reader.get().getLocation());
        }
    }

    /**
     * Treat a PDFAExtension schema
     * 
     * @param schema
     *            PDFA/Extension schema where save information found
     * @param metadata
     *            Metadata to attach new elements
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownValueTypeException
     *             When ValueType found not correspond to basic type and not has
     *             been declared in current schema
     * @throws BadFieldValueException
     *             When one of a field property include to describe a property
     *             in Schema Description contain an incorrect value
     */
    protected final void parseExtensionSchema(PDFAExtensionSchema schema, XMPMetadata metadata)
            throws XmpParsingException, XMLStreamException, XmpUnknownValueTypeException, BadFieldValueException {
        // <pdfaExtension:schemas>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, "schemas",
                "Cannot find container declaration of schemas descriptions ");
        // <rdf:Bag>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, BAG_NAME,
                "Cannot find bag declaration for container of schemas descriptions");
        // now work on each rdf:li corresponding to each schema description
        int type = reader.get().nextTag();
        while (type == XMLStreamReader.START_ELEMENT) {
            parseSchemaDescription(schema, metadata);
            type = reader.get().nextTag();
        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, "schemas",
                "Cannot find end of container declaration in schemas descriptions ");
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, "Description",
                "Cannot find end of PDF/A Extension definition ");

    }

    /**
     * Treat one Schema description defined in the extension Schema found
     * 
     * @param schema
     *            PDFA/Extension schema where save information found
     * @param metadata
     *            Metadata to attach new elements
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XmpUnknownValueTypeException
     *             When ValueType found not correspond to basic type and not has
     *             been declared in current schema
     * @throws BadFieldValueException
     *             When one of a field property contain an incorrect value
     */
    private void parseSchemaDescription(PDFAExtensionSchema schema, XMPMetadata metadata)
            throws XMLStreamException, XmpParsingException, XmpUnknownValueTypeException, BadFieldValueException {
        expectCurrentLocalName("li");
        SchemaDescription desc = schema.createSchemaDescription();
        int type = reader.get().nextTag();
        while (type == XMLStreamReader.START_ELEMENT) {
            if (reader.get().getLocalName().equals("schema") || reader.get().getLocalName().equals("namespaceURI")
                    || reader.get().getLocalName().equals("prefix")) {
                try {
                    // System.out.println(reader.get().getPrefix()+";"+reader.get().getLocalName()+";"+reader.get().getElementText());
                    desc.addProperty(new TextType(metadata, reader.get().getPrefix(), reader.get().getLocalName(),
                            reader.get().getElementText()));
                } catch (IllegalArgumentException e) {
                    throw new XmpPropertyFormatException(
                            "Unexpected value for '" + reader.get().getLocalName() + "' property");
                }
            } else if (reader.get().getLocalName().equals("property")) {
                parsePropertyDefinition(desc);
            } else if (reader.get().getLocalName().equals(VALUE_TYPE_NAME)) {
                parseValueTypeDefinition(desc, metadata);

            } else {
                throw new XmpUnexpectedElementException(
                        "Unexpected property definition in one of PDF/A Extension schemas description");
            }
            type = reader.get().nextTag();
        }
        schema.addSchemaDescription(desc);
        nsMap.setNamespaceDefinition(desc);

    }

    /**
     * Treat value type definition for a specific Schema Description
     * 
     * @param desc
     *            the current Schema Description analyzed
     * @param metadata
     *            Metadata to attach new elements
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     */
    private void parseValueTypeDefinition(SchemaDescription desc, XMPMetadata metadata)
            throws XmpParsingException, XMLStreamException {
        // <rdf:Seq>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, SEQ_NAME, "Expected Seq Declaration");
        int elmtType = reader.get().nextTag();
        String type, namespaceURI, prefix, description;
        List<PDFAFieldDescription> fields;
        while (elmtType == XMLStreamReader.START_ELEMENT) {
            type = null;
            namespaceURI = null;
            prefix = null;
            description = null;
            fields = null;
            expectCurrentLocalName("li");
            elmtType = reader.get().nextTag();
            while (elmtType == XMLStreamReader.START_ELEMENT) {
                if (reader.get().getLocalName().equals("type")) {
                    type = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("namespaceURI")) {
                    namespaceURI = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("prefix")) {
                    prefix = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("description")) {
                    description = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("field")) {
                    fields = parseFieldDescription(metadata);

                } else {
                    throw new XmpUnexpectedElementException(
                            "Unexpected property definition in one of ValueType Descriptions of PDF/A Extension schemas description");
                }
                elmtType = reader.get().nextTag();
            }
            if ((type != null) && (namespaceURI != null) && (prefix != null) && (description != null)) {
                desc.addValueType(type, namespaceURI, prefix, description, fields);
            } else {
                throw new XmpRequiredPropertyException(
                        "one property declaration in PDF/A Extension is not complete");
            }
            elmtType = reader.get().nextTag();
        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, VALUE_TYPE_NAME,
                "Expected End of ValueType Declaration");

    }

    /**
     * Treat field description on the current analyzed value type description
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @return A list of parsed fields
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpParsingException
     *             When element expected not found
     */
    private List<PDFAFieldDescription> parseFieldDescription(XMPMetadata metadata)
            throws XmpParsingException, XMLStreamException {
        List<PDFAFieldDescription> fields = new ArrayList<PDFAFieldDescription>();
        // <rdf:Seq>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, SEQ_NAME, "Expected Seq Declaration");
        int elmtType = reader.get().nextTag();
        String name, type, description;
        while (elmtType == XMLStreamReader.START_ELEMENT) {
            expectCurrentLocalName("li");
            elmtType = reader.get().nextTag();
            name = null;
            type = null;
            description = null;

            while (elmtType == XMLStreamReader.START_ELEMENT) {
                if (reader.get().getLocalName().equals("name")) {
                    name = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals(VALUE_TYPE_NAME)) {
                    type = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("description")) {
                    description = reader.get().getElementText();
                } else {
                    throw new XmpUnexpectedElementException(
                            "Unexpected property definition in one of ValueType Field Descriptions of PDF/A Extension schemas description");
                }
                elmtType = reader.get().nextTag();
            }
            if ((name != null) && (type != null) && (description != null)) {
                PDFAFieldDescription tmp = new PDFAFieldDescription(metadata);
                tmp.setNameValue(name);
                tmp.setValueTypeValue(type);
                tmp.setDescriptionValue(description);
                fields.add(tmp);
            } else {
                throw new XmpRequiredPropertyException(
                        "One valuetype field declaration in PDF/A Extension is not complete");
            }
            // expectNextTag(XMLStreamReader.END_ELEMENT,"Expected element end");
            elmtType = reader.get().nextTag();
        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, "field", "Expected End of Properties Declaration");
        if (fields.size() != 0) {
            return fields;
        }
        return null;
    }

    /**
     * Treat a property definition for a specific Schema Description
     * 
     * @param desc
     *            the current Schema Description analyzed
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws BadFieldValueException
     *             When one of a field property contain an incorrect value
     */
    private void parsePropertyDefinition(SchemaDescription desc)
            throws XmpParsingException, XMLStreamException, BadFieldValueException {
        // <rdf:Seq>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, SEQ_NAME, "Expected Seq Declaration");
        // Each property definition
        int elmtType = reader.get().nextTag();
        String name, type, category, description;
        while (elmtType == XMLStreamReader.START_ELEMENT) {
            expectCurrentLocalName("li");
            elmtType = reader.get().nextTag();
            name = null;
            type = null;
            category = null;
            description = null;

            while (elmtType == XMLStreamReader.START_ELEMENT) {
                if (reader.get().getLocalName().equals("name")) {
                    name = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals(VALUE_TYPE_NAME)) {
                    type = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("category")) {
                    category = reader.get().getElementText();
                } else if (reader.get().getLocalName().equals("description")) {
                    description = reader.get().getElementText();
                } else {
                    throw new XmpUnexpectedElementException(
                            "Unexpected property definition in one of Properties Descriptions of PDF/A Extension schemas description");
                }
                elmtType = reader.get().nextTag();
            }
            if ((name != null) && (type != null) && (category != null) && (description != null)) {
                desc.addProperty(name, type, category, description);
            } else {
                throw new XmpRequiredPropertyException(
                        "one property declaration in PDF/A Extension is not complete");
            }
            // expectNextTag(XMLStreamReader.END_ELEMENT,"Expected element end");
            elmtType = reader.get().nextTag();
        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, "property", "Expected End of Properties Declaration");
    }

    /**
     * Check for all namespaces declared for the specified schema if the
     * property searched exists and return its type or null
     * 
     * @param schema
     *            The Schema to analyze
     * @param prop
     *            The property Qualified Name
     * @return The property value type or null if not found in schema
     * @throws XmpParsingException
     *             When element expected not found
     */
    private String getPropertyDeclarationInNamespaces(XMPSchema schema, QName prop) throws XmpParsingException {

        Iterator<Attribute> it = schema.getAllAttributes().iterator();
        Attribute tmp;
        ArrayList<Attribute> list = new ArrayList<Attribute>();
        while (it.hasNext()) {
            tmp = it.next();
            if (tmp.getPrefix() != null) {
                if (tmp.getPrefix().equals("xmlns")) {
                    list.add(tmp);
                }
            }
        }
        it = list.iterator();
        String type;
        StringBuffer unknownNS = new StringBuffer();
        while (it.hasNext()) {
            String namespace = it.next().getValue();
            if (!nsMap.isContainedNamespace(namespace)) {
                unknownNS.append(" '").append(namespace).append("' ");
                continue;
            }
            type = nsMap.getSpecifiedPropertyType(namespace, prop);
            if (type != null) {
                return type;
            }
        }
        String uns = unknownNS.toString().trim();
        if ((uns == null) || "".equals(uns)) {
            throw new XmpUnknownPropertyException(
                    "Cannot find a description for '" + prop.getLocalPart() + "' property");
        } else {
            throw new XmpUnknownSchemaException("Cannot find a definition for the namespace " + uns + " ");
        }

    }

    /**
     * Build a property with the specific type defined in schema or complex
     * property and add it to the object representation
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param propertyName
     *            The fully qualified name of the property
     * @param stype
     *            Type of the property
     * @param container
     *            the entity where place the property representation
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     */
    protected void parseXmpSimpleProperty(XMPMetadata metadata, QName propertyName, XmpPropertyType stype,
            ComplexPropertyContainer container)
            throws XmpUnknownPropertyTypeException, XmpPropertyFormatException, XMLStreamException {
        try {
            AbstractSimpleProperty prop = null;
            ArrayList<Attribute> attributes = new ArrayList<Attribute>();
            int cpt = reader.get().getAttributeCount();
            for (int i = 0; i < cpt; i++) {
                attributes.add(new Attribute(null, reader.get().getAttributePrefix(i),
                        reader.get().getAttributeLocalName(i), reader.get().getAttributeValue(i)));
            }
            if (stype == XmpPropertyType.Text) {
                prop = new TextType(metadata, propertyName.getPrefix(), propertyName.getLocalPart(),
                        reader.get().getElementText());
            } else if (stype == XmpPropertyType.Integer) {
                prop = new IntegerType(metadata, propertyName.getPrefix(), propertyName.getLocalPart(),
                        reader.get().getElementText());
            } else if (stype == XmpPropertyType.Date) {
                prop = new DateType(metadata, propertyName.getPrefix(), propertyName.getLocalPart(),
                        reader.get().getElementText());
            } else if (stype == XmpPropertyType.Boolean) {
                prop = new BooleanType(metadata, propertyName.getPrefix(), propertyName.getLocalPart(),
                        reader.get().getElementText());
            } else if (stype == XmpPropertyType.Real) {
                prop = new RealType(metadata, propertyName.getPrefix(), propertyName.getLocalPart(),
                        reader.get().getElementText());
            }
            if (prop != null) {
                container.addProperty(prop);
                // ADD ATTRIBUTES
                for (Attribute att : attributes) {
                    prop.setAttribute(att);
                }
            } else {
                throw new XmpUnknownPropertyTypeException("Unknown simple type found");
            }
        } catch (IllegalArgumentException e) {
            throw new XmpPropertyFormatException(
                    "Unexpected type found for the property '" + propertyName.getLocalPart() + "'", e);
        }
    }

    /**
     * Parse a bag property (unordered array) with the specific type defined in
     * schema or complex property and add it to the object representation
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param bagName
     *            name of bag property
     * @param stype
     *            type of values contained in this bag
     * @param container
     *            the entity where place the property representation
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    protected void parseBagProperty(XMPMetadata metadata, QName bagName, XmpPropertyType stype,
            ComplexPropertyContainer container) throws XmpUnexpectedTypeException, XmpParsingException,
            XMLStreamException, XmpUnknownPropertyTypeException, XmpPropertyFormatException {
        ComplexProperty bag = new ComplexProperty(metadata, bagName.getPrefix(), bagName.getLocalPart(),
                ComplexProperty.UNORDERED_ARRAY);
        container.addProperty(bag);
        // <rdf:Bag>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, BAG_NAME, "Expected Bag Declaration");
        // Each property definition
        int elmtType = reader.get().nextTag();
        while ((elmtType != XMLStreamReader.END_ELEMENT)
                && !reader.get().getName().getLocalPart().equals(BAG_NAME)) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), stype, bag.getContainer());
            elmtType = reader.get().nextTag();

        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, bagName.getLocalPart(), "Expected end of Bag property");

    }

    protected void parseComplexBagProperty(XMPMetadata metadata, QName bagName,
            StructuredPropertyParser complexParser, ComplexPropertyContainer container)
            throws XmpUnexpectedTypeException, XmpParsingException, XMLStreamException,
            XmpUnknownPropertyTypeException, XmpPropertyFormatException {
        ComplexProperty bag = new ComplexProperty(metadata, bagName.getPrefix(), bagName.getLocalPart(),
                ComplexProperty.UNORDERED_ARRAY);
        container.addProperty(bag);
        // <rdf:Bag>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, BAG_NAME, "Expected Bag Declaration");
        // Each property definition
        int elmtType = reader.get().nextTag();
        while ((elmtType != XMLStreamReader.END_ELEMENT)
                && !reader.get().getName().getLocalPart().equals(BAG_NAME)) {
            complexParser.parse(metadata, reader.get().getName(), bag.getContainer());
            elmtType = reader.get().nextTag();

        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, bagName.getLocalPart(), "Expected end of Bag property");

    }

    /**
     * Parse a seq property (ordered array) with the specific type defined in
     * schema or complex property and add it to the object representation
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param seqName
     *            name of the seq
     * @param stype
     *            type of values contained in this bag
     * @param container
     *            the entity where place the property representation
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    protected void parseSeqProperty(XMPMetadata metadata, QName seqName, XmpPropertyType stype,
            ComplexPropertyContainer container) throws XmpUnexpectedTypeException, XmpParsingException,
            XMLStreamException, XmpUnknownPropertyTypeException, XmpPropertyFormatException {
        ComplexProperty seq = new ComplexProperty(metadata, seqName.getPrefix(), seqName.getLocalPart(),
                ComplexProperty.ORDERED_ARRAY);
        container.addProperty(seq);
        // <rdf:Bag>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, SEQ_NAME, "Expected Seq Declaration");
        // Each property definition
        int elmtType = reader.get().nextTag();
        while ((elmtType != XMLStreamReader.END_ELEMENT)
                && !reader.get().getName().getLocalPart().equals(SEQ_NAME)) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), stype, seq.getContainer());
            elmtType = reader.get().nextTag();

        }
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, seqName.getLocalPart(), "Expected end of Seq property");
    }

    /**
     * Parse Alt property (Alternative language property) with the specific type
     * defined in schema or complex property and add it to the object
     * representation
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param altName
     *            name of Alt property
     * @param stype
     *            type of values contained in this bag
     * @param container
     *            the entity where place the property representation
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    protected void parseAltProperty(XMPMetadata metadata, QName altName, XmpPropertyType stype,
            ComplexPropertyContainer container) throws XmpUnexpectedTypeException, XmpParsingException,
            XMLStreamException, XmpUnknownPropertyTypeException, XmpPropertyFormatException {
        ComplexProperty alt = new ComplexProperty(metadata, altName.getPrefix(), altName.getLocalPart(),
                ComplexProperty.ALTERNATIVE_ARRAY);
        container.addProperty(alt);
        // <rdf:Alt>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, ALT_NAME, "Expected Alt Declaration");
        int elmtType = reader.get().nextTag();
        while (!((elmtType == XMLStreamReader.END_ELEMENT)
                && reader.get().getName().getLocalPart().equals(ALT_NAME))) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), stype, alt.getContainer());
            elmtType = reader.get().nextTag();

        }
        // <dc:description><rdf:Alt><rdf:li>sujet</rdf:li></rdf:Alt></dc:description>
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, altName.getLocalPart(), "Expected end of alt property");

    }

    /**
     * Create a property in a specified container (complexproperty or schema)
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param type
     *            type of value contained in the property
     * @param container
     *            the entity where place the property representation
     * @return True if property has been treated (according to its type)
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     */
    private boolean createAndAddPropertyToContainer(XMPMetadata metadata, String type,
            ComplexPropertyContainer container) throws XmpParsingException, XmpUnexpectedTypeException,
            XmpUnknownPropertyTypeException, XmpPropertyFormatException, XMLStreamException {
        if (type.equals("Text")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else if (type.equals("Integer")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Integer, container);

        } else if (type.equals("Boolean")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Boolean, container);

        } else if (type.equals("Real")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Real, container);
        } else if (type.equals("Date")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Date, container);

        } else if (type.equals("URI")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);

        } else if (type.equals("URL")) {
            parseXmpSimpleProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);

        } else if (type.equals("bag Text")) {
            parseBagProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else if (type.equals("bag ProperName")) {
            parseBagProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else if (type.equals("bag Job")) {
            parseComplexBagProperty(metadata, reader.get().getName(), new JobParser(this), container);
        } else if (type.equals("bag Xpath")) {
            parseBagProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else if (type.equals("seq Text")) {
            parseSeqProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else if (type.equals("seq Field")) {
            parseSeqProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else if (type.equals("seq Date")) {
            parseSeqProperty(metadata, reader.get().getName(), XmpPropertyType.Date, container);
        } else if (type.equals("Lang Alt")) {
            parseAltProperty(metadata, reader.get().getName(), XmpPropertyType.Text, container);
        } else {
            return false;
        }
        return true;
    }

    /**
     * Parse a specific field
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param propertyName
     *            the full qualified name of this property
     * @param schema
     *            The schema where save this property
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    protected void parseFieldProperty(XMPMetadata metadata, QName propertyName, XMPSchema schema)
            throws XmpUnexpectedTypeException, XmpPropertyFormatException, XmpParsingException, XMLStreamException,
            XmpUnknownPropertyTypeException {
        ComplexPropertyContainer field = new ComplexPropertyContainer(metadata, propertyName.getPrefix(),
                propertyName.getLocalPart());
        schema.addProperty(field);
        field.setAttribute(new Attribute(null, "rdf", "parseType", "Resource"));
        String type;
        int elmtType = reader.get().nextTag();
        while ((elmtType != XMLStreamReader.END_ELEMENT)
                && !reader.get().getName().getLocalPart().equals(SEQ_NAME)) {

            type = getPropertyDeclarationInNamespaces(schema, reader.get().getName());
            if (!createAndAddPropertyToContainer(metadata, type, field)) {
                if (type.equals("Field")) {
                    String stype = getPropertyDeclarationInNamespaces(schema, reader.get().getName());
                    createAndAddPropertyToContainer(metadata, stype, field);
                } else {
                    throw new XmpUnknownPropertyTypeException("Unknown type : " + type);
                }
            }
            elmtType = reader.get().nextTag();
        }
        expectCurrentLocalName(propertyName.getLocalPart());

        // expectNextSpecificTag(XMLStreamReader.END_ELEMENT,
        // propertyName.getLocalPart(), "Expected end of field declaration");

    }

    /**
     * analyze one property in the stream, retrieve its type according to the
     * schema information and call its object representation building
     * 
     * @param schema
     *            The schema where find information
     * @param metadata
     *            Metadata to attach new elements
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMPUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    protected void parseProperty(XMPSchema schema, XMPMetadata metadata)
            throws XmpParsingException, XmpPropertyFormatException, XmpUnexpectedTypeException, XMLStreamException,
            XmpUnknownPropertyTypeException {
        QName propertyName = reader.get().getName();
        nsMap.resetComplexBasicTypesDeclarationInPropertyLevel();
        int cptNs = reader.get().getNamespaceCount();
        for (int i = 0; i < cptNs; i++) {
            if (nsMap.isComplexBasicTypes(reader.get().getNamespaceURI(i))) {
                nsMap.setComplexBasicTypesDeclarationForLevelSchema(reader.get().getNamespaceURI(i),
                        reader.get().getNamespacePrefix(i));
            }
        }
        String type = getPropertyDeclarationInNamespaces(schema, propertyName);
        if (type.equals("Unmanaged")) {
            // do not parse the property, no validation, no reserialization
            boolean cont = true;
            while (cont) {
                int t = reader.get().next();
                if (t == XMLStreamReader.END_ELEMENT) {
                    if (propertyName.equals(reader.get().getName())) {
                        cont = false;
                    }
                }
            }
        } else if (type.equals("Text")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());

        } else if (type.equals("Integer")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Integer, schema.getContent());

        } else if (type.equals("Boolean")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Boolean, schema.getContent());

        } else if (type.equals("Real")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Real, schema.getContent());
        } else if (type.equals("Date")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Date, schema.getContent());

        } else if (type.equals("URI")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());

        } else if (type.equals("URL")) {
            parseXmpSimpleProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());

        } else if (type.equals("bag Text")) {
            parseBagProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());
        } else if (type.equals("bag ProperName")) {
            parseBagProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());
        } else if (type.equals("bag Job")) {
            parseComplexBagProperty(metadata, propertyName, new JobParser(this), schema.getContent());
        } else if (type.equals("bag Xpath")) {
            parseBagProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());
        } else if (type.equals("seq Text")) {
            parseSeqProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());
        } else if (type.equals("seq Date")) {
            parseSeqProperty(metadata, propertyName, XmpPropertyType.Date, schema.getContent());
        } else if (type.equals("Lang Alt")) {
            parseAltProperty(metadata, propertyName, XmpPropertyType.Text, schema.getContent());
        } else if (type.equals("Field")) {
            parseFieldProperty(metadata, propertyName, schema);
        } else if (type.equals("Thumbnail")) {
            parseThumbnailProperty(metadata, propertyName, schema.getContent());
        } else if (type.equals("Alt Thumbnail")) {
            parseAltThumbnailProperty(metadata, propertyName, schema.getContent());
        } else {
            throw new XmpUnknownPropertyTypeException("Unknown type : " + type);
        }

    }

    /**
     * Treat Alternative Thumbnails property
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param altName
     *            name of thumbnails alternative property
     * @param container
     *            the container where record this representation
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    private void parseAltThumbnailProperty(XMPMetadata metadata, QName altName, ComplexPropertyContainer container)
            throws XmpUnexpectedTypeException, XmpParsingException, XMLStreamException,
            XmpUnknownPropertyTypeException, XmpPropertyFormatException {
        ComplexProperty alt = new ComplexProperty(metadata, altName.getPrefix(), altName.getLocalPart(),
                ComplexProperty.ALTERNATIVE_ARRAY);
        container.addProperty(alt);
        // <rdf:Alt>
        expectNextSpecificTag(XMLStreamReader.START_ELEMENT, ALT_NAME, "Expected Alt Declaration");
        int elmtType = reader.get().nextTag();
        while (!((elmtType == XMLStreamReader.END_ELEMENT)
                && reader.get().getName().getLocalPart().equals(ALT_NAME))) {
            parseThumbnailProperty(metadata, reader.get().getName(), alt.getContainer());
            elmtType = reader.get().nextTag();
        }

        // <dc:description><rdf:Alt><rdf:li>sujet</rdf:li></rdf:Alt></dc:description>
        expectNextSpecificTag(XMLStreamReader.END_ELEMENT, altName.getLocalPart(), "Expected end of alt property");
    }

    /**
     * * Treat a thumbnail property
     * 
     * @param metadata
     *            Metadata to attach new elements
     * @param altName
     *            name of thumbnail property
     * @param container
     *            The container where save property representation
     * @throws XmpUnexpectedTypeException
     *             When DOM Element type found unexpected
     * @throws XmpParsingException
     *             When element expected not found
     * @throws XMLStreamException
     *             When error during reading the rest of xmp stream
     * @throws XmpUnknownPropertyTypeException
     *             Value Type property is incorrect or the basic value type
     *             can't be treat at the moment
     * @throws XmpPropertyFormatException
     *             Unexpected type found (IllegalArgumentException)
     */
    private void parseThumbnailProperty(XMPMetadata metadata, QName altName, ComplexPropertyContainer container)
            throws XmpUnexpectedTypeException, XmpParsingException, XMLStreamException,
            XmpUnknownPropertyTypeException, XmpPropertyFormatException {
        expectCurrentLocalName("li");
        ThumbnailType thumbnail = new ThumbnailType(metadata, altName.getPrefix(), altName.getLocalPart());
        int elmtType = reader.get().nextTag();
        QName eltName;
        String eltContent;
        while (!((elmtType == XMLStreamReader.END_ELEMENT) && reader.get().getName().getLocalPart().equals("li"))) {
            eltName = reader.get().getName();
            eltContent = reader.get().getElementText();
            if (eltName.getLocalPart().equals("height")) {
                thumbnail.setHeight(eltName.getPrefix(), eltName.getLocalPart(), Integer.valueOf(eltContent));
            } else if (eltName.getLocalPart().equals("width")) {
                thumbnail.setWidth(eltName.getPrefix(), eltName.getLocalPart(), Integer.valueOf(eltContent));
            } else if (eltName.getLocalPart().equals("image")) {
                thumbnail.setImg(eltName.getPrefix(), eltName.getLocalPart(), eltContent);
            } else if (eltName.getLocalPart().equals("format")) {
                thumbnail.setFormat(eltName.getPrefix(), eltName.getLocalPart(), eltContent);
            } else {
                throw new XmpParsingException(
                        "Unknown property name for a thumbnail element : " + eltName.getLocalPart());
            }
            elmtType = reader.get().nextTag();
        }
        container.addProperty(thumbnail);
    }

}