FELIX-3828 Fixed both the Adapter and the Aspect filter index.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1425276 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java
new file mode 100644
index 0000000..272f162
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AbstractFactoryFilterIndex.java
@@ -0,0 +1,74 @@
+package org.apache.felix.dm.impl.index;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.ServiceUtil;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+public abstract class AbstractFactoryFilterIndex {
+
+	protected final Map /* <Long, SortedSet<ServiceReference>> */ m_sidToServiceReferencesMap = new HashMap();
+	protected final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+    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);
+            }
+        }
+    }
+    
+    protected boolean referenceMatchesObjectClass(ServiceReference ref, String objectClass) {
+    	boolean matches = false;
+    	Object value = ref.getProperty(Constants.OBJECTCLASS);
+    	matches = Arrays.asList((String[])value).contains(objectClass);
+    	return matches;
+    }
+    
+    /** Structure to hold internal filter data. */
+    protected static class FilterData {
+        public long serviceId;
+        public String objectClass;
+        public int ranking;
+
+		public String toString() {
+			return "FilterData [serviceId=" + serviceId + "]";
+		}
+    }
+}
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
index 36447e5..e212f39 100644
--- 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
@@ -23,9 +23,9 @@
 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 java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.felix.dm.DependencyManager;
 import org.apache.felix.dm.FilterIndex;
@@ -42,18 +42,17 @@
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class AdapterFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+public class AdapterFilterIndex extends AbstractFactoryFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
 	// (&(objectClass=foo.Bar)(|(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 static final String FILTER_REGEXP = "\\(&\\(" + Constants.OBJECTCLASS + "=([a-zA-Z\\.\\$0-9]*)\\)\\(\\|\\(" 
+									+ Constants.SERVICE_ID + "=([0-9]*)\\)\\(" 
+									+ DependencyManager.ASPECT + "=([0-9]*)\\)\\)\\)";
+	private static final Pattern PATTERN = Pattern.compile(FILTER_REGEXP);
     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();
+    protected final Map /* <ServiceListener, String> */ m_listenerToObjectClassMap = new HashMap();
 
     public void open(BundleContext context) {
         synchronized (m_lock) {
@@ -91,51 +90,47 @@
     /** 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=foo.Bar)(|(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;
+    	// (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))  
+    	FilterData resultData = null;
+    	if (filter != null) {
+	    	Matcher matcher = PATTERN.matcher(filter);
+	    	if (matcher.matches()) {
+	    		String sid = matcher.group(2);
+	    		String sid2 = matcher.group(3);
+	    		if (sid.equals(sid2)) {
+	    			resultData = new FilterData();
+	    			resultData.serviceId = Long.parseLong(sid);
+	    		}
+	    	}
+    	}
+    	return resultData;
     }
 
-    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());
-        			}
-        		}
+	public List getAllServiceReferences(String clazz, String filter) {
+		List /* <ServiceReference> */result = new ArrayList();
+		Matcher matcher = PATTERN.matcher(filter);
+		if (matcher.matches()) {
+			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()) {
+							ServiceReference ref = (ServiceReference) iterator.next();
+							String objectClass = matcher.group(1);
+							if (referenceMatchesObjectClass(ref, objectClass)) {
+								result.add(ref);
+							}
+						}
+					}
+				}
 			}
-        }
-        return result;
-    }
-
+		}
+		return result;
+	}
+    
     public void serviceChanged(ServiceEvent event) {
         ServiceReference reference = event.getServiceReference();
         Long sid = ServiceUtil.getServiceIdObject(reference);
@@ -143,7 +138,14 @@
         synchronized (m_sidToListenersMap) {
             List /* <ServiceListener> */ list = (ArrayList) m_sidToListenersMap.get(sid);
             if (list != null) {
-                notificationList.addAll(list);
+            	Iterator iterator = list.iterator();
+            	while (iterator.hasNext()) {
+                	ServiceListener listener = (ServiceListener) iterator.next();
+                	String objectClass = (String) m_listenerToObjectClassMap.get(listener);
+                	if (referenceMatchesObjectClass(reference, objectClass)) {
+                		notificationList.add(listener);
+                	} 
+            	}
             }
         }
         // notify
@@ -166,12 +168,21 @@
             	}
             	listeners.add(listener);
             	m_listenerToFilterMap.put(listener, filter);
+        		Matcher matcher = PATTERN.matcher(filter);
+        		if (matcher.matches()) {
+        			String objectClass = matcher.group(1);
+        			m_listenerToObjectClassMap.put(listener, objectClass);
+        		} else {
+        			throw new IllegalArgumentException("Filter string does not match index pattern");
+        		}
+
             }
         }
     }
 
     public void removeServiceListener(ServiceListener listener) {
         synchronized (m_sidToListenersMap) {
+        	m_listenerToObjectClassMap.remove(listener);
             String filter = (String) m_listenerToFilterMap.remove(listener);
             if (filter != null) {
             	// the listener does exist
@@ -200,45 +211,6 @@
         }
     }
 
-    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[");
@@ -249,9 +221,4 @@
         return sb.toString();
     }
 
-    /** Structure to hold internal filter data. */
-    private static class FilterData {
-        public long serviceId;
-    }
-
 }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java
index 4e79eb4..6a98bfa 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java
@@ -19,16 +19,15 @@
 package org.apache.felix.dm.impl.index;
 
 import java.util.ArrayList;
+import java.util.Collection;
 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.FilterIndex;
@@ -45,7 +44,8 @@
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class AspectFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+public class AspectFilterIndex extends AbstractFactoryFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+	// (&(objectClass=foo.Bar)(|(!(service.ranking=*))(service.ranking<=99))(|(service.id=4451)(org.apache.felix.dependencymanager.aspect=4451)))
     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 + "=";
@@ -54,9 +54,8 @@
     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();
+    
+    private final Map /* <Long, Map<String, SortedMap<Integer, Collection<ServiceListener>>> */ m_sidToObjectClassToRankingToListenersMap = new HashMap();
 
     public void open(BundleContext context) {
         synchronized (m_lock) {
@@ -96,8 +95,8 @@
         // something like:
         // (&(objectClass=foo.Bar)(&(|(!(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))
+            && (filter.startsWith(FILTER_START)) // (&(objectClass=
+            && (filter.endsWith(FILTER_END)) // ))))
             ) {
             int i0 = filter.indexOf(FILTER_SUBSTRING_0);
             if (i0 == -1) {
@@ -117,7 +116,7 @@
                 return null;
             }
             FilterData result = new FilterData();
-            result.className = filter.substring(FILTER_START.length(), i0);
+            result.objectClass = filter.substring(FILTER_START.length(), i0);
             result.serviceId = sid;
             result.ranking = Integer.parseInt(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1));
             return result;
@@ -136,7 +135,7 @@
         			Iterator iterator = list.iterator();
         			while (iterator.hasNext()) {
         				ServiceReference reference = (ServiceReference) iterator.next();
-        				if (ServiceUtil.getRanking(reference) <= data.ranking) {
+        				if (referenceMatchesObjectClass(reference, data.objectClass) && ServiceUtil.getRanking(reference) <= data.ranking) {
         					result.add(reference);
         				}
         			}
@@ -149,20 +148,29 @@
     public void serviceChanged(ServiceEvent event) {
         List list = new ArrayList();
         ServiceReference reference = event.getServiceReference();
-        Long sid = ServiceUtil.getServiceIdObject(reference);
+        Long sidObject = 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());
-                    }
-                }
-            }
-        }
+        String[] objectClasses = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+        
+        synchronized (m_sidToObjectClassToRankingToListenersMap) {
+        	for (int i = 0; i < objectClasses.length; i++) {
+        		// handle each of the object classes separately since aspects only work on one object class at a time
+        		String objectClass = objectClasses[i];
+        		Map /* <String, Map<Integer, Collection<ServiceListener>>> */ objectClassToRankingToListenersMap = (SortedMap) m_sidToObjectClassToRankingToListenersMap.get(sidObject);
+        		if (objectClassToRankingToListenersMap != null) {
+        			SortedMap /* Integer, ServiceListener> */ rankingToListenersMap = (SortedMap) objectClassToRankingToListenersMap.get(objectClass);
+        			if (rankingToListenersMap != null) {
+        				Iterator iterator = rankingToListenersMap.entrySet().iterator();
+        				while (iterator.hasNext()) {
+        					Entry entry = (Entry) iterator.next();
+        					if (ranking <= ((Integer) entry.getKey()).intValue()) {
+        						list.addAll((Collection)entry.getValue());
+        					}
+        				}
+        			}
+        		}
+        	}
+		}
         Iterator iterator = list.iterator();
         while (iterator.hasNext()) {
             ServiceListener listener = (ServiceListener) iterator.next();
@@ -174,34 +182,63 @@
         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);
+            synchronized (m_sidToObjectClassToRankingToListenersMap) {
+            	Map /* <String, Map<Integer, Collection<ServiceListener>>> */ objectClassToRankingToListenersMap = (SortedMap) m_sidToObjectClassToRankingToListenersMap.get(sidObject);
+            	if (objectClassToRankingToListenersMap == null) {
+            		objectClassToRankingToListenersMap = new TreeMap();
+            		m_sidToObjectClassToRankingToListenersMap.put(sidObject, objectClassToRankingToListenersMap);
+            	}
+            	
+            	SortedMap /* Integer, ServiceListener> */ rankingToListenersMap = (SortedMap) objectClassToRankingToListenersMap.get(data.objectClass);
                 if (rankingToListenersMap == null) {
                     rankingToListenersMap = new TreeMap();
-                    m_sidToRankingToListenersMap.put(sidObject, rankingToListenersMap);
-                }
-                rankingToListenersMap.put(Integer.valueOf(data.ranking), listener);
+                    objectClassToRankingToListenersMap.put(data.objectClass, rankingToListenersMap);
+                }            	
+            	
+            	Collection listeners = (Collection) rankingToListenersMap.get(Integer.valueOf(data.ranking));
+            	if (listeners == null) {
+            		listeners = new ArrayList();
+            		rankingToListenersMap.put(Integer.valueOf(data.ranking), listeners);
+            	}
+            	
+            	listeners.add(listener);
                 m_listenerToFilterMap.put(listener, filter);
             }
         }
     }
 
     public void removeServiceListener(ServiceListener listener) {
-        synchronized (m_sidToRankingToListenersMap) {
-            String filter = (String) m_listenerToFilterMap.remove(listener);
-            if (filter != null) {
-            	// the listener does exist
-	            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));
-	                    }
-	                }
-	            }
-            }
-        }
+    	synchronized (m_sidToObjectClassToRankingToListenersMap) {
+    		String filter = (String) m_listenerToFilterMap.remove(listener);
+    		if (filter != null) {
+    			// the listener does exist
+    			FilterData data = getFilterData(null, filter);
+    			if (data != null) {
+    				// this index is applicable
+    				Long sidObject = Long.valueOf(data.serviceId);
+                	Map /* <String, Map<Integer, Collection<ServiceListener>>> */ objectClassToRankingToListenersMap = (SortedMap) m_sidToObjectClassToRankingToListenersMap.get(sidObject);
+                	if (objectClassToRankingToListenersMap != null) {
+                		SortedMap /* Integer, ServiceListener> */ rankingToListenersMap = (SortedMap) objectClassToRankingToListenersMap.get(data.objectClass);
+                		if (rankingToListenersMap != null) {
+                			Collection listeners = (Collection) rankingToListenersMap.get(Integer.valueOf(data.ranking));
+                			if (listeners != null) {
+                				listeners.remove(listener);
+                			}
+                			// cleanup 
+                			if (listeners.isEmpty()) {
+                				rankingToListenersMap.remove(Integer.valueOf(data.ranking));
+                			}
+                			if (rankingToListenersMap.isEmpty()) {
+                				objectClassToRankingToListenersMap.remove(data.objectClass);
+                			}
+                			if (objectClassToRankingToListenersMap.isEmpty()) {
+                				m_sidToObjectClassToRankingToListenersMap.remove(sidObject);
+                			}
+                		}
+                	}
+    			}
+    		}
+		}
     }
 
     public Object addingService(ServiceReference reference) {
@@ -216,60 +253,15 @@
             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("S2R2L: " + m_sidToObjectClassToRankingToListenersMap.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;
-    }
 }