Bug 571139 - handle require wires correctly for visible packages

Change-Id: I6db5e32f45a7c367f47d860d1bf729be69f61ac4
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
index 1304ab3..75b1c00 100755
--- a/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
+++ b/bundles/org.eclipse.equinox.console/src/org/eclipse/equinox/console/commands/EquinoxCommandProvider.java
@@ -29,11 +29,14 @@
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Properties;
 import java.util.Set;
 import java.util.StringTokenizer;
@@ -75,7 +78,6 @@
 import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
 import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
 import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
-//import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.permissionadmin.PermissionAdmin;
@@ -881,7 +883,7 @@
 			title = true;
 
 			// Get all resolved imports
-			Map<String, List<PackageSource>> packages = getPackagesInternal(wiring);
+			Map<String, Set<PackageSource>> packages = getPackagesInternal(wiring);
 			List<BundleRequirement> unresolvedImports = getUnresolvedImports(packages, wiring);
 
 			title = printImportedPackages(packages, title);
@@ -940,14 +942,14 @@
 	}
 
 	private List<BundleRequirement> getUnresolvedImports(
-			Map<String, List<PackageSource>> packages, BundleWiring wiring) {
+			Map<String, Set<PackageSource>> packages, BundleWiring wiring) {
 
 		// TODO need to get this information
 		return Collections.emptyList();
 	}
 
-	private boolean printImportedPackages(Map<String, List<PackageSource>> packages, boolean title) {
-		for (List<PackageSource> packageList : packages.values()) {
+	private boolean printImportedPackages(Map<String, Set<PackageSource>> packages, boolean title) {
+		for (Set<PackageSource> packageList : packages.values()) {
 			for (PackageSource packageSource : packageList) {
 				if (title) {
 					System.out.print("  "); //$NON-NLS-1$
@@ -1656,10 +1658,10 @@
 			return;
 		}
 
-		Map<String, List<PackageSource>> packages = getPackagesInternal(wiring);
-		for (List<PackageSource> packageSources : packages.values()) {
+		Map<String, Set<PackageSource>> packages = getPackagesInternal(wiring);
+		for (Set<PackageSource> packageSources : packages.values()) {
 			for (PackageSource packageSource : packageSources) {
-				printCapability("  ", packageSource.getCapability(), packageSource.getWire(), PackageNamespace.PACKAGE_NAMESPACE);
+				printCapability("  ", packageSource.getCapability(), packageSource.getWire(), PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
 			}
 		}
 	}
@@ -1680,82 +1682,103 @@
 		BundleWire getWire() {
 			return wire;
 		}
+
+		@Override
+		public boolean equals(Object o) {
+			if (o instanceof PackageSource) {
+				return Objects.equals(cap, ((PackageSource) o).cap) && Objects.equals(wire.getProvider(), ((PackageSource) o).wire.getProvider());
+			}
+			return false;
+		}
+
+		@Override
+		public int hashCode() {
+			return cap.hashCode() ^ wire.getProvider().hashCode();
+		}
 	}
-	private Map<String, List<PackageSource>> getPackagesInternal(BundleWiring wiring) {
-		Map<String, List<PackageSource>> packages = new TreeMap<>();
+	private Map<String, Set<PackageSource>> getPackagesInternal(BundleWiring wiring) {
+		return getPackagesInternal0(wiring, null);
+	}
+	private Map<String, Set<PackageSource>> getPackagesInternal0(BundleWiring wiring, Map<BundleWiring, Map<String, Set<PackageSource>>> allSources) {
+		if (allSources == null) {
+			allSources = new HashMap<>();
+		}
+		Map<String, Set<PackageSource>> packages = allSources.get(wiring);
+		if (packages != null) {
+			return packages;
+		}
+		packages = new TreeMap<>();
+		allSources.put(wiring, packages);
+
 		// first get the imported packages
 		List<BundleWire> packageWires = wiring.getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE);
 		Set<String> importedPackageNames = new HashSet<>();
 		for (BundleWire packageWire : packageWires) {
 			String packageName = (String) packageWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
 			importedPackageNames.add(packageName);
-			List<PackageSource> packageSources = new ArrayList<>();
-			packageSources.add(new PackageSource(packageWire.getCapability(), packageWire));
-			packages.put(packageName, packageSources);
+			addAggregatePackageSource(packageWire.getCapability(), packageName, packageWire, packages, allSources);
 		}
 
 		// now get packages from required bundles
 		for (BundleWire requiredWire : wiring.getRequiredWires(BundleNamespace.BUNDLE_NAMESPACE)) {
-			getRequiredBundlePackages(requiredWire, importedPackageNames, packages);
+			getRequiredBundlePackages(requiredWire, importedPackageNames, packages, allSources);
 		}
 
 		return packages;
 	}
 
-	private void getRequiredBundlePackages(BundleWire requiredWire, Set<String> importedPackageNames, Map<String, List<PackageSource>> packages) {
-			BundleWiring providerWiring = requiredWire.getProviderWiring();
-			for (BundleCapability packageCapability : providerWiring.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
-				String packageName = (String) packageCapability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
-				if (!importedPackageNames.contains(packageName)) {
-					List<PackageSource> packageSources = packages.get(packageName);
-					if (packageSources == null) {
-						packageSources = new ArrayList<>();
-						packages.put(packageName, packageSources);
-					}
-					boolean sourceFound = false;
-					for (PackageSource packageSource : packageSources) {
-						sourceFound |= packageCapability.equals(packageSource.getCapability());
-						if (sourceFound) {
-							break;
-						}
-					}
-					if (!sourceFound) {
-						packageSources.add(new PackageSource(packageCapability, requiredWire));
-					}
-				}
-			}
+	private void addAggregatePackageSource(BundleCapability packageCap, String packageName, BundleWire wire, Map<String, Set<PackageSource>> packages, Map<BundleWiring, Map<String, Set<PackageSource>>> allSources) {
+		Set<PackageSource> packageSources = packages.get(packageName);
+		if (packageSources == null) {
+			packageSources = new LinkedHashSet<>();
+			packages.put(packageName, packageSources);
+		}
+		packageSources.add(new PackageSource(packageCap, wire));
+		// source may be a split package aggregate
+		Set<PackageSource> providerSource = getPackagesInternal0(wire.getProviderWiring(), allSources).get(packageName);
+		if (providerSource != null) {
+			packageSources.addAll(providerSource);
+		}
+	}
 
-			// get substituted packages
-			Set<String> declaredPackageNames = new HashSet<>();
-			for (BundleCapability declaredPackage : providerWiring.getRevision().getDeclaredCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
+	private void getRequiredBundlePackages(BundleWire requiredWire, Set<String> importedPackageNames, Map<String, Set<PackageSource>> packages, Map<BundleWiring, Map<String, Set<PackageSource>>> allSources) {
+		BundleWiring providerWiring = requiredWire.getProviderWiring();
+		for (BundleCapability packageCapability : providerWiring.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
+			String packageName = (String) packageCapability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+			// if imported then packages from required bundles do not get added
+			if (!importedPackageNames.contains(packageName)) {
+				addAggregatePackageSource(packageCapability, packageName, requiredWire, packages, allSources);
+			}
+		}
+
+		// get the declared packages
+		Set<String> declaredPackageNames = new HashSet<>();
+		for (BundleCapability declaredPackage : providerWiring.getRevision().getDeclaredCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
+			declaredPackageNames.add((String) declaredPackage.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
+		}
+		// and from attached fragments
+		for (BundleWire fragmentWire : providerWiring.getProvidedWires(HostNamespace.HOST_NAMESPACE)) {
+			for (BundleCapability declaredPackage : fragmentWire.getRequirer().getDeclaredCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
 				declaredPackageNames.add((String) declaredPackage.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
 			}
-			// and fragments
-			for (BundleWire fragmentWire : providerWiring.getProvidedWires(HostNamespace.HOST_NAMESPACE)) {
-				for (BundleCapability declaredPackage : fragmentWire.getRequirer().getDeclaredCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
-					declaredPackageNames.add((String) declaredPackage.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
-				}
-			}
+		}
 
-			for (BundleWire packageWire : providerWiring.getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE)) {
-				String packageName = (String) packageWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
-				if (declaredPackageNames.contains(packageName)) {
-					List<PackageSource> packageSources = packages.get(packageName);
-					if (packageSources == null) {
-						packageSources = new ArrayList<>();
-						packages.put(packageName, packageSources);
-					}
-					packageSources.add(new PackageSource(packageWire.getCapability(), packageWire));
-				}
+		for (BundleWire packageWire : providerWiring.getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE)) {
+			String packageName = (String) packageWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+			if (!importedPackageNames.contains(packageName) && declaredPackageNames.contains(packageName)) {
+				// if the package is a declared capability AND the wiring imports the package
+				// then it is substituted
+				addAggregatePackageSource(packageWire.getCapability(), packageName, packageWire, packages, allSources);
 			}
+		}
 
-			// now get packages from re-exported requires of the required bundle
-			for (BundleWire providerBundleWire : providerWiring.getRequiredWires(BundleNamespace.BUNDLE_NAMESPACE)) {
-				String visibilityDirective = providerBundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
-				if (BundleNamespace.VISIBILITY_REEXPORT.equals(visibilityDirective)) {
-					getRequiredBundlePackages(providerBundleWire, importedPackageNames, packages);
-				}
+		// now get packages from re-exported requires of the required bundle
+		for (BundleWire providerBundleWire : providerWiring.getRequiredWires(BundleNamespace.BUNDLE_NAMESPACE)) {
+			String visibilityDirective = providerBundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
+			if (BundleNamespace.VISIBILITY_REEXPORT.equals(visibilityDirective)) {
+				getRequiredBundlePackages(providerBundleWire, importedPackageNames, packages, allSources);
 			}
+		}
 	}
 
 	/**