You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ui...@apache.org on 2012/02/24 11:47:51 UTC

svn commit: r1293173 [6/8] - in /felix/sandbox/uiterlix/dependencymanager/core: ./ .externalToolBuilders/ .settings/ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/fel...

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AspectFilterIndex.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,269 @@
+/*
+ * 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.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;
+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 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, 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;
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptor.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptor.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptor.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptor.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,137 @@
+/*
+ * 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.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.felix.dm.FilterIndex;
+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 BundleContextInterceptor extends BundleContextInterceptorBase {
+    private final ServiceRegistryCache m_cache;
+
+    public BundleContextInterceptor(ServiceRegistryCache cache, BundleContext context) {
+        super(context);
+        m_cache = cache;
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, filter);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, filter);
+        }
+        else {
+//            System.out.println("BCI:Listener " + listener.getClass().getName() + " filter " + filter);
+            m_context.addServiceListener(listener, filter);
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, null);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, null);
+        }
+        else {
+//            System.out.println("BCI:Listener " + listener.getClass().getName() + " without filter");
+            m_context.addServiceListener(listener);
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, null);
+        if (filterIndex != null) {
+            filterIndex.removeServiceListener(listener);
+        }
+        else {
+            m_context.removeServiceListener(listener);
+        }
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            List /* <ServiceReference> */ result = filterIndex.getAllServiceReferences(clazz, filter);
+            Iterator iterator = result.iterator();
+            while (iterator.hasNext()) {
+                ServiceReference reference = (ServiceReference) iterator.next();
+                String[] list = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+                for (int i = 0; i < list.length; i++) {
+                    if (!reference.isAssignableTo(m_context.getBundle(), list[i])) {
+                        iterator.remove();
+                        break;
+                    }
+                }
+            }
+            if (result == null || result.size() == 0) {
+                return null;
+            }
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            return m_context.getServiceReferences(clazz, filter);
+        }
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            List /* <ServiceReference> */ result = filterIndex.getAllServiceReferences(clazz, filter);
+            if (result == null || result.size() == 0) {
+                return null;
+            }
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            return m_context.getAllServiceReferences(clazz, filter);
+        }
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        ServiceReference[] references;
+        try {
+            references = getServiceReferences(clazz, null);
+            if (references == null || references.length == 0) {
+                return null;
+            }
+            Arrays.sort(references);
+            return references[references.length - 1];
+        }
+        catch (InvalidSyntaxException e) {
+            throw new Error("Invalid filter syntax thrown for null filter.", e);
+        }
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        m_cache.serviceChangedForFilterIndices(event);
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/BundleContextInterceptorBase.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,163 @@
+/*
+ * 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.io.File;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Base class for bundle context interceptors that keep track of service listeners and delegate incoming changes to them.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class BundleContextInterceptorBase implements BundleContext, ServiceListener {
+    protected final BundleContext m_context;
+    /** Keeps track of all service listeners and their optional filters. */
+    private final Map /* <ServiceListener, String> */m_serviceListenerFilterMap = new HashMap();
+    private long m_currentVersion = 0;
+    private long m_entryVersion = -1;
+    private Entry[] m_serviceListenerFilterMapEntries;
+
+    public BundleContextInterceptorBase(BundleContext context) {
+        m_context = context;
+    }
+
+    public String getProperty(String key) {
+        return m_context.getProperty(key);
+    }
+
+    public Bundle getBundle() {
+        return m_context.getBundle();
+    }
+
+    public Bundle installBundle(String location) throws BundleException {
+        return m_context.installBundle(location);
+    }
+
+    public Bundle installBundle(String location, InputStream input) throws BundleException {
+        return m_context.installBundle(location, input);
+    }
+
+    public Bundle getBundle(long id) {
+        return m_context.getBundle(id);
+    }
+
+    public Bundle[] getBundles() {
+        return m_context.getBundles();
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, filter);
+            m_currentVersion++;
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, null);
+            m_currentVersion++;
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.remove(listener);
+            m_currentVersion++;
+        }
+    }
+
+    public void addBundleListener(BundleListener listener) {
+        m_context.addBundleListener(listener);
+    }
+
+    public void removeBundleListener(BundleListener listener) {
+        m_context.removeBundleListener(listener);
+    }
+
+    public void addFrameworkListener(FrameworkListener listener) {
+        m_context.addFrameworkListener(listener);
+    }
+
+    public void removeFrameworkListener(FrameworkListener listener) {
+        m_context.removeFrameworkListener(listener);
+    }
+
+    public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+        return m_context.registerService(clazzes, service, properties);
+    }
+
+    public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
+        return m_context.registerService(clazz, service, properties);
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getAllServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        return m_context.getServiceReference(clazz);
+    }
+
+    public Object getService(ServiceReference reference) {
+        return m_context.getService(reference);
+    }
+
+    public boolean ungetService(ServiceReference reference) {
+        return m_context.ungetService(reference);
+    }
+
+    public File getDataFile(String filename) {
+        return m_context.getDataFile(filename);
+    }
+
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        return m_context.createFilter(filter);
+    }
+
+    protected Entry[] synchronizeCollection() {
+        // lazy copy on write: we make a new copy only if writes have changed the collection
+        synchronized (m_serviceListenerFilterMap) {
+            if (m_currentVersion != m_entryVersion) {
+                m_serviceListenerFilterMapEntries = (Entry[]) m_serviceListenerFilterMap.entrySet().toArray(new Entry[m_serviceListenerFilterMap.size()]);
+                m_entryVersion = m_currentVersion;
+            }
+        }
+        return m_serviceListenerFilterMapEntries;
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/FilterIndexBundleContext.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,64 @@
+/*
+ * 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.Map.Entry;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FilterIndexBundleContext extends BundleContextInterceptorBase {
+    public FilterIndexBundleContext(BundleContext context) {
+        super(context);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        Entry[] entries = synchronizeCollection();
+        for (int i = 0; i < entries.length; i++) {
+            Entry serviceListenerFilterEntry = entries[i];
+            ServiceListener serviceListener = (ServiceListener) serviceListenerFilterEntry.getKey();
+            String filter = (String) serviceListenerFilterEntry.getValue();
+            if (filter == null) {
+                serviceListener.serviceChanged(event);
+            }
+            else {
+                // call service changed on the listener if the filter matches the event
+                // TODO review if we can be smarter here
+                try {
+                    if ("(objectClass=*)".equals(filter)) {
+                        serviceListener.serviceChanged(event);
+                    }
+                    else {
+                        if (m_context.createFilter(filter).match(event.getServiceReference())) {
+                            serviceListener.serviceChanged(event);
+                        }
+                    }
+                }
+                catch (InvalidSyntaxException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/MultiPropertyExactFilter.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/MultiPropertyExactFilter.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/MultiPropertyExactFilter.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/MultiPropertyExactFilter.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,427 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.FilterIndex;
+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 MultiPropertyExactFilter implements FilterIndex, ServiceTrackerCustomizer {
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+    private final TreeSet /* <String> */ m_propertyKeys = new TreeSet(String.CASE_INSENSITIVE_ORDER);
+    private final Map /* <String, List<ServiceReference>> */ m_keyToServiceReferencesMap = new HashMap();
+    private final Map /* <String, List<ServiceListener>> */ m_keyToListenersMap = new HashMap();
+    private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+    public MultiPropertyExactFilter(String[] propertyKeys) {
+        for (int i = 0; i < propertyKeys.length; i++) {
+            m_propertyKeys.add(propertyKeys[i]);
+        }
+    }
+    
+    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 List /* <ServiceReference> */ getAllServiceReferences(String clazz, String filter) {
+        List /* <ServiceReference> */ result = new ArrayList();
+        List keys = createKeysFromFilter(clazz, filter);
+        Iterator iterator = keys.iterator();
+        while (iterator.hasNext()) {
+            String key = (String) iterator.next();
+            ServiceReference reference;
+            synchronized (m_keyToServiceReferencesMap) {
+                List references = (List) m_keyToServiceReferencesMap.get(key);
+                if (references != null) {
+                    result.addAll(references);
+                }
+            }
+        }
+        return result;
+    }
+
+    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) {
+        if (isApplicable(reference.getPropertyKeys())) {
+            add(reference);
+        }
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference.getPropertyKeys())) {
+            modify(reference);
+        }
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference.getPropertyKeys())) {
+            remove(reference);
+        }
+    }
+
+    public void add(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references == null) {
+                    references = new ArrayList();
+                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+                }
+                references.add(reference);
+            }
+        }
+    }
+
+    public void modify(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            // TODO this is a quite expensive linear scan over the existing collection
+            // because we first need to remove any existing references and they can be
+            // all over the place :)
+            Iterator iterator = m_keyToServiceReferencesMap.values().iterator();
+            while (iterator.hasNext()) {
+                List /* <ServiceReference> */ list = (List) iterator.next();
+                if (list != null) {
+                    Iterator i2 = list.iterator();
+                    while (i2.hasNext()) {
+                        ServiceReference ref = (ServiceReference) i2.next();
+                        if (ref.equals(reference)) {
+                            i2.remove();
+                        }
+                    }
+                }
+            }
+            
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references == null) {
+                    references = new ArrayList();
+                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+                }
+                references.add(reference);
+            }
+        }
+    }
+
+    public void remove(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references != null) {
+                    references.remove(reference);
+                }
+            }
+        }
+    }
+
+    public boolean isApplicable(String[] propertyKeys) {
+        TreeSet list = new TreeSet(String.CASE_INSENSITIVE_ORDER);
+        for (int i = 0; i < propertyKeys.length; i++) {
+            list.add(propertyKeys[i]);
+        }
+        Iterator iterator = m_propertyKeys.iterator();
+        while (iterator.hasNext()) {
+            String item = (String) iterator.next();
+            if (!(list.contains(item))) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    public boolean isApplicable(String clazz, String filter) {
+        // "(&(a=b)(c=d))"
+        // "(&(&(a=b)(c=d))(objC=aaa))"
+        // startsWith "(&" en split in "(x=y)" -> elke x bestaat in m_propertykeys
+        
+        // (&(objectClass=xyz)(&(a=x)(b=y)))
+        
+        Set /* <String> */found = new HashSet();
+        if (filter != null && filter.startsWith("(&(") && filter.substring(3, 14).equalsIgnoreCase(Constants.OBJECTCLASS) && filter.substring(14, 15).equals("=") && filter.contains(")(&(") && filter.endsWith(")))")) {
+            int i1 = filter.indexOf(")(&(");
+            String className = filter.substring(("(&(" + Constants.OBJECTCLASS + "=").length(), i1);
+            if (!m_propertyKeys.contains(Constants.OBJECTCLASS)) {
+                return false;
+            }
+            else {
+                found.add(Constants.OBJECTCLASS);
+            }
+            String[] parts = filter.substring(i1 + ")(&(".length(), filter.length() - ")))".length()).split("\\)\\(");
+            for (int i = 0; i < parts.length; i++) {
+                String part = parts[i];
+                String[] tuple = part.split("=");
+                if (!m_propertyKeys.contains(tuple[0])) {
+                    return false;
+                }
+                else {
+                    found.add(tuple[0]);
+                }
+                // TODO check value tuple[1]
+            }
+            return found.size() == m_propertyKeys.size();
+        }
+        else if (filter != null && filter.startsWith("(&(") && filter.endsWith("))")) {
+            String[] parts = filter.substring(3, filter.length() - 2).split("\\)\\(");
+            for (int i = 0; i < parts.length; i++) {
+                String part = parts[i];
+                String[] tuple = part.split("=");
+                if (!m_propertyKeys.contains(tuple[0])) {
+                    return false;
+                }
+                else {
+                    found.add(tuple[0]);
+                }
+                // TODO check value tuple[1]
+            }
+            return found.size() == m_propertyKeys.size();
+        }
+        else if (filter != null && filter.startsWith("(") && filter.endsWith(")") && m_propertyKeys.size() == 1) { // TODO quick hack
+            String part = filter.substring(1, filter.length() - 1);
+            String[] tuple = part.split("=");
+            if (!m_propertyKeys.contains(tuple[0])) {
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        else if (clazz != null && filter == null && m_propertyKeys.size() == 1 && Constants.OBJECTCLASS.equalsIgnoreCase((String) m_propertyKeys.first())) {
+            return true;
+        }
+        return false;
+    }
+    
+    private List /* <String> */ createKeys(ServiceReference reference) {
+        List /* <String> */ results = new ArrayList();
+        
+        results.add(""); // ???
+        
+        String[] keys = reference.getPropertyKeys();
+        Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < keys.length; i++) {
+            String key = keys[i].toLowerCase();
+            if (m_propertyKeys.contains(key)) {
+                Object value = reference.getProperty(key);
+                if (value instanceof String[]) {
+                    String[] values = (String[]) value;
+                    List newResults = new ArrayList();
+                    for (int j = 0; j < values.length; j++) {
+                        String val = values[j];
+                        for (int k = 0; k < results.size(); k++) {
+                            String head = (String) results.get(k);
+                            if (head != null && head.length() > 0) {
+                                head = head + ";";
+                            }
+                            newResults.add(head + key + "=" + val);
+                        }
+                    }
+                    results = newResults;
+                }
+                else {
+                    for (int k = 0; k < results.size(); k++) {
+                        String head = (String) results.get(k);
+                        if (head != null && head.length() > 0) {
+                            head = head + ";";
+                        }
+                        results.set(k, head + key + "=" + value);
+                    }
+                }
+            }
+        }
+        return results;
+    }
+    
+    private List /* <String> */ createKeysFromFilter(String clazz, String filter) {
+        List result = new ArrayList();
+        StringBuffer index = new StringBuffer();
+        Iterator iterator = m_propertyKeys.iterator();
+        while (iterator.hasNext()) {
+            String key = ((String) iterator.next()).toLowerCase();
+            if (index.length() > 0) {
+                index.append(';');
+            }
+            index.append(key);
+            index.append('=');
+            String value = null;
+            if (clazz != null && Constants.OBJECTCLASS.equalsIgnoreCase(key)) {
+                value = clazz;
+            } // (&(obC=a)(&(a=b)(c=d)))
+            if (filter != null) {
+                String startString = "(" + key + "=";
+                int i1 = filter.toLowerCase().indexOf(startString);
+                if (i1 != -1) {
+                    int i2 = filter.indexOf(")(", i1);
+                    if (i2 == -1) {
+                        if (filter.endsWith(")))")) {
+                            i2 = filter.length() - 3;
+                        }
+                        else if (filter.endsWith("))")) {
+                            i2 = filter.length() - 2;
+                        }
+                        else {
+                            i2 = filter.length() - 1;
+                        }
+                    }
+                    String value2 = filter.substring(i1 + startString.length(), i2);
+                    if (value != null && !value.equals(value2)) {
+                        // corner case: someone specified a clazz and
+                        // also a filter containing a different clazz
+                        return result;
+                    }
+                    value = value2;
+                }
+            }
+            index.append(value);
+        }
+        result.add(index.toString());
+        return result;
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        if (isApplicable(event.getServiceReference().getPropertyKeys())) {
+            List /* <String> */ keys = createKeys(event.getServiceReference());
+            List list = new ArrayList();
+            synchronized (m_keyToListenersMap) {
+                for (int i = 0; i < keys.size(); i++) {
+                    String key = (String) keys.get(i);
+                    List listeners = (List) m_keyToListenersMap.get(key);
+                    if (listeners != null) {
+                        list.addAll(listeners);
+                    }
+                }
+            }
+            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) {
+        List keys = createKeysFromFilter(null, filter);
+        Iterator iterator = keys.iterator();
+        while (iterator.hasNext()) {
+            String key = (String) iterator.next();
+            synchronized (m_keyToListenersMap) {
+                List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
+                if (listeners == null) {
+                    listeners = new CopyOnWriteArrayList();
+                    m_keyToListenersMap.put(key, listeners);
+                }
+                listeners.add(listener);
+                m_listenerToFilterMap.put(listener, filter);
+            }
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_keyToListenersMap) {
+            String filter = (String) m_listenerToFilterMap.remove(listener);
+            List keys = createKeysFromFilter(null, filter);
+            Iterator iterator = keys.iterator();
+            while (iterator.hasNext()) {
+                String key = (String) iterator.next();
+                
+                boolean result = filter != null;
+                if (result) {
+                    List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
+                    if (listeners != null) {
+                        listeners.remove(listener);
+                    }
+                    // TODO actually, if listeners == null that would be strange....
+                }
+            }
+        }
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("MultiPropertyExactFilter[");
+        sb.append("K2L: " + m_keyToListenersMap.size());
+        sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/ServiceRegistryCache.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/ServiceRegistryCache.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/ServiceRegistryCache.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/ServiceRegistryCache.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,172 @@
+/*
+ * 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.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.dm.FilterIndex;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRegistryCache implements ServiceListener/*, CommandProvider*/ {
+    private final List /* <FilterIndex> */ m_filterIndexList = new CopyOnWriteArrayList();
+    private final BundleContext m_context;
+    private final FilterIndexBundleContext m_filterIndexBundleContext;
+    private final Map /* <BundleContext, BundleContextInterceptor> */ m_bundleContextInterceptorMap = new HashMap();
+    private long m_currentVersion = 0;
+    private long m_arrayVersion = -1;
+    private BundleContextInterceptor[] m_interceptors = null;
+    private ServiceRegistration m_registration;
+
+    
+    public ServiceRegistryCache(BundleContext context) {
+        m_context = context;
+        m_filterIndexBundleContext = new FilterIndexBundleContext(m_context);
+    }
+    
+    public void open() {
+        m_context.addServiceListener(this);
+//        m_registration = m_context.registerService(CommandProvider.class.getName(), this, null);
+    }
+    
+    public void close() {
+//        m_registration.unregister();
+        m_context.removeServiceListener(this);
+    }
+    
+    public void addFilterIndex(FilterIndex index) {
+        m_filterIndexList.add(index);
+        index.open(m_filterIndexBundleContext);
+    }
+    
+    public void removeFilterIndex(FilterIndex index) {
+        index.close();
+        m_filterIndexList.remove(index);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        // any incoming event is first dispatched to the list of filter indices
+        m_filterIndexBundleContext.serviceChanged(event);
+        // and then all the other listeners can access it
+        synchronized (m_bundleContextInterceptorMap) {
+            if (m_currentVersion != m_arrayVersion) {
+                // if our copy is out of date, we make a new one
+                m_interceptors = (BundleContextInterceptor[]) m_bundleContextInterceptorMap.values().toArray(new BundleContextInterceptor[m_bundleContextInterceptorMap.size()]);
+                m_arrayVersion = m_currentVersion;
+            }
+        }
+        
+        serviceChangedForFilterIndices(event);
+    }
+    
+    /** Creates an interceptor for a bundle context that uses our cache. */
+    public BundleContext createBundleContextInterceptor(BundleContext context) {
+        synchronized (m_bundleContextInterceptorMap) {
+            BundleContextInterceptor bundleContextInterceptor = (BundleContextInterceptor) m_bundleContextInterceptorMap.get(context);
+            if (bundleContextInterceptor == null) {
+                bundleContextInterceptor = new BundleContextInterceptor(this, context);
+                m_bundleContextInterceptorMap.put(context, bundleContextInterceptor);
+                m_currentVersion++;
+                // TODO figure out a good way to clean up bundle contexts that are no longer valid so they can be garbage collected
+            }
+            return bundleContextInterceptor;
+        }
+    }
+
+    public FilterIndex hasFilterIndexFor(String clazz, String filter) {
+        Iterator iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = (FilterIndex) iterator.next();
+            if (filterIndex.isApplicable(clazz, filter)) {
+                return filterIndex;
+            }
+        }
+        return null;
+    }
+
+    public void serviceChangedForFilterIndices(ServiceEvent event) {
+        Iterator iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = (FilterIndex) iterator.next();
+            filterIndex.serviceChanged(event);
+        }
+    }
+
+//    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();
+        sb.append("ServiceRegistryCache[");
+        sb.append("FilterIndices: " + m_filterIndexList.size());
+        sb.append(", BundleContexts intercepted: " + m_bundleContextInterceptorMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/AttributeDefinitionImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,76 @@
+/*
+ * 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.metatype;
+
+import org.osgi.service.metatype.AttributeDefinition;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AttributeDefinitionImpl implements AttributeDefinition {
+    private PropertyMetaDataImpl m_propertyMetaData;
+    private Resource m_resource;
+
+    public AttributeDefinitionImpl(PropertyMetaDataImpl propertyMetaData, Resource resource) {
+        m_propertyMetaData = propertyMetaData;
+        m_resource = resource;
+    }
+
+    public int getCardinality() {
+        return m_propertyMetaData.getCardinality();
+    }
+
+    public String[] getDefaultValue() {
+        return m_propertyMetaData.getDefaults();
+    }
+
+    public String getDescription() {
+        return m_resource.localize(m_propertyMetaData.getDescription());
+    }
+
+    public String getID() {
+        return m_propertyMetaData.getId();
+    }
+
+    public String getName() {
+        return m_resource.localize(m_propertyMetaData.getHeading());
+    }
+
+    public String[] getOptionLabels() {
+        String[] labels = m_propertyMetaData.getOptionLabels();
+        if (labels != null) {
+            for (int i = 0; i < labels.length; i++) {
+                labels[i] = m_resource.localize(labels[i]);
+            }
+        }
+        return labels;
+    }
+
+    public String[] getOptionValues() {
+        return m_propertyMetaData.getOptionValues();
+    }
+
+    public int getType() {
+        return m_propertyMetaData.getType();
+    }
+
+    public String validate(String value) {
+        return null;
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/MetaTypeProviderImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,258 @@
+/*
+ * 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.metatype;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.PropertyMetaData;
+import org.apache.felix.dm.impl.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * When a ConfigurationDepdendency is configured with properties metadata, we provide
+ * a specific ManagedService which also implements the MetaTypeProvider interface. This interface
+ * allows the MetaTypeService to retrieve our properties metadata, which will then be handled by webconsole.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MetaTypeProviderImpl implements MetaTypeProvider, ManagedService, ManagedServiceFactory {
+    private ManagedService m_managedServiceDelegate;
+    private ManagedServiceFactory m_managedServiceFactoryDelegate;
+    private List m_propertiesMetaData = new ArrayList();
+    private String m_description;
+    private String m_heading;
+    private String m_localization;
+    private Map m_localesProperties = new HashMap();
+    private Logger m_logger;
+    private BundleContext m_bctx;
+    private String m_pid;
+
+    public MetaTypeProviderImpl(String pid, BundleContext ctx, Logger logger, ManagedService msDelegate, ManagedServiceFactory msfDelegate) {
+        m_pid = pid;
+        m_bctx = ctx;
+        m_logger = logger;
+        m_managedServiceDelegate = msDelegate;
+        m_managedServiceFactoryDelegate = msfDelegate;
+        // Set the default localization file base name (see core specification, in section Localization on page 68).
+        // By default, this file can be stored in OSGI-INF/l10n/bundle.properties (and corresponding localized version
+        // in OSGI-INF/l10n/bundle_en_GB_welsh.properties,  OSGI-INF/l10n/bundle_en_GB.properties, etc ...
+        // This default localization property file name can be overriden using the PropertyMetaData.setLocalization method.
+        m_localization = (String) m_bctx.getBundle().getHeaders().get(Constants.BUNDLE_LOCALIZATION);
+        if (m_localization == null) {
+            m_localization = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+        }
+    }
+
+    /**
+     * Registers the metatype information of a given configuration property
+     * @param property
+     */
+    public void add(PropertyMetaData property) {
+        m_propertiesMetaData.add(property);
+    }
+
+    /**
+     * A human readable description of the PID this annotation is associated with. Example: "Configuration for the PrinterService bundle".
+     * @return A human readable description of the PID this annotation is associated with (may be localized)
+     */
+    public void setDescription(String description) {
+        m_description = description;
+    }
+
+    /**
+     * The label used to display the tab name (or section) where the properties are displayed. Example: "Printer Service".
+     * @return The label used to display the tab name where the properties are displayed (may be localized)
+     */
+    public void setName(String heading) {
+        m_heading = heading;
+    }
+
+    /**
+     * Points to the basename of the Properties file that can localize the Meta Type informations.
+     * By default, (e.g. <code>setLocalization("person")</code> would match person_du_NL.properties in the root bundle directory.
+     * The default localization base name for the properties is OSGI-INF/l10n/bundle, but can
+     * be overridden by the manifest Bundle-Localization header (see core specification, in section Localization on page 68).
+     */
+    public void setLocalization(String path) {
+        if (path.endsWith(".properties")) {
+            throw new IllegalArgumentException(
+                "path must point to the base name of the propertie file, "
+                + "excluding local suffixes. For example: "
+                + "foo/bar/person is valid and matches the property file \"foo/bar/person_bundle_en_GB_welsh.properties\"");
+        }
+        m_localization = path.startsWith("/") ? path.substring(1) : path;
+    }
+
+    // --------------- MetaTypeProvider interface -------------------------------------------------
+
+    /**
+     * Returns all the Locales our bundle is containing. For instance, if our bundle contains the following localization files:
+     * OSGI-INF/l10n/bundle_en_GB_welsh.properties and OSGI-INF/l10n/bundle_en_GB.properties, then this method will return
+     * "en_GB", "en_GB_welsh" ...
+     * @return the list of Locale supported by our bundle.
+     */
+    public String[] getLocales() {
+        int lastSlash = m_localization.lastIndexOf("/");
+        String path = (lastSlash == -1) ? "/" : ("/" + m_localization.substring(0, lastSlash - 1));
+        String base = (lastSlash == -1) ? m_localization : m_localization.substring(lastSlash + 1);
+        Enumeration e = m_bctx.getBundle().findEntries(path,
+            base + "*.properties", false);
+        if (e == null) {
+            return null;
+        }
+        
+        TreeSet set = new TreeSet();
+        while (e.hasMoreElements()) {
+            // We have found a locale property file in the form of "path/file[_language[_ country[_variation]].properties"
+            // And now, we have to get the "language[_country[_variation]]" part ...
+            URL url = (URL) e.nextElement();
+            String name = url.getPath();
+            name = name.substring(name.lastIndexOf("/") + 1);
+            int underscore = name.indexOf("_");
+            if (underscore != -1) {
+                name = name.substring(underscore + 1, name.length() - ".properties".length());
+            }
+            if (name.length() > 0) {
+                set.add(name);
+            }
+        }
+
+        String[] locales = (String[]) set.toArray(new String[set.size()]);
+        return locales.length == 0 ? null : locales;
+    }
+
+    /**
+     * Returns the ObjectClassDefinition for a given Pid/Locale.
+     */
+    public ObjectClassDefinition getObjectClassDefinition(String id, String locale) {
+        try {
+            // Check if the id matches our PID
+            if (!id.equals(m_pid)) {
+                m_logger.log(LogService.LOG_ERROR, "id " + id + " does not match pid " + m_pid);
+                return null;
+            }
+
+            Properties localeProperties = getLocaleProperties(locale);
+            return new ObjectClassDefinitionImpl(m_pid, m_heading,
+                m_description, m_propertiesMetaData, new Resource(localeProperties));
+        }
+
+        catch (Throwable t) {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "Unexpected exception while geting ObjectClassDefinition for " + id + " (locale="
+                    + locale + ")", t);
+            return null;
+        }
+    }
+
+    /**
+     * We also implements the ManagedService and we just delegates the configuration handling to
+     * our associated ConfigurationDependency.
+     */
+    public void updated(Dictionary properties) throws ConfigurationException {
+        m_managedServiceDelegate.updated(properties);
+    }
+
+    /**
+     * Gets the properties for a given Locale.
+     * @param locale
+     * @return
+     * @throws IOException
+     */
+    private synchronized Properties getLocaleProperties(String locale) throws IOException {
+        locale = locale == null ? Locale.getDefault().toString() : locale;
+        Properties properties = (Properties) m_localesProperties.get(locale);
+        if (properties == null) {
+            properties = new Properties();
+            URL url = m_bctx.getBundle().getEntry(m_localization + ".properties");
+            if (url != null) {
+                loadLocale(properties, url);
+            }
+
+            String path = m_localization;
+            StringTokenizer tok = new StringTokenizer(locale, "_");
+            while (tok.hasMoreTokens()) {
+                path += "_" + tok.nextToken();
+                url = m_bctx.getBundle().getEntry(path + ".properties");
+                if (url != null) {
+                    properties = new Properties(properties);
+                    loadLocale(properties, url);
+                }
+            }
+            m_localesProperties.put(locale, properties);
+        }
+        return properties;
+    }
+
+    /**
+     * Loads a Locale Properties file.
+     * @param properties
+     * @param url
+     * @throws IOException
+     */
+    private void loadLocale(Properties properties, URL url) throws IOException {
+        InputStream in = null;
+        try {
+            in = url.openStream();
+            properties.load(in);
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    // ManagedServiceFactory implementation
+    public void deleted(String pid) {
+        m_managedServiceFactoryDelegate.deleted(pid);
+    }
+
+    public String getName() {
+        return m_pid;
+    }
+
+    public void updated(String pid, Dictionary properties) throws ConfigurationException {
+        m_managedServiceFactoryDelegate.updated(pid, properties);
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/ObjectClassDefinitionImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,101 @@
+/*
+ * 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.metatype;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * ObjectClassDefinition implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ObjectClassDefinitionImpl implements ObjectClassDefinition {
+    // Our OCD name (may be localized)
+    private String m_name;
+    
+    // Our OCD description (may be localized)
+    private String m_description;
+    
+    // Our OCD id
+    private String m_id;
+    
+    // The list of Properties MetaData objects (from DependencyManager API)
+    private List m_propertiesMetaData;
+    
+    // The localized resource that can be used when localizing some parameters
+    private Resource m_resource;
+
+    public ObjectClassDefinitionImpl(String id, String name, String description, List propertiesMetaData, Resource resource) {
+        m_id = id;
+        m_name = name;
+        m_description = description;
+        m_propertiesMetaData = propertiesMetaData;
+        m_resource = resource;
+    }
+
+    // --------------------- ObjectClassDefinition ----------------------------------------
+
+    public AttributeDefinition[] getAttributeDefinitions(int filter) {
+        List attrs = new ArrayList();
+        for (int i = 0; i < m_propertiesMetaData.size(); i++) {
+            PropertyMetaDataImpl metaData = (PropertyMetaDataImpl) m_propertiesMetaData.get(i);
+            switch (filter) {
+                case ObjectClassDefinition.ALL:
+                    attrs.add(new AttributeDefinitionImpl(metaData, m_resource));
+                    break;
+                case ObjectClassDefinition.OPTIONAL:
+                    if (!metaData.isRequired()) {
+                        attrs.add(new AttributeDefinitionImpl(metaData, m_resource));
+                    }
+                    break;
+                case ObjectClassDefinition.REQUIRED:
+                    if (metaData.isRequired()) {
+                        attrs.add(new AttributeDefinitionImpl(metaData, m_resource));
+                    }
+                    break;
+            }
+        }
+
+        AttributeDefinition[] array = new AttributeDefinitionImpl[attrs.size()];
+        return (AttributeDefinition[]) attrs.toArray(array);
+    }
+
+    public String getDescription() {
+        return m_resource.localize(m_description);
+    }
+
+    public String getID() {
+        return m_id;
+    }
+
+    public InputStream getIcon(int size) throws IOException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String getName() {
+        return m_resource.localize(m_name);
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/PropertyMetaDataImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,204 @@
+/*
+ * 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.metatype;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.dm.PropertyMetaData;
+import org.osgi.service.metatype.AttributeDefinition;
+
+/**
+ * DependencyManager PropertyMetaData Implementation. This class describes meta informations regarding
+ * one given configuration property.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PropertyMetaDataImpl implements PropertyMetaData {
+    /**
+     * List of option labels (may be localized)
+     */
+    List m_optionsLabels = new ArrayList();
+    
+    /**
+     * List of option values
+     */
+    List m_optionsValues = new ArrayList();
+    
+    /**
+     * Property cardinality.
+     * @see {@link AttributeDefinition#getCardinality()}
+     */
+    private int m_cardinality;
+    
+    /**
+     * Valid default property values
+     */
+    private String[] m_defaults;
+    
+    /**
+     * Property description.
+     */
+    private String m_description;
+    
+    /**
+     * Property title.
+     */
+    private String m_heading;
+    
+    /**
+     * Property unique Id
+     */
+    private String m_id;
+    
+    /**
+     * Required flag.
+     */
+    private boolean m_required;
+    
+    /**
+     * Property Type.
+     * @see {@link AttributeDefinition#getType()}
+     */
+    private int m_type = AttributeDefinition.STRING;
+    
+    /**
+     * Mapping between java types and valid MetaType types.
+     * @see {@link AttributeDefinition#getType()}
+     */
+    private final static Map m_typeMapping = new HashMap() {{
+        put(Boolean.class, new Integer(AttributeDefinition.BOOLEAN));
+        put(Byte.class, new Integer(AttributeDefinition.BYTE));
+        put(Character.class, new Integer(AttributeDefinition.CHARACTER));
+        put(Double.class, new Integer(AttributeDefinition.FLOAT));
+        put(Integer.class, new Integer(AttributeDefinition.INTEGER));
+        put(Long.class, new Integer(AttributeDefinition.LONG));
+        put(Short.class, new Integer(AttributeDefinition.SHORT));
+        put(String.class, new Integer(AttributeDefinition.STRING));
+    }};
+
+    public PropertyMetaData addOption(String optionLabel, String optionValue) {
+        m_optionsLabels.add(optionLabel);
+        m_optionsValues.add(optionValue);
+        return this;
+    }
+
+    public PropertyMetaData setCardinality(int cardinality) {
+        m_cardinality = cardinality;
+        return this;
+    }
+
+    public PropertyMetaData setDefaults(String[] defaults) {
+        m_defaults = defaults;
+        return this;
+    }
+
+    public PropertyMetaData setDescription(String description) {
+        m_description = description;
+        return this;
+    }
+
+    public PropertyMetaData setHeading(String heading) {
+        m_heading = heading;
+        return this;
+    }
+
+    public PropertyMetaData setId(String id) {
+        m_id = id;
+        return this;
+    }
+
+    public PropertyMetaData setRequired(boolean required) {
+        m_required = required;
+        return this;
+    }
+
+    public PropertyMetaData setType(Class classType) {
+        Integer type = (Integer) m_typeMapping.get(classType);
+        if (type == null) {
+            throw new IllegalArgumentException("Invalid type: " + classType + ". Valid types are "
+                + m_typeMapping.keySet());
+        }
+        m_type = type.intValue();
+        return this;
+    }
+
+    public String[] getOptionLabels() {
+        String[] optionLabels = new String[m_optionsLabels.size()];
+        return (String[]) m_optionsLabels.toArray(optionLabels);
+    }
+
+    public String[] getOptionValues() {
+        String[] optionValues = new String[m_optionsValues.size()];
+        return (String[]) m_optionsValues.toArray(optionValues);
+    }
+
+    public int getCardinality() {
+        return m_cardinality;
+    }
+
+    public String[] getDefaults() {
+        return m_defaults;
+    }
+
+    public String getDescription() {
+        return m_description;
+    }
+
+    public String getHeading() {
+        return m_heading;
+    }
+
+    public String getId() {
+        return m_id;
+    }
+
+    public boolean isRequired() {
+        return m_required;
+    }
+
+    public int getType() {
+        return m_type;
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("cardinality=").append(m_cardinality);
+        sb.append("; defaults="); 
+        for (int i = 0; i < m_defaults.length; i ++) {
+            sb.append(m_defaults[i]).append(" ");
+        }
+        sb.append("; description=").append(m_description);
+        sb.append("; heading=").append(m_heading);
+        sb.append("; id=").append(m_id);
+        sb.append("; required=").append(m_required);
+        sb.append("; type=").append(getType());
+        sb.append("; optionLabels=");
+        for (int i = 0; i < m_optionsLabels.size(); i ++) {
+            sb.append(m_optionsLabels.get(i)).append(" ");
+        }
+        sb.append("; optionValues=");
+        for (int i = 0; i < m_optionsValues.size(); i ++) {
+            sb.append(m_optionsValues.get(i)).append(" ");
+        }
+        return sb.toString();
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/Resource.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/Resource.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/Resource.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/metatype/Resource.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,42 @@
+/*
+ * 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.metatype;
+
+import java.util.Properties;
+
+/**
+ * Helper class used to localize a given Property Meta Data.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Resource {
+    private Properties m_properties;
+
+    public Resource(Properties properties) {
+        m_properties = properties;
+    }
+    
+    public String localize(String param) {
+        if (m_properties != null && param != null && param.startsWith("%")) {
+            param = param.substring(1);
+            return m_properties.getProperty(param);
+        }
+        return param;
+    }
+}