resolved FELIX-3424, resolved FELIX-3425.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1308952 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
index befe82a..07a41a4 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
@@ -40,6 +40,7 @@
 import org.apache.felix.dm.impl.dependencies.TemporalServiceDependencyImpl;
 import org.apache.felix.dm.impl.index.AspectFilterIndex;
 import org.apache.felix.dm.impl.index.MultiPropertyExactFilter;
+import org.apache.felix.dm.impl.index.AdapterFilterIndex;
 import org.apache.felix.dm.impl.index.ServiceRegistryCache;
 import org.apache.felix.dm.impl.metatype.PropertyMetaDataImpl;
 import org.osgi.framework.Bundle;
@@ -104,6 +105,8 @@
                 for (int i = 0; i < props.length; i++) {
                     if (props[i].equals("*aspect*")) {
                         m_serviceRegistryCache.addFilterIndex(new AspectFilterIndex());
+                    } else if (props[i].equals("*adapter*")) {
+                    	m_serviceRegistryCache.addFilterIndex(new AdapterFilterIndex());
                     }
                     else {
                         String[] propList = props[i].split(",");
@@ -264,7 +267,7 @@
      * @return a service that acts as a factory for generating aspects
      */
     public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String autoConfig) {
-        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null);
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null, null);
     }
     /**
      * Creates a new aspect. The aspect will be applied to any service that
@@ -289,7 +292,7 @@
      * @return a service that acts as a factory for generating aspects
      */
     public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking) {
-        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null);
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null, null);
     }
     /**
      * Creates a new aspect. The aspect will be applied to any service that
@@ -317,7 +320,37 @@
      * @return a service that acts as a factory for generating aspects
      */
     public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String add, String change, String remove) {
-        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove);
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, null);
+    }
+    
+    /**
+     * Creates a new aspect. The aspect will be applied to any service that
+     * matches the specified interface and filter. For each matching service
+     * an aspect will be created based on the aspect implementation class.
+     * The aspect will be registered with the same interface and properties
+     * as 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.
+     * 
+     * <h3>Usage Example</h3>
+     * 
+     * <blockquote><pre>
+     * manager.createAspectService(ExistingService.class, "(foo=bar)", 10, "add", "change", "remove")
+     *     .setImplementation(ExistingServiceAspect.class)
+     * );
+     * </pre></blockquote>
+     * 
+     * @param serviceInterface the service interface to apply the aspect to
+     * @param serviceFilter the filter condition to use with the service interface
+     * @param ranking the level used to organize the aspect chain ordering
+     * @param add name of the callback method to invoke on add
+     * @param change name of the callback method to invoke on change
+     * @param remove name of the callback method to invoke on remove
+     * @param swap name of the callback method to invoke on swap
+     * @return a service that acts as a factory for generating aspects
+     */    
+    public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String add, String change, String remove, String swap) {
+        return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, swap);
     }
     
     /**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
index 0e28f76..504552e 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
@@ -38,10 +38,10 @@
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class AspectServiceImpl extends FilterService {
-    public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove)
+    public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap)
     { 
         super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
-        m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove))
+        m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove, swap))
              .add(dm.createServiceDependency()
                   .setService(aspectInterface, createDependencyFilterForAspect(aspectFilter))
                   .setAutoConfig(false)
@@ -70,8 +70,9 @@
         private final String m_add;
         private final String m_change;
         private final String m_remove;
+        private final String m_swap;
       
-        public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove) {
+        public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) {
             m_aspectInterface = aspectInterface;
             m_aspectFilter = aspectFilter;
             m_ranking = ranking;
@@ -79,6 +80,7 @@
             m_add = add;
             m_change = change;
             m_remove = remove;
+            m_swap = swap;
         }
         
         public Component createService(Object[] params) {
@@ -93,8 +95,8 @@
             if (m_autoConfig != null) {
                 dependency.setAutoConfig(m_autoConfig);
             }
-            if (m_add != null || m_change != null || m_remove != null) {
-                dependency.setCallbacks(m_add, m_change, m_remove);
+            if (m_add != null || m_change != null || m_remove != null || m_swap != null) {
+                dependency.setCallbacks(m_add, m_change, m_remove, m_swap);
             }
             Component service = m_manager.createComponent()
                 .setInterface(serviceInterfaces, serviceProperties)
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java
new file mode 100644
index 0000000..740e000
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java
@@ -0,0 +1,250 @@
+/*
+ * 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.impl.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.FilterIndex;
+import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+	// (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))
+    private static final String FILTER_START = "(&(" + Constants.OBJECTCLASS + "=";
+    private static final String FILTER_SUBSTRING_0 = ")(|(" + Constants.SERVICE_ID + "=";
+    private static final String FILTER_SUBSTRING_1 = ")(" + DependencyManager.ASPECT + "=";
+    private static final String FILTER_END = ")))";
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+    private final Map /* <Long, SortedSet<ServiceReference>> */ m_sidToServiceReferencesMap = new HashMap();
+    private final Map /* <String, List<ServiceListener>> */ m_sidToListenersMap = new HashMap();
+    private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+    public void open(BundleContext context) {
+        synchronized (m_lock) {
+            if (m_context != null) {
+                throw new IllegalStateException("Filter already open.");
+            }
+            try {
+                m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
+            }
+            catch (InvalidSyntaxException e) {
+                throw new Error();
+            }
+            m_context = context;
+        }
+        m_tracker.open(true, true);
+    }
+
+    public void close() {
+        ServiceTracker tracker;
+        synchronized (m_lock) {
+            if (m_context == null) {
+                throw new IllegalStateException("Filter already closed.");
+            }
+            tracker = m_tracker;
+            m_tracker = null;
+            m_context = null;
+        }
+        tracker.close();
+    }
+
+    public boolean isApplicable(String clazz, String filter) {
+        return getFilterData(clazz, filter) != null;
+    }
+
+    /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */
+    private FilterData getFilterData(String clazz, String filter) {
+        // something like:
+    	// (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))    	
+        if ((filter != null)
+            && (filter.startsWith(FILTER_START))
+            && (filter.endsWith(FILTER_END))
+            ) {
+        	// service-id = 
+            int i0 = filter.indexOf(FILTER_SUBSTRING_0);
+            if (i0 == -1) {
+                return null;
+            }
+            // org.apache.felix.dependencymanager.aspect =
+            int i1 = filter.indexOf(FILTER_SUBSTRING_1);
+            if (i1 == -1 || i1 <= i0) {
+                return null;
+            }
+            long sid = Long.parseLong(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1));
+            long sid2 = Long.parseLong(filter.substring(i1 + FILTER_SUBSTRING_1.length(), filter.length() - FILTER_END.length()));
+            if (sid != sid2) {
+                return null;
+            }
+            FilterData result = new FilterData();
+            result.serviceId = sid;
+            return result;
+        }
+        return null;
+    }
+
+    public List getAllServiceReferences(String clazz, String filter) {
+        List /* <ServiceReference> */ result = new ArrayList();
+        FilterData data = getFilterData(clazz, filter);
+        if (data != null) {
+        	SortedSet /* <ServiceReference> */ list = null;
+        	synchronized (m_sidToServiceReferencesMap) {
+        		list = (SortedSet) m_sidToServiceReferencesMap.get(Long.valueOf(data.serviceId));
+			}
+            if (list != null) {
+                Iterator iterator = list.iterator();
+                while (iterator.hasNext()) {
+                    result.add((ServiceReference) iterator.next());
+                }
+            }
+        }
+        return result;
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        ServiceReference reference = event.getServiceReference();
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToListenersMap) {
+            List /* <Integer, ServiceListener> */ list = (ArrayList) m_sidToListenersMap.get(sid);
+            if (list != null) {
+                Iterator iterator = list.iterator();
+                while (iterator.hasNext()) {
+                    ServiceListener listener = (ServiceListener) iterator.next();
+                    listener.serviceChanged(event);
+                }
+            }
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) {
+        FilterData data = getFilterData(null, filter);
+        if (data != null) {
+            Long sidObject = Long.valueOf(data.serviceId);
+            synchronized (m_sidToListenersMap) {
+            	List /* <ServiceListener> */ listeners = (List) m_sidToListenersMap.get(sidObject);
+            	if (listeners == null) {
+            		listeners = new ArrayList();
+            		m_sidToListenersMap.put(sidObject, listeners);
+            	}
+            	listeners.add(listener);
+            }
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_sidToListenersMap) {
+            String filter = (String) m_listenerToFilterMap.remove(listener);
+            FilterData data = getFilterData(null, filter);
+            if (data != null) {
+            	Long sidObject = Long.valueOf(data.serviceId);
+            	List /* ServiceListener */ listeners = (List) m_sidToListenersMap.get(sidObject);
+            	if (listeners != null) {
+            		listeners.remove(listener);
+            	}
+            }
+        }
+    }
+
+    public Object addingService(ServiceReference reference) {
+        BundleContext context;
+        synchronized (m_lock) {
+            context = m_context;
+        }
+        if (context != null) {
+            return context.getService(reference);
+        }
+        else {
+            throw new IllegalStateException("No valid bundle context.");
+        }
+    }
+
+    public void addedService(ServiceReference reference, Object service) {
+        add(reference);
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        modify(reference);
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        remove(reference);
+    }
+
+    public void add(ServiceReference reference) {
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToServiceReferencesMap) {
+            Set list = (Set) m_sidToServiceReferencesMap.get(sid);
+            if (list == null) {
+                list = new TreeSet();
+                m_sidToServiceReferencesMap.put(sid, list);
+            }
+            list.add(reference);
+        }
+    }
+
+    public void modify(ServiceReference reference) {
+        remove(reference);
+        add(reference);
+    }
+
+    public void remove(ServiceReference reference) {
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        synchronized (m_sidToServiceReferencesMap) {
+            Set list = (Set) m_sidToServiceReferencesMap.get(sid);
+            if (list != null) {
+                list.remove(reference);
+            }
+        }
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("AdapterFilterIndex[");
+        sb.append("S2L: " + m_sidToListenersMap.size());
+        sb.append(", S2SR: " + m_sidToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+
+    /** Structure to hold internal filter data. */
+    private static class FilterData {
+        public long serviceId;
+    }
+
+}