Merge branch 'master' into dynamic-reference-properties
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java
index 0340a55..02836f4 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java
@@ -37,6 +37,7 @@
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.felix.framework.FilterImpl;
+import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.DynamicReference;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.Reference;
 import org.apache.sling.testing.mock.osgi.OsgiServiceUtil.ReferenceInfo;
 import org.apache.sling.testing.mock.osgi.OsgiServiceUtil.ServiceInfo;
@@ -142,6 +143,11 @@
         List<ReferenceInfo> affectedDynamicReferences = OsgiServiceUtil.getMatchingDynamicReferences(registeredServices, registration);
         for (ReferenceInfo referenceInfo : affectedDynamicReferences) {
             Reference reference = referenceInfo.getReference();
+            // Look for a target override
+            Object o = referenceInfo.getServiceRegistration().getProperties().get(reference.getName() + ".target");
+            if (o != null && o instanceof String) {
+                reference = new DynamicReference(reference,(String)o);
+            }
             if (reference.matchesTargetFilter(registration.getReference())) {
                 switch (reference.getCardinality()) {
                 case MANDATORY_UNARY:
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java
index 7c07578..4d22717 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java
@@ -145,7 +145,20 @@
      * @return true if all dependencies could be injected, false if the service has no dependencies.
      */
     public static boolean injectServices(Object target, BundleContext bundleContext) {
-        return OsgiServiceUtil.injectServices(target, bundleContext);
+        return MockOsgi.injectServices(target, bundleContext, (Map<String, Object>)null);
+    }
+
+    /**
+     * Simulate OSGi service dependency injection. Injects direct references and
+     * multiple references. If a some references could not be injected no error
+     * is thrown.
+     * @param target Service instance
+     * @param bundleContext Bundle context from which services are fetched to inject.
+     * @param properties Service properties (used to resolve dynamic reference properties)
+     * @return true if all dependencies could be injected, false if the service has no dependencies.
+     */
+    public static boolean injectServices(Object target, BundleContext bundleContext, Map<String, Object> properties) {
+        return OsgiServiceUtil.injectServices(target, bundleContext, properties);
     }
 
     /**
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java
index 1fca7a5..1e0b8d3 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java
@@ -470,20 +470,20 @@
 
     static class Reference {
 
-        private final Class<?> clazz;
-        private final String name;
-        private final String interfaceType;
-        private final ReferenceCardinality cardinality;
-        private final ReferencePolicy policy;
-        private final ReferencePolicyOption policyOption;
-        private final String bind;
-        private final String unbind;
-        private final String field;
-        private final FieldCollectionType fieldCollectionType;
-        private final String target;
-        private final Filter targetFilter;
+        protected final Class<?> clazz;
+        protected final String name;
+        protected final String interfaceType;
+        protected final ReferenceCardinality cardinality;
+        protected final ReferencePolicy policy;
+        protected final ReferencePolicyOption policyOption;
+        protected final String bind;
+        protected final String unbind;
+        protected final String field;
+        protected final FieldCollectionType fieldCollectionType;
+        protected String target;
+        protected Filter targetFilter;
 
-        private Reference(Class<?> clazz, Node node) {
+        protected Reference(Class<?> clazz, Node node) {
             this.clazz = clazz;
             this.name = getAttributeValue(node, "name");
             this.interfaceType = getAttributeValue(node, "interface");
@@ -507,6 +507,21 @@
             }
         }
 
+        protected Reference(Reference reference) {
+            this.clazz = reference.clazz;
+            this.name = reference.name;
+            this.interfaceType = reference.interfaceType;
+            this.cardinality = reference.cardinality;
+            this.policy = reference.policy;
+            this.policyOption = reference.policyOption;
+            this.bind = reference.bind;
+            this.unbind = reference.unbind;
+            this.field = reference.field;
+            this.fieldCollectionType = reference.fieldCollectionType;
+            this.target = reference.target;
+            this.targetFilter = reference.targetFilter;
+        }
+
         public Class<?> getServiceClass() {
             return clazz;
         }
@@ -614,6 +629,22 @@
 
     }
 
+    static class DynamicReference extends Reference {
+        public DynamicReference(Reference reference, String target) {
+            super(reference);
+            this.target = target;
+            if (StringUtils.isNotEmpty(this.target)) {
+                try {
+                    this.targetFilter = new FilterImpl(this.target);
+                } catch (InvalidSyntaxException ex) {
+                    throw new RuntimeException("Invalid target filter in reference '" + this.name + "' of class " + clazz.getName(), ex);
+                }
+            }
+            else {
+                this.targetFilter = null;
+            }
+        }
+    }
 
     /**
      * Options for {@link Reference#cardinality()} property.
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java
index b3fd20c..488b4fe 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java
@@ -31,6 +31,7 @@
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.felix.scr.impl.inject.Annotations;
+import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.DynamicReference;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.FieldCollectionType;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.OsgiMetadata;
 import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.Reference;
@@ -369,9 +370,10 @@
      * multiple references.
      * @param target Service instance
      * @param bundleContext Bundle context from which services are fetched to inject.
+     * @param properties Services properties (used to resolve dynamic reference properties)
      * @return true if all dependencies could be injected, false if the service has no dependencies.
      */
-    public static boolean injectServices(Object target, BundleContext bundleContext) {
+    public static boolean injectServices(Object target, BundleContext bundleContext, Map<String, Object> properties) {
 
         // collect all declared reference annotations on class and field level
         Class<?> targetClass = target.getClass();
@@ -387,6 +389,13 @@
 
         // try to inject services
         for (Reference reference : references) {
+            if (properties != null) {
+                // Look for a target override
+                Object o = properties.get(reference.getName() + ".target");
+                if (o != null && o instanceof String) {
+                    reference = new DynamicReference(reference,(String)o);
+                }
+            }
             injectServiceReference(reference, target, bundleContext);
         }
         return true;
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java
index acb96e7..ae9d0b2 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java
@@ -150,7 +150,7 @@
      * @return Registered service instance
      */
     public final <T> T registerInjectActivateService(final T service, final Map<String, Object> properties) {
-        MockOsgi.injectServices(service, bundleContext());
+        MockOsgi.injectServices(service, bundleContext(), properties);
         MockOsgi.activate(service, bundleContext(), properties);
         registerService(null, service, properties);
         return service;
diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java b/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java
index d1f57fc..db6c8d4 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java
@@ -19,5 +19,5 @@
 /**
  * Mock implementation of selected OSGi APIs.
  */
-@org.osgi.annotation.versioning.Version("3.3")
+@org.osgi.annotation.versioning.Version("3.4")
 package org.apache.sling.testing.mock.osgi;
diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/MockBundleContextDynamicReferencesOsgiR6Test.java b/src/test/java/org/apache/sling/testing/mock/osgi/MockBundleContextDynamicReferencesOsgiR6Test.java
index b7d022c..fa8a8c4 100644
--- a/src/test/java/org/apache/sling/testing/mock/osgi/MockBundleContextDynamicReferencesOsgiR6Test.java
+++ b/src/test/java/org/apache/sling/testing/mock/osgi/MockBundleContextDynamicReferencesOsgiR6Test.java
@@ -63,6 +63,8 @@
     private ServiceSuperInterface3 dependency3a;
     @Mock
     private ServiceSuperInterface3 dependency3b;
+    @Mock
+    private ServiceSuperInterface3 dependency3c;
 
     @Before
     public void setUp() {
@@ -75,7 +77,7 @@
         service = new Service3OsgiR6();
         MockOsgi.injectServices(service, bundleContext);
         MockOsgi.activate(service, bundleContext);
-        bundleContext.registerService(Service3OsgiR6.class.getName(), service, null);
+        bundleContext.registerService(Service3OsgiR6.class.getName(), service, MapUtil.toDictionary(ImmutableMap.<String,Object>of("reference3DynamicFiltered.target","(prop1=def)")));
         
         assertDependency1(dependency1a);
         assertDependency1Optional(null);
@@ -152,6 +154,22 @@
         assertDependencies3Filtered(dependency3a);
     }
     
+    @Test
+    public void testReferenceWithDynamicTargetFilter() {
+        assertDependencies3DynamicFiltered(null);
+        
+        bundleContext.registerService(ServiceInterface3.class.getName(), dependency3a, 
+                MapUtil.toDictionary(ImmutableMap.<String, Object>of("prop1", "abc")));
+
+        bundleContext.registerService(ServiceInterface3.class.getName(), dependency3b, 
+                MapUtil.toDictionary(ImmutableMap.<String, Object>of("prop1", "def")));
+
+        bundleContext.registerService(ServiceInterface3.class.getName(), dependency3c, 
+                MapUtil.toDictionary(ImmutableMap.<String, Object>of("prop1", "hij")));
+        
+        assertDependencies3DynamicFiltered(dependency3b);
+    }
+
     private void assertDependency1(ServiceInterface1 instance) {
         if (instance == null) {
             assertNull(service.getReference1());
@@ -184,5 +202,9 @@
         assertEquals(ImmutableSet.<ServiceSuperInterface3>copyOf(instances), 
                 ImmutableSet.<ServiceSuperInterface3>copyOf(service.getReferences3Filtered()));
     }
-    
+
+    private void assertDependencies3DynamicFiltered(ServiceSuperInterface3 instance) {
+        assertEquals(instance,service.getReference3DynamicFiltered());
+    }
+
 }
diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtilTest.java b/src/test/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtilTest.java
index 1f92fa7..6ae014a 100644
--- a/src/test/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtilTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtilTest.java
@@ -341,6 +341,7 @@
         private List<ServiceReference> references2;
         private List<ServiceSuperInterface3> references3;
         private List<ServiceSuperInterface3> references3Filtered;
+        private ServiceSuperInterface3 reference3DynamicFiltered;
 
         private ComponentContext componentContext;
         private Map<String, Object> config;
@@ -385,6 +386,11 @@
             return this.references3Filtered;
         }
 
+
+        public ServiceSuperInterface3 getReference3DynamicFiltered() {
+            return this.reference3DynamicFiltered;
+        }
+
         public ComponentContext getComponentContext() {
             return this.componentContext;
         }
diff --git a/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml b/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml
index 0229572..7698b9e 100644
--- a/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml
+++ b/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml
@@ -51,6 +51,7 @@
     <reference name="reference2" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface2" cardinality="1..n" policy="dynamic" field="references2" field-collection-type="reference"/>
     <reference name="reference3" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface3" cardinality="0..n" policy="dynamic" field="references3" field-collection-type="service"/>
     <reference name="references3Filtered" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface3" cardinality="0..n" policy="dynamic" field="references3Filtered" field-collection-type="service" target="(prop1=abc)"/>
+    <reference name="reference3DynamicFiltered" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface3" cardinality="0..1" policy="dynamic" field="reference3DynamicFiltered" field-collection-type="service"/>
   </scr:component>
   <scr:component name="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service3StaticGreedy" activate="activate" deactivate="deactivate" modified="modified">
     <implementation class="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service3StaticGreedy"/>