added support for annotated adapter services

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@912251 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
new file mode 100644
index 0000000..7ff1016
--- /dev/null
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/AdapterService.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates an Adapater Service. The adapter will be applied to any service that
+ * matches the implemented interface and filter. For each matching service
+ * an adapter will be created based on the adapter implementation class.
+ * The adapter will be registered with the specified interface and existing properties
+ * from the original service plus any extra properties you supply here.
+ * It will also inherit all dependencies, and if you declare the original
+ * service as a member it will be injected.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface AdapterService
+{
+    /**
+     * Returns the adapter service interface . By default, the directly implemented interface is used.
+     * @return The service interface to apply the adapter to.
+     */
+    Class<?> adapterService() default Object.class;
+
+    /**
+     * The adapter service properites. They will be added to the adapted service properties.
+     * @return additional properties to use with the adapter service registration
+     */
+    Param[] adapterProperties() default {};
+
+    /**
+     * The adapted service interface
+     */
+    Class<?> adapteeService();
+    
+    /**
+     * the filter condition to use with the adapted service interface.
+     * @return the filter condition to use with the adapted ervice interface
+     */
+    String adapteeFilter() default "";
+}
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 a4a4da5..1be5b58 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
@@ -29,6 +29,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.felix.dm.annotation.api.AdapterService;
 import org.apache.felix.dm.annotation.api.AspectService;
 import org.apache.felix.dm.annotation.api.Composition;
 import org.apache.felix.dm.annotation.api.ConfigurationDependency;
@@ -70,6 +71,8 @@
         + Properties.class.getName().replace('.', '/') + ";";
     private final static String A_ASPECT_SERVICE = "L"
         + AspectService.class.getName().replace('.', '/') + ";";
+    private final static String A_ADAPTER_SERVICE = "L"
+        + AdapterService.class.getName().replace('.', '/') + ";";
 
     private Reporter m_reporter;
     private String m_className;
@@ -104,6 +107,7 @@
     {
         Service, 
         AspectService,
+        AdapterService,
         ServiceDependency, 
         TemporalServiceDependency, 
         ConfigurationDependency,
@@ -133,7 +137,11 @@
         pid, 
         propagate, 
         updated, 
-        timeout
+        timeout,
+        adapterService,
+        adapterProperties,
+        adapteeService,
+        adapteeFilter
     };
 
     /**
@@ -348,6 +356,10 @@
         {
             parseAspectService(annotation);
         }
+        else if (annotation.getName().equals(A_ADAPTER_SERVICE))
+        {
+            parseAdapterService(annotation);
+        }
         else if (annotation.getName().equals(A_INIT))
         {
             checkMethod(m_voidMethodPattern);
@@ -574,12 +586,23 @@
     private void parseAspectService(Annotation annotation)
     {
         Info info = new Info(EntryTypes.AspectService);
-        m_infos.add(info);
+        m_infos.add(info);        
 
         // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
         addInitStartStopDestroyCompositionParams(info);
         
-        // Parse Service interface.
+        // Parse service filter
+        String filter = annotation.get(Params.filter.toString());
+        Verifier.verifyFilter(filter, 0);
+        info.addParam(Params.filter, filter);
+                
+        // Generate Aspect Implementation
+        info.addParam(Params.impl, m_className);
+        
+        // Parse Aspect properties.
+        parseParameters(annotation, Params.properties, info);
+        
+        // Parse service interface this aspect is applying to
         Object service = annotation.get(Params.service.toString());
         if (service == null) {
             if (m_interfaces == null)
@@ -593,23 +616,94 @@
                     "the service attribute has not been set and the class " + m_className + " implements more than one interface");
             }
             
-            service = m_interfaces[0];
+            info.addParam(Params.service, m_interfaces[0]);
+        } else
+        {
+            checkClassImplements(annotation, Params.service);
+            info.addClassParam(annotation, Params.service, null);
         }
-        info.addClassParam(annotation, Params.service, service.toString());
-        
-        // Parse service filter
-        String filter = annotation.get(Params.filter.toString());
-        Verifier.verifyFilter(filter, 0);
-        info.addParam(Params.filter, filter);
-                
-        // Generate Aspect Implementation
-        info.addParam(Params.impl, m_className);
-        
-        // Parse Aspect properties.
-        parseParameters(annotation, Params.properties, info);
     }
 
     /**
+     * Parses an AspectService annotation.
+     * @param annotation
+     */
+    private void parseAdapterService(Annotation annotation) 
+    {
+        Info info = new Info(EntryTypes.AdapterService);
+        m_infos.add(info);
+        
+        // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+        addInitStartStopDestroyCompositionParams(info);
+        
+        // Generate Adapter Implementation
+        info.addParam(Params.impl, m_className);
+      
+        // Parse adaptee filter
+        String adapteeFilter = annotation.get(Params.adapteeFilter.toString());
+        if (adapteeFilter != null)
+        {
+            Verifier.verifyFilter(adapteeFilter, 0);
+            info.addParam(Params.adapteeFilter, adapteeFilter);
+        }
+        
+        // Parse the mandatory adapted service interface.
+        info.addClassParam(annotation, Params.adapteeService, null);
+        
+        // Parse Adapter properties.
+        parseParameters(annotation, Params.adapterProperties, info);
+
+        // Parse the optional adapter service (use directed implemented interface by default).
+        Object adapterService = annotation.get(Params.adapterService.toString());
+        if (adapterService == null) {
+            if (m_interfaces == null)
+            {
+                throw new IllegalStateException("Invalid AdapterService annotation: " +
+                    "the adapterService attribute has not been set and the class " + m_className + 
+                    " does not implement any interfaces");
+            }
+            if (m_interfaces.length != 1) 
+            {
+                throw new IllegalStateException("Invalid AdapterService annotation: " +
+                    "the adapterService attribute has not been set and the class " + m_className +
+                    " implements more than one interface");
+            }
+            
+            info.addParam(Params.adapterService, m_interfaces[0]);
+        } else 
+        {
+            checkClassImplements(annotation, Params.adapterService);
+            info.addClassParam(annotation, Params.adapterService, null);
+        }
+    }
+
+    /**
+     * Checks if an annotation attribute references an implemented interface. 
+     * @param annotation the parsed annotation
+     * @param attribute an annotation attribute that references an interface this class must
+     * implement.
+     */
+    private void checkClassImplements(Annotation annotation, Params attribute)
+    {
+        String iface = annotation.get(attribute.toString());
+        iface = parseClass(iface, m_classPattern, 1);
+        
+        if (m_interfaces != null)
+        {
+            for (String implemented : m_interfaces)
+            {
+                if (implemented.equals(iface))
+                {
+                    return;
+                }
+            }
+        }
+
+        throw new IllegalArgumentException("Class " + m_className + " does not implement the "
+            + iface + " interface.");
+    }
+    
+    /**
      * Parses a Param annotation (which represents a list of key-value pari).
      * @param annotation the annotation where the Param annotation is defined
      * @param attribute the attribute name which is of Param type
@@ -719,7 +813,7 @@
         }
 
         // We must have at least a Service or an AspectService annotation.
-        checkServiceDeclared(EntryTypes.Service, EntryTypes.AspectService);
+        checkServiceDeclared(EntryTypes.Service, EntryTypes.AspectService, EntryTypes.AdapterService);
 
         StringBuilder sb = new StringBuilder();
         sb.append("Parsed annotation for class ");