FELIX-4644 - stricter parsing of MetaType files:

- do some additional validations on the number of allowed elements
  in a MetaType during the parsing. For example, only allow exactly
  one Object in a Designate or at least one AD for MetaType files
  with a namespace version less than 1.3; 
- expose the namespace of the metatype file so it can be used for
  external validation purposes.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1653489 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/metatype/src/main/java/org/apache/felix/metatype/MetaData.java b/metatype/src/main/java/org/apache/felix/metatype/MetaData.java
index 0985c87..b2c9bbd 100644
--- a/metatype/src/main/java/org/apache/felix/metatype/MetaData.java
+++ b/metatype/src/main/java/org/apache/felix/metatype/MetaData.java
@@ -18,13 +18,11 @@
  */
 package org.apache.felix.metatype;
 
-
 import java.net.URL;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-
 /**
  * The <code>MetaData</code> class represents the <code>MetaData</code>
  * element of the meta type descriptor.
@@ -33,81 +31,83 @@
  */
 public class MetaData extends OptionalAttributes
 {
-
+    private String namespace;
     private String localePrefix;
     private Map objectClassDefinitions;
     private Map designates;
     private URL source;
 
-
     public String getLocalePrefix()
     {
         return localePrefix;
     }
 
-
-    public void setLocalePrefix( String localePrefix )
+    public void setLocalePrefix(String localePrefix)
     {
         this.localePrefix = localePrefix;
     }
 
-
     public Map getObjectClassDefinitions()
     {
         return objectClassDefinitions;
     }
 
-
-    public void addObjectClassDefinition( OCD objectClassDefinition )
+    public void addObjectClassDefinition(OCD objectClassDefinition)
     {
-        if ( objectClassDefinition != null )
+        if (objectClassDefinition != null)
         {
-            if ( objectClassDefinitions == null )
+            if (objectClassDefinitions == null)
             {
                 objectClassDefinitions = new LinkedHashMap();
             }
 
-            objectClassDefinitions.put( objectClassDefinition.getID(), objectClassDefinition );
-            objectClassDefinition.setMetadata( this );
+            objectClassDefinitions.put(objectClassDefinition.getID(), objectClassDefinition);
+            objectClassDefinition.setMetadata(this);
         }
     }
 
-
     public Map getDesignates()
     {
         return designates;
     }
 
-
-    public void addDesignate( Designate designate )
+    public void addDesignate(Designate designate)
     {
-        if ( designate != null )
+        if (designate != null)
         {
-            if ( designates == null )
+            if (designates == null)
             {
                 designates = new HashMap();
             }
 
-            if ( designate.getFactoryPid() != null )
+            if (designate.getFactoryPid() != null)
             {
-                designates.put( designate.getFactoryPid(), designate );
+                designates.put(designate.getFactoryPid(), designate);
             }
             else
             {
-                designates.put( designate.getPid(), designate );
+                designates.put(designate.getPid(), designate);
             }
         }
     }
 
-
     public URL getSource()
     {
         return source;
     }
 
-
-    public void setSource( URL source )
+    public void setSource(URL source)
     {
         this.source = source;
     }
+
+    public String getNamespace()
+    {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace)
+    {
+        this.namespace = namespace;
+    }
 }
diff --git a/metatype/src/main/java/org/apache/felix/metatype/MetaDataReader.java b/metatype/src/main/java/org/apache/felix/metatype/MetaDataReader.java
index d27f8d9..08c0aae 100644
--- a/metatype/src/main/java/org/apache/felix/metatype/MetaDataReader.java
+++ b/metatype/src/main/java/org/apache/felix/metatype/MetaDataReader.java
@@ -18,7 +18,6 @@
  */
 package org.apache.felix.metatype;
 
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
@@ -34,7 +33,6 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-
 /**
  * The <code>MetaDataReader</code> provides two methods to read meta type
  * documents according to the MetaType schema (105.8 XML Schema). The
@@ -75,27 +73,21 @@
     static final String NAMESPACE_1_2 = "http://www.osgi.org/xmlns/metatype/v1.2.0";
 
     /**
-     * The XML Namespace for Metatype 1.2 descriptors.
+     * The XML Namespace for Metatype 1.3 descriptors.
      */
     static final String NAMESPACE_1_3 = "http://www.osgi.org/xmlns/metatype/v1.3.0";
 
     /** The XML parser used to read the XML documents */
     private KXmlParser parser = new KXmlParser();
+    private String namespace = NAMESPACE_1_0;
 
     /** Sets of attributes belonging to XML elements. */
-    private final Set AD_ATTRIBUTES = new HashSet( Arrays.asList( new String[]
-        { "name", "description", "id", "type", "cardinality", "min", "max", "default", "required" } ) );
-    private final Set ATTRIBUTE_ATTRIBUTES = new HashSet( Arrays.asList( new String[]
-        { "adref", "content" } ) );
-    private final Set DESIGNATE_ATTRIBUTES = new HashSet( Arrays.asList( new String[]
-        { "pid", "factoryPid", "bundle", "optional", "merge" } ) );
-    private final Set DESIGNATEOBJECT_ATTRIBUTES = new HashSet( Arrays.asList( new String[]
-        { "ocdref" } ) );
-    private final Set METADATA_ATTRIBUTES = new HashSet( Arrays.asList( new String[]
-        { "localization" } ) );
-    private final Set OCD_ATTRIBUTES = new HashSet( Arrays.asList( new String[]
-        { "name", "description", "id" } ) );
-
+    private static final Set AD_ATTRIBUTES = new HashSet(Arrays.asList(new String[] { "name", "description", "id", "type", "cardinality", "min", "max", "default", "required" }));
+    private static final Set ATTRIBUTE_ATTRIBUTES = new HashSet(Arrays.asList(new String[] { "adref", "content" }));
+    private static final Set DESIGNATE_ATTRIBUTES = new HashSet(Arrays.asList(new String[] { "pid", "factoryPid", "bundle", "optional", "merge" }));
+    private static final Set DESIGNATEOBJECT_ATTRIBUTES = new HashSet(Arrays.asList(new String[] { "ocdref" }));
+    private static final Set METADATA_ATTRIBUTES = new HashSet(Arrays.asList(new String[] { "localization" }));
+    private static final Set OCD_ATTRIBUTES = new HashSet(Arrays.asList(new String[] { "name", "description", "id" }));
 
     /**
      * Parses the XML document provided by the <code>url</code>. The XML document
@@ -113,33 +105,34 @@
      * @throws IOException If an I/O error occurs accessing the stream or
      *      parsing the XML document.
      */
-    public MetaData parse( URL url ) throws IOException
+    public MetaData parse(URL url) throws IOException
     {
         InputStream ins = null;
         try
         {
             ins = url.openStream();
-            this.parser.setProperty( "http://xmlpull.org/v1/doc/properties.html#location", url.toString() );
-            MetaData md = this.parse( ins );
-            if ( md != null )
+
+            this.parser.setProperty("http://xmlpull.org/v1/doc/properties.html#location", url.toString());
+            MetaData md = parse(ins);
+            if (md != null)
             {
-                md.setSource( url );
+                md.setSource(url);
             }
             return md;
         }
-        catch ( XmlPullParserException e )
+        catch (XmlPullParserException e)
         {
-            throw new IOException( "XML parsing exception while reading metadata: " + e.getMessage() );
+            throw new IOException("XML parsing exception while reading metadata: " + e.getMessage());
         }
         finally
         {
-            if ( ins != null )
+            if (ins != null)
             {
                 try
                 {
                     ins.close();
                 }
-                catch ( IOException ioe )
+                catch (IOException ioe)
                 {
                     // ignore
                 }
@@ -147,26 +140,6 @@
         }
     }
 
-
-    /**
-     * Checks if this document has a meta type name space.
-     *
-     * @throws IOException when there the meta type name space is not valid
-     */
-    private void checkMetatypeNamespace() throws IOException
-    {
-        final String namespace = this.parser.getNamespace();
-        if ( namespace != null && namespace.length() > 0 && 
-            !NAMESPACE_1_0.equals( namespace ) &&
-            !NAMESPACE_1_1.equals( namespace ) &&
-            !NAMESPACE_1_2.equals( namespace ) &&
-            !NAMESPACE_1_3.equals( namespace ) )
-        {
-            throw new IOException( "Unsupported Namespace " + namespace );
-        }
-    }
-
-
     /**
      * Parses the XML document in the given input stream.
      * <p>
@@ -182,91 +155,110 @@
      * @throws IOException If an I/O error occurs accessing the stream or
      *      parsing the XML document.
      */
-    public MetaData parse( InputStream ins ) throws IOException
+    public MetaData parse(InputStream ins) throws IOException
     {
         MetaData mti = null;
         try
         {
-            this.parser.setFeature( KXmlParser.FEATURE_PROCESS_NAMESPACES, true );
-
+            this.parser.setFeature(KXmlParser.FEATURE_PROCESS_NAMESPACES, true);
             // set the parser input, use null encoding to force detection with <?xml?>
-            this.parser.setInput( ins, null );
+            this.parser.setInput(ins, null);
 
             int eventType = this.parser.getEventType();
-            while ( eventType != XmlPullParser.END_DOCUMENT )
+            while (eventType != XmlPullParser.END_DOCUMENT)
             {
-                if ( eventType == XmlPullParser.START_TAG )
+                String tagName = this.parser.getName();
+                if (eventType == XmlPullParser.START_TAG)
                 {
-                    if ( "MetaData".equals( this.parser.getName() ) )
+                    if ("MetaData".equals(tagName))
                     {
-                        checkMetatypeNamespace();
-                        mti = this.readMetaData();
+                        mti = readMetaData();
                     }
                     else
                     {
-                        this.ignoreElement();
+                        ignoreElement();
                     }
                 }
                 eventType = this.parser.next();
             }
         }
-        catch ( XmlPullParserException e )
+        catch (XmlPullParserException e)
         {
-            throw new IOException( "XML parsing exception while reading metadata: " + e.getMessage() );
+            throw new IOException("XML parsing exception while reading metadata: " + e.getMessage());
         }
 
         return mti;
     }
 
+    /**
+     * Checks if this document has a meta type name space.
+     *
+     * @throws IOException when there the meta type name space is not valid
+     */
+    private void checkMetatypeNamespace() throws IOException
+    {
+        final String namespace = this.parser.getNamespace();
+        if (namespace != null && !"".equals(namespace.trim()))
+        {
+            if (!NAMESPACE_1_0.equals(namespace) && !NAMESPACE_1_1.equals(namespace) && !NAMESPACE_1_2.equals(namespace) && !NAMESPACE_1_3.equals(namespace))
+            {
+                throw new IOException("Unsupported Namespace: '" + namespace + "'");
+            }
+            this.namespace = namespace;
+        }
+    }
 
-    private void readOptionalAttributes( OptionalAttributes entity, Set attributes )
+    private void readOptionalAttributes(OptionalAttributes entity, Set attributes)
     {
         int count = this.parser.getAttributeCount();
-        for ( int i = 0; i < count; i++ )
+        for (int i = 0; i < count; i++)
         {
-            String name = this.parser.getAttributeName( i );
-            if ( !attributes.contains( name ) )
+            String name = this.parser.getAttributeName(i);
+            if (!attributes.contains(name))
             {
-                String value = this.parser.getAttributeValue( i );
-                entity.addOptionalAttribute( name, value );
+                String value = this.parser.getAttributeValue(i);
+                entity.addOptionalAttribute(name, value);
             }
         }
     }
 
-
     private MetaData readMetaData() throws IOException, XmlPullParserException
     {
-        MetaData mti = this.createMetaData();
-        mti.setLocalePrefix( this.getOptionalAttribute( "localization" ) );
+        checkMetatypeNamespace();
 
-        readOptionalAttributes( mti, METADATA_ATTRIBUTES );
+        MetaData mti = createMetaData();
+        mti.setNamespace(this.namespace);
+        mti.setLocalePrefix(getOptionalAttribute("localization"));
+
+        readOptionalAttributes(mti, METADATA_ATTRIBUTES);
 
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            String tagName = this.parser.getName();
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( "OCD".equals( this.parser.getName() ) )
+                if ("OCD".equals(tagName))
                 {
-                    mti.addObjectClassDefinition( this.readOCD() );
+                    mti.addObjectClassDefinition(readOCD());
                 }
-                else if ( "Designate".equals( this.parser.getName() ) )
+                else if ("Designate".equals(tagName))
                 {
-                    mti.addDesignate( this.readDesignate() );
+                    mti.addDesignate(readDesignate());
                 }
                 else
                 {
-                    this.ignoreElement();
+                    ignoreElement();
                 }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( "MetaData".equals( this.parser.getName() ) )
+                if ("MetaData".equals(tagName))
                 {
                     break;
                 }
 
-                throw this.unexpectedElement();
+                throw unexpectedElement(tagName);
             }
             eventType = this.parser.next();
         }
@@ -274,54 +266,63 @@
         return mti;
     }
 
-
     private OCD readOCD() throws IOException, XmlPullParserException
     {
-        OCD ocd = this.createOCD();
-        ocd.setId( this.getRequiredAttribute( "id" ) );
-        ocd.setName( this.getRequiredAttribute( "name" ) );
-        ocd.setDescription( this.getOptionalAttribute( "description" ) );
+        OCD ocd = createOCD();
+        ocd.setId(getRequiredAttribute("id"));
+        ocd.setName(getRequiredAttribute("name"));
+        ocd.setDescription(getOptionalAttribute("description"));
 
-        readOptionalAttributes( ocd, OCD_ATTRIBUTES );
+        readOptionalAttributes(ocd, OCD_ATTRIBUTES);
 
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            String tagName = this.parser.getName();
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( "AD".equals( this.parser.getName() ) )
+                if ("AD".equals(tagName))
                 {
-                    ocd.addAttributeDefinition( this.readAD() );
+                    ocd.addAttributeDefinition(readAD());
                 }
-                else if ( "Icon".equals( this.parser.getName() ) )
+                else if ("Icon".equals(tagName))
                 {
-                    String res = this.getRequiredAttribute( "resource" );
-                    String sizeString = this.getRequiredAttribute( "size" );
+                    String res = getRequiredAttribute("resource");
+                    String sizeString = getRequiredAttribute("size");
                     try
                     {
-                        Integer size = Integer.decode( sizeString );
-                        ocd.addIcon( size, res );
+                        Integer size = Integer.decode(sizeString);
+                        ocd.addIcon(size, res);
                     }
-                    catch ( NumberFormatException nfe )
+                    catch (NumberFormatException nfe)
                     {
-                        Activator.log( LogService.LOG_DEBUG, "readOCD: Icon size '" + sizeString
-                            + "' is not a valid number" );
+                        Activator.log(LogService.LOG_DEBUG, "readOCD: Icon size '" + sizeString + "' is not a valid number");
                     }
                 }
                 else
                 {
-                    this.ignoreElement();
+                    ignoreElement();
                 }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( "OCD".equals( this.parser.getName() ) )
+                if ("OCD".equals(tagName))
                 {
+                    if (getNamespaceVersion() < 12 && ocd.getIcons() != null && ocd.getIcons().size() > 1)
+                    {
+                        // Only one icon is allowed in versions 1.0 & 1.1...
+                        throw unexpectedElement("Icon");
+                    }
+                    if (getNamespaceVersion() < 13 && ocd.getAttributeDefinitions() == null)
+                    {
+                        // Need at least one AD in versions 1.0, 1.1 & 1.2...
+                        throw missingElement("AD");
+                    }
                     break;
                 }
-                else if ( !"Icon".equals( this.parser.getName() ) )
+                else if (!"Icon".equals(tagName))
                 {
-                    throw this.unexpectedElement();
+                    throw unexpectedElement(tagName);
                 }
             }
             eventType = this.parser.next();
@@ -330,47 +331,58 @@
         return ocd;
     }
 
-
     private Designate readDesignate() throws IOException, XmlPullParserException
     {
-        final String pid = this.getOptionalAttribute( "pid" );
-        final String factoryPid = this.getOptionalAttribute( "factoryPid" );
-        if ( pid == null && factoryPid == null )
+        final String pid = getOptionalAttribute("pid");
+        final String factoryPid = getOptionalAttribute("factoryPid");
+        if (pid == null && factoryPid == null)
         {
-            missingAttribute( "pid or factoryPid" );
+            missingAttribute("pid or factoryPid");
         }
 
         Designate designate = this.createDesignate();
-        designate.setPid( pid );
-        designate.setFactoryPid( factoryPid );
-        designate.setBundleLocation( this.getOptionalAttribute( "bundle" ) );
-        designate.setOptional( this.getOptionalAttribute( "optional", false ) );
-        designate.setMerge( this.getOptionalAttribute( "merge", false ) );
+        designate.setPid(pid);
+        designate.setFactoryPid(factoryPid);
+        designate.setBundleLocation(getOptionalAttribute("bundle"));
+        designate.setOptional(getOptionalAttribute("optional", false));
+        designate.setMerge(getOptionalAttribute("merge", false));
 
-        readOptionalAttributes( designate, DESIGNATE_ATTRIBUTES );
+        readOptionalAttributes(designate, DESIGNATE_ATTRIBUTES);
 
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            String tagName = this.parser.getName();
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( "Object".equals( this.parser.getName() ) )
+                if ("Object".equals(tagName))
                 {
-                    designate.setObject( this.readObject() );
+                    if (designate.getObject() != null)
+                    {
+                        // Only 1 Object is allowed...
+                        throw unexpectedElement(tagName);
+                    }
+
+                    designate.setObject(readObject());
                 }
                 else
                 {
                     this.ignoreElement();
                 }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( "Designate".equals( this.parser.getName() ) )
+                if ("Designate".equals(tagName))
                 {
+                    if (designate.getObject() == null)
+                    {
+                        // Exactly 1 Object is allowed...
+                        throw missingElement("Object");
+                    }
                     break;
                 }
 
-                throw this.unexpectedElement();
+                throw unexpectedElement(tagName);
             }
             eventType = this.parser.next();
         }
@@ -378,92 +390,93 @@
         return designate;
     }
 
-
     private AD readAD() throws IOException, XmlPullParserException
     {
-        AD ad = this.createAD();
-        ad.setID( this.getRequiredAttribute( "id" ) );
-        ad.setName( this.getOptionalAttribute( "name" ) );
-        ad.setDescription( this.getOptionalAttribute( "description" ) );
-        ad.setType( this.getRequiredAttribute( "type" ) );
-        ad.setCardinality( this.getOptionalAttribute( "cardinality", 0 ) );
-        ad.setMin( this.getOptionalAttribute( "min" ) );
-        ad.setMax( this.getOptionalAttribute( "max" ) );
-        ad.setDefaultValue( this.getOptionalAttribute( "default" ) );
-        ad.setRequired( this.getOptionalAttribute( "required", true ) );
+        AD ad = createAD();
+        ad.setID(getRequiredAttribute("id"));
+        ad.setName(getOptionalAttribute("name"));
+        ad.setDescription(getOptionalAttribute("description"));
+        ad.setType(getRequiredAttribute("type"));
+        ad.setCardinality(getOptionalAttribute("cardinality", 0));
+        ad.setMin(getOptionalAttribute("min"));
+        ad.setMax(getOptionalAttribute("max"));
+        ad.setDefaultValue(getOptionalAttribute("default"));
+        ad.setRequired(getOptionalAttribute("required", true));
 
-        readOptionalAttributes( ad, AD_ATTRIBUTES );
+        readOptionalAttributes(ad, AD_ATTRIBUTES);
 
         Map options = new LinkedHashMap();
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            String tagName = this.parser.getName();
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( "Option".equals( this.parser.getName() ) )
+                if ("Option".equals(tagName))
                 {
-                    String value = this.getRequiredAttribute( "value" );
-                    String label = this.getRequiredAttribute( "label" );
-                    options.put( value, label );
+                    String value = getRequiredAttribute("value");
+                    String label = getRequiredAttribute("label");
+                    options.put(value, label);
                 }
                 else
                 {
-                    this.ignoreElement();
+                    ignoreElement();
                 }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( "AD".equals( this.parser.getName() ) )
+                if ("AD".equals(tagName))
                 {
                     break;
                 }
-                else if ( !"Option".equals( this.parser.getName() ) )
+                else if (!"Option".equals(tagName))
                 {
-                    throw this.unexpectedElement();
+                    throw unexpectedElement(tagName);
                 }
             }
             eventType = this.parser.next();
         }
 
-        ad.setOptions( options );
+        ad.setOptions(options);
 
         // reset value to force an options check (FELIX-3884)
-        if ( ad.getDefaultValue() != null )
+        if (ad.getDefaultValue() != null)
         {
-            ad.setDefaultValue( ad.getDefaultValue() );
+            ad.setDefaultValue(ad.getDefaultValue());
         }
         return ad;
     }
 
-
     private DesignateObject readObject() throws IOException, XmlPullParserException
     {
-        DesignateObject oh = this.createDesignateObject();
-        oh.setOcdRef( this.getRequiredAttribute( "ocdref" ) );
+        DesignateObject oh = createDesignateObject();
+        oh.setOcdRef(getRequiredAttribute("ocdref"));
 
-        readOptionalAttributes( oh, DESIGNATEOBJECT_ATTRIBUTES );
+        readOptionalAttributes(oh, DESIGNATEOBJECT_ATTRIBUTES);
 
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            String tagName = this.parser.getName();
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( "Attribute".equals( this.parser.getName() ) )
+                if ("Attribute".equals(tagName))
                 {
-                    oh.addAttribute( this.readAttribute() );
+                    oh.addAttribute(readAttribute());
                 }
                 else
                 {
-                    this.ignoreElement();
+                    ignoreElement();
                 }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( "Object".equals( this.parser.getName() ) )
+                if ("Object".equals(tagName))
                 {
                     break;
                 }
-                throw this.unexpectedElement();
+
+                throw unexpectedElement(tagName);
             }
             eventType = this.parser.next();
         }
@@ -471,37 +484,39 @@
         return oh;
     }
 
-
     private Attribute readAttribute() throws IOException, XmlPullParserException
     {
-        Attribute ah = this.createAttribute();
-        ah.setAdRef( this.getRequiredAttribute( "adref" ) );
-        ah.addContent( this.getOptionalAttribute( "content" ), true );
+        Attribute ah = createAttribute();
+        ah.setAdRef(getRequiredAttribute("adref"));
+        ah.addContent(getOptionalAttribute("content"), true);
 
-        readOptionalAttributes( ah, ATTRIBUTE_ATTRIBUTES );
+        readOptionalAttributes(ah, ATTRIBUTE_ATTRIBUTES);
 
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            String tagName = this.parser.getName();
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( "Value".equals( this.parser.getName() ) )
+                if ("Value".equals(tagName))
                 {
-                    ah.addContent( this.parser.nextText(), false );
+                    ah.addContent(this.parser.nextText(), false);
                     eventType = this.parser.getEventType();
-                    continue;
                 }
-                this.ignoreElement();
+                else
+                {
+                    ignoreElement();
+                }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( "Attribute".equals( this.parser.getName() ) )
+                if ("Attribute".equals(tagName))
                 {
                     break;
                 }
-                else if ( !"Value".equals( this.parser.getName() ) )
+                else if (!"Value".equals(tagName))
                 {
-                    throw this.unexpectedElement();
+                    throw unexpectedElement(tagName);
                 }
             }
             eventType = this.parser.next();
@@ -510,62 +525,75 @@
         return ah;
     }
 
-
     //---------- Attribute access helper --------------------------------------
 
-    private String getRequiredAttribute( String attrName ) throws XmlPullParserException
+    private String getRequiredAttribute(String attrName) throws XmlPullParserException
     {
-        String attrVal = this.parser.getAttributeValue( null, attrName );
-        if ( attrVal != null )
+        String attrVal = this.parser.getAttributeValue(null, attrName);
+        if (attrVal != null)
         {
             return attrVal;
         }
-
         // fail if value is missing
-        throw this.missingAttribute( attrName );
+        throw missingAttribute(attrName);
     }
 
-
-    private String getOptionalAttribute( String attrName )
+    private String getOptionalAttribute(String attrName)
     {
-        return this.getOptionalAttribute( attrName, ( String ) null );
+        return getOptionalAttribute(attrName, (String) null);
     }
 
-
-    private String getOptionalAttribute( String attrName, String defaultValue )
+    private String getOptionalAttribute(String attrName, String defaultValue)
     {
-        String attrVal = this.parser.getAttributeValue( null, attrName );
-        return ( attrVal != null ) ? attrVal : defaultValue;
+        String attrVal = this.parser.getAttributeValue(null, attrName);
+        return (attrVal != null) ? attrVal : defaultValue;
     }
 
-
-    private boolean getOptionalAttribute( String attrName, boolean defaultValue )
+    private boolean getOptionalAttribute(String attrName, boolean defaultValue)
     {
-        String attrVal = this.parser.getAttributeValue( null, attrName );
-        return ( attrVal != null ) ? "true".equalsIgnoreCase( attrVal ) : defaultValue;
+        String attrVal = this.parser.getAttributeValue(null, attrName);
+        return (attrVal != null) ? "true".equalsIgnoreCase(attrVal) : defaultValue;
     }
 
-
-    private int getOptionalAttribute( String attrName, int defaultValue )
+    private int getOptionalAttribute(String attrName, int defaultValue)
     {
-        String attrVal = this.parser.getAttributeValue( null, attrName );
-        if ( attrVal != null && attrVal.length() > 0 )
+        String attrVal = this.parser.getAttributeValue(null, attrName);
+        if (attrVal != null && !"".equals(attrVal))
         {
             try
             {
-                return Integer.decode( attrVal ).intValue();
+                return Integer.decode(attrVal).intValue();
             }
-            catch ( NumberFormatException nfe )
+            catch (NumberFormatException nfe)
             {
-                Activator.log( LogService.LOG_DEBUG, "getOptionalAttribute: Value '" + attrVal + "' of attribute "
-                    + attrName + " is not a valid number. Using default value " + defaultValue );
+                Activator.log(LogService.LOG_DEBUG, "getOptionalAttribute: Value '" + attrVal + "' of attribute " + attrName + " is not a valid number. Using default value " + defaultValue);
             }
         }
-
         // fallback to default
         return defaultValue;
     }
 
+    private int getNamespaceVersion()
+    {
+        if (NAMESPACE_1_0.equals(this.namespace))
+        {
+            return 10;
+        }
+        else if (NAMESPACE_1_1.equals(this.namespace))
+        {
+            return 11;
+        }
+        else if (NAMESPACE_1_2.equals(this.namespace))
+        {
+            return 12;
+        }
+        else if (NAMESPACE_1_3.equals(this.namespace))
+        {
+            return 13;
+        }
+        // Undetermined...
+        return Integer.MAX_VALUE;
+    }
 
     //---------- Error Handling support ---------------------------------------
 
@@ -575,20 +603,20 @@
 
         int depth = 0; // enable nested ignored elements
         int eventType = this.parser.next();
-        while ( eventType != XmlPullParser.END_DOCUMENT )
+        while (eventType != XmlPullParser.END_DOCUMENT)
         {
-            if ( eventType == XmlPullParser.START_TAG )
+            if (eventType == XmlPullParser.START_TAG)
             {
-                if ( ignoredElement.equals( this.parser.getName() ) )
+                if (ignoredElement.equals(this.parser.getName()))
                 {
                     depth++;
                 }
             }
-            else if ( eventType == XmlPullParser.END_TAG )
+            else if (eventType == XmlPullParser.END_TAG)
             {
-                if ( ignoredElement.equals( this.parser.getName() ) )
+                if (ignoredElement.equals(this.parser.getName()))
                 {
-                    if ( depth <= 0 )
+                    if (depth <= 0)
                     {
                         return;
                     }
@@ -600,20 +628,23 @@
         }
     }
 
-
-    private XmlPullParserException missingAttribute( String attrName )
+    private XmlPullParserException missingAttribute(String attrName)
     {
-        String message = "Missing Attribute " + attrName + " in element " + this.parser.getName();
-        return new XmlPullParserException( message, this.parser, null );
+        String message = "Missing attribute " + attrName + " in element " + this.parser.getName();
+        return new XmlPullParserException(message, this.parser, null);
     }
 
-
-    private XmlPullParserException unexpectedElement()
+    private XmlPullParserException missingElement(String elementName)
     {
-        String message = "Illegal Element " + this.parser.getName();
-        return new XmlPullParserException( message, this.parser, null );
+        String message = "Missing element " + elementName + " in element " + this.parser.getName();
+        return new XmlPullParserException(message, this.parser, null);
     }
 
+    private XmlPullParserException unexpectedElement(String elementName)
+    {
+        String message = "Unexpected element " + elementName;
+        return new XmlPullParserException(message, this.parser, null);
+    }
 
     //---------- Factory methods ----------------------------------------------
 
@@ -628,7 +659,6 @@
         return new MetaData();
     }
 
-
     /**
      * Creates a new {@link OCD} object to hold the contents of the
      * <code>OCD</code> element.
@@ -640,7 +670,6 @@
         return new OCD();
     }
 
-
     /**
      * Creates a new {@link AD} object to hold the contents of the
      * <code>AD</code> element.
@@ -652,7 +681,6 @@
         return new AD();
     }
 
-
     /**
      * Creates a new {@link DesignateObject} object to hold the contents of the
      * <code>Object</code> element.
@@ -664,7 +692,6 @@
         return new DesignateObject();
     }
 
-
     /**
      * Creates a new {@link Attribute} object to hold the contents of the
      * <code>Attribute</code> element.
@@ -676,7 +703,6 @@
         return new Attribute();
     }
 
-
     /**
      * Creates a new {@link Designate} object to hold the contents of the
      * <code>Designate</code> element.
diff --git a/metatype/src/test/java/org/apache/felix/metatype/MetaDataReaderTest.java b/metatype/src/test/java/org/apache/felix/metatype/MetaDataReaderTest.java
index 6e1cd4d..894c698 100644
--- a/metatype/src/test/java/org/apache/felix/metatype/MetaDataReaderTest.java
+++ b/metatype/src/test/java/org/apache/felix/metatype/MetaDataReaderTest.java
@@ -18,17 +18,16 @@
  */
 package org.apache.felix.metatype;
 
-
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Map;
 
 import junit.framework.TestCase;
 
 import org.osgi.service.metatype.AttributeDefinition;
 import org.xmlpull.v1.XmlPullParserException;
 
-
 /**
  * The <code>MetaDataReaderTest</code> class tests the
  * <code>MetaDataReader</code> class.
@@ -37,10 +36,8 @@
  */
 public class MetaDataReaderTest extends TestCase
 {
-
     private MetaDataReader reader;
 
-
     protected void setUp() throws Exception
     {
         super.setUp();
@@ -48,7 +45,6 @@
         reader = new MetaDataReader();
     }
 
-
     protected void tearDown() throws Exception
     {
         reader = null;
@@ -56,147 +52,264 @@
         super.tearDown();
     }
 
-
     public void testEmpty() throws IOException, XmlPullParserException
     {
         String empty = "<MetaData />";
-        MetaData mti = read( empty );
+        MetaData mti = read(empty);
 
-        assertNull( mti.getLocalePrefix() );
-        assertNull( mti.getObjectClassDefinitions() );
+        // Implicitly assume the lowest version...
+        assertEquals(MetaDataReader.NAMESPACE_1_0, mti.getNamespace());
+        assertNull(mti.getLocalePrefix());
+        assertNull(mti.getObjectClassDefinitions());
     }
 
-
     public void testOptionalAttributesInMetaData() throws IOException, XmlPullParserException
     {
         String name = "myattribute";
         String value = "working";
         String localization = "test";
         String empty = "<MetaData " + name + "=\"" + value + "\" localization=\"" + localization + "\" />";
-        MetaData mti = read( empty );
+        MetaData mti = read(empty);
 
-        assertEquals( localization, mti.getLocalePrefix() );
-        assertNull( mti.getObjectClassDefinitions() );
-        assertNotNull( mti.getOptionalAttributes() );
-        assertEquals( 1, mti.getOptionalAttributes().size() );
-        assertEquals( value, mti.getOptionalAttributes().get( name ) );
+        assertEquals(localization, mti.getLocalePrefix());
+        assertNull(mti.getObjectClassDefinitions());
+        assertNotNull(mti.getOptionalAttributes());
+        assertEquals(1, mti.getOptionalAttributes().size());
+        assertEquals(value, mti.getOptionalAttributes().get(name));
     }
 
-
     public void testWithNamespace_1_0_0() throws IOException, XmlPullParserException
     {
-        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_0 + "\" "
-            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
-        MetaData mti = read( empty );
+        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_0 + "\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
+        MetaData mti = read(empty);
 
-        assertNotNull( mti );
-        assertNull( mti.getLocalePrefix() );
-        assertNull( mti.getObjectClassDefinitions() );
+        assertNotNull(mti);
+        assertEquals(MetaDataReader.NAMESPACE_1_0, mti.getNamespace());
+        assertNull(mti.getLocalePrefix());
+        assertNull(mti.getObjectClassDefinitions());
     }
 
-
     public void testWithNamespace_1_1_0() throws IOException, XmlPullParserException
     {
-        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_1 + "\" "
-            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
-        MetaData mti = read( empty );
+        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_1 + "\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
+        MetaData mti = read(empty);
 
-        assertNotNull( mti );
-        assertNull( mti.getLocalePrefix() );
-        assertNull( mti.getObjectClassDefinitions() );
+        assertNotNull(mti);
+        assertEquals(MetaDataReader.NAMESPACE_1_1, mti.getNamespace());
+        assertNull(mti.getLocalePrefix());
+        assertNull(mti.getObjectClassDefinitions());
     }
 
-
     public void testWithNamespace_1_2_0() throws IOException, XmlPullParserException
     {
-        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_2 + "\" "
-            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
-        MetaData mti = read( empty );
+        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_2 + "\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
+        MetaData mti = read(empty);
 
-        assertNotNull( mti );
-        assertNull( mti.getLocalePrefix() );
-        assertNull( mti.getObjectClassDefinitions() );
+        assertNotNull(mti);
+        assertEquals(MetaDataReader.NAMESPACE_1_2, mti.getNamespace());
+        assertNull(mti.getLocalePrefix());
+        assertNull(mti.getObjectClassDefinitions());
     }
 
-
     public void testWithNamespace_1_3_0() throws IOException, XmlPullParserException
     {
-        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_3 + "\" "
-            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
-        MetaData mti = read( empty );
+        String empty = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_3 + "\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
+        MetaData mti = read(empty);
 
-        assertNotNull( mti );
-        assertNull( mti.getLocalePrefix() );
-        assertNull( mti.getObjectClassDefinitions() );
+        assertNotNull(mti);
+        assertEquals(MetaDataReader.NAMESPACE_1_3, mti.getNamespace());
+        assertNull(mti.getLocalePrefix());
+        assertNull(mti.getObjectClassDefinitions());
     }
 
-
     public void testWithInvalidNamespaceUri()
     {
-        String empty = "<metatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/datatype/v1.0.0\" "
-            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
+        String empty = "<metatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/datatype/v1.0.0\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></metatype:MetaData>";
 
         try
         {
-            read( empty );
-            fail( "Parse failure expected for unsupported namespace URI" );
+            read(empty);
+            fail("Parse failure expected for unsupported namespace URI");
         }
-        catch ( IOException e )
+        catch (IOException e)
         {
             // expected due to unsupported namespace URI
         }
     }
 
-
     public void testWithInvalidNamespaceName()
     {
-        String empty = "<datatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/metatype/v1.0.0\" "
-            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></datatype:MetaData>";
+        String empty = "<datatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/metatype/v1.0.0\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ></datatype:MetaData>";
 
         try
         {
-            read( empty );
-            fail( "Parse failure expected for undefined namespace prefix" );
+            read(empty);
+            fail("Parse failure expected for undefined namespace prefix");
         }
-        catch ( IOException e )
+        catch (IOException e)
         {
             // expected due to undefined namespace prefix used
         }
     }
 
-
     public void testEmptyLocalization() throws IOException, XmlPullParserException
     {
         String testLoc = "OSGI-INF/folder/base";
         String empty = "<MetaData localization=\"" + testLoc + "\"/>";
-        MetaData mti = read( empty );
+        MetaData mti = read(empty);
 
-        assertEquals( testLoc, mti.getLocalePrefix() );
+        assertEquals(testLoc, mti.getLocalePrefix());
     }
 
-
-    public void testSingleEmptyOCD() throws IOException, XmlPullParserException
+    public void testSingleOCDOk() throws IOException, XmlPullParserException
     {
         String ocdName = "ocd0";
         String ocdId = "id.ocd0";
         String ocdDescription = "ocd0 description";
 
-        String empty = "<MetaData><OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription
-            + "\" /></MetaData>";
-        MetaData mti = read( empty );
+        String empty = "<MetaData><OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription + "\"><AD id=\"attr\" type=\"String\" required=\"false\" /></OCD></MetaData>";
+        MetaData mti = read(empty);
 
-        assertNull( mti.getLocalePrefix() );
-        assertNotNull( mti.getObjectClassDefinitions() );
-        assertEquals( 1, mti.getObjectClassDefinitions().size() );
+        assertNull(mti.getLocalePrefix());
+        assertNotNull(mti.getObjectClassDefinitions());
+        assertEquals(1, mti.getObjectClassDefinitions().size());
 
-        OCD ocd = ( OCD ) mti.getObjectClassDefinitions().values().iterator().next();
-        assertEquals( ocdId, ocd.getID() );
-        assertEquals( ocdName, ocd.getName() );
-        assertEquals( ocdDescription, ocd.getDescription() );
+        OCD ocd = (OCD) mti.getObjectClassDefinitions().values().iterator().next();
+        assertEquals(ocdId, ocd.getID());
+        assertEquals(ocdName, ocd.getName());
+        assertEquals(ocdDescription, ocd.getDescription());
 
-        assertNull( ocd.getAttributeDefinitions() );
+        assertNotNull(ocd.getAttributeDefinitions());
     }
 
+    /**
+     * FELIX-4644 - Enforce that we can only have one Object in a Designate element. 
+     */
+    public void testOCDWithoutADFail() throws IOException, XmlPullParserException
+    {
+        String ocdName = "ocd0";
+        String ocdId = "id.ocd0";
+        String ocdDescription = "ocd0 description";
+
+        String xml = "<MetaData><OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription + "\" /></MetaData>";
+
+        try
+        {
+            read(xml); // should fail!
+            fail("IOException expected!");
+        }
+        catch (IOException e)
+        {
+            assertTrue(e.getMessage().contains("Missing element AD"));
+        }
+    }
+
+    /**
+     * FELIX-4644 - MetaType v1.3 allows OCDs without ADs.
+     */
+    public void testOCDWithoutADOk_v13() throws IOException, XmlPullParserException
+    {
+        String ocdName = "ocd0";
+        String ocdId = "id.ocd0";
+        String ocdDescription = "ocd0 description";
+
+        String xml = "<metatype:MetaData xmlns:metatype=\"" + MetaDataReader.NAMESPACE_1_3 + "\" " + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ><OCD id=\"" + ocdId + "\" name=\""
+            + ocdName + "\" description=\"" + ocdDescription + "\" /></metatype:MetaData>";
+
+        MetaData mti = read(xml);
+
+        assertNull(mti.getLocalePrefix());
+        assertNotNull(mti.getObjectClassDefinitions());
+        assertEquals(1, mti.getObjectClassDefinitions().size());
+
+        OCD ocd = (OCD) mti.getObjectClassDefinitions().values().iterator().next();
+        assertEquals(ocdId, ocd.getID());
+        assertEquals(ocdName, ocd.getName());
+        assertEquals(ocdDescription, ocd.getDescription());
+
+        assertNull(ocd.getAttributeDefinitions());
+    }
+
+    /**
+     * FELIX-4644 - Enforce that we can only have one Object in a Designate element. 
+     */
+    public void testDesignateWithoutObjectFail() throws IOException, XmlPullParserException
+    {
+        String ocdName = "ocd0";
+        String ocdId = "id.ocd0";
+        String ocdDescription = "ocd0 description";
+        String pid = "myPID";
+
+        String xml = "<MetaData><OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription + "\"><AD id=\"attr\" type=\"String\" required=\"false\" /></OCD><Designate pid=\""
+            + pid + "\" bundle=\"*\"></Designate></MetaData>";
+
+        try
+        {
+            read(xml); // should fail!
+            fail("IOException expected!");
+        }
+        catch (IOException e)
+        {
+            assertTrue(e.getMessage().contains("Missing element Object"));
+        }
+    }
+
+    /**
+     * FELIX-4644 - Enforce that we can only have one Object in a Designate element. 
+     */
+    public void testDesignateWithObjectOk() throws IOException, XmlPullParserException
+    {
+        String ocdName = "ocd0";
+        String ocdId = "id.ocd0";
+        String ocdDescription = "ocd0 description";
+        String pid = "myPID";
+
+        String xml = "<MetaData><OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription + "\"><AD id=\"attr\" type=\"String\" required=\"false\" /></OCD><Designate pid=\""
+            + pid + "\" bundle=\"*\"><Object ocdref=\"" + ocdId + "\"></Object></Designate></MetaData>";
+        MetaData mti = read(xml);
+
+        assertNull(mti.getLocalePrefix());
+        assertNotNull(mti.getObjectClassDefinitions());
+        assertEquals(1, mti.getObjectClassDefinitions().size());
+
+        OCD ocd = (OCD) mti.getObjectClassDefinitions().values().iterator().next();
+        assertEquals(ocdId, ocd.getID());
+        assertEquals(ocdName, ocd.getName());
+        assertEquals(ocdDescription, ocd.getDescription());
+
+        assertNotNull(ocd.getAttributeDefinitions());
+
+        Map designates = ocd.getMetadata().getDesignates();
+        assertEquals(1, designates.size());
+
+        Designate designate = (Designate) designates.get(pid);
+        assertEquals(pid, designate.getPid());
+        assertNotNull(designate.getObject());
+    }
+
+    /**
+     * FELIX-4644 - Enforce that we can only have one Object in a Designate element. 
+     */
+    public void testDesignateWithTwoObjectsFail() throws IOException, XmlPullParserException
+    {
+        String ocdName = "ocd0";
+        String ocdId = "id.ocd0";
+        String ocdDescription = "ocd0 description";
+        String pid = "myPID";
+
+        String xml = "<MetaData><OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription + "\"><AD id=\"attr\" type=\"String\" required=\"false\" /></OCD><Designate pid=\""
+            + pid + "\" bundle=\"*\"><Object ocdref=\"" + ocdId + "\"></Object><Object ocdref=\"" + ocdId + "\"></Object></Designate></MetaData>";
+
+        try
+        {
+            read(xml); // should fail!
+            fail("IOException expected!");
+        }
+        catch (IOException e)
+        {
+            assertTrue(e.getMessage().contains("Unexpected element Object"));
+        }
+    }
 
     public void testSingleOCDSingleRequiredAttr() throws IOException, XmlPullParserException
     {
@@ -209,7 +322,7 @@
         testSingleOCDSingleRequiredAttr("Char", AttributeDefinition.CHARACTER, MetaDataReader.NAMESPACE_1_1);
         testSingleOCDSingleRequiredAttr("Char", AttributeDefinition.CHARACTER, MetaDataReader.NAMESPACE_1_2);
         testSingleOCDSingleRequiredAttr("Char", AttributeDefinition.CHARACTER, MetaDataReader.NAMESPACE_1_3);
-        
+
         testSingleOCDSingleRequiredAttr("Character", AttributeDefinition.CHARACTER, MetaDataReader.NAMESPACE_1_0);
         testSingleOCDSingleRequiredAttr("Character", AttributeDefinition.CHARACTER, MetaDataReader.NAMESPACE_1_1);
         testSingleOCDSingleRequiredAttr("Character", AttributeDefinition.CHARACTER, MetaDataReader.NAMESPACE_1_2);
@@ -217,9 +330,7 @@
 
     }
 
-
-    private void testSingleOCDSingleRequiredAttr(String adType, int typeCode,
-        String namespace) throws IOException
+    private void testSingleOCDSingleRequiredAttr(String adType, int typeCode, String namespace) throws IOException
     {
         String ocdName = "ocd0";
         String ocdId = "id.ocd0";
@@ -231,40 +342,38 @@
         int adCardinality = 789;
         String adDefault = "    a    ,   b    ,    c    ";
 
-        String empty = "<metatype:MetaData xmlns:metatype=\"" + namespace + "\">" + "<OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\""
-            + ocdDescription + "\">" + "<AD id=\"" + adId + "\" name=\"" + adName + "\" type=\"" + adType
-            + "\" description=\"" + adDescription + "\" cardinality=\"" + adCardinality + "\" default=\"" + adDefault
-            + "\">" + "</AD>" + "</OCD>" + "</metatype:MetaData>";
-        MetaData mti = read( empty );
+        String empty = "<metatype:MetaData xmlns:metatype=\"" + namespace + "\">" + "<OCD id=\"" + ocdId + "\" name=\"" + ocdName + "\" description=\"" + ocdDescription + "\">" + "<AD id=\"" + adId
+            + "\" name=\"" + adName + "\" type=\"" + adType + "\" description=\"" + adDescription + "\" cardinality=\"" + adCardinality + "\" default=\"" + adDefault + "\">" + "</AD>" + "</OCD>"
+            + "</metatype:MetaData>";
+        MetaData mti = read(empty);
 
-        assertNull( mti.getLocalePrefix() );
-        assertNotNull( mti.getObjectClassDefinitions() );
-        assertEquals( 1, mti.getObjectClassDefinitions().size() );
+        assertNull(mti.getLocalePrefix());
+        assertNotNull(mti.getObjectClassDefinitions());
+        assertEquals(1, mti.getObjectClassDefinitions().size());
 
-        OCD ocd = ( OCD ) mti.getObjectClassDefinitions().values().iterator().next();
+        OCD ocd = (OCD) mti.getObjectClassDefinitions().values().iterator().next();
 
-        assertNotNull( ocd.getAttributeDefinitions() );
-        assertEquals( 1, ocd.getAttributeDefinitions().size() );
+        assertNotNull(ocd.getAttributeDefinitions());
+        assertEquals(1, ocd.getAttributeDefinitions().size());
 
-        AD ad = ( AD ) ocd.getAttributeDefinitions().values().iterator().next();
-        assertEquals( adId, ad.getID() );
-        assertEquals( adName, ad.getName() );
-        assertEquals( adDescription, ad.getDescription() );
-        assertEquals( typeCode, ad.getType() );
-        assertEquals( adCardinality, ad.getCardinality() );
-        assertNotNull( ad.getDefaultValue() );
-        assertEquals( 3, ad.getDefaultValue().length );
+        AD ad = (AD) ocd.getAttributeDefinitions().values().iterator().next();
+        assertEquals(adId, ad.getID());
+        assertEquals(adName, ad.getName());
+        assertEquals(adDescription, ad.getDescription());
+        assertEquals(typeCode, ad.getType());
+        assertEquals(adCardinality, ad.getCardinality());
+        assertNotNull(ad.getDefaultValue());
+        assertEquals(3, ad.getDefaultValue().length);
 
         String[] defaultValue = ad.getDefaultValue();
-        assertEquals( "a", defaultValue[0] );
-        assertEquals( "b", defaultValue[1] );
-        assertEquals( "c", defaultValue[2] );
+        assertEquals("a", defaultValue[0]);
+        assertEquals("b", defaultValue[1]);
+        assertEquals("c", defaultValue[2]);
     }
 
-
-    private MetaData read( String data ) throws IOException
+    private MetaData read(String data) throws IOException
     {
-        InputStream input = new ByteArrayInputStream( data.getBytes( "UTF-8" ) );
-        return reader.parse( input );
+        InputStream input = new ByteArrayInputStream(data.getBytes("UTF-8"));
+        return reader.parse(input);
     }
 }