Bug 558380 - Augmenting existing capability attributes and directives

Change-Id: I6fcfc7679cc68bbbcd496fc37dc344141a5dd9dd
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java b/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java
index fc9d094..f211bb9 100644
--- a/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java
+++ b/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java
@@ -31,6 +31,7 @@
 import org.eclipse.osgi.container.Module;
 import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent;
 import org.eclipse.osgi.container.ModuleRevisionBuilder;
+import org.eclipse.osgi.container.ModuleRevisionBuilder.GenericInfo;
 import org.eclipse.osgi.internal.hookregistry.ActivatorHookFactory;
 import org.eclipse.osgi.internal.hookregistry.HookConfigurator;
 import org.eclipse.osgi.internal.hookregistry.HookRegistry;
@@ -105,6 +106,14 @@
 					attrs.put("test.origin", origin.getLocation());
 					builder.addCapability("test.file.path", dirs, attrs);
 				}
+				if (TestHookConfigurator.adaptCapabilityAttribute) {
+					for (GenericInfo c : builder.getCapabilities()) {
+						if (BundleNamespace.BUNDLE_NAMESPACE.equals(c.getNamespace())) {
+							c.getAttributes().put("matching.attribute", "testAttribute");
+							c.getDirectives().put("matching.directive", "testDirective");
+						}
+					}
+				}
 				return builder;
 			}
 		}
@@ -163,6 +172,7 @@
 	public static volatile boolean validateCalled;
 	public static volatile boolean deletingGenerationCalled;
 	public static volatile boolean adaptManifest;
+	public static volatile boolean adaptCapabilityAttribute;
 	public static volatile boolean replaceModuleBuilder;
 	public static volatile boolean handleContentConnection;
 	public static volatile boolean returnNullStorageHook;
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java
index 9e8c2d6..b776cfb 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java
@@ -29,6 +29,8 @@
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.resource.Capability;
 import org.osgi.service.packageadmin.PackageAdmin;
@@ -44,6 +46,7 @@
 	private static final String HOOK_CONFIGURATOR_FIELD_VALIDATE_CALLED = "validateCalled";
 	private static final String HOOK_CONFIGURATOR_FIELD_DELETING_CALLED = "deletingGenerationCalled";
 	private static final String HOOK_CONFIGURATOR_FIELD_ADAPT_MANIFEST = "adaptManifest";
+	private static final String HOOK_CONFIGURATOR_FIELD_ADAPT_CAPABILITY_ATTRIBUTE = "adaptCapabilityAttribute";
 	private static final String HOOK_CONFIGURATOR_FIELD_REPLACE_BUILDER = "replaceModuleBuilder";
 	private static final String HOOK_CONFIGURATOR_FIELD_HANDLE_CONTENT = "handleContentConnection";
 	private static final String HOOK_CONFIGURATOR_FIELD_NULL_STORAGE_HOOK = "returnNullStorageHook";
@@ -223,6 +226,15 @@
 		assertEquals("Wrong BSN.", "replace", b.getSymbolicName());
 		testCaps = b.adapt(BundleRevision.class).getCapabilities("replace");
 		assertEquals("Wrong number of capabilities.", 1, testCaps.size());
+
+		setFactoryClassReplaceBuilder(false);
+		setFactoryClassAdaptCapabilityAttribute(true);
+		b.uninstall();
+		installBundle();
+		b = framework.getBundleContext().getBundle(location);
+		BundleCapability bundleCap = b.adapt(BundleRevision.class).getDeclaredCapabilities(BundleNamespace.BUNDLE_NAMESPACE).iterator().next();
+		assertEquals("Wrong attribute value", "testAttribute", bundleCap.getAttributes().get("matching.attribute"));
+		assertEquals("Wrong attribute value", "testDirective", bundleCap.getDirectives().get("matching.directive"));
 	}
 
 	@SuppressWarnings("deprecation")
@@ -390,6 +402,11 @@
 		clazz.getField(HOOK_CONFIGURATOR_FIELD_ADAPT_MANIFEST).set(null, value);
 	}
 
+	private void setFactoryClassAdaptCapabilityAttribute(boolean value) throws Exception {
+		Class<?> clazz = classLoader.loadClass(HOOK_CONFIGURATOR_CLASS);
+		clazz.getField(HOOK_CONFIGURATOR_FIELD_ADAPT_CAPABILITY_ATTRIBUTE).set(null, value);
+	}
+
 	private void setFactoryClassReplaceBuilder(boolean value) throws Exception {
 		Class<?> clazz = classLoader.loadClass(HOOK_CONFIGURATOR_CLASS);
 		clazz.getField(HOOK_CONFIGURATOR_FIELD_REPLACE_BUILDER).set(null, value);
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java
index 801b085..2239908 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java
@@ -15,6 +15,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -60,11 +61,27 @@
 			return Collections.emptyList();
 		List<ModuleCapability> result = new ArrayList<>(capabilityInfos.size());
 		for (GenericInfo info : capabilityInfos) {
-			result.add(new ModuleCapability(info.namespace, info.directives, info.attributes, this));
+			if (info.mutable) {
+				result.add(new ModuleCapability(info.namespace, copyUnmodifiableMap(info.directives), copyUnmodifiableMap(info.attributes), this));
+			} else {
+				result.add(new ModuleCapability(info.namespace, info.directives, info.attributes, this));
+			}
 		}
 		return result;
 	}
 
+	private static <K, V> Map<K, V> copyUnmodifiableMap(Map<K, V> map) {
+		int size = map.size();
+		if (size == 0) {
+			return Collections.emptyMap();
+		}
+		if (size == 1) {
+			Map.Entry<K, V> entry = map.entrySet().iterator().next();
+			return Collections.singletonMap(entry.getKey(), entry.getValue());
+		}
+		return Collections.unmodifiableMap(new HashMap<>(map));
+	}
+
 	private List<ModuleRequirement> createRequirements(List<GenericInfo> requirementInfos) {
 		if (requirementInfos == null || requirementInfos.isEmpty())
 			return Collections.emptyList();
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevisionBuilder.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevisionBuilder.java
index 5cc6ac0..a3b5e4b 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevisionBuilder.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevisionBuilder.java
@@ -18,7 +18,6 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.eclipse.osgi.internal.framework.FilterImpl;
@@ -53,11 +52,13 @@
 		final String namespace;
 		final Map<String, String> directives;
 		final Map<String, Object> attributes;
+		final boolean mutable;
 
-		GenericInfo(String namespace, Map<String, String> directives, Map<String, Object> attributes) {
+		GenericInfo(String namespace, Map<String, String> directives, Map<String, Object> attributes, boolean mutable) {
 			this.namespace = namespace;
 			this.directives = directives;
 			this.attributes = attributes;
+			this.mutable = mutable;
 		}
 
 		/**
@@ -295,23 +296,11 @@
 		}
 	}
 
-	private static void addGenericInfo(List<GenericInfo> infos, String namespace, Map<String, String> directives, Map<String, Object> attributes) {
+	private void addGenericInfo(List<GenericInfo> infos, String namespace, Map<String, String> directives, Map<String, Object> attributes) {
 		if (infos == null) {
 			infos = new ArrayList<>();
 		}
-		infos.add(new GenericInfo(namespace, copyUnmodifiableMap(directives), copyUnmodifiableMap(attributes)));
-	}
-
-	private static <K, V> Map<K, V> copyUnmodifiableMap(Map<K, V> map) {
-		int size = map.size();
-		if (size == 0) {
-			return Collections.emptyMap();
-		}
-		if (size == 1) {
-			Map.Entry<K, V> entry = map.entrySet().iterator().next();
-			return Collections.singletonMap(entry.getKey(), entry.getValue());
-		}
-		return Collections.unmodifiableMap(new HashMap<>(map));
+		infos.add(new GenericInfo(namespace, directives, attributes, true));
 	}
 
 	void basicAddCapability(String namespace, Map<String, String> directives, Map<String, Object> attributes) {
@@ -323,7 +312,7 @@
 	}
 
 	private static void basicAddGenericInfo(List<GenericInfo> infos, String namespace, Map<String, String> directives, Map<String, Object> attributes) {
-		infos.add(new GenericInfo(namespace, unmodifiableMap(directives), unmodifiableMap(attributes)));
+		infos.add(new GenericInfo(namespace, unmodifiableMap(directives), unmodifiableMap(attributes), false));
 	}
 
 	@SuppressWarnings("unchecked")