metatype support

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@907340 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/pom.xml b/dependencymanager/annotation/pom.xml
index 146d7f7..b67d94c 100644
--- a/dependencymanager/annotation/pom.xml
+++ b/dependencymanager/annotation/pom.xml
@@ -46,6 +46,12 @@
     </dependency>
 
     <dependency>
+      <groupId>com.thoughtworks.xstream</groupId>
+      <artifactId>xstream</artifactId>
+      <version>1.3.1</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-plugin-api</artifactId>
       <version>2.0</version>
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index aebda9c..dbfb735 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -33,7 +33,7 @@
 import org.apache.felix.dm.annotation.api.ConfigurationDependency;
 import org.apache.felix.dm.annotation.api.Destroy;
 import org.apache.felix.dm.annotation.api.Init;
-import org.apache.felix.dm.annotation.api.Param;
+import org.apache.felix.dm.annotation.api.Properties;
 import org.apache.felix.dm.annotation.api.Service;
 import org.apache.felix.dm.annotation.api.ServiceDependency;
 import org.apache.felix.dm.annotation.api.Start;
@@ -65,6 +65,8 @@
         + ConfigurationDependency.class.getName().replace('.', '/') + ";";
     private final static String A_TEMPORAL_SERVICE_DEPENDENCY = "L"
         + TemporalServiceDependency.class.getName().replace('.', '/') + ";";
+    private final static String A_PROPERTIES = "L"
+        + Properties.class.getName().replace('.', '/') + ";";
 
     private Reporter m_reporter;
     private String m_className;
@@ -75,6 +77,7 @@
     private String m_descriptor;
     private Set<String> m_methods = new HashSet<String>();
     private List<Info> m_infos = new ArrayList<Info>();
+    private MetaType m_metaType;
 
     // Pattern used to parse the class parameter from the bind methods ("bind(Type)" or "bind(Map, Type)")
     private final static Pattern m_bindClassPattern = Pattern.compile("\\((Ljava/util/Map;)?L([^;]+);\\)V");
@@ -254,10 +257,11 @@
      * Makes a new Collector for parsing a given class.
      * @param reporter the object used to report logs.
      */
-    public AnnotationCollector(Reporter reporter)
+    public AnnotationCollector(Reporter reporter, MetaType metaType)
     {
         m_reporter = reporter;
         m_infos.add(new Info(EntryTypes.Service));
+        m_metaType = metaType;
     }
 
     /**
@@ -367,6 +371,10 @@
         else if (annotation.getName().equals(A_TEMPORAL_SERVICE_DEPENDENCY))
         {
             parseServiceDependencyAnnotation(annotation, true);
+        } 
+        else if (annotation.getName().equals(A_PROPERTIES)) 
+        {
+            parsePropertiesMetaData(annotation);
         }
     }
 
@@ -486,6 +494,46 @@
     }
 
     /**
+     * Parses a Properties annotation which declares Config Admin Properties meta data.
+     * @param properties the Properties annotation to be parsed.
+     */
+    private void parsePropertiesMetaData(Annotation properties)
+    {
+        String propertiesPid = get(properties, "pid", m_className);
+        String propertiesHeading = properties.get("heading");
+        String propertiesDesc = properties.get("description");
+
+        MetaType.OCD ocd = new MetaType.OCD(propertiesPid, propertiesHeading, propertiesDesc);
+        for (Object p : (Object[]) properties.get("properties"))
+        {
+            Annotation property = (Annotation) p;
+            String heading = property.get("heading");
+            String id = property.get("id");
+            String type = (String) property.get("type");
+            type = (type != null) ? parseClass(type, m_classPattern, 1) : null;
+            Object[] defaults = (Object[]) property.get("defaults");
+            String description = property.get("description");
+            Integer cardinality = property.get("cardinality");
+            Boolean required = property.get("required");
+
+            MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description, cardinality, required);
+            Object[] options = property.get("options");
+            if (options != null) {
+                for (Object o : (Object[]) property.get("options"))
+                {
+                    Annotation option = (Annotation) o;
+                    ad.add(new MetaType.Option((String) option.get("name"), (String) option.get("value")));
+                }
+            }
+            ocd.add(ad);
+        }
+
+        m_metaType.add(ocd);
+        MetaType.Designate designate = new MetaType.Designate(propertiesPid);
+        m_metaType.add(designate);
+    }
+
+    /**
      * Parses a class.
      * @param clazz the class to be parsed (the package is "/" separated).
      * @param pattern the pattern used to match the class.
@@ -523,6 +571,20 @@
     }
 
     /**
+     * Get an annotation attribute, and return a default value if its not present.
+     * @param <T> the type of the variable which is assigned to the return value of this method.
+     * @param properties The annotation we are parsing
+     * @param name the attribute name to get from the annotation
+     * @param defaultValue the default value to return if the attribute is not found in the annotation
+     * @return the annotation attribute value, or the defaultValue if not found
+     */
+    private <T> T get(Annotation properties, String name, T defaultValue)
+    {
+        T value = (T) properties.get(name);
+        return value != null ? value : defaultValue;
+    }
+
+    /**
      * Finishes up the class parsing. This method must be called once the parseClassFileWithCollector method has returned.
      */
     public void finish()
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
index db5ea2a..0709918 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
@@ -56,6 +56,13 @@
             {
                 analyzer.getJar().putResource(entry.getKey(), entry.getValue());
             }
+
+            // Inser the metatype resource, if any.
+            Resource metaType = generator.getMetaTypeResource();
+            if (metaType != null)
+            {
+                analyzer.getJar().putResource("OSGI-INF/metatype/metatype.xml", metaType);
+            }
         }
         return false;
     }
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
index 82f0705..55dad6f 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -53,6 +54,11 @@
     Map<String, Resource> m_resources = new HashMap<String, Resource>();
 
     /**
+     * This is the generated MetaType XML descriptor, if any Properties/Property annotations have been found.
+     */
+    private Resource m_metaTypeResource;
+
+    /**
      * Creates a new descriptor generator.
      * @param analyzer The bnd analyzer used to lookup classes containing DM annotations.
      */
@@ -76,16 +82,19 @@
             // Try to locate any classes in the wildcarded universe
             // that are annotated with the DependencyManager "Service" annotations.
             Collection<Clazz> expanded = m_analyzer.getClasses("",
-            // Then limit the ones with component annotations.
+                // Then limit the ones with component annotations.
                 QUERY.ANNOTATION.toString(), Service.class.getName(),
                 // Parse everything
                 QUERY.NAMED.toString(), "*");
 
+            // Create the object which will collect Config Admin MetaTypes.
+            MetaType metaType = new MetaType();
+            
             for (Clazz c : expanded)
             {
                 clazz = c;
                 // Let's parse all annotations from that class !
-                AnnotationCollector reader = new AnnotationCollector(this);
+                AnnotationCollector reader = new AnnotationCollector(this, metaType);
                 c.parseClassFileWithCollector(reader);
                 reader.finish();
                 // And store the generated component descriptors in our resource list.
@@ -95,6 +104,10 @@
                 annotationsFound = true;
             }
 
+            // If some Meta Types have been parsed, then creates the corresponding resource file.
+            if (metaType.getSize() > 0) {
+                m_metaTypeResource = createMetaTypeResource(metaType);
+            }
             return annotationsFound;
         }
         catch (Throwable err)
@@ -106,7 +119,7 @@
                 sb.append(" from class " + clazz);
             }
             sb.append(": ");
-            sb.append(err.toString());
+            sb.append(parse(err));
             error(sb.toString(), err.getCause());
             return false;
         }
@@ -147,6 +160,13 @@
     }
 
     /**
+     * Returns the MetaType resource.
+     */
+    public Resource getMetaTypeResource() {
+        return m_metaTypeResource;
+    }
+    
+    /**
      * Creates a bnd resource that contains the generated dm descriptor.
      * @param collector 
      * @return
@@ -162,4 +182,33 @@
         out.close();
         return new EmbeddedResource(data, 0);
     }
+    
+    /**
+     * Creates a bnd resource that contains the generated metatype descriptor.
+     * @param metaType the Object that has collected all meta type informations.
+     * @return the meta type resource
+     * @throws IOException on any errors
+     */
+    private Resource createMetaTypeResource(MetaType metaType) throws IOException
+    {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
+        metaType.writeTo(pw);
+        pw.close();
+        byte[] data = out.toByteArray();
+        out.close();
+        return new EmbeddedResource(data, 0);    
+    }
+    
+    /**
+     * Parse an exception into a string.
+     * @param e The exception to parse
+     * @return the parsed exception
+     */
+    private static String parse(Throwable e) {
+      StringWriter buffer = new StringWriter();
+      PrintWriter  pw = new PrintWriter(buffer);
+      e.printStackTrace(pw);
+      return (buffer.toString());
+    } 
 }