added helper class which allows to generation metatype XML descriptor

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@907334 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
new file mode 100644
index 0000000..1c9868f
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/MetaType.java
@@ -0,0 +1,287 @@
+/*
+ * 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.felix.dm.annotation.plugin.bnd;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+
+/**
+ * Helper class used to represent a data structure which is complying to the MetaType XML.
+ * We use XStream in order to generating the XML from this class.
+ */
+public class MetaType
+{
+    /**
+     * The list of Object Class Definitions used to group the attributes of a given
+     * set of properties.
+     */
+    private List<OCD> m_ocdList = new ArrayList<OCD>();
+
+    /**
+     * The list of Designate elements.
+     */
+    private List<Designate> m_designateList = new ArrayList<Designate>();
+
+    /**
+     * The default localization directory.
+     */
+    private final static String LOCALIZATION = "OSGI-INF/metatype/metatype";
+
+    /**
+     * Adds an Object Class Definition into this meta type.
+     * @param ocd the Object Class Definition.
+     */
+    public void add(OCD ocd)
+    {
+        m_ocdList.add(ocd);
+    }
+
+    /**
+     * Adds a Designate element, which maps a PID to an OCD.
+     * @param designate the Designate element.
+     */
+    public void add(Designate designate)
+    {
+        m_designateList.add(designate);
+    }
+
+    /**
+     * Returns the number of OCD contained in this meta type.
+     * @return the number of OCD contained in this meta type.
+     */
+    public int getSize()
+    {
+        return m_ocdList.size();
+    }
+
+    /**
+     * Generates an XML representation of this metatype.
+     * @param pw a PrintWriter where the XML is written
+     */
+    public void writeTo(PrintWriter pw)
+    {
+        XStream xStream = new XStream();
+        xStream.processAnnotations(new Class[] { OCD.class, AD.class, Option.class,
+                Designate.class, OBject.class });
+        StringBuilder xml = new StringBuilder("");
+        xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+        xml.append("<metatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/metatype/v1.0.0\" localization=\""
+            + LOCALIZATION + "\">\n");
+        for (OCD ocd : m_ocdList)
+        {
+            xml.append(xStream.toXML(ocd) + "\n");
+        }
+        for (Designate designate : m_designateList)
+        {
+            xml.append(xStream.toXML(designate) + "\n");
+        }
+        xml.append("</metatype:MetaData>");
+        pw.println(xml);
+    }
+
+    /**
+     * An Object Class Definition, which contains a set of Attributes properies.
+     */
+    @XStreamAlias("OCD")
+    public static class OCD
+    {
+        @XStreamAsAttribute
+        String id;
+
+        @XStreamAsAttribute
+        String name;
+
+        @XStreamAsAttribute
+        String description;
+
+        @XStreamImplicit(itemFieldName = "AD")
+        List<AD> attributes = new ArrayList<AD>();
+
+        OCD(String pid, String name, String desc)
+        {
+            this.id = pid;
+            this.name = name;
+            this.description = desc;
+        }
+
+        public void add(AD ad)
+        {
+            attributes.add(ad);
+        }
+    }
+
+    /**
+     * An Attribute Definition, which describes a given Properties
+     */
+    @SuppressWarnings("serial")
+    @XStreamAlias("AD")
+    public static class AD
+    {
+        @XStreamAsAttribute
+        String id;
+
+        @XStreamAsAttribute
+        String type;
+
+        @XStreamAsAttribute
+        @XStreamAlias("default")
+        String defaults;
+
+        @XStreamAsAttribute
+        String name;
+
+        @XStreamAsAttribute
+        String description;
+
+        @XStreamImplicit(itemFieldName = "")
+        List<Option> options = new ArrayList<Option>();
+
+        @XStreamAsAttribute
+        Integer cardinality;
+
+        @XStreamAsAttribute
+        Boolean required;
+
+        private final static Map<String, String> _allowedTypes = new HashMap<String, String>()
+        {
+            {
+                put(String.class.getName(), "String");
+                put(Long.class.getName(), "Long");
+                put(Integer.class.getName(), "Integer");
+                put(Character.class.getName(), "Char");
+                put(Byte.class.getName(), "Byte");
+                put(Double.class.getName(), "Double");
+                put(Float.class.getName(), "Float");
+                put(Boolean.class.getName(), "Boolean");
+            }
+        };
+
+        public AD(String id, String type, Object[] defaults, String name, String desc, Integer cardinality, Boolean required)
+        {
+            this.id = id;
+            this.type = (type == null) ? "String" : getType(type);
+            this.name = name;
+            this.description = desc;
+            this.cardinality = cardinality;
+            this.required = required;
+
+            if (defaults != null)
+            {
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < defaults.length; i++)
+                {
+                    sb.append(defaults[i].toString());
+                    if (i < defaults.length - 1)
+                    {
+                        sb.append(",");
+                    }
+                }
+                this.defaults = sb.toString();
+
+                // Check if the number of default values is consistent with the cardinality.
+                if (cardinality != null)
+                {
+                    int max = (cardinality.intValue() == 0) ? 1 : Math.abs(cardinality.intValue());
+                    if (defaults.length > max)
+                    {
+                        throw new IllegalArgumentException("number of default values ("
+                            + defaults.length + ") is inconsistent with cardinality ("
+                            + cardinality + ")");
+                    }
+                }
+            }
+        }
+
+        private String getType(String t)
+        {
+            String result = _allowedTypes.get(t);
+            if (result == null)
+            {
+                throw new IllegalArgumentException("Invalid Property type: " + type);
+            }
+            return result;
+        }
+
+        public void add(Option option)
+        {
+            options.add(option);
+        }
+    }
+
+    /**
+     * An Option datastructure, which can be associated with an Attribute.
+     */
+    @XStreamAlias("Option")
+    public static class Option
+    {
+        @XStreamAsAttribute
+        String value;
+
+        @XStreamAsAttribute
+        String label;
+
+        Option(String value, String label)
+        {
+            this.value = value;
+            this.label = label;
+        }
+    }
+
+    /**
+     * A Designate element, which maps a PID to a given Object Class Definition.
+     */
+    @XStreamAlias("Designate")
+    public static class Designate
+    {
+        @XStreamAsAttribute
+        String pid;
+
+        @XStreamAlias(value = "Object")
+        OBject object;
+
+        public Designate(String pid)
+        {
+            this.pid = pid;
+            this.object = new OBject(pid);
+        }
+    }
+
+    /**
+     * A definition of an instance.
+     */
+    @XStreamAlias("Object")
+    public static class OBject
+    {
+        @XStreamAsAttribute
+        String ocdref;
+
+        OBject(String ocdref)
+        {
+            this.ocdref = ocdref;
+        }
+    }
+}