Implemented a filter index that speeds up aspects.
Some bugfixes.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1095365 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 5ac4974..07ec03a 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
@@ -35,6 +35,7 @@
 import org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl;
 import org.apache.felix.dm.impl.dependencies.TemporalServiceDependencyImpl;
 import org.apache.felix.dm.impl.metatype.PropertyMetaDataImpl;
+import org.apache.felix.dm.index.AspectFilterIndex;
 import org.apache.felix.dm.index.MultiPropertyExactFilter;
 import org.apache.felix.dm.index.ServiceRegistryCache;
 import org.osgi.framework.BundleContext;
@@ -78,13 +79,18 @@
     private static ServiceRegistryCache m_serviceRegistryCache;
     static {
         String index = System.getProperty(SERVICEREGISTRY_CACHE_INDICES);
-        m_serviceRegistryCache = new ServiceRegistryCache(FrameworkUtil.getBundle(DependencyManager.class).getBundleContext());
-        m_serviceRegistryCache.open(); // TODO close it somewhere
         if (index != null) {
+            m_serviceRegistryCache = new ServiceRegistryCache(FrameworkUtil.getBundle(DependencyManager.class).getBundleContext());
+            m_serviceRegistryCache.open(); // TODO close it somewhere
             String[] props = index.split(";");
             for (int i = 0; i < props.length; i++) {
-                String[] propList = props[i].split(",");
-                m_serviceRegistryCache.addFilterIndex(new MultiPropertyExactFilter(propList));
+                if (props[i].equals("*aspect*")) {
+                    m_serviceRegistryCache.addFilterIndex(new AspectFilterIndex());
+                }
+                else {
+                    String[] propList = props[i].split(",");
+                    m_serviceRegistryCache.addFilterIndex(new MultiPropertyExactFilter(propList));
+                }
 //                System.out.println("DM: Creating index on " + props[i]);
             }
         }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/AspectFilterIndex.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/AspectFilterIndex.java
new file mode 100644
index 0000000..2223f7d
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/AspectFilterIndex.java
@@ -0,0 +1,265 @@
+/*
+ * 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.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.DependencyManager;
+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;
+
+public class AspectFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+    private static final String FILTER_START = "(&(" + Constants.OBJECTCLASS + "=";
+    private static final String FILTER_SUBSTRING_0 = ")(&(|(!(" + Constants.SERVICE_RANKING + "=*))(" + Constants.SERVICE_RANKING + "<=";
+    private static final String FILTER_SUBSTRING_1 = "))(|(" + Constants.SERVICE_ID + "=";
+    private static final String FILTER_SUBSTRING_2 = ")(" + 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 /* <Long, SortedMap<Integer, ServiceListener>> */ m_sidToRankingToListenersMap = 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);
+    }
+
+    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.wiringtest.Model)(&(|(!(service.ranking=*))(service.ranking<=9))(|(service.id=37)(org.apache.felix.dependencymanager.aspect=37))))
+        if ((filter != null)
+            && (filter.startsWith(FILTER_START))
+            && (filter.endsWith(FILTER_END))
+            ) {
+            int i0 = filter.indexOf(FILTER_SUBSTRING_0);
+            if (i0 == -1) {
+                return null;
+            }
+            int i1 = filter.indexOf(FILTER_SUBSTRING_1);
+            if (i1 == -1 || i1 <= i0) {
+                return null;
+            }
+            int i2 = filter.indexOf(FILTER_SUBSTRING_2);
+            if (i2 == -1 || i2 <= i1) {
+                return null;
+            }
+            long sid = Long.parseLong(filter.substring(i1 + FILTER_SUBSTRING_1.length(), i2));
+            long sid2 = Long.parseLong(filter.substring(i2 + FILTER_SUBSTRING_2.length(), filter.length() - FILTER_END.length()));
+            if (sid != sid2) {
+                return null;
+            }
+            FilterData result = new FilterData();
+            result.className = filter.substring(FILTER_START.length(), i0);
+            result.serviceId = sid;
+            result.ranking = Integer.parseInt(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1));
+            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 = (SortedSet) m_sidToServiceReferencesMap.get(Long.valueOf(data.serviceId));
+            if (list != null) {
+                Iterator iterator = list.iterator();
+                while (iterator.hasNext()) {
+                    ServiceReference reference = (ServiceReference) iterator.next();
+                    if (ServiceUtil.getRanking(reference) <= data.ranking) {
+                        result.add(reference);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        List list = new ArrayList();
+        ServiceReference reference = event.getServiceReference();
+        Long sid = ServiceUtil.getServiceIdObject(reference);
+        int ranking = ServiceUtil.getRanking(reference);
+        synchronized (m_sidToRankingToListenersMap) {
+            SortedMap /* <Integer, ServiceListener> */ map = (SortedMap) m_sidToRankingToListenersMap.get(sid);
+            if (map != null) {
+                Iterator iterator = map.entrySet().iterator();
+                while (iterator.hasNext()) {
+                    Entry entry = (Entry) iterator.next();
+                    if (ranking <= ((Integer) entry.getKey()).intValue()) {
+                        list.add((ServiceListener) entry.getValue());
+                    }
+                }
+            }
+        }
+        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_sidToRankingToListenersMap) {
+                SortedMap /* <Integer, ServiceListener> */ rankingToListenersMap = (SortedMap) m_sidToRankingToListenersMap.get(sidObject);
+                if (rankingToListenersMap == null) {
+                    rankingToListenersMap = new TreeMap();
+                    m_sidToRankingToListenersMap.put(sidObject, rankingToListenersMap);
+                }
+                rankingToListenersMap.put(Integer.valueOf(data.ranking), listener);
+                m_listenerToFilterMap.put(listener, filter);
+            }
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_sidToRankingToListenersMap) {
+            String filter = (String) m_listenerToFilterMap.remove(listener);
+            FilterData data = getFilterData(null, filter);
+            if (data != null) {
+                synchronized (m_sidToRankingToListenersMap) {
+                    SortedMap /* <Integer, ServiceListener> */ rankingToListenersMap = (SortedMap) m_sidToRankingToListenersMap.get(Long.valueOf(data.serviceId));
+                    if (rankingToListenersMap != null) {
+                        rankingToListenersMap.remove(Integer.valueOf(data.ranking));
+                    }
+                }
+            }
+        }
+    }
+
+    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("AspectFilterIndex[");
+        sb.append("S2R2L: " + m_sidToRankingToListenersMap.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 String className;
+        public long serviceId;
+        public int ranking;
+    }
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java
index c34775a..7f4281d 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/BundleContextInterceptor.java
@@ -108,6 +108,9 @@
         ServiceReference[] references;
         try {
             references = getServiceReferences(clazz, null);
+            if (references == null || references.length == 0) {
+                return null;
+            }
             Arrays.sort(references);
             return references[references.length - 1];
         }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java
index 7f4f871..052675e 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/index/ServiceRegistryCache.java
@@ -24,14 +24,12 @@
 import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 
-import org.eclipse.osgi.framework.console.CommandInterpreter;
-import org.eclipse.osgi.framework.console.CommandProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceRegistration;
 
-public class ServiceRegistryCache implements ServiceListener, CommandProvider {
+public class ServiceRegistryCache implements ServiceListener/*, CommandProvider*/ {
     private final List /* <FilterIndex> */ m_filterIndexList = new CopyOnWriteArrayList();
     private final BundleContext m_context;
     private final FilterIndexBundleContext m_filterIndexBundleContext;
@@ -49,11 +47,11 @@
     
     public void open() {
         m_context.addServiceListener(this);
-        m_registration = m_context.registerService(CommandProvider.class.getName(), this, null);
+//        m_registration = m_context.registerService(CommandProvider.class.getName(), this, null);
     }
     
     public void close() {
-        m_registration.unregister();
+//        m_registration.unregister();
         m_context.removeServiceListener(this);
     }
     
@@ -117,49 +115,49 @@
         }
     }
 
-    public void _sc(CommandInterpreter ci) {
-        ci.println(toString());
-    }
-    
-    public void _fi(CommandInterpreter ci) {
-        String arg = ci.nextArgument();
-        if (arg != null) {
-            int x = Integer.parseInt(arg);
-            FilterIndex filterIndex = (FilterIndex) m_filterIndexList.get(x);
-            String a1 = ci.nextArgument();
-            String a2 = null;
-            if (a1 != null) {
-                if ("-".equals(a1)) {
-                    a1 = null;
-                }
-                a2 = ci.nextArgument();
-            }
-            if (filterIndex.isApplicable(a1, a2)) {
-                List /* <ServiceReference> */ references = filterIndex.getAllServiceReferences(a1, a2);
-                ci.println("Found " + references.size() + " references:");
-                for (int i = 0; i < references.size(); i++) {
-                    ci.println("" + i + " - " + references.get(i));
-                }
-            }
-            else {
-                ci.println("Filter not applicable.");
-            }
-        }
-        else {
-            ci.println("FilterIndices:");
-            Iterator iterator = m_filterIndexList.iterator();
-            int index = 0;
-            while (iterator.hasNext()) {
-                FilterIndex filterIndex = (FilterIndex) iterator.next();
-                ci.println("" + index + " " + filterIndex);
-                index++;
-            }
-        }
-    }
-    
-    public String getHelp() {
-        return "I'm not going to help you!";
-    }
+//    public void _sc(CommandInterpreter ci) {
+//        ci.println(toString());
+//    }
+//    
+//    public void _fi(CommandInterpreter ci) {
+//        String arg = ci.nextArgument();
+//        if (arg != null) {
+//            int x = Integer.parseInt(arg);
+//            FilterIndex filterIndex = (FilterIndex) m_filterIndexList.get(x);
+//            String a1 = ci.nextArgument();
+//            String a2 = null;
+//            if (a1 != null) {
+//                if ("-".equals(a1)) {
+//                    a1 = null;
+//                }
+//                a2 = ci.nextArgument();
+//            }
+//            if (filterIndex.isApplicable(a1, a2)) {
+//                List /* <ServiceReference> */ references = filterIndex.getAllServiceReferences(a1, a2);
+//                ci.println("Found " + references.size() + " references:");
+//                for (int i = 0; i < references.size(); i++) {
+//                    ci.println("" + i + " - " + references.get(i));
+//                }
+//            }
+//            else {
+//                ci.println("Filter not applicable.");
+//            }
+//        }
+//        else {
+//            ci.println("FilterIndices:");
+//            Iterator iterator = m_filterIndexList.iterator();
+//            int index = 0;
+//            while (iterator.hasNext()) {
+//                FilterIndex filterIndex = (FilterIndex) iterator.next();
+//                ci.println("" + index + " " + filterIndex);
+//                index++;
+//            }
+//        }
+//    }
+//    
+//    public String getHelp() {
+//        return "I'm not going to help you!";
+//    }
     
     public String toString() {
         StringBuffer sb = new StringBuffer();