You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gn...@apache.org on 2018/04/20 09:23:35 UTC
svn commit: r1829639 [1/3] - in /felix/trunk:
bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/
bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/
utils/ utils/src/main/java/org/apache/felix/utils/collections/ u...
Author: gnodet
Date: Fri Apr 20 09:23:35 2018
New Revision: 1829639
URL: http://svn.apache.org/viewvc?rev=1829639&view=rev
Log:
Provide optimized resource / filter / capability / requirement / capability set
Added:
felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java
felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java
- copied, changed from r1829637, felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/ResourceBuilder.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/ResourceImpl.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/SimpleFilter.java
felix/trunk/utils/src/test/java/org/apache/felix/utils/collections/
felix/trunk/utils/src/test/java/org/apache/felix/utils/collections/StringArrayMapTest.java
felix/trunk/utils/src/test/java/org/apache/felix/utils/resource/SimpleFilterTest.java
Removed:
felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyHashMap.java
felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java
Modified:
felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java
felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java
felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java
felix/trunk/utils/pom.xml
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java
felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java
felix/trunk/utils/src/test/java/org/apache/felix/utils/resource/CapabilityImplTest.java
felix/trunk/utils/src/test/java/org/apache/felix/utils/resource/RequirementImplTest.java
Modified: felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java (original)
+++ felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java Fri Apr 20 09:23:35 2018
@@ -42,12 +42,12 @@ public class FelixResourceAdapter implem
if (namespace == null || namespace.equals(IdentityNamespace.IDENTITY_NAMESPACE))
{
- CapabilityImpl c = OSGiRepositoryImpl.newOSGiIdentityCapability(resource, this);
+ CapabilityImpl c = OSGiRepositoryImpl.newOSGiIdentityCapability(this, resource);
result.add(c);
}
if (namespace == null || namespace.equals(ContentNamespace.CONTENT_NAMESPACE))
{
- CapabilityImpl c = OSGiRepositoryImpl.newOSGiContentCapability(resource, this);
+ CapabilityImpl c = OSGiRepositoryImpl.newOSGiContentCapability(this, resource);
result.add(c);
}
Added: felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java?rev=1829639&view=auto
==============================================================================
--- felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java (added)
+++ felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java Fri Apr 20 09:23:35 2018
@@ -0,0 +1,67 @@
+/*
+ * 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.bundlerepository.impl;
+
+import java.util.Map;
+
+import org.apache.felix.utils.collections.StringArrayMap;
+
+/**
+ * A map that can delay the computation of certain values up until the moment that they
+ * are actually needed. Useful for expensive to compute values such as the SHA-256.
+ * This map does <b>not</b> support {@code null} values.
+ */
+@SuppressWarnings("serial")
+public class LazyStringMap<V> extends StringArrayMap<V>
+{
+ public LazyStringMap(Map<String, ? extends V> map) {
+ super(map);
+ }
+
+ public LazyStringMap() {
+ }
+
+ public LazyStringMap(int capacity) {
+ super(capacity);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public V get(Object key)
+ {
+ V val = super.get(key);
+ if (val instanceof LazyValue) {
+ val = ((LazyValue<V>) val).compute();
+ if (val == null) {
+ throw new NullPointerException("Lazy computed values may not be null");
+ }
+ put((String) key, val);
+ }
+ return val;
+ }
+
+ public void putLazy(String key, LazyValue<V> lazy) {
+ super.doPut(key, lazy);
+ }
+
+ public interface LazyValue<V>
+ {
+ V compute();
+ }
+}
Modified: felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java (original)
+++ felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java Fri Apr 20 09:23:35 2018
@@ -30,10 +30,9 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Callable;
import org.apache.felix.bundlerepository.RepositoryAdmin;
-import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
+import org.apache.felix.bundlerepository.Resource;
import org.apache.felix.utils.resource.CapabilityImpl;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
@@ -41,7 +40,6 @@ import org.osgi.framework.namespace.Iden
import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
-import org.osgi.resource.Resource;
import org.osgi.service.repository.ContentNamespace;
import org.osgi.service.repository.Repository;
@@ -120,8 +118,7 @@ class OSGiRepositoryImpl implements Repo
caps.add(idCap);
}
- static CapabilityImpl newOSGiIdentityCapability(org.apache.felix.bundlerepository.Resource res,
- org.osgi.resource.Resource targetResource)
+ static CapabilityImpl newOSGiIdentityCapability(org.osgi.resource.Resource or, org.apache.felix.bundlerepository.Resource res)
{
@SuppressWarnings("unchecked")
Map<String, Object> idAttrs = new HashMap<String, Object>(res.getProperties());
@@ -132,32 +129,34 @@ class OSGiRepositoryImpl implements Repo
if (idAttrs.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE) == null)
idAttrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, IdentityNamespace.TYPE_BUNDLE);
- return new CapabilityImpl(IdentityNamespace.IDENTITY_NAMESPACE, idAttrs, Collections.<String, String> emptyMap(), targetResource);
+ return new CapabilityImpl(or, IdentityNamespace.IDENTITY_NAMESPACE, Collections.<String, String> emptyMap(), idAttrs);
}
- static CapabilityImpl newOSGiContentCapability(org.apache.felix.bundlerepository.Resource resource,
- org.osgi.resource.Resource targetResource)
+ static CapabilityImpl newOSGiContentCapability(org.osgi.resource.Resource or, Resource resource)
{
final String uri = resource.getURI();
- LazyValue<String, Object> lazyValue =
- new LazyValue<String, Object>(ContentNamespace.CONTENT_NAMESPACE, new Callable<Object>()
- {
- public Object call() throws Exception
- {
- // This is expensive to do, so only compute it when actually obtained...
+ LazyStringMap.LazyValue<String> content = new LazyStringMap.LazyValue<String>() {
+ public String compute() {
+ // This is expensive to do, so only compute it when actually obtained...
+ try {
return OSGiRepositoryImpl.getSHA256(uri);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
}
- });
-
+ }
+ };
Object mime = resource.getProperties().get("mime");
if (mime == null)
mime = "application/vnd.osgi.bundle";
- Map<String, Object> contentAttrs = new LazyHashMap<String, Object>(Collections.singleton(lazyValue));
+ Map<String, Object> contentAttrs = new LazyStringMap<Object>(4);
contentAttrs.put(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE, mime);
contentAttrs.put(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE, resource.getSize());
contentAttrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, uri);
- return new ContentCapabilityImpl(contentAttrs, targetResource);
+ contentAttrs.put(ContentNamespace.CONTENT_NAMESPACE, content);
+ return new CapabilityImpl(or, ContentNamespace.CONTENT_NAMESPACE, Collections.<String, String> emptyMap(), contentAttrs);
}
static String getSHA256(String uri) throws IOException, NoSuchAlgorithmException // TODO find a good place for this
@@ -183,19 +182,4 @@ class OSGiRepositoryImpl implements Repo
return sb.toString();
}
- // This capability variant does not take a private copy of the capabilities so that it
- // can lazily compute the content hash.
- private static class ContentCapabilityImpl extends CapabilityImpl implements Capability {
- private final Map<String, Object> contentAttributes;
-
- public ContentCapabilityImpl(Map<String, Object> contentAttrs, Resource targetResource) {
- super(ContentNamespace.CONTENT_NAMESPACE, null, null, targetResource);
- contentAttributes = Collections.unmodifiableMap(contentAttrs);
- }
-
- @Override
- public Map<String, Object> getAttributes() {
- return contentAttributes;
- }
- }
}
Copied: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java (from r1829637, felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java)
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java?p2=felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java&p1=felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java&r1=1829637&r2=1829639&rev=1829639&view=diff
==============================================================================
--- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java (original)
+++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java Fri Apr 20 09:23:35 2018
@@ -21,41 +21,39 @@ package org.apache.felix.bundlerepositor
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase;
+import org.apache.felix.bundlerepository.impl.LazyStringMap.LazyValue;
-import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
-
-public class LazyHashMapTest extends TestCase
+public class LazyStringMapTest extends TestCase
{
public void testLazyHashMap() {
final AtomicInteger lv1Computed = new AtomicInteger(0);
- LazyValue<String, Long> lv1 = new LazyValue<String, Long>("42", new Callable<Long>()
- {
- public Long call() throws Exception
- {
+ LazyValue<Long> lv1 = new LazyValue<Long>() {
+ public Long compute() {
lv1Computed.incrementAndGet();
return 24L;
}
- });
+ };
final AtomicInteger lv2Computed = new AtomicInteger(0);
- LazyValue<String, Long> lv2 = new LazyValue<String, Long>("zero", new Callable<Long>()
- {
- public Long call() throws Exception
- {
+ LazyValue<Long> lv2 = new LazyValue<Long>() {
+ public Long compute() {
lv2Computed.incrementAndGet();
return 0L;
}
- });
+ };
- Collection<LazyValue<String, Long>> lazyValues = new ArrayList<LazyHashMap.LazyValue<String,Long>>();
+ Collection<LazyValue<Long>> lazyValues = new ArrayList<LazyValue<Long>>();
lazyValues.add(lv1);
lazyValues.add(lv2);
- HashMap<String, Long> lhm = new LazyHashMap<String, Long>(lazyValues);
+ LazyStringMap<Long> lhm = new LazyStringMap<Long>();
lhm.put("1", 2L);
+ lhm.putLazy("42", lv1);
+ lhm.putLazy("zero", lv2);
assertEquals(new Long(2L), lhm.get("1"));
assertEquals("No computation should have happened yet", 0, lv1Computed.get());
Modified: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java (original)
+++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java Fri Apr 20 09:23:35 2018
@@ -58,7 +58,7 @@ public class OSGiRepositoryImplTest exte
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("osgi.identity", null);
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.identity", null);
Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
assertEquals(1, result.size());
@@ -117,7 +117,7 @@ public class OSGiRepositoryImplTest exte
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("osgi.identity", "(osgi.identity=test_file_2)");
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.identity", "(osgi.identity=test_file_2)");
Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
assertEquals(1, result.size());
@@ -137,7 +137,7 @@ public class OSGiRepositoryImplTest exte
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("foo", "(someKey=someOtherVal)");
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "foo", "(someKey=someOtherVal)");
Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
assertEquals(1, result.size());
@@ -157,7 +157,7 @@ public class OSGiRepositoryImplTest exte
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("foo", "(someKey=*)");
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "foo", "(someKey=*)");
Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
assertEquals(1, result.size());
@@ -181,7 +181,7 @@ public class OSGiRepositoryImplTest exte
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("osgi.wiring.package",
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.wiring.package",
"(&(osgi.wiring.package=org.apache.commons.logging)(version>=1.0.1)(!(version>=2)))");
Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
@@ -230,12 +230,12 @@ public class OSGiRepositoryImplTest exte
BundleRevision br = Mockito.mock(BundleRevision.class);
Mockito.when(sysBundle.adapt(BundleRevision.class)).thenReturn(br);
- Capability cap1 = new CapabilityImpl("some.system.cap",
- Collections.<String, Object>singletonMap("sys.cap", "something"),
- Collections.singletonMap("x", "y"));
- Capability cap2 = new CapabilityImpl("some.system.cap",
- Collections.<String, Object>singletonMap("sys.cap", "somethingelse"),
- Collections.<String, String>emptyMap());
+ Capability cap1 = new CapabilityImpl(Mockito.mock(Resource.class), "some.system.cap",
+ Collections.singletonMap("x", "y"),
+ Collections.<String, Object>singletonMap("sys.cap", "something"));
+ Capability cap2 = new CapabilityImpl(Mockito.mock(Resource.class), "some.system.cap",
+ Collections.<String, String>emptyMap(),
+ Collections.<String, Object>singletonMap("sys.cap", "somethingelse"));
Mockito.when(br.getCapabilities(null)).thenReturn(Arrays.asList(cap1, cap2));
BundleContext bc = Mockito.mock(BundleContext.class);
Modified: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java (original)
+++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java Fri Apr 20 09:23:35 2018
@@ -51,7 +51,8 @@ public class OSGiRepositoryXMLTest exten
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("osgi.identity",
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
+ "osgi.identity",
"(osgi.identity=cdi-subsystem)");
Map<Requirement, Collection<Capability>> result = repo
@@ -126,7 +127,8 @@ public class OSGiRepositoryXMLTest exten
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("osgi.identity",
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
+ "osgi.identity",
"(license=http://www.opensource.org/licenses/mytestlicense)");
Map<Requirement, Collection<Capability>> result = repo
@@ -145,7 +147,7 @@ public class OSGiRepositoryXMLTest exten
repoAdmin.addRepository(url);
Repository repo = new OSGiRepositoryImpl(repoAdmin);
- Requirement req = new RequirementImpl("foo", "(bar=toast)");
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class),"foo", "(bar=toast)");
Map<Requirement, Collection<Capability>> result = repo
.findProviders(Collections.singleton(req));
Modified: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java (original)
+++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java Fri Apr 20 09:23:35 2018
@@ -24,7 +24,9 @@ import java.util.Map;
import junit.framework.TestCase;
import org.apache.felix.utils.resource.RequirementImpl;
+import org.mockito.Mockito;
import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
public class OSGiRequirementAdapterTest extends TestCase
{
@@ -38,7 +40,7 @@ public class OSGiRequirementAdapterTest
dirs.put("resolution", "optional");
dirs.put("test", "test");
- Requirement req = new RequirementImpl("osgi.wiring.package", attrs, dirs);
+ Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.wiring.package", dirs, attrs);
OSGiRequirementAdapter adapter = new OSGiRequirementAdapter(req);
assertEquals("(package=y)", adapter.getFilter());
Modified: felix/trunk/utils/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/utils/pom.xml?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/utils/pom.xml (original)
+++ felix/trunk/utils/pom.xml Fri Apr 20 09:23:35 2018
@@ -36,17 +36,21 @@
<url>http://svn.apache.org/repos/asf/felix/utils</url>
</scm>
+ <properties>
+ <felix.java.version>7</felix.java.version>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
+ <artifactId>osgi.core</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- <version>4.2.0</version>
+ <artifactId>osgi.cmpn</artifactId>
+ <version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -62,6 +66,13 @@
</excludes>
</configuration>
</plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
Added: felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java
URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java?rev=1829639&view=auto
==============================================================================
--- felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java (added)
+++ felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java Fri Apr 20 09:23:35 2018
@@ -0,0 +1,339 @@
+/*
+ * 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.utils.collections;
+
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+public class StringArrayMap<V> implements Map<String, V> {
+
+ protected Object[] table;
+ protected int size;
+
+ public static <T> Map<String, T> reduceMemory(Map<String, T> map) {
+ if (map == null) {
+ return Collections.emptyMap();
+ }
+ switch (map.size()) {
+ case 0:
+ return Collections.emptyMap();
+ case 1:
+ Entry<String, T> e = map.entrySet().iterator().next();
+ return Collections.singletonMap(e.getKey().intern(), e.getValue());
+ default:
+ if (map instanceof StringArrayMap) {
+ @SuppressWarnings("unchecked")
+ StringArrayMap<T> m = (StringArrayMap) map;
+ if (m.size == m.table.length / 2) {
+ return map;
+ }
+ }
+ return new StringArrayMap<>(map);
+ }
+ }
+
+ public StringArrayMap(Map<String, ? extends V> map) {
+ if (map instanceof StringArrayMap) {
+ size = ((StringArrayMap) map).size;
+ table = Arrays.copyOf(((StringArrayMap) map).table, size * 2);
+ } else {
+ size = 0;
+ table = new Object[map.size() * 2];
+ for (Entry<String, ? extends V> e : map.entrySet()) {
+ int i = size++ << 1;
+ table[i++] = e.getKey().intern();
+ table[i] = e.getValue();
+ }
+ }
+ }
+
+ public StringArrayMap() {
+ this(32);
+ }
+
+ public StringArrayMap(int capacity) {
+ table = new Object[capacity * 2];
+ size = 0;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V get(Object key) {
+ String k = ((String) key).intern();
+ for (int i = 0, l = size << 1; i < l; i += 2) {
+ if (k == table[i]) {
+ return (V) table[i + 1];
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V put(String key, V value) {
+ return (V) doPut(key, value);
+ }
+
+ protected Object doPut(String key, Object value) {
+ key = key.intern();
+ for (int i = 0, l = size << 1; i < l; i += 2) {
+ if (key == table[i]) {
+ Object old = table[i + 1];
+ table[i + 1] = value;
+ return old;
+ }
+ }
+ if (table.length == 0) {
+ table = new Object[2];
+ } else if (size * 2 == table.length) {
+ Object[] n = new Object[table.length * 2];
+ System.arraycopy(table, 0, n, 0, table.length);
+ table = n;
+ }
+ int i = size++ << 1;
+ table[i++] = key;
+ table[i] = value;
+ return null;
+ }
+
+ public Set<String> keySet() {
+ return new AbstractSet<String>() {
+ @Override
+ public Iterator<String> iterator() {
+ return new Iterator<String>() {
+ int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @Override
+ public String next() {
+ if (index >= size) {
+ throw new NoSuchElementException();
+ }
+ return (String) table[(index++ << 1)];
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ public Collection<V> values() {
+ return new AbstractCollection<V>() {
+ @Override
+ public Iterator<V> iterator() {
+ return new Iterator<V>() {
+ int index = 0;
+
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V next() {
+ if (index >= size) {
+ throw new NoSuchElementException();
+ }
+ return (V) table[(index++ << 1) + 1];
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ public Set<Entry<String, V>> entrySet() {
+ return new AbstractSet<Entry<String, V>>() {
+ @Override
+ public Iterator<Entry<String, V>> iterator() {
+ return new Iterator<Entry<String, V>>() {
+ int index = 0;
+
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Entry<String, V> next() {
+ if (index >= size) {
+ throw new NoSuchElementException();
+ }
+ final int i = index << 1;
+ index++;
+ return new Entry<String, V>() {
+
+ public String getKey() {
+ return (String) table[i];
+ }
+
+ public V getValue() {
+ return (V) table[i + 1];
+ }
+
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ public boolean containsKey(Object key) {
+ String k = ((String) key).intern();
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (table[i] == k) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean containsValue(Object value) {
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (Objects.equals(table[i + 1], value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V remove(Object key) {
+ String k = ((String) key).intern();
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (table[i] == k) {
+ Object v = table[i + 1];
+ if (i < l - 2) {
+ System.arraycopy(table, i + 2, table, i, l - 2 - i);
+ }
+ table[l - 1] = null;
+ table[l - 2] = null;
+ size--;
+ return (V) v;
+ }
+ }
+ return null;
+ }
+
+ public void putAll(Map<? extends String, ? extends V> m) {
+ for (Entry<? extends String, ? extends V> e : m.entrySet()) {
+ put(e.getKey(), e.getValue());
+ }
+ }
+
+ public void clear() {
+ size = 0;
+ Arrays.fill(table, null);
+ }
+
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < size * 2; i++)
+ result = 31 * result + (table[i] == null ? 0 : table[i].hashCode());
+ return result;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Map))
+ return false;
+ Map<?,?> m = (Map<?,?>) o;
+ if (m.size() != size())
+ return false;
+ try {
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ Object key = table[i];
+ Object value = table[i+1];
+ if (value == null) {
+ if (!(m.get(key)==null && m.containsKey(key)))
+ return false;
+ } else {
+ if (!value.equals(m.get(key)))
+ return false;
+ }
+ }
+ } catch (ClassCastException | NullPointerException unused) {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString() {
+ if (size == 0)
+ return "{}";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (i > 0) {
+ sb.append(',').append(' ');
+ }
+ sb.append(table[i]);
+ sb.append('=');
+ sb.append(table[i+1] == this ? "(this Map)" : table[i+1]);
+ }
+ return sb.append('}').toString();
+ }
+
+}
Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java
URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java (original)
+++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java Fri Apr 20 09:23:35 2018
@@ -16,38 +16,32 @@
*/
package org.apache.felix.utils.resource;
+import org.apache.felix.utils.collections.StringArrayMap;
+import org.osgi.framework.Version;
import org.osgi.resource.Resource;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
abstract class AbstractCapabilityRequirement {
- /** The namespace. Required. */
- private final String namespace;
+ /** The resource. Required. */
+ protected final Resource resource;
- /** Optional resource. */
- private final Resource resource;
+ /** The namespace. Required. */
+ protected final String namespace;
/** Optional attributes. Never null. */
- private final Map<String, Object> attributes;
+ protected final Map<String, String> directives;
/** Optional attributes. Never null. */
- private final Map<String, String> directives;
+ protected final Map<String, Object> attributes;
- AbstractCapabilityRequirement(final String ns, final Map<String, Object> attrs, final Map<String, String> dirs, final Resource res) {
- if ( ns == null ) {
- throw new IllegalArgumentException("Namespace must not be null.");
- }
- namespace = ns;
- attributes = attrs == null
- ? Collections.<String, Object>emptyMap()
- : Collections.unmodifiableMap(new HashMap<String, Object>(attrs));
- directives = dirs == null
- ? Collections.<String,String>emptyMap()
- : Collections.unmodifiableMap(new HashMap<String,String>(dirs));
- resource = res;
+ AbstractCapabilityRequirement(final Resource res, final String ns, final Map<String, String> dirs, final Map<String, Object> attrs) {
+ resource = Objects.requireNonNull(res, "Resource must not be null.");
+ namespace = Objects.requireNonNull(ns, "Namespace must not be null.");
+ directives = StringArrayMap.reduceMemory(dirs);
+ attributes = StringArrayMap.reduceMemory(attrs);
}
/**
@@ -82,45 +76,98 @@ abstract class AbstractCapabilityRequire
return resource;
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + attributes.hashCode();
- result = prime * result + directives.hashCode();
- result = prime * result + namespace.hashCode();
-
- if (resource != null)
- result = prime * result + resource.hashCode();
- return result;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AbstractCapabilityRequirement that = (AbstractCapabilityRequirement) o;
+ return Objects.equals(resource, that.resource) &&
+ Objects.equals(namespace, that.namespace) &&
+ Objects.equals(attributes, that.attributes) &&
+ Objects.equals(directives, that.directives);
}
@Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- AbstractCapabilityRequirement other = (AbstractCapabilityRequirement) obj;
- if (!namespace.equals(other.namespace))
- return false;
- if (!attributes.equals(other.attributes))
- return false;
- if (!directives.equals(other.directives))
- return false;
- if (resource == null) {
- return other.resource == null;
- } else {
- return resource.equals(other.resource);
- }
+ public int hashCode() {
+ return Objects.hash(resource, namespace, attributes, directives);
}
@Override
public String toString() {
- return getClass().getSimpleName() + " [resource=" + resource + ", namespace=" + namespace + ", attributes=" + attributes
- + ", directives=" + directives + "]";
+ return toString(getResource(), getNamespace(), getAttributes(), getDirectives());
+ }
+
+ public static String toString(Resource res, String namespace, Map<String, Object> attrs, Map<String, String> dirs) {
+ StringBuilder sb = new StringBuilder();
+ if (res != null) {
+ sb.append("[").append(res).append("] ");
+ }
+ sb.append(namespace);
+ for (String key : attrs.keySet()) {
+ sb.append("; ");
+ append(sb, key, attrs.get(key), true);
+ }
+ for (String key : dirs.keySet()) {
+ sb.append("; ");
+ append(sb, key, dirs.get(key), false);
+ }
+ return sb.toString();
+ }
+
+ private static void append(StringBuilder sb, String key, Object val, boolean attribute) {
+ sb.append(key);
+ if (val instanceof Version) {
+ sb.append(":Version=");
+ sb.append(val);
+ } else if (val instanceof Long) {
+ sb.append(":Long=");
+ sb.append(val);
+ } else if (val instanceof Double) {
+ sb.append(":Double=");
+ sb.append(val);
+ } else if (val instanceof Iterable) {
+ Iterable<?> it = (Iterable<?>) val;
+ String scalar = null;
+ for (Object o : it) {
+ String ts;
+ if (o instanceof String) {
+ ts = "String";
+ } else if (o instanceof Long) {
+ ts = "Long";
+ } else if (o instanceof Double) {
+ ts = "Double";
+ } else if (o instanceof Version) {
+ ts = "Version";
+ } else {
+ throw new IllegalArgumentException("Unsupported scalar type: " + o);
+ }
+ if (scalar == null) {
+ scalar = ts;
+ } else if (!scalar.equals(ts)) {
+ throw new IllegalArgumentException("Unconsistent list type for attribute " + key);
+ }
+ }
+ sb.append(":List<").append(scalar).append(">=");
+ sb.append("\"");
+ boolean first = true;
+ for (Object o : it) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(o.toString().replace("\"", "\\\"").replace(",", "\\,"));
+ }
+ sb.append("\"");
+ } else {
+ sb.append(attribute ? "=" : ":=");
+ String s = val.toString();
+ if (s.matches("[0-9a-zA-Z_\\-.]*")) {
+ sb.append(s);
+ } else {
+ sb.append("\"").append(s.replace("\"", "\\\\")).append("\"");
+ }
+ }
}
}
Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java (original)
+++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java Fri Apr 20 09:23:35 2018
@@ -16,35 +16,50 @@
*/
package org.apache.felix.utils.resource;
+import org.osgi.framework.Constants;
import org.osgi.resource.Capability;
import org.osgi.resource.Resource;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Implementation of the OSGi Capability interface.
*/
public class CapabilityImpl extends AbstractCapabilityRequirement implements Capability {
- /**
- * Create a capability that is not associated with a resource.
- * @param res The resource associated with the capability. May be null.
- * @param ns The namespace of the capability.
- * @param attrs The attributes of the capability.
- * @param dirs The directives of the capability.
- */
- public CapabilityImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs) {
- this(ns, attrs, dirs, null);
- }
+
+ protected final Set<String> mandatory;
/**
* Create a capability.
+ * @param res The resource associated with the capability.
* @param ns The namespace of the capability.
* @param attrs The attributes of the capability.
* @param dirs The directives of the capability.
- * @param res The resource associated with the capability. May be null.
*/
- public CapabilityImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs, Resource res) {
- super(ns, attrs, dirs, res);
+ public CapabilityImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
+ super(res, ns, dirs, attrs);
+
+ // Handle mandatory directive
+ Set<String> mandatory = Collections.emptySet();
+ String value = this.directives.get(Constants.MANDATORY_DIRECTIVE);
+ if (value != null) {
+ List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
+ mandatory = new HashSet<>(names.size());
+ for (String name : names) {
+ // If attribute exists, then record it as mandatory.
+ if (this.attributes.containsKey(name)) {
+ mandatory.add(name);
+ // Otherwise, report an error.
+ } else {
+ throw new IllegalArgumentException("Mandatory attribute '" + name + "' does not exist.");
+ }
+ }
+ }
+ this.mandatory = mandatory;
}
/**
@@ -54,6 +69,10 @@ public class CapabilityImpl extends Abst
* @param resource The resource to be associated with the capability
*/
public CapabilityImpl(Resource resource, Capability capability) {
- this(capability.getNamespace(), capability.getAttributes(), capability.getDirectives(), resource);
+ this(resource, capability.getNamespace(), capability.getDirectives(), capability.getAttributes());
+ }
+
+ public boolean isAttributeMandatory(String name) {
+ return !mandatory.isEmpty() && mandatory.contains(name);
}
}
Added: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java
URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java?rev=1829639&view=auto
==============================================================================
--- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java (added)
+++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java Fri Apr 20 09:23:35 2018
@@ -0,0 +1,469 @@
+/*
+ * 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.utils.resource;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.felix.utils.version.VersionTable;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+
+@SuppressWarnings("rawtypes")
+public class CapabilitySet {
+
+ private static final Class<?>[] STRING_CLASS = new Class[] {String.class};
+
+ private final Map<String, Map<Object, Set<Capability>>> indices;
+ private final Set<Capability> capSet = new HashSet<>();
+
+ public CapabilitySet(List<String> indexProps) {
+ indices = new TreeMap<>();
+ for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++) {
+ indices.put(indexProps.get(i), new HashMap<Object, Set<Capability>>());
+ }
+ }
+
+ public void dump() {
+ for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
+ boolean header1 = false;
+ for (Entry<Object, Set<Capability>> entry2 : entry.getValue().entrySet()) {
+ boolean header2 = false;
+ for (Capability cap : entry2.getValue()) {
+ if (!header1) {
+ System.out.println(entry.getKey() + ":");
+ header1 = true;
+ }
+ if (!header2) {
+ System.out.println(" " + entry2.getKey());
+ header2 = true;
+ }
+ System.out.println(" " + cap);
+ }
+ }
+ }
+ }
+
+ public void addCapability(Capability cap) {
+ capSet.add(cap);
+
+ // Index capability.
+ for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
+ Object value = cap.getAttributes().get(entry.getKey());
+ if (value != null) {
+ if (value.getClass().isArray()) {
+ value = convertArrayToList(value);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (value instanceof Collection) {
+ Collection c = (Collection) value;
+ for (Object o : c) {
+ indexCapability(index, cap, o);
+ }
+ } else {
+ indexCapability(index, cap, value);
+ }
+ }
+ }
+ }
+
+ private void indexCapability(Map<Object, Set<Capability>> index, Capability cap, Object capValue) {
+ // TODO: when JDK8, should be:
+ // TODO: index.computeIfAbsent(capValue, k -> new HashSet<>()).add(cap);
+ Set<Capability> set = index.get(capValue);
+ if (set == null) {
+ set = new HashSet<>();
+ index.put(capValue, set);
+ }
+ set.add(cap);
+ }
+
+ public void removeCapability(Capability cap) {
+ if (capSet.remove(cap)) {
+ for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
+ Object value = cap.getAttributes().get(entry.getKey());
+ if (value != null) {
+ if (value.getClass().isArray()) {
+ value = convertArrayToList(value);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (value instanceof Collection) {
+ Collection c = (Collection) value;
+ for (Object o : c) {
+ deindexCapability(index, cap, o);
+ }
+ } else {
+ deindexCapability(index, cap, value);
+ }
+ }
+ }
+ }
+ }
+
+ private void deindexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object value) {
+ Set<Capability> caps = index.get(value);
+ if (caps != null) {
+ caps.remove(cap);
+ if (caps.isEmpty()) {
+ index.remove(value);
+ }
+ }
+ }
+
+ public Set<Capability> match(SimpleFilter sf, boolean obeyMandatory) {
+ Set<Capability> matches = match(capSet, sf);
+ return obeyMandatory
+ ? matchMandatory(matches, sf)
+ : matches;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Set<Capability> match(Set<Capability> caps, SimpleFilter sf) {
+ Set<Capability> matches = new HashSet<>();
+
+ if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
+ matches.addAll(caps);
+ } else if (sf.getOperation() == SimpleFilter.AND) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++) {
+ matches = match(caps, sfs.get(i));
+ caps = matches;
+ }
+ } else if (sf.getOperation() == SimpleFilter.OR) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (SimpleFilter sf1 : sfs) {
+ matches.addAll(match(caps, sf1));
+ }
+ } else if (sf.getOperation() == SimpleFilter.NOT) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matches.addAll(caps);
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (SimpleFilter sf1 : sfs) {
+ matches.removeAll(match(caps, sf1));
+ }
+ } else {
+ Map<Object, Set<Capability>> index = indices.get(sf.getName());
+ if ((sf.getOperation() == SimpleFilter.EQ) && (index != null)) {
+ Set<Capability> existingCaps = index.get(sf.getValue());
+ if (existingCaps != null) {
+ matches.addAll(existingCaps);
+ matches.retainAll(caps);
+ }
+ } else {
+ for (Capability cap : caps) {
+ Object lhs = cap.getAttributes().get(sf.getName());
+ if (lhs != null) {
+ if (compare(lhs, sf.getValue(), sf.getOperation())) {
+ matches.add(cap);
+ }
+ }
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ public static boolean matches(Capability cap, SimpleFilter sf) {
+ return matchesInternal(cap, sf) && matchMandatory(cap, sf);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static boolean matchesInternal(Capability cap, SimpleFilter sf) {
+ boolean matched = true;
+
+ if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
+ matched = true;
+ } else if (sf.getOperation() == SimpleFilter.AND) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; matched && (i < sfs.size()); i++) {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ } else if (sf.getOperation() == SimpleFilter.OR) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matched = false;
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; !matched && (i < sfs.size()); i++) {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ } else if (sf.getOperation() == SimpleFilter.NOT) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (SimpleFilter sf1 : sfs) {
+ matched = !(matchesInternal(cap, sf1));
+ }
+ } else {
+ matched = false;
+ Object lhs = cap.getAttributes().get(sf.getName());
+ if (lhs != null) {
+ matched = compare(lhs, sf.getValue(), sf.getOperation());
+ }
+ }
+
+ return matched;
+ }
+
+ private static Set<Capability> matchMandatory(
+ Set<Capability> caps, SimpleFilter sf) {
+ for (Iterator<Capability> it = caps.iterator(); it.hasNext();) {
+ Capability cap = it.next();
+ if (!matchMandatory(cap, sf)) {
+ it.remove();
+ }
+ }
+ return caps;
+ }
+
+ private static boolean matchMandatory(Capability cap, SimpleFilter sf) {
+ if (cap instanceof CapabilityImpl) {
+ for (Entry<String, Object> entry : cap.getAttributes().entrySet()) {
+ if (((CapabilityImpl) cap).isAttributeMandatory(entry.getKey())
+ && !matchMandatoryAttribute(entry.getKey(), sf)) {
+ return false;
+ }
+ }
+ } else {
+ String value = cap.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
+ if (value != null) {
+ List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
+ for (Entry<String, Object> entry : cap.getAttributes().entrySet()) {
+ if (names.contains(entry.getKey())
+ && !matchMandatoryAttribute(entry.getKey(), sf)) {
+ return false;
+ }
+ }
+ }
+
+ }
+ return true;
+ }
+
+ private static boolean matchMandatoryAttribute(String attrName, SimpleFilter sf) {
+ if ((sf.getName() != null) && sf.getName().equals(attrName)) {
+ return true;
+ } else if (sf.getOperation() == SimpleFilter.AND) {
+ List list = (List) sf.getValue();
+ for (Object aList : list) {
+ SimpleFilter sf2 = (SimpleFilter) aList;
+ if ((sf2.getName() != null)
+ && sf2.getName().equals(attrName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static boolean compare(Object lhs, Object rhsUnknown, int op) {
+ if (lhs == null) {
+ return false;
+ }
+
+ // If this is a PRESENT operation, then just return true immediately
+ // since we wouldn't be here if the attribute wasn't present.
+ if (op == SimpleFilter.PRESENT) {
+ return true;
+ }
+
+ // If the type is comparable, then we can just return the
+ // result immediately.
+ if (lhs instanceof Comparable) {
+ // Spec says SUBSTRING is false for all types other than string.
+ if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String)) {
+ return false;
+ }
+
+ Object rhs;
+ if (op == SimpleFilter.SUBSTRING) {
+ rhs = rhsUnknown;
+ } else {
+ try {
+ rhs = coerceType(lhs, (String) rhsUnknown);
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ switch (op) {
+ case SimpleFilter.EQ:
+ try {
+ return ((Comparable) lhs).compareTo(rhs) == 0;
+ } catch (Exception ex) {
+ return false;
+ }
+ case SimpleFilter.GTE:
+ try {
+ return ((Comparable) lhs).compareTo(rhs) >= 0;
+ } catch (Exception ex) {
+ return false;
+ }
+ case SimpleFilter.LTE:
+ try {
+ return ((Comparable) lhs).compareTo(rhs) <= 0;
+ } catch (Exception ex) {
+ return false;
+ }
+ case SimpleFilter.APPROX:
+ return compareApproximate(lhs, rhs);
+ case SimpleFilter.SUBSTRING:
+ return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
+ default:
+ throw new RuntimeException("Unknown comparison operator: " + op);
+ }
+ }
+
+ // If the LHS is not a comparable or boolean, check if it is an
+ // array. If so, convert it to a list so we can treat it as a
+ // collection.
+ if (lhs.getClass().isArray()) {
+ lhs = convertArrayToList(lhs);
+ }
+
+ // If LHS is a collection, then call compare() on each element
+ // of the collection until a match is found.
+ if (lhs instanceof Collection) {
+ for (Object o : (Collection) lhs) {
+ if (compare(o, rhsUnknown, op)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Spec says SUBSTRING is false for all types other than string.
+ if (op == SimpleFilter.SUBSTRING) {
+ return false;
+ }
+
+ // Since we cannot identify the LHS type, then we can only perform
+ // equality comparison.
+ try {
+ return lhs.equals(coerceType(lhs, (String) rhsUnknown));
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ private static boolean compareApproximate(Object lhs, Object rhs) {
+ if (rhs instanceof String) {
+ return removeWhitespace((String) lhs)
+ .equalsIgnoreCase(removeWhitespace((String) rhs));
+ } else if (rhs instanceof Character) {
+ return Character.toLowerCase((Character) lhs)
+ == Character.toLowerCase((Character) rhs);
+ }
+ return lhs.equals(rhs);
+ }
+
+ private static String removeWhitespace(String s) {
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ if (!Character.isWhitespace(s.charAt(i))) {
+ sb.append(s.charAt(i));
+ }
+ }
+ return sb.toString();
+ }
+
+ private static Object coerceType(Object lhs, String rhsString) throws Exception {
+ // If the LHS expects a string, then we can just return
+ // the RHS since it is a string.
+ if (lhs.getClass() == rhsString.getClass()) {
+ return rhsString;
+ }
+
+ // Try to convert the RHS type to the LHS type by using
+ // the string constructor of the LHS class, if it has one.
+ Object rhs;
+ try {
+ if (lhs instanceof Version) {
+ rhs = VersionTable.getVersion(rhsString, false);
+ } else
+ // The Character class is a special case, since its constructor
+ // does not take a string, so handle it separately.
+ if (lhs instanceof Character) {
+ rhs = rhsString.charAt(0);
+ } else {
+ // Spec says we should trim number types.
+ if ((lhs instanceof Number) || (lhs instanceof Boolean)) {
+ rhsString = rhsString.trim();
+ }
+ Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
+ ctor.setAccessible(true);
+ rhs = ctor.newInstance(rhsString);
+ }
+ } catch (Exception ex) {
+ throw new Exception(
+ "Could not instantiate class "
+ + lhs.getClass().getName()
+ + " from string constructor with argument '"
+ + rhsString + "' because " + ex
+ );
+ }
+
+ return rhs;
+ }
+
+ /**
+ * This is an ugly utility method to convert an array of primitives
+ * to an array of primitive wrapper objects. This method simplifies
+ * processing LDAP filters since the special case of primitive arrays
+ * can be ignored.
+ *
+ * @param array An array of primitive types.
+ * @return An corresponding array using pritive wrapper objects.
+ */
+ private static List<Object> convertArrayToList(Object array) {
+ int len = Array.getLength(array);
+ List<Object> list = new ArrayList<>(len);
+ for (int i = 0; i < len; i++) {
+ list.add(Array.get(array, i));
+ }
+ return list;
+ }
+}
Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
==============================================================================
--- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java (original)
+++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java Fri Apr 20 09:23:35 2018
@@ -16,6 +16,8 @@
*/
package org.apache.felix.utils.resource;
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
@@ -27,26 +29,19 @@ import java.util.Map;
* Implementation of the OSGi Requirement interface.
*/
public class RequirementImpl extends AbstractCapabilityRequirement implements Requirement {
- /**
- * Create a requirement that is not associated with a resource.
- * @param res The resource associated with the requirement.
- * @param ns The namespace of the requirement.
- * @param attrs The attributes of the requirement.
- * @param dirs The directives of the requirement.
- */
- public RequirementImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs) {
- this(ns, attrs, dirs, null);
- }
+
+ private final SimpleFilter filter;
+ private final boolean optional;
/**
* Create a requirement.
+ * @param res The resource associated with the requirement.
* @param ns The namespace of the requirement.
* @param attrs The attributes of the requirement.
* @param dirs The directives of the requirement.
- * @param res The resource associated with the requirement.
*/
- public RequirementImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs, Resource res) {
- super(ns, attrs, dirs, res);
+ public RequirementImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
+ this(res, ns, dirs, attrs, null);
}
/**
@@ -54,14 +49,16 @@ public class RequirementImpl extends Abs
*
* This is a convenience method that creates a requirement with
* an empty attributes map and a single 'filter' directive.
+ * @param res The resource associated with the requirement.
* @param ns The namespace for the requirement.
* @param filter The filter.
*/
- public RequirementImpl(String ns, String filter)
+ public RequirementImpl(Resource res, String ns, String filter)
{
- this(ns, Collections.<String, Object>emptyMap(),
- filter == null ? Collections.<String, String> emptyMap() :
- Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter));
+ this(res, ns,
+ filter == null ? Collections.<String, String>emptyMap() :
+ Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter),
+ null);
}
/**
@@ -71,6 +68,26 @@ public class RequirementImpl extends Abs
* @param resource The resource to be associated with the requirement
*/
public RequirementImpl(Resource resource, Requirement requirement) {
- this(requirement.getNamespace(), requirement.getAttributes(), requirement.getDirectives(), resource);
+ this(resource, requirement.getNamespace(), requirement.getDirectives(), requirement.getAttributes());
+ }
+
+ public RequirementImpl(Resource resource, String path, Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter sf) {
+ super(resource, path, dirs, attrs);
+ this.filter = sf != null ? sf : SimpleFilter.convert(attributes);
+ // Find resolution import directives.
+ this.optional = Constants.RESOLUTION_OPTIONAL.equals(directives.get(Constants.RESOLUTION_DIRECTIVE));
+ }
+
+ public boolean matches(Capability cap) {
+ return CapabilitySet.matches(cap, getFilter());
}
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public SimpleFilter getFilter() {
+ return filter;
+ }
+
}
Re: svn commit: r1829639 [1/3] - in /felix/trunk: bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/
bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/ utils/
utils/src/main/java/org/apache/felix/utils/collections/ u...
Posted by David Bosschaert <da...@gmail.com>.
Hi all,
This commit disallows the Resource on a Requirement from being null. It
also inverted the tests that were there around this...
The OSGi spec clearly states that Resources on Requirements can be null,
see
https://osgi.org/javadoc/r6/core/org/osgi/resource/Requirement.html#getResource()
so I have changed this behaviour back to allow null Resources.
Best regards,
David
On 20 April 2018 at 10:43, Karl Pauls <ka...@gmail.com> wrote:
> This is great but please try to reference JIRA issues - if we do this
> kind of bigger things we should have a JIRA so that we don't get
> confused in the future.
>
> regards,
>
> Karl
>
>
> On Fri, Apr 20, 2018 at 11:23 AM, <gn...@apache.org> wrote:
> > Author: gnodet
> > Date: Fri Apr 20 09:23:35 2018
> > New Revision: 1829639
> >
> > URL: http://svn.apache.org/viewvc?rev=1829639&view=rev
> > Log:
> > Provide optimized resource / filter / capability / requirement /
> capability set
> >
> > Added:
> > felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/LazyStringMap.java
> > felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/LazyStringMapTest.java
> > - copied, changed from r1829637, felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> collections/StringArrayMap.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/CapabilitySet.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/ResourceBuilder.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/ResourceImpl.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/SimpleFilter.java
> > felix/trunk/utils/src/test/java/org/apache/felix/utils/collections/
> > felix/trunk/utils/src/test/java/org/apache/felix/utils/collections/
> StringArrayMapTest.java
> > felix/trunk/utils/src/test/java/org/apache/felix/utils/
> resource/SimpleFilterTest.java
> > Removed:
> > felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/LazyHashMap.java
> > felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/LazyHashMapTest.java
> > Modified:
> > felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/FelixResourceAdapter.java
> > felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImpl.java
> > felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImplTest.java
> > felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
> > felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRequirementAdapterTest.java
> > felix/trunk/utils/pom.xml
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/
> AbstractCapabilityRequirement.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/CapabilityImpl.java
> > felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/RequirementImpl.java
> > felix/trunk/utils/src/test/java/org/apache/felix/utils/
> resource/CapabilityImplTest.java
> > felix/trunk/utils/src/test/java/org/apache/felix/utils/
> resource/RequirementImplTest.java
> >
> > Modified: felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/FelixResourceAdapter.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/main/java/org/apache/felix/bundlerepository/impl/
> FelixResourceAdapter.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/FelixResourceAdapter.java (original)
> > +++ felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/FelixResourceAdapter.java Fri Apr 20 09:23:35
> 2018
> > @@ -42,12 +42,12 @@ public class FelixResourceAdapter implem
> >
> > if (namespace == null || namespace.equals(
> IdentityNamespace.IDENTITY_NAMESPACE))
> > {
> > - CapabilityImpl c = OSGiRepositoryImpl.
> newOSGiIdentityCapability(resource, this);
> > + CapabilityImpl c = OSGiRepositoryImpl.
> newOSGiIdentityCapability(this, resource);
> > result.add(c);
> > }
> > if (namespace == null || namespace.equals(
> ContentNamespace.CONTENT_NAMESPACE))
> > {
> > - CapabilityImpl c = OSGiRepositoryImpl.
> newOSGiContentCapability(resource, this);
> > + CapabilityImpl c = OSGiRepositoryImpl.newOSGiContentCapability(this,
> resource);
> > result.add(c);
> > }
> >
> >
> > Added: felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/LazyStringMap.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/main/java/org/apache/felix/bundlerepository/impl/
> LazyStringMap.java?rev=1829639&view=auto
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/LazyStringMap.java (added)
> > +++ felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/LazyStringMap.java Fri Apr 20 09:23:35 2018
> > @@ -0,0 +1,67 @@
> > +/*
> > + * 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.bundlerepository.impl;
> > +
> > +import java.util.Map;
> > +
> > +import org.apache.felix.utils.collections.StringArrayMap;
> > +
> > +/**
> > + * A map that can delay the computation of certain values up until the
> moment that they
> > + * are actually needed. Useful for expensive to compute values such as
> the SHA-256.
> > + * This map does <b>not</b> support {@code null} values.
> > + */
> > +@SuppressWarnings("serial")
> > +public class LazyStringMap<V> extends StringArrayMap<V>
> > +{
> > + public LazyStringMap(Map<String, ? extends V> map) {
> > + super(map);
> > + }
> > +
> > + public LazyStringMap() {
> > + }
> > +
> > + public LazyStringMap(int capacity) {
> > + super(capacity);
> > + }
> > +
> > + @Override
> > + @SuppressWarnings("unchecked")
> > + public V get(Object key)
> > + {
> > + V val = super.get(key);
> > + if (val instanceof LazyValue) {
> > + val = ((LazyValue<V>) val).compute();
> > + if (val == null) {
> > + throw new NullPointerException("Lazy computed values
> may not be null");
> > + }
> > + put((String) key, val);
> > + }
> > + return val;
> > + }
> > +
> > + public void putLazy(String key, LazyValue<V> lazy) {
> > + super.doPut(key, lazy);
> > + }
> > +
> > + public interface LazyValue<V>
> > + {
> > + V compute();
> > + }
> > +}
> >
> > Modified: felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImpl.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/main/java/org/apache/felix/bundlerepository/impl/
> OSGiRepositoryImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImpl.java (original)
> > +++ felix/trunk/bundlerepository/src/main/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImpl.java Fri Apr 20 09:23:35
> 2018
> > @@ -30,10 +30,9 @@ import java.util.Collections;
> > import java.util.HashMap;
> > import java.util.List;
> > import java.util.Map;
> > -import java.util.concurrent.Callable;
> >
> > import org.apache.felix.bundlerepository.RepositoryAdmin;
> > -import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
> > +import org.apache.felix.bundlerepository.Resource;
> > import org.apache.felix.utils.resource.CapabilityImpl;
> > import org.osgi.framework.Filter;
> > import org.osgi.framework.FrameworkUtil;
> > @@ -41,7 +40,6 @@ import org.osgi.framework.namespace.Iden
> > import org.osgi.resource.Capability;
> > import org.osgi.resource.Namespace;
> > import org.osgi.resource.Requirement;
> > -import org.osgi.resource.Resource;
> > import org.osgi.service.repository.ContentNamespace;
> > import org.osgi.service.repository.Repository;
> >
> > @@ -120,8 +118,7 @@ class OSGiRepositoryImpl implements Repo
> > caps.add(idCap);
> > }
> >
> > - static CapabilityImpl newOSGiIdentityCapability(org.
> apache.felix.bundlerepository.Resource res,
> > - org.osgi.resource.Resource targetResource)
> > + static CapabilityImpl newOSGiIdentityCapability(org.osgi.resource.Resource
> or, org.apache.felix.bundlerepository.Resource res)
> > {
> > @SuppressWarnings("unchecked")
> > Map<String, Object> idAttrs = new HashMap<String,
> Object>(res.getProperties());
> > @@ -132,32 +129,34 @@ class OSGiRepositoryImpl implements Repo
> > if (idAttrs.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE)
> == null)
> > idAttrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
> IdentityNamespace.TYPE_BUNDLE);
> >
> > - return new CapabilityImpl(IdentityNamespace.IDENTITY_NAMESPACE,
> idAttrs, Collections.<String, String> emptyMap(), targetResource);
> > + return new CapabilityImpl(or, IdentityNamespace.IDENTITY_NAMESPACE,
> Collections.<String, String> emptyMap(), idAttrs);
> > }
> >
> > - static CapabilityImpl newOSGiContentCapability(org.
> apache.felix.bundlerepository.Resource resource,
> > - org.osgi.resource.Resource targetResource)
> > + static CapabilityImpl newOSGiContentCapability(org.osgi.resource.Resource
> or, Resource resource)
> > {
> > final String uri = resource.getURI();
> > - LazyValue<String, Object> lazyValue =
> > - new LazyValue<String, Object>(ContentNamespace.CONTENT_NAMESPACE,
> new Callable<Object>()
> > - {
> > - public Object call() throws Exception
> > - {
> > - // This is expensive to do, so only compute it when
> actually obtained...
> > + LazyStringMap.LazyValue<String> content = new
> LazyStringMap.LazyValue<String>() {
> > + public String compute() {
> > + // This is expensive to do, so only compute it when
> actually obtained...
> > + try {
> > return OSGiRepositoryImpl.getSHA256(uri);
> > + } catch (IOException e) {
> > + throw new RuntimeException(e);
> > + } catch (NoSuchAlgorithmException e) {
> > + throw new RuntimeException(e);
> > }
> > - });
> > -
> > + }
> > + };
> > Object mime = resource.getProperties().get("mime");
> > if (mime == null)
> > mime = "application/vnd.osgi.bundle";
> >
> > - Map<String, Object> contentAttrs = new LazyHashMap<String,
> Object>(Collections.singleton(lazyValue));
> > + Map<String, Object> contentAttrs = new LazyStringMap<Object>(4);
> > contentAttrs.put(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE,
> mime);
> > contentAttrs.put(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE,
> resource.getSize());
> > contentAttrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE,
> uri);
> > - return new ContentCapabilityImpl(contentAttrs, targetResource);
> > + contentAttrs.put(ContentNamespace.CONTENT_NAMESPACE, content);
> > + return new CapabilityImpl(or, ContentNamespace.CONTENT_NAMESPACE,
> Collections.<String, String> emptyMap(), contentAttrs);
> > }
> >
> > static String getSHA256(String uri) throws IOException,
> NoSuchAlgorithmException // TODO find a good place for this
> > @@ -183,19 +182,4 @@ class OSGiRepositoryImpl implements Repo
> > return sb.toString();
> > }
> >
> > - // This capability variant does not take a private copy of the
> capabilities so that it
> > - // can lazily compute the content hash.
> > - private static class ContentCapabilityImpl extends CapabilityImpl
> implements Capability {
> > - private final Map<String, Object> contentAttributes;
> > -
> > - public ContentCapabilityImpl(Map<String, Object> contentAttrs,
> Resource targetResource) {
> > - super(ContentNamespace.CONTENT_NAMESPACE, null, null,
> targetResource);
> > - contentAttributes = Collections.unmodifiableMap(
> contentAttrs);
> > - }
> > -
> > - @Override
> > - public Map<String, Object> getAttributes() {
> > - return contentAttributes;
> > - }
> > - }
> > }
> >
> > Copied: felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/LazyStringMapTest.java (from r1829637,
> felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/LazyHashMapTest.java)
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/
> LazyStringMapTest.java?p2=felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/
> LazyStringMapTest.java&p1=felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/
> LazyHashMapTest.java&r1=1829637&r2=1829639&rev=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/LazyHashMapTest.java (original)
> > +++ felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/LazyStringMapTest.java Fri Apr 20 09:23:35
> 2018
> > @@ -21,41 +21,39 @@ package org.apache.felix.bundlerepositor
> > import java.util.ArrayList;
> > import java.util.Collection;
> > import java.util.HashMap;
> > +import java.util.Map;
> > import java.util.concurrent.Callable;
> > import java.util.concurrent.atomic.AtomicInteger;
> >
> > import junit.framework.TestCase;
> > +import org.apache.felix.bundlerepository.impl.LazyStringMap.LazyValue;
> >
> > -import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
> > -
> > -public class LazyHashMapTest extends TestCase
> > +public class LazyStringMapTest extends TestCase
> > {
> > public void testLazyHashMap() {
> > final AtomicInteger lv1Computed = new AtomicInteger(0);
> > - LazyValue<String, Long> lv1 = new LazyValue<String, Long>("42",
> new Callable<Long>()
> > - {
> > - public Long call() throws Exception
> > - {
> > + LazyValue<Long> lv1 = new LazyValue<Long>() {
> > + public Long compute() {
> > lv1Computed.incrementAndGet();
> > return 24L;
> > }
> > - });
> > + };
> >
> > final AtomicInteger lv2Computed = new AtomicInteger(0);
> > - LazyValue<String, Long> lv2 = new LazyValue<String,
> Long>("zero", new Callable<Long>()
> > - {
> > - public Long call() throws Exception
> > - {
> > + LazyValue<Long> lv2 = new LazyValue<Long>() {
> > + public Long compute() {
> > lv2Computed.incrementAndGet();
> > return 0L;
> > }
> > - });
> > + };
> >
> > - Collection<LazyValue<String, Long>> lazyValues = new
> ArrayList<LazyHashMap.LazyValue<String,Long>>();
> > + Collection<LazyValue<Long>> lazyValues = new
> ArrayList<LazyValue<Long>>();
> > lazyValues.add(lv1);
> > lazyValues.add(lv2);
> > - HashMap<String, Long> lhm = new LazyHashMap<String,
> Long>(lazyValues);
> > + LazyStringMap<Long> lhm = new LazyStringMap<Long>();
> > lhm.put("1", 2L);
> > + lhm.putLazy("42", lv1);
> > + lhm.putLazy("zero", lv2);
> >
> > assertEquals(new Long(2L), lhm.get("1"));
> > assertEquals("No computation should have happened yet", 0,
> lv1Computed.get());
> >
> > Modified: felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImplTest.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/
> OSGiRepositoryImplTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImplTest.java (original)
> > +++ felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryImplTest.java Fri Apr 20
> 09:23:35 2018
> > @@ -58,7 +58,7 @@ public class OSGiRepositoryImplTest exte
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("osgi.identity", null);
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> "osgi.identity", null);
> >
> > Map<Requirement, Collection<Capability>> result =
> repo.findProviders(Collections.singleton(req));
> > assertEquals(1, result.size());
> > @@ -117,7 +117,7 @@ public class OSGiRepositoryImplTest exte
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("osgi.identity",
> "(osgi.identity=test_file_2)");
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> "osgi.identity", "(osgi.identity=test_file_2)");
> >
> > Map<Requirement, Collection<Capability>> result =
> repo.findProviders(Collections.singleton(req));
> > assertEquals(1, result.size());
> > @@ -137,7 +137,7 @@ public class OSGiRepositoryImplTest exte
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("foo",
> "(someKey=someOtherVal)");
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> "foo", "(someKey=someOtherVal)");
> >
> > Map<Requirement, Collection<Capability>> result =
> repo.findProviders(Collections.singleton(req));
> > assertEquals(1, result.size());
> > @@ -157,7 +157,7 @@ public class OSGiRepositoryImplTest exte
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("foo", "(someKey=*)");
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> "foo", "(someKey=*)");
> >
> > Map<Requirement, Collection<Capability>> result =
> repo.findProviders(Collections.singleton(req));
> > assertEquals(1, result.size());
> > @@ -181,7 +181,7 @@ public class OSGiRepositoryImplTest exte
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("osgi.wiring.package",
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> "osgi.wiring.package",
> > "(&(osgi.wiring.package=org.apache.commons.logging)(
> version>=1.0.1)(!(version>=2)))");
> >
> > Map<Requirement, Collection<Capability>> result =
> repo.findProviders(Collections.singleton(req));
> > @@ -230,12 +230,12 @@ public class OSGiRepositoryImplTest exte
> >
> > BundleRevision br = Mockito.mock(BundleRevision.class);
> > Mockito.when(sysBundle.adapt(BundleRevision.class)).
> thenReturn(br);
> > - Capability cap1 = new CapabilityImpl("some.system.cap",
> > - Collections.<String, Object>singletonMap("sys.cap",
> "something"),
> > - Collections.singletonMap("x", "y"));
> > - Capability cap2 = new CapabilityImpl("some.system.cap",
> > - Collections.<String, Object>singletonMap("sys.cap",
> "somethingelse"),
> > - Collections.<String, String>emptyMap());
> > + Capability cap1 = new CapabilityImpl(Mockito.mock(Resource.class),
> "some.system.cap",
> > + Collections.singletonMap("x", "y"),
> > + Collections.<String, Object>singletonMap("sys.cap",
> "something"));
> > + Capability cap2 = new CapabilityImpl(Mockito.mock(Resource.class),
> "some.system.cap",
> > + Collections.<String, String>emptyMap(),
> > + Collections.<String, Object>singletonMap("sys.cap",
> "somethingelse"));
> > Mockito.when(br.getCapabilities(null)).thenReturn(Arrays.asList(cap1,
> cap2));
> >
> > BundleContext bc = Mockito.mock(BundleContext.class);
> >
> > Modified: felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/
> OSGiRepositoryXMLTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryXMLTest.java (original)
> > +++ felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRepositoryXMLTest.java Fri Apr 20
> 09:23:35 2018
> > @@ -51,7 +51,8 @@ public class OSGiRepositoryXMLTest exten
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("osgi.identity",
> > + Requirement req = new RequirementImpl(Mockito.mock(
> Resource.class),
> > + "osgi.identity",
> > "(osgi.identity=cdi-subsystem)");
> >
> > Map<Requirement, Collection<Capability>> result = repo
> > @@ -126,7 +127,8 @@ public class OSGiRepositoryXMLTest exten
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("osgi.identity",
> > + Requirement req = new RequirementImpl(Mockito.mock(
> Resource.class),
> > + "osgi.identity",
> > "(license=http://www.opensource.org/licenses/
> mytestlicense)");
> >
> > Map<Requirement, Collection<Capability>> result = repo
> > @@ -145,7 +147,7 @@ public class OSGiRepositoryXMLTest exten
> > repoAdmin.addRepository(url);
> >
> > Repository repo = new OSGiRepositoryImpl(repoAdmin);
> > - Requirement req = new RequirementImpl("foo", "(bar=toast)");
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),"foo",
> "(bar=toast)");
> >
> > Map<Requirement, Collection<Capability>> result = repo
> > .findProviders(Collections.singleton(req));
> >
> > Modified: felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRequirementAdapterTest.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/
> src/test/java/org/apache/felix/bundlerepository/impl/
> OSGiRequirementAdapterTest.java?rev=1829639&r1=1829638&
> r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRequirementAdapterTest.java (original)
> > +++ felix/trunk/bundlerepository/src/test/java/org/apache/
> felix/bundlerepository/impl/OSGiRequirementAdapterTest.java Fri Apr 20
> 09:23:35 2018
> > @@ -24,7 +24,9 @@ import java.util.Map;
> > import junit.framework.TestCase;
> >
> > import org.apache.felix.utils.resource.RequirementImpl;
> > +import org.mockito.Mockito;
> > import org.osgi.resource.Requirement;
> > +import org.osgi.resource.Resource;
> >
> > public class OSGiRequirementAdapterTest extends TestCase
> > {
> > @@ -38,7 +40,7 @@ public class OSGiRequirementAdapterTest
> > dirs.put("resolution", "optional");
> > dirs.put("test", "test");
> >
> > - Requirement req = new RequirementImpl("osgi.wiring.package",
> attrs, dirs);
> > + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> "osgi.wiring.package", dirs, attrs);
> > OSGiRequirementAdapter adapter = new
> OSGiRequirementAdapter(req);
> >
> > assertEquals("(package=y)", adapter.getFilter());
> >
> > Modified: felix/trunk/utils/pom.xml
> > URL: http://svn.apache.org/viewvc/felix/trunk/utils/pom.xml?rev=
> 1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/utils/pom.xml (original)
> > +++ felix/trunk/utils/pom.xml Fri Apr 20 09:23:35 2018
> > @@ -36,17 +36,21 @@
> > <url>http://svn.apache.org/repos/asf/felix/utils</url>
> > </scm>
> >
> > + <properties>
> > + <felix.java.version>7</felix.java.version>
> > + </properties>
> > +
> > <dependencies>
> > <dependency>
> > <groupId>org.osgi</groupId>
> > - <artifactId>org.osgi.core</artifactId>
> > + <artifactId>osgi.core</artifactId>
> > <version>5.0.0</version>
> > <scope>provided</scope>
> > </dependency>
> > <dependency>
> > <groupId>org.osgi</groupId>
> > - <artifactId>org.osgi.compendium</artifactId>
> > - <version>4.2.0</version>
> > + <artifactId>osgi.cmpn</artifactId>
> > + <version>5.0.0</version>
> > <scope>provided</scope>
> > </dependency>
> > </dependencies>
> > @@ -62,6 +66,13 @@
> > </excludes>
> > </configuration>
> > </plugin>
> > + <plugin>
> > + <artifactId>maven-compiler-plugin</artifactId>
> > + <configuration>
> > + <source>1.7</source>
> > + <target>1.7</target>
> > + </configuration>
> > + </plugin>
> > </plugins>
> > </build>
> > </project>
> >
> > Added: felix/trunk/utils/src/main/java/org/apache/felix/utils/
> collections/StringArrayMap.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/
> java/org/apache/felix/utils/collections/StringArrayMap.
> java?rev=1829639&view=auto
> > ============================================================
> ==================
> > --- felix/trunk/utils/src/main/java/org/apache/felix/utils/
> collections/StringArrayMap.java (added)
> > +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/
> collections/StringArrayMap.java Fri Apr 20 09:23:35 2018
> > @@ -0,0 +1,339 @@
> > +/*
> > + * 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.utils.collections;
> > +
> > +import java.util.AbstractCollection;
> > +import java.util.AbstractSet;
> > +import java.util.Arrays;
> > +import java.util.Collection;
> > +import java.util.Collections;
> > +import java.util.Iterator;
> > +import java.util.Map;
> > +import java.util.NoSuchElementException;
> > +import java.util.Objects;
> > +import java.util.Set;
> > +
> > +public class StringArrayMap<V> implements Map<String, V> {
> > +
> > + protected Object[] table;
> > + protected int size;
> > +
> > + public static <T> Map<String, T> reduceMemory(Map<String, T> map) {
> > + if (map == null) {
> > + return Collections.emptyMap();
> > + }
> > + switch (map.size()) {
> > + case 0:
> > + return Collections.emptyMap();
> > + case 1:
> > + Entry<String, T> e = map.entrySet().iterator().next();
> > + return Collections.singletonMap(e.getKey().intern(),
> e.getValue());
> > + default:
> > + if (map instanceof StringArrayMap) {
> > + @SuppressWarnings("unchecked")
> > + StringArrayMap<T> m = (StringArrayMap) map;
> > + if (m.size == m.table.length / 2) {
> > + return map;
> > + }
> > + }
> > + return new StringArrayMap<>(map);
> > + }
> > + }
> > +
> > + public StringArrayMap(Map<String, ? extends V> map) {
> > + if (map instanceof StringArrayMap) {
> > + size = ((StringArrayMap) map).size;
> > + table = Arrays.copyOf(((StringArrayMap) map).table, size *
> 2);
> > + } else {
> > + size = 0;
> > + table = new Object[map.size() * 2];
> > + for (Entry<String, ? extends V> e : map.entrySet()) {
> > + int i = size++ << 1;
> > + table[i++] = e.getKey().intern();
> > + table[i] = e.getValue();
> > + }
> > + }
> > + }
> > +
> > + public StringArrayMap() {
> > + this(32);
> > + }
> > +
> > + public StringArrayMap(int capacity) {
> > + table = new Object[capacity * 2];
> > + size = 0;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + public V get(Object key) {
> > + String k = ((String) key).intern();
> > + for (int i = 0, l = size << 1; i < l; i += 2) {
> > + if (k == table[i]) {
> > + return (V) table[i + 1];
> > + }
> > + }
> > + return null;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + public V put(String key, V value) {
> > + return (V) doPut(key, value);
> > + }
> > +
> > + protected Object doPut(String key, Object value) {
> > + key = key.intern();
> > + for (int i = 0, l = size << 1; i < l; i += 2) {
> > + if (key == table[i]) {
> > + Object old = table[i + 1];
> > + table[i + 1] = value;
> > + return old;
> > + }
> > + }
> > + if (table.length == 0) {
> > + table = new Object[2];
> > + } else if (size * 2 == table.length) {
> > + Object[] n = new Object[table.length * 2];
> > + System.arraycopy(table, 0, n, 0, table.length);
> > + table = n;
> > + }
> > + int i = size++ << 1;
> > + table[i++] = key;
> > + table[i] = value;
> > + return null;
> > + }
> > +
> > + public Set<String> keySet() {
> > + return new AbstractSet<String>() {
> > + @Override
> > + public Iterator<String> iterator() {
> > + return new Iterator<String>() {
> > + int index = 0;
> > +
> > + @Override
> > + public boolean hasNext() {
> > + return index < size;
> > + }
> > +
> > + @Override
> > + public String next() {
> > + if (index >= size) {
> > + throw new NoSuchElementException();
> > + }
> > + return (String) table[(index++ << 1)];
> > + }
> > +
> > + public void remove() {
> > + throw new UnsupportedOperationException(
> "remove");
> > + }
> > + };
> > + }
> > +
> > + @Override
> > + public int size() {
> > + return size;
> > + }
> > + };
> > + }
> > +
> > + public Collection<V> values() {
> > + return new AbstractCollection<V>() {
> > + @Override
> > + public Iterator<V> iterator() {
> > + return new Iterator<V>() {
> > + int index = 0;
> > +
> > + public boolean hasNext() {
> > + return index < size;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + public V next() {
> > + if (index >= size) {
> > + throw new NoSuchElementException();
> > + }
> > + return (V) table[(index++ << 1) + 1];
> > + }
> > +
> > + public void remove() {
> > + throw new UnsupportedOperationException(
> "remove");
> > + }
> > + };
> > + }
> > +
> > + @Override
> > + public int size() {
> > + return size;
> > + }
> > + };
> > + }
> > +
> > + public Set<Entry<String, V>> entrySet() {
> > + return new AbstractSet<Entry<String, V>>() {
> > + @Override
> > + public Iterator<Entry<String, V>> iterator() {
> > + return new Iterator<Entry<String, V>>() {
> > + int index = 0;
> > +
> > + public boolean hasNext() {
> > + return index < size;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + public Entry<String, V> next() {
> > + if (index >= size) {
> > + throw new NoSuchElementException();
> > + }
> > + final int i = index << 1;
> > + index++;
> > + return new Entry<String, V>() {
> > +
> > + public String getKey() {
> > + return (String) table[i];
> > + }
> > +
> > + public V getValue() {
> > + return (V) table[i + 1];
> > + }
> > +
> > + public V setValue(V value) {
> > + throw new UnsupportedOperationException(
> );
> > + }
> > + };
> > + }
> > +
> > + public void remove() {
> > + throw new UnsupportedOperationException(
> "remove");
> > + }
> > + };
> > + }
> > +
> > + @Override
> > + public int size() {
> > + return size;
> > + }
> > + };
> > + }
> > +
> > + public int size() {
> > + return size;
> > + }
> > +
> > + public boolean isEmpty() {
> > + return size == 0;
> > + }
> > +
> > + public boolean containsKey(Object key) {
> > + String k = ((String) key).intern();
> > + for (int i = 0, l = size * 2; i < l; i += 2) {
> > + if (table[i] == k) {
> > + return true;
> > + }
> > + }
> > + return false;
> > + }
> > +
> > + public boolean containsValue(Object value) {
> > + for (int i = 0, l = size * 2; i < l; i += 2) {
> > + if (Objects.equals(table[i + 1], value)) {
> > + return true;
> > + }
> > + }
> > + return false;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + public V remove(Object key) {
> > + String k = ((String) key).intern();
> > + for (int i = 0, l = size * 2; i < l; i += 2) {
> > + if (table[i] == k) {
> > + Object v = table[i + 1];
> > + if (i < l - 2) {
> > + System.arraycopy(table, i + 2, table, i, l - 2 - i);
> > + }
> > + table[l - 1] = null;
> > + table[l - 2] = null;
> > + size--;
> > + return (V) v;
> > + }
> > + }
> > + return null;
> > + }
> > +
> > + public void putAll(Map<? extends String, ? extends V> m) {
> > + for (Entry<? extends String, ? extends V> e : m.entrySet()) {
> > + put(e.getKey(), e.getValue());
> > + }
> > + }
> > +
> > + public void clear() {
> > + size = 0;
> > + Arrays.fill(table, null);
> > + }
> > +
> > + public int hashCode() {
> > + int result = 1;
> > + for (int i = 0; i < size * 2; i++)
> > + result = 31 * result + (table[i] == null ? 0 :
> table[i].hashCode());
> > + return result;
> > + }
> > +
> > + public boolean equals(Object o) {
> > + if (o == this)
> > + return true;
> > + if (!(o instanceof Map))
> > + return false;
> > + Map<?,?> m = (Map<?,?>) o;
> > + if (m.size() != size())
> > + return false;
> > + try {
> > + for (int i = 0, l = size * 2; i < l; i += 2) {
> > + Object key = table[i];
> > + Object value = table[i+1];
> > + if (value == null) {
> > + if (!(m.get(key)==null && m.containsKey(key)))
> > + return false;
> > + } else {
> > + if (!value.equals(m.get(key)))
> > + return false;
> > + }
> > + }
> > + } catch (ClassCastException | NullPointerException unused) {
> > + return false;
> > + }
> > + return true;
> > + }
> > +
> > + public String toString() {
> > + if (size == 0)
> > + return "{}";
> > +
> > + StringBuilder sb = new StringBuilder();
> > + sb.append('{');
> > + for (int i = 0, l = size * 2; i < l; i += 2) {
> > + if (i > 0) {
> > + sb.append(',').append(' ');
> > + }
> > + sb.append(table[i]);
> > + sb.append('=');
> > + sb.append(table[i+1] == this ? "(this Map)" : table[i+1]);
> > + }
> > + return sb.append('}').toString();
> > + }
> > +
> > +}
> >
> > Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/AbstractCapabilityRequirement.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/
> java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.
> java?rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/
> AbstractCapabilityRequirement.java (original)
> > +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/
> AbstractCapabilityRequirement.java Fri Apr 20 09:23:35 2018
> > @@ -16,38 +16,32 @@
> > */
> > package org.apache.felix.utils.resource;
> >
> > +import org.apache.felix.utils.collections.StringArrayMap;
> > +import org.osgi.framework.Version;
> > import org.osgi.resource.Resource;
> >
> > -import java.util.Collections;
> > -import java.util.HashMap;
> > import java.util.Map;
> > +import java.util.Objects;
> >
> > abstract class AbstractCapabilityRequirement {
> >
> > - /** The namespace. Required. */
> > - private final String namespace;
> > + /** The resource. Required. */
> > + protected final Resource resource;
> >
> > - /** Optional resource. */
> > - private final Resource resource;
> > + /** The namespace. Required. */
> > + protected final String namespace;
> >
> > /** Optional attributes. Never null. */
> > - private final Map<String, Object> attributes;
> > + protected final Map<String, String> directives;
> >
> > /** Optional attributes. Never null. */
> > - private final Map<String, String> directives;
> > + protected final Map<String, Object> attributes;
> >
> > - AbstractCapabilityRequirement(final String ns, final Map<String,
> Object> attrs, final Map<String, String> dirs, final Resource res) {
> > - if ( ns == null ) {
> > - throw new IllegalArgumentException("Namespace must not be
> null.");
> > - }
> > - namespace = ns;
> > - attributes = attrs == null
> > - ? Collections.<String, Object>emptyMap()
> > - : Collections.unmodifiableMap(new HashMap<String,
> Object>(attrs));
> > - directives = dirs == null
> > - ? Collections.<String,String>emptyMap()
> > - : Collections.unmodifiableMap(new
> HashMap<String,String>(dirs));
> > - resource = res;
> > + AbstractCapabilityRequirement(final Resource res, final String ns,
> final Map<String, String> dirs, final Map<String, Object> attrs) {
> > + resource = Objects.requireNonNull(res, "Resource must not be
> null.");
> > + namespace = Objects.requireNonNull(ns, "Namespace must not be
> null.");
> > + directives = StringArrayMap.reduceMemory(dirs);
> > + attributes = StringArrayMap.reduceMemory(attrs);
> > }
> >
> > /**
> > @@ -82,45 +76,98 @@ abstract class AbstractCapabilityRequire
> > return resource;
> > }
> >
> > - @Override
> > - public int hashCode() {
> > - final int prime = 31;
> > - int result = 1;
> > - result = prime * result + attributes.hashCode();
> > - result = prime * result + directives.hashCode();
> > - result = prime * result + namespace.hashCode();
> > -
> > - if (resource != null)
> > - result = prime * result + resource.hashCode();
> >
> > - return result;
> > + @Override
> > + public boolean equals(Object o) {
> > + if (this == o) return true;
> > + if (o == null || getClass() != o.getClass()) return false;
> > + AbstractCapabilityRequirement that = (AbstractCapabilityRequirement)
> o;
> > + return Objects.equals(resource, that.resource) &&
> > + Objects.equals(namespace, that.namespace) &&
> > + Objects.equals(attributes, that.attributes) &&
> > + Objects.equals(directives, that.directives);
> > }
> >
> > @Override
> > - public boolean equals(Object obj) {
> > - if (this == obj)
> > - return true;
> > - if (obj == null)
> > - return false;
> > - if (getClass() != obj.getClass())
> > - return false;
> > - AbstractCapabilityRequirement other = (AbstractCapabilityRequirement)
> obj;
> > - if (!namespace.equals(other.namespace))
> > - return false;
> > - if (!attributes.equals(other.attributes))
> > - return false;
> > - if (!directives.equals(other.directives))
> > - return false;
> > - if (resource == null) {
> > - return other.resource == null;
> > - } else {
> > - return resource.equals(other.resource);
> > - }
> > + public int hashCode() {
> > + return Objects.hash(resource, namespace, attributes,
> directives);
> > }
> >
> > @Override
> > public String toString() {
> > - return getClass().getSimpleName() + " [resource=" + resource +
> ", namespace=" + namespace + ", attributes=" + attributes
> > - + ", directives=" + directives + "]";
> > + return toString(getResource(), getNamespace(), getAttributes(),
> getDirectives());
> > + }
> > +
> > + public static String toString(Resource res, String namespace,
> Map<String, Object> attrs, Map<String, String> dirs) {
> > + StringBuilder sb = new StringBuilder();
> > + if (res != null) {
> > + sb.append("[").append(res).append("] ");
> > + }
> > + sb.append(namespace);
> > + for (String key : attrs.keySet()) {
> > + sb.append("; ");
> > + append(sb, key, attrs.get(key), true);
> > + }
> > + for (String key : dirs.keySet()) {
> > + sb.append("; ");
> > + append(sb, key, dirs.get(key), false);
> > + }
> > + return sb.toString();
> > + }
> > +
> > + private static void append(StringBuilder sb, String key, Object
> val, boolean attribute) {
> > + sb.append(key);
> > + if (val instanceof Version) {
> > + sb.append(":Version=");
> > + sb.append(val);
> > + } else if (val instanceof Long) {
> > + sb.append(":Long=");
> > + sb.append(val);
> > + } else if (val instanceof Double) {
> > + sb.append(":Double=");
> > + sb.append(val);
> > + } else if (val instanceof Iterable) {
> > + Iterable<?> it = (Iterable<?>) val;
> > + String scalar = null;
> > + for (Object o : it) {
> > + String ts;
> > + if (o instanceof String) {
> > + ts = "String";
> > + } else if (o instanceof Long) {
> > + ts = "Long";
> > + } else if (o instanceof Double) {
> > + ts = "Double";
> > + } else if (o instanceof Version) {
> > + ts = "Version";
> > + } else {
> > + throw new IllegalArgumentException("Unsupported
> scalar type: " + o);
> > + }
> > + if (scalar == null) {
> > + scalar = ts;
> > + } else if (!scalar.equals(ts)) {
> > + throw new IllegalArgumentException("Unconsistent
> list type for attribute " + key);
> > + }
> > + }
> > + sb.append(":List<").append(scalar).append(">=");
> > + sb.append("\"");
> > + boolean first = true;
> > + for (Object o : it) {
> > + if (first) {
> > + first = false;
> > + } else {
> > + sb.append(",");
> > + }
> > + sb.append(o.toString().replace("\"",
> "\\\"").replace(",", "\\,"));
> > + }
> > + sb.append("\"");
> > + } else {
> > + sb.append(attribute ? "=" : ":=");
> > + String s = val.toString();
> > + if (s.matches("[0-9a-zA-Z_\\-.]*")) {
> > + sb.append(s);
> > + } else {
> > + sb.append("\"").append(s.replace("\"",
> "\\\\")).append("\"");
> > + }
> > + }
> > }
> > }
> >
> > Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/CapabilityImpl.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/
> java/org/apache/felix/utils/resource/CapabilityImpl.java?
> rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java
> (original)
> > +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java
> Fri Apr 20 09:23:35 2018
> > @@ -16,35 +16,50 @@
> > */
> > package org.apache.felix.utils.resource;
> >
> > +import org.osgi.framework.Constants;
> > import org.osgi.resource.Capability;
> > import org.osgi.resource.Resource;
> >
> > +import java.util.Collections;
> > +import java.util.HashSet;
> > +import java.util.List;
> > import java.util.Map;
> > +import java.util.Set;
> >
> > /**
> > * Implementation of the OSGi Capability interface.
> > */
> > public class CapabilityImpl extends AbstractCapabilityRequirement
> implements Capability {
> > - /**
> > - * Create a capability that is not associated with a resource.
> > - * @param res The resource associated with the capability. May be
> null.
> > - * @param ns The namespace of the capability.
> > - * @param attrs The attributes of the capability.
> > - * @param dirs The directives of the capability.
> > - */
> > - public CapabilityImpl(String ns, Map<String, Object> attrs,
> Map<String, String> dirs) {
> > - this(ns, attrs, dirs, null);
> > - }
> > +
> > + protected final Set<String> mandatory;
> >
> > /**
> > * Create a capability.
> > + * @param res The resource associated with the capability.
> > * @param ns The namespace of the capability.
> > * @param attrs The attributes of the capability.
> > * @param dirs The directives of the capability.
> > - * @param res The resource associated with the capability. May be
> null.
> > */
> > - public CapabilityImpl(String ns, Map<String, Object> attrs,
> Map<String, String> dirs, Resource res) {
> > - super(ns, attrs, dirs, res);
> > + public CapabilityImpl(Resource res, String ns, Map<String, String>
> dirs, Map<String, Object> attrs) {
> > + super(res, ns, dirs, attrs);
> > +
> > + // Handle mandatory directive
> > + Set<String> mandatory = Collections.emptySet();
> > + String value = this.directives.get(Constants.
> MANDATORY_DIRECTIVE);
> > + if (value != null) {
> > + List<String> names = ResourceBuilder.parseDelimitedString(value,
> ",");
> > + mandatory = new HashSet<>(names.size());
> > + for (String name : names) {
> > + // If attribute exists, then record it as mandatory.
> > + if (this.attributes.containsKey(name)) {
> > + mandatory.add(name);
> > + // Otherwise, report an error.
> > + } else {
> > + throw new IllegalArgumentException("Mandatory
> attribute '" + name + "' does not exist.");
> > + }
> > + }
> > + }
> > + this.mandatory = mandatory;
> > }
> >
> > /**
> > @@ -54,6 +69,10 @@ public class CapabilityImpl extends Abst
> > * @param resource The resource to be associated with the capability
> > */
> > public CapabilityImpl(Resource resource, Capability capability) {
> > - this(capability.getNamespace(), capability.getAttributes(),
> capability.getDirectives(), resource);
> > + this(resource, capability.getNamespace(),
> capability.getDirectives(), capability.getAttributes());
> > + }
> > +
> > + public boolean isAttributeMandatory(String name) {
> > + return !mandatory.isEmpty() && mandatory.contains(name);
> > }
> > }
> >
> > Added: felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/CapabilitySet.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/
> java/org/apache/felix/utils/resource/CapabilitySet.java?
> rev=1829639&view=auto
> > ============================================================
> ==================
> > --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java
> (added)
> > +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java
> Fri Apr 20 09:23:35 2018
> > @@ -0,0 +1,469 @@
> > +/*
> > + * 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.utils.resource;
> > +
> > +import java.lang.reflect.Array;
> > +import java.lang.reflect.Constructor;
> > +import java.util.ArrayList;
> > +import java.util.Collection;
> > +import java.util.HashMap;
> > +import java.util.HashSet;
> > +import java.util.Iterator;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Map.Entry;
> > +import java.util.Set;
> > +import java.util.TreeMap;
> > +
> > +import org.apache.felix.utils.version.VersionTable;
> > +import org.osgi.framework.Constants;
> > +import org.osgi.framework.Version;
> > +import org.osgi.resource.Capability;
> > +
> > +@SuppressWarnings("rawtypes")
> > +public class CapabilitySet {
> > +
> > + private static final Class<?>[] STRING_CLASS = new Class[]
> {String.class};
> > +
> > + private final Map<String, Map<Object, Set<Capability>>> indices;
> > + private final Set<Capability> capSet = new HashSet<>();
> > +
> > + public CapabilitySet(List<String> indexProps) {
> > + indices = new TreeMap<>();
> > + for (int i = 0; (indexProps != null) && (i <
> indexProps.size()); i++) {
> > + indices.put(indexProps.get(i), new HashMap<Object,
> Set<Capability>>());
> > + }
> > + }
> > +
> > + public void dump() {
> > + for (Entry<String, Map<Object, Set<Capability>>> entry :
> indices.entrySet()) {
> > + boolean header1 = false;
> > + for (Entry<Object, Set<Capability>> entry2 :
> entry.getValue().entrySet()) {
> > + boolean header2 = false;
> > + for (Capability cap : entry2.getValue()) {
> > + if (!header1) {
> > + System.out.println(entry.getKey() + ":");
> > + header1 = true;
> > + }
> > + if (!header2) {
> > + System.out.println(" " + entry2.getKey());
> > + header2 = true;
> > + }
> > + System.out.println(" " + cap);
> > + }
> > + }
> > + }
> > + }
> > +
> > + public void addCapability(Capability cap) {
> > + capSet.add(cap);
> > +
> > + // Index capability.
> > + for (Entry<String, Map<Object, Set<Capability>>> entry :
> indices.entrySet()) {
> > + Object value = cap.getAttributes().get(entry.getKey());
> > + if (value != null) {
> > + if (value.getClass().isArray()) {
> > + value = convertArrayToList(value);
> > + }
> > +
> > + Map<Object, Set<Capability>> index = entry.getValue();
> > +
> > + if (value instanceof Collection) {
> > + Collection c = (Collection) value;
> > + for (Object o : c) {
> > + indexCapability(index, cap, o);
> > + }
> > + } else {
> > + indexCapability(index, cap, value);
> > + }
> > + }
> > + }
> > + }
> > +
> > + private void indexCapability(Map<Object, Set<Capability>> index,
> Capability cap, Object capValue) {
> > + // TODO: when JDK8, should be:
> > + // TODO: index.computeIfAbsent(capValue, k -> new
> HashSet<>()).add(cap);
> > + Set<Capability> set = index.get(capValue);
> > + if (set == null) {
> > + set = new HashSet<>();
> > + index.put(capValue, set);
> > + }
> > + set.add(cap);
> > + }
> > +
> > + public void removeCapability(Capability cap) {
> > + if (capSet.remove(cap)) {
> > + for (Entry<String, Map<Object, Set<Capability>>> entry :
> indices.entrySet()) {
> > + Object value = cap.getAttributes().get(entry.getKey());
> > + if (value != null) {
> > + if (value.getClass().isArray()) {
> > + value = convertArrayToList(value);
> > + }
> > +
> > + Map<Object, Set<Capability>> index =
> entry.getValue();
> > +
> > + if (value instanceof Collection) {
> > + Collection c = (Collection) value;
> > + for (Object o : c) {
> > + deindexCapability(index, cap, o);
> > + }
> > + } else {
> > + deindexCapability(index, cap, value);
> > + }
> > + }
> > + }
> > + }
> > + }
> > +
> > + private void deindexCapability(
> > + Map<Object, Set<Capability>> index, Capability cap, Object
> value) {
> > + Set<Capability> caps = index.get(value);
> > + if (caps != null) {
> > + caps.remove(cap);
> > + if (caps.isEmpty()) {
> > + index.remove(value);
> > + }
> > + }
> > + }
> > +
> > + public Set<Capability> match(SimpleFilter sf, boolean
> obeyMandatory) {
> > + Set<Capability> matches = match(capSet, sf);
> > + return obeyMandatory
> > + ? matchMandatory(matches, sf)
> > + : matches;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + private Set<Capability> match(Set<Capability> caps, SimpleFilter
> sf) {
> > + Set<Capability> matches = new HashSet<>();
> > +
> > + if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
> > + matches.addAll(caps);
> > + } else if (sf.getOperation() == SimpleFilter.AND) {
> > + // Evaluate each subfilter against the remaining
> capabilities.
> > + // For AND we calculate the intersection of each subfilter.
> > + // We can short-circuit the AND operation if there are no
> > + // remaining capabilities.
> > + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> > + for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++)
> {
> > + matches = match(caps, sfs.get(i));
> > + caps = matches;
> > + }
> > + } else if (sf.getOperation() == SimpleFilter.OR) {
> > + // Evaluate each subfilter against the remaining
> capabilities.
> > + // For OR we calculate the union of each subfilter.
> > + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> > + for (SimpleFilter sf1 : sfs) {
> > + matches.addAll(match(caps, sf1));
> > + }
> > + } else if (sf.getOperation() == SimpleFilter.NOT) {
> > + // Evaluate each subfilter against the remaining
> capabilities.
> > + // For OR we calculate the union of each subfilter.
> > + matches.addAll(caps);
> > + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> > + for (SimpleFilter sf1 : sfs) {
> > + matches.removeAll(match(caps, sf1));
> > + }
> > + } else {
> > + Map<Object, Set<Capability>> index =
> indices.get(sf.getName());
> > + if ((sf.getOperation() == SimpleFilter.EQ) && (index !=
> null)) {
> > + Set<Capability> existingCaps = index.get(sf.getValue());
> > + if (existingCaps != null) {
> > + matches.addAll(existingCaps);
> > + matches.retainAll(caps);
> > + }
> > + } else {
> > + for (Capability cap : caps) {
> > + Object lhs = cap.getAttributes().get(sf.getName());
> > + if (lhs != null) {
> > + if (compare(lhs, sf.getValue(),
> sf.getOperation())) {
> > + matches.add(cap);
> > + }
> > + }
> > + }
> > + }
> > + }
> > +
> > + return matches;
> > + }
> > +
> > + public static boolean matches(Capability cap, SimpleFilter sf) {
> > + return matchesInternal(cap, sf) && matchMandatory(cap, sf);
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + private static boolean matchesInternal(Capability cap, SimpleFilter
> sf) {
> > + boolean matched = true;
> > +
> > + if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
> > + matched = true;
> > + } else if (sf.getOperation() == SimpleFilter.AND) {
> > + // Evaluate each subfilter against the remaining
> capabilities.
> > + // For AND we calculate the intersection of each subfilter.
> > + // We can short-circuit the AND operation if there are no
> > + // remaining capabilities.
> > + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> > + for (int i = 0; matched && (i < sfs.size()); i++) {
> > + matched = matchesInternal(cap, sfs.get(i));
> > + }
> > + } else if (sf.getOperation() == SimpleFilter.OR) {
> > + // Evaluate each subfilter against the remaining
> capabilities.
> > + // For OR we calculate the union of each subfilter.
> > + matched = false;
> > + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> > + for (int i = 0; !matched && (i < sfs.size()); i++) {
> > + matched = matchesInternal(cap, sfs.get(i));
> > + }
> > + } else if (sf.getOperation() == SimpleFilter.NOT) {
> > + // Evaluate each subfilter against the remaining
> capabilities.
> > + // For OR we calculate the union of each subfilter.
> > + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> > + for (SimpleFilter sf1 : sfs) {
> > + matched = !(matchesInternal(cap, sf1));
> > + }
> > + } else {
> > + matched = false;
> > + Object lhs = cap.getAttributes().get(sf.getName());
> > + if (lhs != null) {
> > + matched = compare(lhs, sf.getValue(),
> sf.getOperation());
> > + }
> > + }
> > +
> > + return matched;
> > + }
> > +
> > + private static Set<Capability> matchMandatory(
> > + Set<Capability> caps, SimpleFilter sf) {
> > + for (Iterator<Capability> it = caps.iterator(); it.hasNext();) {
> > + Capability cap = it.next();
> > + if (!matchMandatory(cap, sf)) {
> > + it.remove();
> > + }
> > + }
> > + return caps;
> > + }
> > +
> > + private static boolean matchMandatory(Capability cap, SimpleFilter
> sf) {
> > + if (cap instanceof CapabilityImpl) {
> > + for (Entry<String, Object> entry :
> cap.getAttributes().entrySet()) {
> > + if (((CapabilityImpl) cap).isAttributeMandatory(
> entry.getKey())
> > + && !matchMandatoryAttribute(entry.getKey(),
> sf)) {
> > + return false;
> > + }
> > + }
> > + } else {
> > + String value = cap.getDirectives().get(
> Constants.MANDATORY_DIRECTIVE);
> > + if (value != null) {
> > + List<String> names = ResourceBuilder.parseDelimitedString(value,
> ",");
> > + for (Entry<String, Object> entry :
> cap.getAttributes().entrySet()) {
> > + if (names.contains(entry.getKey())
> > + && !matchMandatoryAttribute(entry.getKey(),
> sf)) {
> > + return false;
> > + }
> > + }
> > + }
> > +
> > + }
> > + return true;
> > + }
> > +
> > + private static boolean matchMandatoryAttribute(String attrName,
> SimpleFilter sf) {
> > + if ((sf.getName() != null) && sf.getName().equals(attrName)) {
> > + return true;
> > + } else if (sf.getOperation() == SimpleFilter.AND) {
> > + List list = (List) sf.getValue();
> > + for (Object aList : list) {
> > + SimpleFilter sf2 = (SimpleFilter) aList;
> > + if ((sf2.getName() != null)
> > + && sf2.getName().equals(attrName)) {
> > + return true;
> > + }
> > + }
> > + }
> > + return false;
> > + }
> > +
> > + @SuppressWarnings("unchecked")
> > + private static boolean compare(Object lhs, Object rhsUnknown, int
> op) {
> > + if (lhs == null) {
> > + return false;
> > + }
> > +
> > + // If this is a PRESENT operation, then just return true
> immediately
> > + // since we wouldn't be here if the attribute wasn't present.
> > + if (op == SimpleFilter.PRESENT) {
> > + return true;
> > + }
> > +
> > + // If the type is comparable, then we can just return the
> > + // result immediately.
> > + if (lhs instanceof Comparable) {
> > + // Spec says SUBSTRING is false for all types other than
> string.
> > + if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof
> String)) {
> > + return false;
> > + }
> > +
> > + Object rhs;
> > + if (op == SimpleFilter.SUBSTRING) {
> > + rhs = rhsUnknown;
> > + } else {
> > + try {
> > + rhs = coerceType(lhs, (String) rhsUnknown);
> > + } catch (Exception ex) {
> > + return false;
> > + }
> > + }
> > +
> > + switch (op) {
> > + case SimpleFilter.EQ:
> > + try {
> > + return ((Comparable) lhs).compareTo(rhs) == 0;
> > + } catch (Exception ex) {
> > + return false;
> > + }
> > + case SimpleFilter.GTE:
> > + try {
> > + return ((Comparable) lhs).compareTo(rhs) >= 0;
> > + } catch (Exception ex) {
> > + return false;
> > + }
> > + case SimpleFilter.LTE:
> > + try {
> > + return ((Comparable) lhs).compareTo(rhs) <= 0;
> > + } catch (Exception ex) {
> > + return false;
> > + }
> > + case SimpleFilter.APPROX:
> > + return compareApproximate(lhs, rhs);
> > + case SimpleFilter.SUBSTRING:
> > + return SimpleFilter.compareSubstring((List<String>)
> rhs, (String) lhs);
> > + default:
> > + throw new RuntimeException("Unknown comparison
> operator: " + op);
> > + }
> > + }
> > +
> > + // If the LHS is not a comparable or boolean, check if it is an
> > + // array. If so, convert it to a list so we can treat it as a
> > + // collection.
> > + if (lhs.getClass().isArray()) {
> > + lhs = convertArrayToList(lhs);
> > + }
> > +
> > + // If LHS is a collection, then call compare() on each element
> > + // of the collection until a match is found.
> > + if (lhs instanceof Collection) {
> > + for (Object o : (Collection) lhs) {
> > + if (compare(o, rhsUnknown, op)) {
> > + return true;
> > + }
> > + }
> > +
> > + return false;
> > + }
> > +
> > + // Spec says SUBSTRING is false for all types other than string.
> > + if (op == SimpleFilter.SUBSTRING) {
> > + return false;
> > + }
> > +
> > + // Since we cannot identify the LHS type, then we can only
> perform
> > + // equality comparison.
> > + try {
> > + return lhs.equals(coerceType(lhs, (String) rhsUnknown));
> > + } catch (Exception ex) {
> > + return false;
> > + }
> > + }
> > +
> > + private static boolean compareApproximate(Object lhs, Object rhs) {
> > + if (rhs instanceof String) {
> > + return removeWhitespace((String) lhs)
> > + .equalsIgnoreCase(removeWhitespace((String) rhs));
> > + } else if (rhs instanceof Character) {
> > + return Character.toLowerCase((Character) lhs)
> > + == Character.toLowerCase((Character) rhs);
> > + }
> > + return lhs.equals(rhs);
> > + }
> > +
> > + private static String removeWhitespace(String s) {
> > + StringBuilder sb = new StringBuilder(s.length());
> > + for (int i = 0; i < s.length(); i++) {
> > + if (!Character.isWhitespace(s.charAt(i))) {
> > + sb.append(s.charAt(i));
> > + }
> > + }
> > + return sb.toString();
> > + }
> > +
> > + private static Object coerceType(Object lhs, String rhsString)
> throws Exception {
> > + // If the LHS expects a string, then we can just return
> > + // the RHS since it is a string.
> > + if (lhs.getClass() == rhsString.getClass()) {
> > + return rhsString;
> > + }
> > +
> > + // Try to convert the RHS type to the LHS type by using
> > + // the string constructor of the LHS class, if it has one.
> > + Object rhs;
> > + try {
> > + if (lhs instanceof Version) {
> > + rhs = VersionTable.getVersion(rhsString, false);
> > + } else
> > + // The Character class is a special case, since its
> constructor
> > + // does not take a string, so handle it separately.
> > + if (lhs instanceof Character) {
> > + rhs = rhsString.charAt(0);
> > + } else {
> > + // Spec says we should trim number types.
> > + if ((lhs instanceof Number) || (lhs instanceof
> Boolean)) {
> > + rhsString = rhsString.trim();
> > + }
> > + Constructor ctor = lhs.getClass().getConstructor(
> STRING_CLASS);
> > + ctor.setAccessible(true);
> > + rhs = ctor.newInstance(rhsString);
> > + }
> > + } catch (Exception ex) {
> > + throw new Exception(
> > + "Could not instantiate class "
> > + + lhs.getClass().getName()
> > + + " from string constructor with argument '"
> > + + rhsString + "' because " + ex
> > + );
> > + }
> > +
> > + return rhs;
> > + }
> > +
> > + /**
> > + * This is an ugly utility method to convert an array of primitives
> > + * to an array of primitive wrapper objects. This method simplifies
> > + * processing LDAP filters since the special case of primitive
> arrays
> > + * can be ignored.
> > + *
> > + * @param array An array of primitive types.
> > + * @return An corresponding array using pritive wrapper objects.
> > + */
> > + private static List<Object> convertArrayToList(Object array) {
> > + int len = Array.getLength(array);
> > + List<Object> list = new ArrayList<>(len);
> > + for (int i = 0; i < len; i++) {
> > + list.add(Array.get(array, i));
> > + }
> > + return list;
> > + }
> > +}
> >
> > Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/
> resource/RequirementImpl.java
> > URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/
> java/org/apache/felix/utils/resource/RequirementImpl.java?
> rev=1829639&r1=1829638&r2=1829639&view=diff
> > ============================================================
> ==================
> > --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java
> (original)
> > +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java
> Fri Apr 20 09:23:35 2018
> > @@ -16,6 +16,8 @@
> > */
> > package org.apache.felix.utils.resource;
> >
> > +import org.osgi.framework.Constants;
> > +import org.osgi.resource.Capability;
> > import org.osgi.resource.Namespace;
> > import org.osgi.resource.Requirement;
> > import org.osgi.resource.Resource;
> > @@ -27,26 +29,19 @@ import java.util.Map;
> > * Implementation of the OSGi Requirement interface.
> > */
> > public class RequirementImpl extends AbstractCapabilityRequirement
> implements Requirement {
> > - /**
> > - * Create a requirement that is not associated with a resource.
> > - * @param res The resource associated with the requirement.
> > - * @param ns The namespace of the requirement.
> > - * @param attrs The attributes of the requirement.
> > - * @param dirs The directives of the requirement.
> > - */
> > - public RequirementImpl(String ns, Map<String, Object> attrs,
> Map<String, String> dirs) {
> > - this(ns, attrs, dirs, null);
> > - }
> > +
> > + private final SimpleFilter filter;
> > + private final boolean optional;
> >
> > /**
> > * Create a requirement.
> > + * @param res The resource associated with the requirement.
> > * @param ns The namespace of the requirement.
> > * @param attrs The attributes of the requirement.
> > * @param dirs The directives of the requirement.
> > - * @param res The resource associated with the requirement.
> > */
> > - public RequirementImpl(String ns, Map<String, Object> attrs,
> Map<String, String> dirs, Resource res) {
> > - super(ns, attrs, dirs, res);
> > + public RequirementImpl(Resource res, String ns, Map<String, String>
> dirs, Map<String, Object> attrs) {
> > + this(res, ns, dirs, attrs, null);
> > }
> >
> > /**
> > @@ -54,14 +49,16 @@ public class RequirementImpl extends Abs
> > *
> > * This is a convenience method that creates a requirement with
> > * an empty attributes map and a single 'filter' directive.
> > + * @param res The resource associated with the requirement.
> > * @param ns The namespace for the requirement.
> > * @param filter The filter.
> > */
> > - public RequirementImpl(String ns, String filter)
> > + public RequirementImpl(Resource res, String ns, String filter)
> > {
> > - this(ns, Collections.<String, Object>emptyMap(),
> > - filter == null ? Collections.<String, String> emptyMap() :
> > - Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE,
> filter));
> > + this(res, ns,
> > + filter == null ? Collections.<String, String>emptyMap() :
> > + Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE,
> filter),
> > + null);
> > }
> >
> > /**
> > @@ -71,6 +68,26 @@ public class RequirementImpl extends Abs
> > * @param resource The resource to be associated with the
> requirement
> > */
> > public RequirementImpl(Resource resource, Requirement requirement) {
> > - this(requirement.getNamespace(), requirement.getAttributes(),
> requirement.getDirectives(), resource);
> > + this(resource, requirement.getNamespace(),
> requirement.getDirectives(), requirement.getAttributes());
> > + }
> > +
> > + public RequirementImpl(Resource resource, String path, Map<String,
> String> dirs, Map<String, Object> attrs, SimpleFilter sf) {
> > + super(resource, path, dirs, attrs);
> > + this.filter = sf != null ? sf : SimpleFilter.convert(
> attributes);
> > + // Find resolution import directives.
> > + this.optional = Constants.RESOLUTION_OPTIONAL.
> equals(directives.get(Constants.RESOLUTION_DIRECTIVE));
> > + }
> > +
> > + public boolean matches(Capability cap) {
> > + return CapabilitySet.matches(cap, getFilter());
> > }
> > +
> > + public boolean isOptional() {
> > + return optional;
> > + }
> > +
> > + public SimpleFilter getFilter() {
> > + return filter;
> > + }
> > +
> > }
> >
> >
>
>
>
> --
> Karl Pauls
> karlpauls@gmail.com
>
Re: svn commit: r1829639 [1/3] - in /felix/trunk: bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/
bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/ utils/
utils/src/main/java/org/apache/felix/utils/collections/ u...
Posted by Karl Pauls <ka...@gmail.com>.
This is great but please try to reference JIRA issues - if we do this
kind of bigger things we should have a JIRA so that we don't get
confused in the future.
regards,
Karl
On Fri, Apr 20, 2018 at 11:23 AM, <gn...@apache.org> wrote:
> Author: gnodet
> Date: Fri Apr 20 09:23:35 2018
> New Revision: 1829639
>
> URL: http://svn.apache.org/viewvc?rev=1829639&view=rev
> Log:
> Provide optimized resource / filter / capability / requirement / capability set
>
> Added:
> felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java
> felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java
> - copied, changed from r1829637, felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/ResourceBuilder.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/ResourceImpl.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/SimpleFilter.java
> felix/trunk/utils/src/test/java/org/apache/felix/utils/collections/
> felix/trunk/utils/src/test/java/org/apache/felix/utils/collections/StringArrayMapTest.java
> felix/trunk/utils/src/test/java/org/apache/felix/utils/resource/SimpleFilterTest.java
> Removed:
> felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyHashMap.java
> felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java
> Modified:
> felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
> felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java
> felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java
> felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
> felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java
> felix/trunk/utils/pom.xml
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java
> felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java
> felix/trunk/utils/src/test/java/org/apache/felix/utils/resource/CapabilityImplTest.java
> felix/trunk/utils/src/test/java/org/apache/felix/utils/resource/RequirementImplTest.java
>
> Modified: felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java (original)
> +++ felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java Fri Apr 20 09:23:35 2018
> @@ -42,12 +42,12 @@ public class FelixResourceAdapter implem
>
> if (namespace == null || namespace.equals(IdentityNamespace.IDENTITY_NAMESPACE))
> {
> - CapabilityImpl c = OSGiRepositoryImpl.newOSGiIdentityCapability(resource, this);
> + CapabilityImpl c = OSGiRepositoryImpl.newOSGiIdentityCapability(this, resource);
> result.add(c);
> }
> if (namespace == null || namespace.equals(ContentNamespace.CONTENT_NAMESPACE))
> {
> - CapabilityImpl c = OSGiRepositoryImpl.newOSGiContentCapability(resource, this);
> + CapabilityImpl c = OSGiRepositoryImpl.newOSGiContentCapability(this, resource);
> result.add(c);
> }
>
>
> Added: felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java?rev=1829639&view=auto
> ==============================================================================
> --- felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java (added)
> +++ felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/LazyStringMap.java Fri Apr 20 09:23:35 2018
> @@ -0,0 +1,67 @@
> +/*
> + * 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.bundlerepository.impl;
> +
> +import java.util.Map;
> +
> +import org.apache.felix.utils.collections.StringArrayMap;
> +
> +/**
> + * A map that can delay the computation of certain values up until the moment that they
> + * are actually needed. Useful for expensive to compute values such as the SHA-256.
> + * This map does <b>not</b> support {@code null} values.
> + */
> +@SuppressWarnings("serial")
> +public class LazyStringMap<V> extends StringArrayMap<V>
> +{
> + public LazyStringMap(Map<String, ? extends V> map) {
> + super(map);
> + }
> +
> + public LazyStringMap() {
> + }
> +
> + public LazyStringMap(int capacity) {
> + super(capacity);
> + }
> +
> + @Override
> + @SuppressWarnings("unchecked")
> + public V get(Object key)
> + {
> + V val = super.get(key);
> + if (val instanceof LazyValue) {
> + val = ((LazyValue<V>) val).compute();
> + if (val == null) {
> + throw new NullPointerException("Lazy computed values may not be null");
> + }
> + put((String) key, val);
> + }
> + return val;
> + }
> +
> + public void putLazy(String key, LazyValue<V> lazy) {
> + super.doPut(key, lazy);
> + }
> +
> + public interface LazyValue<V>
> + {
> + V compute();
> + }
> +}
>
> Modified: felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java (original)
> +++ felix/trunk/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImpl.java Fri Apr 20 09:23:35 2018
> @@ -30,10 +30,9 @@ import java.util.Collections;
> import java.util.HashMap;
> import java.util.List;
> import java.util.Map;
> -import java.util.concurrent.Callable;
>
> import org.apache.felix.bundlerepository.RepositoryAdmin;
> -import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
> +import org.apache.felix.bundlerepository.Resource;
> import org.apache.felix.utils.resource.CapabilityImpl;
> import org.osgi.framework.Filter;
> import org.osgi.framework.FrameworkUtil;
> @@ -41,7 +40,6 @@ import org.osgi.framework.namespace.Iden
> import org.osgi.resource.Capability;
> import org.osgi.resource.Namespace;
> import org.osgi.resource.Requirement;
> -import org.osgi.resource.Resource;
> import org.osgi.service.repository.ContentNamespace;
> import org.osgi.service.repository.Repository;
>
> @@ -120,8 +118,7 @@ class OSGiRepositoryImpl implements Repo
> caps.add(idCap);
> }
>
> - static CapabilityImpl newOSGiIdentityCapability(org.apache.felix.bundlerepository.Resource res,
> - org.osgi.resource.Resource targetResource)
> + static CapabilityImpl newOSGiIdentityCapability(org.osgi.resource.Resource or, org.apache.felix.bundlerepository.Resource res)
> {
> @SuppressWarnings("unchecked")
> Map<String, Object> idAttrs = new HashMap<String, Object>(res.getProperties());
> @@ -132,32 +129,34 @@ class OSGiRepositoryImpl implements Repo
> if (idAttrs.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE) == null)
> idAttrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, IdentityNamespace.TYPE_BUNDLE);
>
> - return new CapabilityImpl(IdentityNamespace.IDENTITY_NAMESPACE, idAttrs, Collections.<String, String> emptyMap(), targetResource);
> + return new CapabilityImpl(or, IdentityNamespace.IDENTITY_NAMESPACE, Collections.<String, String> emptyMap(), idAttrs);
> }
>
> - static CapabilityImpl newOSGiContentCapability(org.apache.felix.bundlerepository.Resource resource,
> - org.osgi.resource.Resource targetResource)
> + static CapabilityImpl newOSGiContentCapability(org.osgi.resource.Resource or, Resource resource)
> {
> final String uri = resource.getURI();
> - LazyValue<String, Object> lazyValue =
> - new LazyValue<String, Object>(ContentNamespace.CONTENT_NAMESPACE, new Callable<Object>()
> - {
> - public Object call() throws Exception
> - {
> - // This is expensive to do, so only compute it when actually obtained...
> + LazyStringMap.LazyValue<String> content = new LazyStringMap.LazyValue<String>() {
> + public String compute() {
> + // This is expensive to do, so only compute it when actually obtained...
> + try {
> return OSGiRepositoryImpl.getSHA256(uri);
> + } catch (IOException e) {
> + throw new RuntimeException(e);
> + } catch (NoSuchAlgorithmException e) {
> + throw new RuntimeException(e);
> }
> - });
> -
> + }
> + };
> Object mime = resource.getProperties().get("mime");
> if (mime == null)
> mime = "application/vnd.osgi.bundle";
>
> - Map<String, Object> contentAttrs = new LazyHashMap<String, Object>(Collections.singleton(lazyValue));
> + Map<String, Object> contentAttrs = new LazyStringMap<Object>(4);
> contentAttrs.put(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE, mime);
> contentAttrs.put(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE, resource.getSize());
> contentAttrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, uri);
> - return new ContentCapabilityImpl(contentAttrs, targetResource);
> + contentAttrs.put(ContentNamespace.CONTENT_NAMESPACE, content);
> + return new CapabilityImpl(or, ContentNamespace.CONTENT_NAMESPACE, Collections.<String, String> emptyMap(), contentAttrs);
> }
>
> static String getSHA256(String uri) throws IOException, NoSuchAlgorithmException // TODO find a good place for this
> @@ -183,19 +182,4 @@ class OSGiRepositoryImpl implements Repo
> return sb.toString();
> }
>
> - // This capability variant does not take a private copy of the capabilities so that it
> - // can lazily compute the content hash.
> - private static class ContentCapabilityImpl extends CapabilityImpl implements Capability {
> - private final Map<String, Object> contentAttributes;
> -
> - public ContentCapabilityImpl(Map<String, Object> contentAttrs, Resource targetResource) {
> - super(ContentNamespace.CONTENT_NAMESPACE, null, null, targetResource);
> - contentAttributes = Collections.unmodifiableMap(contentAttrs);
> - }
> -
> - @Override
> - public Map<String, Object> getAttributes() {
> - return contentAttributes;
> - }
> - }
> }
>
> Copied: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java (from r1829637, felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java)
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java?p2=felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java&p1=felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java&r1=1829637&r2=1829639&rev=1829639&view=diff
> ==============================================================================
> --- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyHashMapTest.java (original)
> +++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/LazyStringMapTest.java Fri Apr 20 09:23:35 2018
> @@ -21,41 +21,39 @@ package org.apache.felix.bundlerepositor
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.HashMap;
> +import java.util.Map;
> import java.util.concurrent.Callable;
> import java.util.concurrent.atomic.AtomicInteger;
>
> import junit.framework.TestCase;
> +import org.apache.felix.bundlerepository.impl.LazyStringMap.LazyValue;
>
> -import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
> -
> -public class LazyHashMapTest extends TestCase
> +public class LazyStringMapTest extends TestCase
> {
> public void testLazyHashMap() {
> final AtomicInteger lv1Computed = new AtomicInteger(0);
> - LazyValue<String, Long> lv1 = new LazyValue<String, Long>("42", new Callable<Long>()
> - {
> - public Long call() throws Exception
> - {
> + LazyValue<Long> lv1 = new LazyValue<Long>() {
> + public Long compute() {
> lv1Computed.incrementAndGet();
> return 24L;
> }
> - });
> + };
>
> final AtomicInteger lv2Computed = new AtomicInteger(0);
> - LazyValue<String, Long> lv2 = new LazyValue<String, Long>("zero", new Callable<Long>()
> - {
> - public Long call() throws Exception
> - {
> + LazyValue<Long> lv2 = new LazyValue<Long>() {
> + public Long compute() {
> lv2Computed.incrementAndGet();
> return 0L;
> }
> - });
> + };
>
> - Collection<LazyValue<String, Long>> lazyValues = new ArrayList<LazyHashMap.LazyValue<String,Long>>();
> + Collection<LazyValue<Long>> lazyValues = new ArrayList<LazyValue<Long>>();
> lazyValues.add(lv1);
> lazyValues.add(lv2);
> - HashMap<String, Long> lhm = new LazyHashMap<String, Long>(lazyValues);
> + LazyStringMap<Long> lhm = new LazyStringMap<Long>();
> lhm.put("1", 2L);
> + lhm.putLazy("42", lv1);
> + lhm.putLazy("zero", lv2);
>
> assertEquals(new Long(2L), lhm.get("1"));
> assertEquals("No computation should have happened yet", 0, lv1Computed.get());
>
> Modified: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java (original)
> +++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryImplTest.java Fri Apr 20 09:23:35 2018
> @@ -58,7 +58,7 @@ public class OSGiRepositoryImplTest exte
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("osgi.identity", null);
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.identity", null);
>
> Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
> assertEquals(1, result.size());
> @@ -117,7 +117,7 @@ public class OSGiRepositoryImplTest exte
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("osgi.identity", "(osgi.identity=test_file_2)");
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.identity", "(osgi.identity=test_file_2)");
>
> Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
> assertEquals(1, result.size());
> @@ -137,7 +137,7 @@ public class OSGiRepositoryImplTest exte
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("foo", "(someKey=someOtherVal)");
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "foo", "(someKey=someOtherVal)");
>
> Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
> assertEquals(1, result.size());
> @@ -157,7 +157,7 @@ public class OSGiRepositoryImplTest exte
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("foo", "(someKey=*)");
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "foo", "(someKey=*)");
>
> Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
> assertEquals(1, result.size());
> @@ -181,7 +181,7 @@ public class OSGiRepositoryImplTest exte
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("osgi.wiring.package",
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.wiring.package",
> "(&(osgi.wiring.package=org.apache.commons.logging)(version>=1.0.1)(!(version>=2)))");
>
> Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
> @@ -230,12 +230,12 @@ public class OSGiRepositoryImplTest exte
>
> BundleRevision br = Mockito.mock(BundleRevision.class);
> Mockito.when(sysBundle.adapt(BundleRevision.class)).thenReturn(br);
> - Capability cap1 = new CapabilityImpl("some.system.cap",
> - Collections.<String, Object>singletonMap("sys.cap", "something"),
> - Collections.singletonMap("x", "y"));
> - Capability cap2 = new CapabilityImpl("some.system.cap",
> - Collections.<String, Object>singletonMap("sys.cap", "somethingelse"),
> - Collections.<String, String>emptyMap());
> + Capability cap1 = new CapabilityImpl(Mockito.mock(Resource.class), "some.system.cap",
> + Collections.singletonMap("x", "y"),
> + Collections.<String, Object>singletonMap("sys.cap", "something"));
> + Capability cap2 = new CapabilityImpl(Mockito.mock(Resource.class), "some.system.cap",
> + Collections.<String, String>emptyMap(),
> + Collections.<String, Object>singletonMap("sys.cap", "somethingelse"));
> Mockito.when(br.getCapabilities(null)).thenReturn(Arrays.asList(cap1, cap2));
>
> BundleContext bc = Mockito.mock(BundleContext.class);
>
> Modified: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java (original)
> +++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java Fri Apr 20 09:23:35 2018
> @@ -51,7 +51,8 @@ public class OSGiRepositoryXMLTest exten
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("osgi.identity",
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> + "osgi.identity",
> "(osgi.identity=cdi-subsystem)");
>
> Map<Requirement, Collection<Capability>> result = repo
> @@ -126,7 +127,8 @@ public class OSGiRepositoryXMLTest exten
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("osgi.identity",
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),
> + "osgi.identity",
> "(license=http://www.opensource.org/licenses/mytestlicense)");
>
> Map<Requirement, Collection<Capability>> result = repo
> @@ -145,7 +147,7 @@ public class OSGiRepositoryXMLTest exten
> repoAdmin.addRepository(url);
>
> Repository repo = new OSGiRepositoryImpl(repoAdmin);
> - Requirement req = new RequirementImpl("foo", "(bar=toast)");
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class),"foo", "(bar=toast)");
>
> Map<Requirement, Collection<Capability>> result = repo
> .findProviders(Collections.singleton(req));
>
> Modified: felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java
> URL: http://svn.apache.org/viewvc/felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java (original)
> +++ felix/trunk/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRequirementAdapterTest.java Fri Apr 20 09:23:35 2018
> @@ -24,7 +24,9 @@ import java.util.Map;
> import junit.framework.TestCase;
>
> import org.apache.felix.utils.resource.RequirementImpl;
> +import org.mockito.Mockito;
> import org.osgi.resource.Requirement;
> +import org.osgi.resource.Resource;
>
> public class OSGiRequirementAdapterTest extends TestCase
> {
> @@ -38,7 +40,7 @@ public class OSGiRequirementAdapterTest
> dirs.put("resolution", "optional");
> dirs.put("test", "test");
>
> - Requirement req = new RequirementImpl("osgi.wiring.package", attrs, dirs);
> + Requirement req = new RequirementImpl(Mockito.mock(Resource.class), "osgi.wiring.package", dirs, attrs);
> OSGiRequirementAdapter adapter = new OSGiRequirementAdapter(req);
>
> assertEquals("(package=y)", adapter.getFilter());
>
> Modified: felix/trunk/utils/pom.xml
> URL: http://svn.apache.org/viewvc/felix/trunk/utils/pom.xml?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/utils/pom.xml (original)
> +++ felix/trunk/utils/pom.xml Fri Apr 20 09:23:35 2018
> @@ -36,17 +36,21 @@
> <url>http://svn.apache.org/repos/asf/felix/utils</url>
> </scm>
>
> + <properties>
> + <felix.java.version>7</felix.java.version>
> + </properties>
> +
> <dependencies>
> <dependency>
> <groupId>org.osgi</groupId>
> - <artifactId>org.osgi.core</artifactId>
> + <artifactId>osgi.core</artifactId>
> <version>5.0.0</version>
> <scope>provided</scope>
> </dependency>
> <dependency>
> <groupId>org.osgi</groupId>
> - <artifactId>org.osgi.compendium</artifactId>
> - <version>4.2.0</version>
> + <artifactId>osgi.cmpn</artifactId>
> + <version>5.0.0</version>
> <scope>provided</scope>
> </dependency>
> </dependencies>
> @@ -62,6 +66,13 @@
> </excludes>
> </configuration>
> </plugin>
> + <plugin>
> + <artifactId>maven-compiler-plugin</artifactId>
> + <configuration>
> + <source>1.7</source>
> + <target>1.7</target>
> + </configuration>
> + </plugin>
> </plugins>
> </build>
> </project>
>
> Added: felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java
> URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java?rev=1829639&view=auto
> ==============================================================================
> --- felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java (added)
> +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/collections/StringArrayMap.java Fri Apr 20 09:23:35 2018
> @@ -0,0 +1,339 @@
> +/*
> + * 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.utils.collections;
> +
> +import java.util.AbstractCollection;
> +import java.util.AbstractSet;
> +import java.util.Arrays;
> +import java.util.Collection;
> +import java.util.Collections;
> +import java.util.Iterator;
> +import java.util.Map;
> +import java.util.NoSuchElementException;
> +import java.util.Objects;
> +import java.util.Set;
> +
> +public class StringArrayMap<V> implements Map<String, V> {
> +
> + protected Object[] table;
> + protected int size;
> +
> + public static <T> Map<String, T> reduceMemory(Map<String, T> map) {
> + if (map == null) {
> + return Collections.emptyMap();
> + }
> + switch (map.size()) {
> + case 0:
> + return Collections.emptyMap();
> + case 1:
> + Entry<String, T> e = map.entrySet().iterator().next();
> + return Collections.singletonMap(e.getKey().intern(), e.getValue());
> + default:
> + if (map instanceof StringArrayMap) {
> + @SuppressWarnings("unchecked")
> + StringArrayMap<T> m = (StringArrayMap) map;
> + if (m.size == m.table.length / 2) {
> + return map;
> + }
> + }
> + return new StringArrayMap<>(map);
> + }
> + }
> +
> + public StringArrayMap(Map<String, ? extends V> map) {
> + if (map instanceof StringArrayMap) {
> + size = ((StringArrayMap) map).size;
> + table = Arrays.copyOf(((StringArrayMap) map).table, size * 2);
> + } else {
> + size = 0;
> + table = new Object[map.size() * 2];
> + for (Entry<String, ? extends V> e : map.entrySet()) {
> + int i = size++ << 1;
> + table[i++] = e.getKey().intern();
> + table[i] = e.getValue();
> + }
> + }
> + }
> +
> + public StringArrayMap() {
> + this(32);
> + }
> +
> + public StringArrayMap(int capacity) {
> + table = new Object[capacity * 2];
> + size = 0;
> + }
> +
> + @SuppressWarnings("unchecked")
> + public V get(Object key) {
> + String k = ((String) key).intern();
> + for (int i = 0, l = size << 1; i < l; i += 2) {
> + if (k == table[i]) {
> + return (V) table[i + 1];
> + }
> + }
> + return null;
> + }
> +
> + @SuppressWarnings("unchecked")
> + public V put(String key, V value) {
> + return (V) doPut(key, value);
> + }
> +
> + protected Object doPut(String key, Object value) {
> + key = key.intern();
> + for (int i = 0, l = size << 1; i < l; i += 2) {
> + if (key == table[i]) {
> + Object old = table[i + 1];
> + table[i + 1] = value;
> + return old;
> + }
> + }
> + if (table.length == 0) {
> + table = new Object[2];
> + } else if (size * 2 == table.length) {
> + Object[] n = new Object[table.length * 2];
> + System.arraycopy(table, 0, n, 0, table.length);
> + table = n;
> + }
> + int i = size++ << 1;
> + table[i++] = key;
> + table[i] = value;
> + return null;
> + }
> +
> + public Set<String> keySet() {
> + return new AbstractSet<String>() {
> + @Override
> + public Iterator<String> iterator() {
> + return new Iterator<String>() {
> + int index = 0;
> +
> + @Override
> + public boolean hasNext() {
> + return index < size;
> + }
> +
> + @Override
> + public String next() {
> + if (index >= size) {
> + throw new NoSuchElementException();
> + }
> + return (String) table[(index++ << 1)];
> + }
> +
> + public void remove() {
> + throw new UnsupportedOperationException("remove");
> + }
> + };
> + }
> +
> + @Override
> + public int size() {
> + return size;
> + }
> + };
> + }
> +
> + public Collection<V> values() {
> + return new AbstractCollection<V>() {
> + @Override
> + public Iterator<V> iterator() {
> + return new Iterator<V>() {
> + int index = 0;
> +
> + public boolean hasNext() {
> + return index < size;
> + }
> +
> + @SuppressWarnings("unchecked")
> + public V next() {
> + if (index >= size) {
> + throw new NoSuchElementException();
> + }
> + return (V) table[(index++ << 1) + 1];
> + }
> +
> + public void remove() {
> + throw new UnsupportedOperationException("remove");
> + }
> + };
> + }
> +
> + @Override
> + public int size() {
> + return size;
> + }
> + };
> + }
> +
> + public Set<Entry<String, V>> entrySet() {
> + return new AbstractSet<Entry<String, V>>() {
> + @Override
> + public Iterator<Entry<String, V>> iterator() {
> + return new Iterator<Entry<String, V>>() {
> + int index = 0;
> +
> + public boolean hasNext() {
> + return index < size;
> + }
> +
> + @SuppressWarnings("unchecked")
> + public Entry<String, V> next() {
> + if (index >= size) {
> + throw new NoSuchElementException();
> + }
> + final int i = index << 1;
> + index++;
> + return new Entry<String, V>() {
> +
> + public String getKey() {
> + return (String) table[i];
> + }
> +
> + public V getValue() {
> + return (V) table[i + 1];
> + }
> +
> + public V setValue(V value) {
> + throw new UnsupportedOperationException();
> + }
> + };
> + }
> +
> + public void remove() {
> + throw new UnsupportedOperationException("remove");
> + }
> + };
> + }
> +
> + @Override
> + public int size() {
> + return size;
> + }
> + };
> + }
> +
> + public int size() {
> + return size;
> + }
> +
> + public boolean isEmpty() {
> + return size == 0;
> + }
> +
> + public boolean containsKey(Object key) {
> + String k = ((String) key).intern();
> + for (int i = 0, l = size * 2; i < l; i += 2) {
> + if (table[i] == k) {
> + return true;
> + }
> + }
> + return false;
> + }
> +
> + public boolean containsValue(Object value) {
> + for (int i = 0, l = size * 2; i < l; i += 2) {
> + if (Objects.equals(table[i + 1], value)) {
> + return true;
> + }
> + }
> + return false;
> + }
> +
> + @SuppressWarnings("unchecked")
> + public V remove(Object key) {
> + String k = ((String) key).intern();
> + for (int i = 0, l = size * 2; i < l; i += 2) {
> + if (table[i] == k) {
> + Object v = table[i + 1];
> + if (i < l - 2) {
> + System.arraycopy(table, i + 2, table, i, l - 2 - i);
> + }
> + table[l - 1] = null;
> + table[l - 2] = null;
> + size--;
> + return (V) v;
> + }
> + }
> + return null;
> + }
> +
> + public void putAll(Map<? extends String, ? extends V> m) {
> + for (Entry<? extends String, ? extends V> e : m.entrySet()) {
> + put(e.getKey(), e.getValue());
> + }
> + }
> +
> + public void clear() {
> + size = 0;
> + Arrays.fill(table, null);
> + }
> +
> + public int hashCode() {
> + int result = 1;
> + for (int i = 0; i < size * 2; i++)
> + result = 31 * result + (table[i] == null ? 0 : table[i].hashCode());
> + return result;
> + }
> +
> + public boolean equals(Object o) {
> + if (o == this)
> + return true;
> + if (!(o instanceof Map))
> + return false;
> + Map<?,?> m = (Map<?,?>) o;
> + if (m.size() != size())
> + return false;
> + try {
> + for (int i = 0, l = size * 2; i < l; i += 2) {
> + Object key = table[i];
> + Object value = table[i+1];
> + if (value == null) {
> + if (!(m.get(key)==null && m.containsKey(key)))
> + return false;
> + } else {
> + if (!value.equals(m.get(key)))
> + return false;
> + }
> + }
> + } catch (ClassCastException | NullPointerException unused) {
> + return false;
> + }
> + return true;
> + }
> +
> + public String toString() {
> + if (size == 0)
> + return "{}";
> +
> + StringBuilder sb = new StringBuilder();
> + sb.append('{');
> + for (int i = 0, l = size * 2; i < l; i += 2) {
> + if (i > 0) {
> + sb.append(',').append(' ');
> + }
> + sb.append(table[i]);
> + sb.append('=');
> + sb.append(table[i+1] == this ? "(this Map)" : table[i+1]);
> + }
> + return sb.append('}').toString();
> + }
> +
> +}
>
> Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java
> URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java (original)
> +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/AbstractCapabilityRequirement.java Fri Apr 20 09:23:35 2018
> @@ -16,38 +16,32 @@
> */
> package org.apache.felix.utils.resource;
>
> +import org.apache.felix.utils.collections.StringArrayMap;
> +import org.osgi.framework.Version;
> import org.osgi.resource.Resource;
>
> -import java.util.Collections;
> -import java.util.HashMap;
> import java.util.Map;
> +import java.util.Objects;
>
> abstract class AbstractCapabilityRequirement {
>
> - /** The namespace. Required. */
> - private final String namespace;
> + /** The resource. Required. */
> + protected final Resource resource;
>
> - /** Optional resource. */
> - private final Resource resource;
> + /** The namespace. Required. */
> + protected final String namespace;
>
> /** Optional attributes. Never null. */
> - private final Map<String, Object> attributes;
> + protected final Map<String, String> directives;
>
> /** Optional attributes. Never null. */
> - private final Map<String, String> directives;
> + protected final Map<String, Object> attributes;
>
> - AbstractCapabilityRequirement(final String ns, final Map<String, Object> attrs, final Map<String, String> dirs, final Resource res) {
> - if ( ns == null ) {
> - throw new IllegalArgumentException("Namespace must not be null.");
> - }
> - namespace = ns;
> - attributes = attrs == null
> - ? Collections.<String, Object>emptyMap()
> - : Collections.unmodifiableMap(new HashMap<String, Object>(attrs));
> - directives = dirs == null
> - ? Collections.<String,String>emptyMap()
> - : Collections.unmodifiableMap(new HashMap<String,String>(dirs));
> - resource = res;
> + AbstractCapabilityRequirement(final Resource res, final String ns, final Map<String, String> dirs, final Map<String, Object> attrs) {
> + resource = Objects.requireNonNull(res, "Resource must not be null.");
> + namespace = Objects.requireNonNull(ns, "Namespace must not be null.");
> + directives = StringArrayMap.reduceMemory(dirs);
> + attributes = StringArrayMap.reduceMemory(attrs);
> }
>
> /**
> @@ -82,45 +76,98 @@ abstract class AbstractCapabilityRequire
> return resource;
> }
>
> - @Override
> - public int hashCode() {
> - final int prime = 31;
> - int result = 1;
> - result = prime * result + attributes.hashCode();
> - result = prime * result + directives.hashCode();
> - result = prime * result + namespace.hashCode();
> -
> - if (resource != null)
> - result = prime * result + resource.hashCode();
>
> - return result;
> + @Override
> + public boolean equals(Object o) {
> + if (this == o) return true;
> + if (o == null || getClass() != o.getClass()) return false;
> + AbstractCapabilityRequirement that = (AbstractCapabilityRequirement) o;
> + return Objects.equals(resource, that.resource) &&
> + Objects.equals(namespace, that.namespace) &&
> + Objects.equals(attributes, that.attributes) &&
> + Objects.equals(directives, that.directives);
> }
>
> @Override
> - public boolean equals(Object obj) {
> - if (this == obj)
> - return true;
> - if (obj == null)
> - return false;
> - if (getClass() != obj.getClass())
> - return false;
> - AbstractCapabilityRequirement other = (AbstractCapabilityRequirement) obj;
> - if (!namespace.equals(other.namespace))
> - return false;
> - if (!attributes.equals(other.attributes))
> - return false;
> - if (!directives.equals(other.directives))
> - return false;
> - if (resource == null) {
> - return other.resource == null;
> - } else {
> - return resource.equals(other.resource);
> - }
> + public int hashCode() {
> + return Objects.hash(resource, namespace, attributes, directives);
> }
>
> @Override
> public String toString() {
> - return getClass().getSimpleName() + " [resource=" + resource + ", namespace=" + namespace + ", attributes=" + attributes
> - + ", directives=" + directives + "]";
> + return toString(getResource(), getNamespace(), getAttributes(), getDirectives());
> + }
> +
> + public static String toString(Resource res, String namespace, Map<String, Object> attrs, Map<String, String> dirs) {
> + StringBuilder sb = new StringBuilder();
> + if (res != null) {
> + sb.append("[").append(res).append("] ");
> + }
> + sb.append(namespace);
> + for (String key : attrs.keySet()) {
> + sb.append("; ");
> + append(sb, key, attrs.get(key), true);
> + }
> + for (String key : dirs.keySet()) {
> + sb.append("; ");
> + append(sb, key, dirs.get(key), false);
> + }
> + return sb.toString();
> + }
> +
> + private static void append(StringBuilder sb, String key, Object val, boolean attribute) {
> + sb.append(key);
> + if (val instanceof Version) {
> + sb.append(":Version=");
> + sb.append(val);
> + } else if (val instanceof Long) {
> + sb.append(":Long=");
> + sb.append(val);
> + } else if (val instanceof Double) {
> + sb.append(":Double=");
> + sb.append(val);
> + } else if (val instanceof Iterable) {
> + Iterable<?> it = (Iterable<?>) val;
> + String scalar = null;
> + for (Object o : it) {
> + String ts;
> + if (o instanceof String) {
> + ts = "String";
> + } else if (o instanceof Long) {
> + ts = "Long";
> + } else if (o instanceof Double) {
> + ts = "Double";
> + } else if (o instanceof Version) {
> + ts = "Version";
> + } else {
> + throw new IllegalArgumentException("Unsupported scalar type: " + o);
> + }
> + if (scalar == null) {
> + scalar = ts;
> + } else if (!scalar.equals(ts)) {
> + throw new IllegalArgumentException("Unconsistent list type for attribute " + key);
> + }
> + }
> + sb.append(":List<").append(scalar).append(">=");
> + sb.append("\"");
> + boolean first = true;
> + for (Object o : it) {
> + if (first) {
> + first = false;
> + } else {
> + sb.append(",");
> + }
> + sb.append(o.toString().replace("\"", "\\\"").replace(",", "\\,"));
> + }
> + sb.append("\"");
> + } else {
> + sb.append(attribute ? "=" : ":=");
> + String s = val.toString();
> + if (s.matches("[0-9a-zA-Z_\\-.]*")) {
> + sb.append(s);
> + } else {
> + sb.append("\"").append(s.replace("\"", "\\\\")).append("\"");
> + }
> + }
> }
> }
>
> Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java
> URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java (original)
> +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilityImpl.java Fri Apr 20 09:23:35 2018
> @@ -16,35 +16,50 @@
> */
> package org.apache.felix.utils.resource;
>
> +import org.osgi.framework.Constants;
> import org.osgi.resource.Capability;
> import org.osgi.resource.Resource;
>
> +import java.util.Collections;
> +import java.util.HashSet;
> +import java.util.List;
> import java.util.Map;
> +import java.util.Set;
>
> /**
> * Implementation of the OSGi Capability interface.
> */
> public class CapabilityImpl extends AbstractCapabilityRequirement implements Capability {
> - /**
> - * Create a capability that is not associated with a resource.
> - * @param res The resource associated with the capability. May be null.
> - * @param ns The namespace of the capability.
> - * @param attrs The attributes of the capability.
> - * @param dirs The directives of the capability.
> - */
> - public CapabilityImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs) {
> - this(ns, attrs, dirs, null);
> - }
> +
> + protected final Set<String> mandatory;
>
> /**
> * Create a capability.
> + * @param res The resource associated with the capability.
> * @param ns The namespace of the capability.
> * @param attrs The attributes of the capability.
> * @param dirs The directives of the capability.
> - * @param res The resource associated with the capability. May be null.
> */
> - public CapabilityImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs, Resource res) {
> - super(ns, attrs, dirs, res);
> + public CapabilityImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
> + super(res, ns, dirs, attrs);
> +
> + // Handle mandatory directive
> + Set<String> mandatory = Collections.emptySet();
> + String value = this.directives.get(Constants.MANDATORY_DIRECTIVE);
> + if (value != null) {
> + List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
> + mandatory = new HashSet<>(names.size());
> + for (String name : names) {
> + // If attribute exists, then record it as mandatory.
> + if (this.attributes.containsKey(name)) {
> + mandatory.add(name);
> + // Otherwise, report an error.
> + } else {
> + throw new IllegalArgumentException("Mandatory attribute '" + name + "' does not exist.");
> + }
> + }
> + }
> + this.mandatory = mandatory;
> }
>
> /**
> @@ -54,6 +69,10 @@ public class CapabilityImpl extends Abst
> * @param resource The resource to be associated with the capability
> */
> public CapabilityImpl(Resource resource, Capability capability) {
> - this(capability.getNamespace(), capability.getAttributes(), capability.getDirectives(), resource);
> + this(resource, capability.getNamespace(), capability.getDirectives(), capability.getAttributes());
> + }
> +
> + public boolean isAttributeMandatory(String name) {
> + return !mandatory.isEmpty() && mandatory.contains(name);
> }
> }
>
> Added: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java
> URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java?rev=1829639&view=auto
> ==============================================================================
> --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java (added)
> +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/CapabilitySet.java Fri Apr 20 09:23:35 2018
> @@ -0,0 +1,469 @@
> +/*
> + * 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.utils.resource;
> +
> +import java.lang.reflect.Array;
> +import java.lang.reflect.Constructor;
> +import java.util.ArrayList;
> +import java.util.Collection;
> +import java.util.HashMap;
> +import java.util.HashSet;
> +import java.util.Iterator;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Map.Entry;
> +import java.util.Set;
> +import java.util.TreeMap;
> +
> +import org.apache.felix.utils.version.VersionTable;
> +import org.osgi.framework.Constants;
> +import org.osgi.framework.Version;
> +import org.osgi.resource.Capability;
> +
> +@SuppressWarnings("rawtypes")
> +public class CapabilitySet {
> +
> + private static final Class<?>[] STRING_CLASS = new Class[] {String.class};
> +
> + private final Map<String, Map<Object, Set<Capability>>> indices;
> + private final Set<Capability> capSet = new HashSet<>();
> +
> + public CapabilitySet(List<String> indexProps) {
> + indices = new TreeMap<>();
> + for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++) {
> + indices.put(indexProps.get(i), new HashMap<Object, Set<Capability>>());
> + }
> + }
> +
> + public void dump() {
> + for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
> + boolean header1 = false;
> + for (Entry<Object, Set<Capability>> entry2 : entry.getValue().entrySet()) {
> + boolean header2 = false;
> + for (Capability cap : entry2.getValue()) {
> + if (!header1) {
> + System.out.println(entry.getKey() + ":");
> + header1 = true;
> + }
> + if (!header2) {
> + System.out.println(" " + entry2.getKey());
> + header2 = true;
> + }
> + System.out.println(" " + cap);
> + }
> + }
> + }
> + }
> +
> + public void addCapability(Capability cap) {
> + capSet.add(cap);
> +
> + // Index capability.
> + for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
> + Object value = cap.getAttributes().get(entry.getKey());
> + if (value != null) {
> + if (value.getClass().isArray()) {
> + value = convertArrayToList(value);
> + }
> +
> + Map<Object, Set<Capability>> index = entry.getValue();
> +
> + if (value instanceof Collection) {
> + Collection c = (Collection) value;
> + for (Object o : c) {
> + indexCapability(index, cap, o);
> + }
> + } else {
> + indexCapability(index, cap, value);
> + }
> + }
> + }
> + }
> +
> + private void indexCapability(Map<Object, Set<Capability>> index, Capability cap, Object capValue) {
> + // TODO: when JDK8, should be:
> + // TODO: index.computeIfAbsent(capValue, k -> new HashSet<>()).add(cap);
> + Set<Capability> set = index.get(capValue);
> + if (set == null) {
> + set = new HashSet<>();
> + index.put(capValue, set);
> + }
> + set.add(cap);
> + }
> +
> + public void removeCapability(Capability cap) {
> + if (capSet.remove(cap)) {
> + for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
> + Object value = cap.getAttributes().get(entry.getKey());
> + if (value != null) {
> + if (value.getClass().isArray()) {
> + value = convertArrayToList(value);
> + }
> +
> + Map<Object, Set<Capability>> index = entry.getValue();
> +
> + if (value instanceof Collection) {
> + Collection c = (Collection) value;
> + for (Object o : c) {
> + deindexCapability(index, cap, o);
> + }
> + } else {
> + deindexCapability(index, cap, value);
> + }
> + }
> + }
> + }
> + }
> +
> + private void deindexCapability(
> + Map<Object, Set<Capability>> index, Capability cap, Object value) {
> + Set<Capability> caps = index.get(value);
> + if (caps != null) {
> + caps.remove(cap);
> + if (caps.isEmpty()) {
> + index.remove(value);
> + }
> + }
> + }
> +
> + public Set<Capability> match(SimpleFilter sf, boolean obeyMandatory) {
> + Set<Capability> matches = match(capSet, sf);
> + return obeyMandatory
> + ? matchMandatory(matches, sf)
> + : matches;
> + }
> +
> + @SuppressWarnings("unchecked")
> + private Set<Capability> match(Set<Capability> caps, SimpleFilter sf) {
> + Set<Capability> matches = new HashSet<>();
> +
> + if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
> + matches.addAll(caps);
> + } else if (sf.getOperation() == SimpleFilter.AND) {
> + // Evaluate each subfilter against the remaining capabilities.
> + // For AND we calculate the intersection of each subfilter.
> + // We can short-circuit the AND operation if there are no
> + // remaining capabilities.
> + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> + for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++) {
> + matches = match(caps, sfs.get(i));
> + caps = matches;
> + }
> + } else if (sf.getOperation() == SimpleFilter.OR) {
> + // Evaluate each subfilter against the remaining capabilities.
> + // For OR we calculate the union of each subfilter.
> + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> + for (SimpleFilter sf1 : sfs) {
> + matches.addAll(match(caps, sf1));
> + }
> + } else if (sf.getOperation() == SimpleFilter.NOT) {
> + // Evaluate each subfilter against the remaining capabilities.
> + // For OR we calculate the union of each subfilter.
> + matches.addAll(caps);
> + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> + for (SimpleFilter sf1 : sfs) {
> + matches.removeAll(match(caps, sf1));
> + }
> + } else {
> + Map<Object, Set<Capability>> index = indices.get(sf.getName());
> + if ((sf.getOperation() == SimpleFilter.EQ) && (index != null)) {
> + Set<Capability> existingCaps = index.get(sf.getValue());
> + if (existingCaps != null) {
> + matches.addAll(existingCaps);
> + matches.retainAll(caps);
> + }
> + } else {
> + for (Capability cap : caps) {
> + Object lhs = cap.getAttributes().get(sf.getName());
> + if (lhs != null) {
> + if (compare(lhs, sf.getValue(), sf.getOperation())) {
> + matches.add(cap);
> + }
> + }
> + }
> + }
> + }
> +
> + return matches;
> + }
> +
> + public static boolean matches(Capability cap, SimpleFilter sf) {
> + return matchesInternal(cap, sf) && matchMandatory(cap, sf);
> + }
> +
> + @SuppressWarnings("unchecked")
> + private static boolean matchesInternal(Capability cap, SimpleFilter sf) {
> + boolean matched = true;
> +
> + if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
> + matched = true;
> + } else if (sf.getOperation() == SimpleFilter.AND) {
> + // Evaluate each subfilter against the remaining capabilities.
> + // For AND we calculate the intersection of each subfilter.
> + // We can short-circuit the AND operation if there are no
> + // remaining capabilities.
> + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> + for (int i = 0; matched && (i < sfs.size()); i++) {
> + matched = matchesInternal(cap, sfs.get(i));
> + }
> + } else if (sf.getOperation() == SimpleFilter.OR) {
> + // Evaluate each subfilter against the remaining capabilities.
> + // For OR we calculate the union of each subfilter.
> + matched = false;
> + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> + for (int i = 0; !matched && (i < sfs.size()); i++) {
> + matched = matchesInternal(cap, sfs.get(i));
> + }
> + } else if (sf.getOperation() == SimpleFilter.NOT) {
> + // Evaluate each subfilter against the remaining capabilities.
> + // For OR we calculate the union of each subfilter.
> + List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
> + for (SimpleFilter sf1 : sfs) {
> + matched = !(matchesInternal(cap, sf1));
> + }
> + } else {
> + matched = false;
> + Object lhs = cap.getAttributes().get(sf.getName());
> + if (lhs != null) {
> + matched = compare(lhs, sf.getValue(), sf.getOperation());
> + }
> + }
> +
> + return matched;
> + }
> +
> + private static Set<Capability> matchMandatory(
> + Set<Capability> caps, SimpleFilter sf) {
> + for (Iterator<Capability> it = caps.iterator(); it.hasNext();) {
> + Capability cap = it.next();
> + if (!matchMandatory(cap, sf)) {
> + it.remove();
> + }
> + }
> + return caps;
> + }
> +
> + private static boolean matchMandatory(Capability cap, SimpleFilter sf) {
> + if (cap instanceof CapabilityImpl) {
> + for (Entry<String, Object> entry : cap.getAttributes().entrySet()) {
> + if (((CapabilityImpl) cap).isAttributeMandatory(entry.getKey())
> + && !matchMandatoryAttribute(entry.getKey(), sf)) {
> + return false;
> + }
> + }
> + } else {
> + String value = cap.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
> + if (value != null) {
> + List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
> + for (Entry<String, Object> entry : cap.getAttributes().entrySet()) {
> + if (names.contains(entry.getKey())
> + && !matchMandatoryAttribute(entry.getKey(), sf)) {
> + return false;
> + }
> + }
> + }
> +
> + }
> + return true;
> + }
> +
> + private static boolean matchMandatoryAttribute(String attrName, SimpleFilter sf) {
> + if ((sf.getName() != null) && sf.getName().equals(attrName)) {
> + return true;
> + } else if (sf.getOperation() == SimpleFilter.AND) {
> + List list = (List) sf.getValue();
> + for (Object aList : list) {
> + SimpleFilter sf2 = (SimpleFilter) aList;
> + if ((sf2.getName() != null)
> + && sf2.getName().equals(attrName)) {
> + return true;
> + }
> + }
> + }
> + return false;
> + }
> +
> + @SuppressWarnings("unchecked")
> + private static boolean compare(Object lhs, Object rhsUnknown, int op) {
> + if (lhs == null) {
> + return false;
> + }
> +
> + // If this is a PRESENT operation, then just return true immediately
> + // since we wouldn't be here if the attribute wasn't present.
> + if (op == SimpleFilter.PRESENT) {
> + return true;
> + }
> +
> + // If the type is comparable, then we can just return the
> + // result immediately.
> + if (lhs instanceof Comparable) {
> + // Spec says SUBSTRING is false for all types other than string.
> + if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String)) {
> + return false;
> + }
> +
> + Object rhs;
> + if (op == SimpleFilter.SUBSTRING) {
> + rhs = rhsUnknown;
> + } else {
> + try {
> + rhs = coerceType(lhs, (String) rhsUnknown);
> + } catch (Exception ex) {
> + return false;
> + }
> + }
> +
> + switch (op) {
> + case SimpleFilter.EQ:
> + try {
> + return ((Comparable) lhs).compareTo(rhs) == 0;
> + } catch (Exception ex) {
> + return false;
> + }
> + case SimpleFilter.GTE:
> + try {
> + return ((Comparable) lhs).compareTo(rhs) >= 0;
> + } catch (Exception ex) {
> + return false;
> + }
> + case SimpleFilter.LTE:
> + try {
> + return ((Comparable) lhs).compareTo(rhs) <= 0;
> + } catch (Exception ex) {
> + return false;
> + }
> + case SimpleFilter.APPROX:
> + return compareApproximate(lhs, rhs);
> + case SimpleFilter.SUBSTRING:
> + return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
> + default:
> + throw new RuntimeException("Unknown comparison operator: " + op);
> + }
> + }
> +
> + // If the LHS is not a comparable or boolean, check if it is an
> + // array. If so, convert it to a list so we can treat it as a
> + // collection.
> + if (lhs.getClass().isArray()) {
> + lhs = convertArrayToList(lhs);
> + }
> +
> + // If LHS is a collection, then call compare() on each element
> + // of the collection until a match is found.
> + if (lhs instanceof Collection) {
> + for (Object o : (Collection) lhs) {
> + if (compare(o, rhsUnknown, op)) {
> + return true;
> + }
> + }
> +
> + return false;
> + }
> +
> + // Spec says SUBSTRING is false for all types other than string.
> + if (op == SimpleFilter.SUBSTRING) {
> + return false;
> + }
> +
> + // Since we cannot identify the LHS type, then we can only perform
> + // equality comparison.
> + try {
> + return lhs.equals(coerceType(lhs, (String) rhsUnknown));
> + } catch (Exception ex) {
> + return false;
> + }
> + }
> +
> + private static boolean compareApproximate(Object lhs, Object rhs) {
> + if (rhs instanceof String) {
> + return removeWhitespace((String) lhs)
> + .equalsIgnoreCase(removeWhitespace((String) rhs));
> + } else if (rhs instanceof Character) {
> + return Character.toLowerCase((Character) lhs)
> + == Character.toLowerCase((Character) rhs);
> + }
> + return lhs.equals(rhs);
> + }
> +
> + private static String removeWhitespace(String s) {
> + StringBuilder sb = new StringBuilder(s.length());
> + for (int i = 0; i < s.length(); i++) {
> + if (!Character.isWhitespace(s.charAt(i))) {
> + sb.append(s.charAt(i));
> + }
> + }
> + return sb.toString();
> + }
> +
> + private static Object coerceType(Object lhs, String rhsString) throws Exception {
> + // If the LHS expects a string, then we can just return
> + // the RHS since it is a string.
> + if (lhs.getClass() == rhsString.getClass()) {
> + return rhsString;
> + }
> +
> + // Try to convert the RHS type to the LHS type by using
> + // the string constructor of the LHS class, if it has one.
> + Object rhs;
> + try {
> + if (lhs instanceof Version) {
> + rhs = VersionTable.getVersion(rhsString, false);
> + } else
> + // The Character class is a special case, since its constructor
> + // does not take a string, so handle it separately.
> + if (lhs instanceof Character) {
> + rhs = rhsString.charAt(0);
> + } else {
> + // Spec says we should trim number types.
> + if ((lhs instanceof Number) || (lhs instanceof Boolean)) {
> + rhsString = rhsString.trim();
> + }
> + Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
> + ctor.setAccessible(true);
> + rhs = ctor.newInstance(rhsString);
> + }
> + } catch (Exception ex) {
> + throw new Exception(
> + "Could not instantiate class "
> + + lhs.getClass().getName()
> + + " from string constructor with argument '"
> + + rhsString + "' because " + ex
> + );
> + }
> +
> + return rhs;
> + }
> +
> + /**
> + * This is an ugly utility method to convert an array of primitives
> + * to an array of primitive wrapper objects. This method simplifies
> + * processing LDAP filters since the special case of primitive arrays
> + * can be ignored.
> + *
> + * @param array An array of primitive types.
> + * @return An corresponding array using pritive wrapper objects.
> + */
> + private static List<Object> convertArrayToList(Object array) {
> + int len = Array.getLength(array);
> + List<Object> list = new ArrayList<>(len);
> + for (int i = 0; i < len; i++) {
> + list.add(Array.get(array, i));
> + }
> + return list;
> + }
> +}
>
> Modified: felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java
> URL: http://svn.apache.org/viewvc/felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java?rev=1829639&r1=1829638&r2=1829639&view=diff
> ==============================================================================
> --- felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java (original)
> +++ felix/trunk/utils/src/main/java/org/apache/felix/utils/resource/RequirementImpl.java Fri Apr 20 09:23:35 2018
> @@ -16,6 +16,8 @@
> */
> package org.apache.felix.utils.resource;
>
> +import org.osgi.framework.Constants;
> +import org.osgi.resource.Capability;
> import org.osgi.resource.Namespace;
> import org.osgi.resource.Requirement;
> import org.osgi.resource.Resource;
> @@ -27,26 +29,19 @@ import java.util.Map;
> * Implementation of the OSGi Requirement interface.
> */
> public class RequirementImpl extends AbstractCapabilityRequirement implements Requirement {
> - /**
> - * Create a requirement that is not associated with a resource.
> - * @param res The resource associated with the requirement.
> - * @param ns The namespace of the requirement.
> - * @param attrs The attributes of the requirement.
> - * @param dirs The directives of the requirement.
> - */
> - public RequirementImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs) {
> - this(ns, attrs, dirs, null);
> - }
> +
> + private final SimpleFilter filter;
> + private final boolean optional;
>
> /**
> * Create a requirement.
> + * @param res The resource associated with the requirement.
> * @param ns The namespace of the requirement.
> * @param attrs The attributes of the requirement.
> * @param dirs The directives of the requirement.
> - * @param res The resource associated with the requirement.
> */
> - public RequirementImpl(String ns, Map<String, Object> attrs, Map<String, String> dirs, Resource res) {
> - super(ns, attrs, dirs, res);
> + public RequirementImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
> + this(res, ns, dirs, attrs, null);
> }
>
> /**
> @@ -54,14 +49,16 @@ public class RequirementImpl extends Abs
> *
> * This is a convenience method that creates a requirement with
> * an empty attributes map and a single 'filter' directive.
> + * @param res The resource associated with the requirement.
> * @param ns The namespace for the requirement.
> * @param filter The filter.
> */
> - public RequirementImpl(String ns, String filter)
> + public RequirementImpl(Resource res, String ns, String filter)
> {
> - this(ns, Collections.<String, Object>emptyMap(),
> - filter == null ? Collections.<String, String> emptyMap() :
> - Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter));
> + this(res, ns,
> + filter == null ? Collections.<String, String>emptyMap() :
> + Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter),
> + null);
> }
>
> /**
> @@ -71,6 +68,26 @@ public class RequirementImpl extends Abs
> * @param resource The resource to be associated with the requirement
> */
> public RequirementImpl(Resource resource, Requirement requirement) {
> - this(requirement.getNamespace(), requirement.getAttributes(), requirement.getDirectives(), resource);
> + this(resource, requirement.getNamespace(), requirement.getDirectives(), requirement.getAttributes());
> + }
> +
> + public RequirementImpl(Resource resource, String path, Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter sf) {
> + super(resource, path, dirs, attrs);
> + this.filter = sf != null ? sf : SimpleFilter.convert(attributes);
> + // Find resolution import directives.
> + this.optional = Constants.RESOLUTION_OPTIONAL.equals(directives.get(Constants.RESOLUTION_DIRECTIVE));
> + }
> +
> + public boolean matches(Capability cap) {
> + return CapabilitySet.matches(cap, getFilter());
> }
> +
> + public boolean isOptional() {
> + return optional;
> + }
> +
> + public SimpleFilter getFilter() {
> + return filter;
> + }
> +
> }
>
>
--
Karl Pauls
karlpauls@gmail.com