Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2012-08-17 16:09:44 +0000
committerThomas Watson2012-10-02 18:54:41 +0000
commit31fa1aac8b141ce36d6477d4107209e543269e86 (patch)
tree37b142dd2a987a3abcc4df1cda5d95f3655708ec /bundles/org.eclipse.osgi.compatibility.state
parent4f277ed781bfb41298899656078984f65238012d (diff)
downloadrt.equinox.framework-31fa1aac8b141ce36d6477d4107209e543269e86.tar.gz
rt.equinox.framework-31fa1aac8b141ce36d6477d4107209e543269e86.tar.xz
rt.equinox.framework-31fa1aac8b141ce36d6477d4107209e543269e86.zip
initial checking of compatibility state fragment
Diffstat (limited to 'bundles/org.eclipse.osgi.compatibility.state')
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/.classpath7
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/.gitignore2
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/.project28
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.pde.core.prefs3
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/META-INF/MANIFEST.MF7
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/build.properties4
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/BundleConstraint.java28
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java17
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java17
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericCapability.java51
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericConstraint.java71
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GroupingChecker.java433
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/MappedList.java82
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/PermissionChecker.java96
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverBundle.java664
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverConstraint.java151
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverExport.java47
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java2243
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImport.java45
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionHashMap.java109
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionSupplier.java71
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java174
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java78
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java1244
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java159
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java239
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java146
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java200
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java174
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ImportPackageSpecificationImpl.java263
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java220
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeSpecificationImpl.java180
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ReadOnlyState.java236
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java82
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateBuilder.java913
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java158
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java646
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateImpl.java1340
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMessages.properties35
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMsg.java46
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java581
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateReader.java873
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateWriter.java711
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/UserState.java63
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java251
46 files changed, 13195 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi.compatibility.state/.classpath b/bundles/org.eclipse.osgi.compatibility.state/.classpath
new file mode 100644
index 000000000..ad32c83a7
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.osgi.compatibility.state/.gitignore b/bundles/org.eclipse.osgi.compatibility.state/.gitignore
new file mode 100644
index 000000000..07a846bd7
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+.DS_Store
diff --git a/bundles/org.eclipse.osgi.compatibility.state/.project b/bundles/org.eclipse.osgi.compatibility.state/.project
new file mode 100644
index 000000000..50487e24b
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.osgi.compatibility.state</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..c537b6306
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 000000000..f29e940a0
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/bundles/org.eclipse.osgi.compatibility.state/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.compatibility.state/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..990af6c69
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/META-INF/MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: State
+Bundle-SymbolicName: org.eclipse.osgi.compatibility.state
+Bundle-Version: 1.0.0.qualifier
+Fragment-Host: org.eclipse.osgi;bundle-version="3.10.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/bundles/org.eclipse.osgi.compatibility.state/build.properties b/bundles/org.eclipse.osgi.compatibility.state/build.properties
new file mode 100644
index 000000000..34d2e4d2d
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/BundleConstraint.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/BundleConstraint.java
new file mode 100644
index 000000000..0f933f41a
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/BundleConstraint.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.*;
+
+/*
+ * A companion to BundleSpecification from the state for use while resolving
+ */
+public class BundleConstraint extends ResolverConstraint {
+ BundleConstraint(ResolverBundle bundle, VersionConstraint bundleConstraint) {
+ super(bundle, bundleConstraint);
+ }
+
+ boolean isOptional() {
+ if (constraint instanceof HostSpecification)
+ return false;
+ return ((BundleSpecification) constraint).isOptional();
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java
new file mode 100644
index 000000000..5f7cdba7d
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+
+public interface CompositeResolveHelper {
+ public boolean giveExports(ExportPackageDescription[] matchingExports);
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java
new file mode 100644
index 000000000..f4cd6fb80
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+
+public interface CompositeResolveHelperRegistry {
+ public CompositeResolveHelper getCompositeResolveHelper(BundleDescription bundle);
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericCapability.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericCapability.java
new file mode 100644
index 000000000..3156e00fa
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericCapability.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.GenericDescription;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.Constants;
+
+public class GenericCapability extends VersionSupplier {
+ final ResolverBundle resolverBundle;
+ final String[] uses;
+ final boolean effective;
+
+ GenericCapability(ResolverBundle resolverBundle, GenericDescription base) {
+ super(base);
+ this.resolverBundle = resolverBundle;
+ String usesDirective = base.getDeclaredDirectives().get(Constants.USES_DIRECTIVE);
+ uses = ManifestElement.getArrayFromList(usesDirective);
+ String effectiveDirective = base.getDeclaredDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+ effective = effectiveDirective == null || Constants.EFFECTIVE_RESOLVE.equals(effectiveDirective);
+ }
+
+ public BundleDescription getBundleDescription() {
+ return getBaseDescription().getSupplier();
+ }
+
+ GenericDescription getGenericDescription() {
+ return (GenericDescription) getBaseDescription();
+ }
+
+ public ResolverBundle getResolverBundle() {
+ return resolverBundle;
+ }
+
+ String[] getUsesDirective() {
+ return uses;
+ }
+
+ boolean isEffective() {
+ return effective;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericConstraint.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericConstraint.java
new file mode 100644
index 000000000..a84a05812
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GenericConstraint.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.internal.resolver.GenericSpecificationImpl;
+import org.eclipse.osgi.service.resolver.GenericSpecification;
+import org.osgi.framework.Constants;
+
+public class GenericConstraint extends ResolverConstraint {
+
+ private final boolean effective;
+ private boolean supplierHasUses;
+
+ GenericConstraint(ResolverBundle bundle, GenericSpecification constraint) {
+ super(bundle, constraint);
+ String effectiveDirective = constraint.getRequirement().getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+ effective = effectiveDirective == null || Constants.EFFECTIVE_RESOLVE.equals(effectiveDirective);
+ }
+
+ boolean isOptional() {
+ return (((GenericSpecification) constraint).getResolution() & GenericSpecification.RESOLUTION_OPTIONAL) != 0;
+ }
+
+ boolean isFromRequiredEE() {
+ return (((GenericSpecification) constraint).getResolution() & GenericSpecificationImpl.RESOLUTION_FROM_BREE) != 0;
+ }
+
+ boolean isMultiple() {
+ return !supplierHasUses && (((GenericSpecification) constraint).getResolution() & GenericSpecification.RESOLUTION_MULTIPLE) != 0;
+ }
+
+ boolean isEffective() {
+ return effective;
+ }
+
+ public String getNameSpace() {
+ return ((GenericSpecification) getVersionConstraint()).getType();
+ }
+
+ public VersionSupplier[] getMatchingCapabilities() {
+ if (isMultiple())
+ return getPossibleSuppliers();
+ VersionSupplier supplier = getSelectedSupplier();
+ return supplier == null ? null : new VersionSupplier[] {supplier};
+ }
+
+ @Override
+ void addPossibleSupplier(VersionSupplier supplier) {
+ // if there is a supplier with uses constraints then we no longer allow multiples
+ supplierHasUses |= ((GenericCapability) supplier).getUsesDirective() != null;
+ super.addPossibleSupplier(supplier);
+ }
+
+ @Override
+ void clearPossibleSuppliers() {
+ super.clearPossibleSuppliers();
+ supplierHasUses = false;
+ }
+
+ boolean supplierHasUses() {
+ return supplierHasUses;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GroupingChecker.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GroupingChecker.java
new file mode 100644
index 000000000..ea39971bd
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/GroupingChecker.java
@@ -0,0 +1,433 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+
+/*
+ * The GroupingChecker checks the 'uses' directive on exported packages for consistency
+ */
+public class GroupingChecker {
+ final PackageRoots nullPackageRoots = new PackageRoots(null);
+ // a mapping of bundles to their package roots; keyed by
+ // ResolverBundle -> HashMap of packages; keyed by
+ // package name -> PackageRoots
+ private Map<ResolverBundle, Map<String, PackageRoots>> bundles = new HashMap<ResolverBundle, Map<String, PackageRoots>>();
+
+ /*
+ * This method fully populates a bundles package roots for the purpose of resolving
+ * a dynamic import. Package roots must be fully populated because we need all the
+ * roots to do proper uses constraint verification on a dynamic import supplier.
+ */
+ public void populateRoots(ResolverBundle bundle) {
+ if (bundles.containsKey(bundle))
+ // only do the full populate the first time (bug 337272)
+ return;
+ // process all requires
+ BundleConstraint[] requires = bundle.getRequires();
+ for (int j = 0; j < requires.length; j++) {
+ ResolverBundle selectedSupplier = (ResolverBundle) requires[j].getSelectedSupplier();
+ if (selectedSupplier != null)
+ isConsistentInternal(bundle, selectedSupplier, new ArrayList<ResolverBundle>(1), true, null);
+ }
+ // process all imports
+ // must check resolved imports to get any dynamically resolved imports
+ ExportPackageDescription[] imports = bundle.getBundleDescription().getResolvedImports();
+ for (ExportPackageDescription importPkg : imports) {
+ List<ResolverExport> exports = bundle.getResolver().getResolverExports().get(importPkg.getName());
+ for (ResolverExport export : exports) {
+ if (export.getExportPackageDescription() == importPkg)
+ isConsistentInternal(bundle, export, true, null);
+ }
+ }
+ }
+
+ /*
+ * Re-populates the package roots or an importing bundle with the given export
+ * This is done after wiring a package from a dynamic import (bug 337272)
+ */
+ public void populateRoots(ResolverBundle importingBundle, ResolverExport export) {
+ Map<String, PackageRoots> packageRoots = bundles.get(importingBundle);
+ if (packageRoots != null)
+ packageRoots.remove(export.getName());
+ PackageRoots roots = getPackageRoots(export.getExporter(), export.getName(), null);
+ packageRoots.put(export.getName(), roots);
+ }
+
+ /*
+ * Verifies the uses constraint consistency for the requiringBundle with the possible matching bundle.
+ * If an inconsistency is found the export inconsistency is returned; otherwise null is returned
+ */
+ public PackageRoots[][] isConsistent(ResolverBundle requiringBundle, ResolverBundle matchingBundle) {
+ List<PackageRoots[]> results = isConsistentInternal(requiringBundle, matchingBundle, new ArrayList<ResolverBundle>(1), false, null);
+ return results == null ? null : results.toArray(new PackageRoots[results.size()][]);
+ }
+
+ private List<PackageRoots[]> isConsistentInternal(ResolverBundle requiringBundle, ResolverBundle matchingBundle, List<ResolverBundle> visited, boolean dynamicImport, List<PackageRoots[]> results) {
+ // needed to prevent endless cycles
+ if (visited.contains(matchingBundle))
+ return results;
+ visited.add(matchingBundle);
+ // check that the packages exported by the matching bundle are consistent
+ ResolverExport[] matchingExports = matchingBundle.getExportPackages();
+ for (int i = 0; i < matchingExports.length; i++) {
+ ResolverExport matchingExport = matchingExports[i];
+ if (matchingExports[i].getSubstitute() != null)
+ matchingExport = (ResolverExport) matchingExports[i].getSubstitute();
+ results = isConsistentInternal(requiringBundle, matchingExport, dynamicImport, results);
+ }
+ // check that the packages from reexported bundles are consistent
+ BundleConstraint[] supplierRequires = matchingBundle.getRequires();
+ for (int j = 0; j < supplierRequires.length; j++) {
+ ResolverBundle reexported = (ResolverBundle) supplierRequires[j].getSelectedSupplier();
+ if (reexported == null || !((BundleSpecification) supplierRequires[j].getVersionConstraint()).isExported())
+ continue;
+ results = isConsistentInternal(requiringBundle, reexported, visited, dynamicImport, results);
+ }
+ return results;
+ }
+
+ /*
+ * Verifies the uses constraint consistency for the importingBundle with the possible matching export.
+ * If an inconsistency is found the export returned; otherwise null is returned
+ */
+ public PackageRoots[][] isConsistent(ResolverBundle importingBundle, ResolverExport matchingExport) {
+ List<PackageRoots[]> results = isConsistentInternal(importingBundle, matchingExport, false, null);
+ return results == null ? null : results.toArray(new PackageRoots[results.size()][]);
+ }
+
+ public PackageRoots[][] isConsistent(ResolverBundle requiringBundle, GenericCapability matchingCapability) {
+ String[] uses = matchingCapability.getUsesDirective();
+ if (uses == null)
+ return null;
+ ArrayList<PackageRoots[]> results = new ArrayList<PackageRoots[]>(0);
+ for (String usedPackage : uses) {
+ PackageRoots providingRoots = getPackageRoots(matchingCapability.getResolverBundle(), usedPackage, null);
+ providingRoots.addConflicts(requiringBundle, usedPackage, null, results);
+ }
+ return results.size() == 0 ? null : results.toArray(new PackageRoots[results.size()][]);
+ }
+
+ /*
+ * Verifies the uses constraint consistency for the importingBundle with the possible dynamic matching export.
+ * If an inconsistency is found the export returned; otherwise null is returned.
+ * Dynamic imports must perform extra checks to ensure that existing wires to package roots are
+ * consistent with the possible matching dynamic export.
+ */
+ public PackageRoots[][] isDynamicConsistent(ResolverBundle importingBundle, ResolverExport matchingExport) {
+ List<PackageRoots[]> results = isConsistentInternal(importingBundle, matchingExport, true, null);
+ return results == null ? null : results.toArray(new PackageRoots[results.size()][]);
+ }
+
+ private List<PackageRoots[]> isConsistentInternal(ResolverBundle importingBundle, ResolverExport matchingExport, boolean dyanamicImport, List<PackageRoots[]> results) {
+ PackageRoots exportingRoots = getPackageRoots(matchingExport.getExporter(), matchingExport.getName(), null);
+ // check that the exports uses packages are consistent with existing package roots
+ results = exportingRoots.isConsistentClassSpace(importingBundle, null, results);
+ if (!dyanamicImport)
+ return results;
+ // for dynamic imports we must check that each existing root is consistent with the possible matching export
+ PackageRoots importingRoots = getPackageRoots(importingBundle, matchingExport.getName(), null);
+ Map<String, PackageRoots> importingPackages = bundles.get(importingBundle);
+ if (importingPackages != null)
+ for (Iterator<PackageRoots> allImportingPackages = importingPackages.values().iterator(); allImportingPackages.hasNext();) {
+ PackageRoots roots = allImportingPackages.next();
+ if (roots != importingRoots)
+ results = roots.isConsistentClassSpace(exportingRoots, matchingExport.getExporter(), null, results);
+ }
+ // We also must check any generic capabilities are consistent
+ GenericConstraint[] genericRequires = importingBundle.getGenericRequires();
+ for (GenericConstraint constraint : genericRequires) {
+ if (!constraint.supplierHasUses())
+ continue;
+ GenericCapability supplier = (GenericCapability) constraint.getSelectedSupplier();
+ String[] uses = supplier.getUsesDirective();
+ if (uses != null)
+ for (String usedPackage : uses) {
+ if (usedPackage.equals(matchingExport.getName())) {
+ results = exportingRoots.addConflicts(supplier.getResolverBundle(), usedPackage, null, results);
+ }
+ }
+ }
+ return results;
+ }
+
+ /*
+ * returns package roots for a specific package name for a specific bundle
+ */
+ PackageRoots getPackageRoots(ResolverBundle bundle, String packageName, List<ResolverBundle> visited) {
+ Map<String, PackageRoots> packages = bundles.get(bundle);
+ if (packages == null) {
+ packages = new HashMap<String, PackageRoots>(5);
+ bundles.put(bundle, packages);
+ }
+ PackageRoots packageRoots = packages.get(packageName);
+ if (packageRoots == null) {
+ packageRoots = createPackageRoots(bundle, packageName, visited == null ? new ArrayList<ResolverBundle>(1) : visited);
+ packages.put(packageName, packageRoots);
+ }
+ return packageRoots != null ? packageRoots : nullPackageRoots;
+ }
+
+ private PackageRoots createPackageRoots(ResolverBundle bundle, String packageName, List<ResolverBundle> visited) {
+ if (visited.contains(bundle))
+ return null;
+ visited.add(bundle); // prevent endless cycles
+ // check imports
+ if (bundle.getBundleDescription().isResolved()) {
+ // must check resolved imports to get any dynamically resolved imports
+ ExportPackageDescription[] imports = bundle.getBundleDescription().getResolvedImports();
+ for (ExportPackageDescription importPkg : imports) {
+ if (importPkg.getExporter() == bundle.getBundleDescription() || !importPkg.getName().equals(packageName))
+ continue;
+ List<ResolverExport> exports = bundle.getResolver().getResolverExports().get(packageName);
+ for (ResolverExport export : exports) {
+ if (export.getExportPackageDescription() == importPkg)
+ return getPackageRoots(export.getExporter(), packageName, visited);
+ }
+ }
+ } else {
+ ResolverImport imported = bundle.getImport(packageName);
+ if (imported != null && imported.getSelectedSupplier() != null) {
+ // make sure we are not resolved to our own import
+ ResolverExport selectedExport = (ResolverExport) imported.getSelectedSupplier();
+ if (selectedExport.getExporter() != bundle) {
+ // found resolved import; get the roots from the resolved exporter;
+ // this is all the roots if the package is imported
+ return getPackageRoots(selectedExport.getExporter(), packageName, visited);
+ }
+ }
+ }
+ // check if the bundle exports the package
+ ResolverExport[] exports = bundle.getExports(packageName);
+ List<PackageRoots> roots = new ArrayList<PackageRoots>(0);
+ // check roots from required bundles
+ BundleConstraint[] requires = bundle.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ ResolverBundle supplier = (ResolverBundle) requires[i].getSelectedSupplier();
+ if (supplier == null)
+ continue; // no supplier, probably optional
+ if (supplier.getExport(packageName) != null) {
+ // the required bundle exports the package; get the package roots from it
+ PackageRoots requiredRoots = getPackageRoots(supplier, packageName, visited);
+ if (requiredRoots != nullPackageRoots)
+ roots.add(requiredRoots);
+ } else {
+ // the bundle does not export the package; but it may reexport another bundle that does
+ BundleConstraint[] supplierRequires = supplier.getRequires();
+ for (int j = 0; j < supplierRequires.length; j++) {
+ ResolverBundle reexported = (ResolverBundle) supplierRequires[j].getSelectedSupplier();
+ if (reexported == null || !((BundleSpecification) supplierRequires[j].getVersionConstraint()).isExported())
+ continue;
+ if (reexported.getExport(packageName) != null) {
+ // the reexported bundle exports the package; get the package roots from it
+ PackageRoots reExportedRoots = getPackageRoots(reexported, packageName, visited);
+ if (reExportedRoots != nullPackageRoots)
+ roots.add(reExportedRoots);
+ }
+ }
+ }
+ }
+ if (exports.length > 0 || roots.size() > 1) {
+ PackageRoots[] requiredRoots = roots.toArray(new PackageRoots[roots.size()]);
+ if (exports.length == 0) {
+ PackageRoots superSet = requiredRoots[0];
+ for (int i = 1; i < requiredRoots.length; i++) {
+ if (requiredRoots[i].superSet(superSet)) {
+ superSet = requiredRoots[i];
+ } else if (!superSet.superSet(requiredRoots[i])) {
+ superSet = null;
+ break;
+ }
+ }
+ if (superSet != null)
+ return superSet;
+ }
+ // in this case we cannot share the package roots object; must create one specific for this bundle
+ PackageRoots result = new PackageRoots(packageName);
+ // first merge all the roots from required bundles
+ for (int i = 0; i < requiredRoots.length; i++)
+ result.merge(requiredRoots[i]);
+ // always add this bundles exports to the end if it exports the package
+ for (int i = 0; i < exports.length; i++)
+ result.addRoot(exports[i]);
+ return result;
+ }
+ return roots.size() == 0 ? nullPackageRoots : roots.get(0);
+ }
+
+ public void clear() {
+ bundles.clear();
+ }
+
+ public void clear(ResolverBundle rb) {
+ bundles.remove(rb);
+ }
+
+ class PackageRoots {
+ private String name;
+ private ResolverExport[] roots;
+
+ PackageRoots(String name) {
+ this.name = name;
+ }
+
+ public boolean hasRoots() {
+ return roots != null && roots.length > 0;
+ }
+
+ public void addRoot(ResolverExport export) {
+ if (roots == null) {
+ roots = new ResolverExport[] {export};
+ return;
+ }
+ // need to do an extra check to make sure we are not adding the same package name
+ // from multiple versions of the same bundle
+ String exportBSN = export.getExporter().getName();
+ if (exportBSN != null) {
+ // first one wins
+ for (int i = 0; i < roots.length; i++)
+ if (export.getExporter() != roots[i].getExporter() && exportBSN.equals(roots[i].getExporter().getName()))
+ return;
+ }
+ if (!contains(export, roots)) {
+ ResolverExport[] newRoots = new ResolverExport[roots.length + 1];
+ System.arraycopy(roots, 0, newRoots, 0, roots.length);
+ newRoots[roots.length] = export;
+ roots = newRoots;
+ }
+ }
+
+ private boolean contains(ResolverExport export, ResolverExport[] exports) {
+ for (int i = 0; i < exports.length; i++)
+ if (exports[i] == export)
+ return true;
+ return false;
+ }
+
+ public void merge(PackageRoots packageRoots) {
+ if (packageRoots == null || packageRoots.roots == null)
+ return;
+ int size = packageRoots.roots.length;
+ for (int i = 0; i < size; i++)
+ addRoot(packageRoots.roots[i]);
+ }
+
+ public List<PackageRoots[]> isConsistentClassSpace(ResolverBundle importingBundle, List<PackageRoots> visited, List<PackageRoots[]> results) {
+ if (roots == null)
+ return results;
+ if (visited == null)
+ visited = new ArrayList<PackageRoots>(1);
+ if (visited.contains(this))
+ return results;
+ visited.add(this);
+ int size = roots.length;
+ for (int i = 0; i < size; i++) {
+ ResolverExport root = roots[i];
+ String[] uses = root.getUsesDirective();
+ if (uses == null)
+ continue;
+ for (int j = 0; j < uses.length; j++) {
+ if (uses[j].equals(root.getName()))
+ continue;
+ PackageRoots thisUsedRoots = getPackageRoots(root.getExporter(), uses[j], null);
+ PackageRoots importingUsedRoots = getPackageRoots(importingBundle, uses[j], null);
+ if (thisUsedRoots == importingUsedRoots)
+ continue;
+ if (thisUsedRoots != nullPackageRoots && importingUsedRoots != nullPackageRoots)
+ if (!(subSet(thisUsedRoots.roots, importingUsedRoots.roots) || subSet(importingUsedRoots.roots, thisUsedRoots.roots))) {
+ if (results == null)
+ results = new ArrayList<PackageRoots[]>(1);
+ results.add(new PackageRoots[] {this, importingUsedRoots});
+ }
+ // need to check the usedRoots consistency for transitive closure
+ results = thisUsedRoots.isConsistentClassSpace(importingBundle, visited, results);
+ }
+ }
+ return results;
+ }
+
+ public List<PackageRoots[]> isConsistentClassSpace(PackageRoots exportingRoots, ResolverBundle exporter, List<PackageRoots> visited, List<PackageRoots[]> results) {
+ if (roots == null)
+ return results;
+ int size = roots.length;
+ for (int i = 0; i < size; i++) {
+ ResolverExport root = roots[i];
+ String[] uses = root.getUsesDirective();
+ if (uses == null)
+ continue;
+ if (visited == null)
+ visited = new ArrayList<PackageRoots>(1);
+ if (visited.contains(this))
+ return results;
+ visited.add(this);
+ for (int j = 0; j < uses.length; j++) {
+ if (uses[j].equals(root.getName()) || !uses[j].equals(exportingRoots.name))
+ continue;
+ PackageRoots thisUsedRoots = getPackageRoots(root.getExporter(), uses[j], null);
+ PackageRoots exportingUsedRoots = getPackageRoots(exporter, uses[j], null);
+ if (thisUsedRoots == exportingRoots)
+ return results;
+ if (thisUsedRoots != nullPackageRoots && exportingUsedRoots != nullPackageRoots)
+ if (!(subSet(thisUsedRoots.roots, exportingUsedRoots.roots) || subSet(exportingUsedRoots.roots, thisUsedRoots.roots))) {
+ if (results == null)
+ results = new ArrayList<PackageRoots[]>(1);
+ results.add(new PackageRoots[] {this, exportingUsedRoots});
+ }
+ // need to check the usedRoots consistency for transitive closure
+ results = thisUsedRoots.isConsistentClassSpace(exportingRoots, exporter, visited, results);
+ }
+ }
+ return results;
+ }
+
+ List<PackageRoots[]> addConflicts(ResolverBundle bundle, String usedPackage, List<PackageRoots> visited, List<PackageRoots[]> results) {
+ PackageRoots bundleUsedRoots = getPackageRoots(bundle, usedPackage, null);
+ if (this == bundleUsedRoots)
+ return results;
+ if (this != nullPackageRoots && bundleUsedRoots != nullPackageRoots)
+ if (!(subSet(this.roots, bundleUsedRoots.roots) || subSet(bundleUsedRoots.roots, this.roots))) {
+ if (results == null)
+ results = new ArrayList<PackageRoots[]>(1);
+ results.add(new PackageRoots[] {this, bundleUsedRoots});
+ }
+ // need to check the usedRoots consistency for transitive closure
+ return this.isConsistentClassSpace(bundleUsedRoots, bundle, visited, results);
+ }
+
+ // TODO this is a behavioral change; before we only required 1 supplier to match; now roots must be subsets
+ private boolean subSet(ResolverExport[] superSet, ResolverExport[] subSet) {
+ for (int i = 0; i < subSet.length; i++) {
+ boolean found = false;
+ for (int j = 0; j < superSet.length; j++)
+ // compare by exporter in case the bundle exports the package multiple times
+ if (subSet[i].getExporter() == superSet[j].getExporter()) {
+ found = true;
+ break;
+ }
+ if (!found)
+ return false;
+ }
+ return true;
+ }
+
+ public boolean superSet(PackageRoots subSet) {
+ return subSet(roots, subSet.roots);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ResolverExport[] getRoots() {
+ return roots;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/MappedList.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/MappedList.java
new file mode 100644
index 000000000..a104a2434
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/MappedList.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import java.util.*;
+
+/*
+ * A MappedList maps values into keyed list arrays. All values with the same key are stored
+ * into the same array. Extending classes may override the sort method to sort the individual
+ * arrays in the MappedList. By default the MappedList appends new values to the end of the array.
+ */
+public class MappedList<K, V> {
+ // the mapping with key -> Object[] mapping
+ protected final HashMap<K, List<V>> internal = new HashMap<K, List<V>>();
+ @SuppressWarnings("unchecked")
+ protected final List<V> empty = Collections.EMPTY_LIST;
+
+ public void put(K key, V value) {
+ List<V> existing = internal.get(key);
+ if (existing == null) {
+ existing = new ArrayList<V>(1);
+ existing.add(value);
+ internal.put(key, existing);
+ return;
+ }
+ // insert the new value
+ int index = insertionIndex(existing, value);
+ existing.add(index, value);
+ }
+
+ protected int insertionIndex(List<V> existing, V value) {
+ // a MappedList is by default not sorted so just insert at the end
+ // extending classes may override this method to provide an index that retains sorted order
+ return existing.size();
+ }
+
+ // removes all values with the specified key
+ public List<V> remove(K key) {
+ return get(key, true);
+ }
+
+ // gets all values with the specified key
+ public List<V> get(K key) {
+ return get(key, false);
+ }
+
+ // gets all values with the specified and optionally removes them
+ private List<V> get(K key, boolean remove) {
+ List<V> result = remove ? internal.remove(key) : internal.get(key);
+ return result == null ? empty : result;
+ }
+
+ // returns the number of keyed lists
+ public int getSize() {
+ return internal.size();
+ }
+
+ // returns all values of all keys
+ public List<V> getAllValues() {
+ if (getSize() == 0)
+ return empty;
+ ArrayList<V> results = new ArrayList<V>(getSize());
+ Iterator<List<V>> iter = internal.values().iterator();
+ while (iter.hasNext())
+ results.addAll(iter.next());
+ return results;
+ }
+
+ // removes all keys from the map
+ public void clear() {
+ internal.clear();
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/PermissionChecker.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/PermissionChecker.java
new file mode 100644
index 000000000..824de81f9
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/PermissionChecker.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import java.security.Permission;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+
+public class PermissionChecker {
+ private boolean checkPermissions = false;
+ private ResolverImpl resolver;
+
+ public PermissionChecker(boolean checkPermissions, ResolverImpl resolver) {
+ this.checkPermissions = checkPermissions;
+ this.resolver = resolver;
+ }
+
+ /*
+ * checks the permission for a bundle to import/reqiure a constraint
+ * and for a bundle to export/provide a package/BSN
+ */
+ public boolean checkPermission(VersionConstraint vc, BaseDescription bd) {
+ if (!checkPermissions)
+ return true;
+ // TODO could optimize out the producer permission check on export package
+ boolean success = false;
+ Permission producerPermission = null, consumerPermission = null;
+ Bundle consumer = null;
+ Bundle producer = bd.getSupplier().getBundle();
+ int errorType = 0;
+ if (vc instanceof ImportPackageSpecification) {
+ errorType = ResolverError.IMPORT_PACKAGE_PERMISSION;
+ producerPermission = new PackagePermission(bd.getName(), PackagePermission.EXPORTONLY);
+ consumerPermission = producer != null ? new PackagePermission(vc.getName(), producer, PackagePermission.IMPORT) : new PackagePermission(vc.getName(), PackagePermission.IMPORT);
+ } else if (vc instanceof BundleSpecification || vc instanceof HostSpecification) {
+ boolean requireBundle = vc instanceof BundleSpecification;
+ errorType = requireBundle ? ResolverError.REQUIRE_BUNDLE_PERMISSION : ResolverError.FRAGMENT_BUNDLE_PERMISSION;
+ producerPermission = new BundlePermission(bd.getName(), requireBundle ? BundlePermission.PROVIDE : BundlePermission.HOST);
+ consumerPermission = new BundlePermission(vc.getName(), requireBundle ? BundlePermission.REQUIRE : BundlePermission.FRAGMENT);
+ } else if (vc instanceof GenericSpecification) {
+ errorType = ResolverError.REQUIRE_CAPABILITY_PERMISSION;
+ GenericDescription gd = (GenericDescription) bd;
+ producerPermission = new CapabilityPermission(gd.getType(), CapabilityPermission.PROVIDE);
+ consumerPermission = new CapabilityPermission(gd.getType(), gd.getDeclaredAttributes(), producer, CapabilityPermission.REQUIRE);
+ }
+ consumer = vc.getBundle().getBundle();
+ if (producer != null && (producer.getState() & Bundle.UNINSTALLED) == 0) {
+ success = producer.hasPermission(producerPermission);
+ if (!success) {
+ switch (errorType) {
+ case ResolverError.IMPORT_PACKAGE_PERMISSION :
+ errorType = ResolverError.EXPORT_PACKAGE_PERMISSION;
+ break;
+ case ResolverError.REQUIRE_BUNDLE_PERMISSION :
+ case ResolverError.FRAGMENT_BUNDLE_PERMISSION :
+ errorType = errorType == ResolverError.REQUIRE_BUNDLE_PERMISSION ? ResolverError.PROVIDE_BUNDLE_PERMISSION : ResolverError.HOST_BUNDLE_PERMISSION;
+ break;
+ case ResolverError.REQUIRE_CAPABILITY_PERMISSION :
+ errorType = ResolverError.PROVIDE_BUNDLE_PERMISSION;
+ break;
+ }
+ resolver.getState().addResolverError(vc.getBundle(), errorType, producerPermission.toString(), vc);
+ }
+ }
+ if (success && consumer != null && (consumer.getState() & Bundle.UNINSTALLED) == 0) {
+ success = consumer.hasPermission(consumerPermission);
+ if (!success)
+ resolver.getState().addResolverError(vc.getBundle(), errorType, consumerPermission.toString(), vc);
+ }
+
+ return success;
+ }
+
+ boolean checkPackagePermission(ExportPackageDescription export) {
+ if (!checkPermissions)
+ return true;
+ export.getSupplier().getBundle();
+ Bundle bundle = export.getSupplier().getBundle();
+ return bundle == null ? false : bundle.hasPermission(new PackagePermission(export.getName(), PackagePermission.EXPORTONLY));
+ }
+
+ boolean checkCapabilityPermission(GenericDescription capability) {
+ if (!checkPermissions)
+ return true;
+ Bundle bundle = capability.getSupplier().getBundle();
+ return bundle == null ? false : bundle.hasPermission(new CapabilityPermission(capability.getType(), CapabilityPermission.PROVIDE));
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverBundle.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverBundle.java
new file mode 100644
index 000000000..8f16f561d
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverBundle.java
@@ -0,0 +1,664 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import java.util.*;
+import java.util.Map.Entry;
+import org.eclipse.osgi.internal.resolver.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.namespace.IdentityNamespace;
+
+/*
+ * A companion to BundleDescription from the state used while resolving.
+ */
+public class ResolverBundle extends VersionSupplier implements Comparable<ResolverBundle> {
+ public static final int UNRESOLVED = 0;
+ public static final int RESOLVING = 1;
+ public static final int RESOLVED = 2;
+
+ private final Long bundleID;
+ private BundleConstraint host;
+ private ResolverImport[] imports;
+ private ResolverExport[] exports;
+ private BundleConstraint[] requires;
+ private GenericCapability[] genericCapabilities;
+ private GenericConstraint[] genericReqiures;
+ // Fragment support
+ private ArrayList<ResolverBundle> fragments;
+ private HashMap<Long, List<ResolverExport>> fragmentExports;
+ private HashMap<Long, List<ResolverImport>> fragmentImports;
+ private HashMap<Long, List<BundleConstraint>> fragmentRequires;
+ private HashMap<Long, List<GenericCapability>> fragmentGenericCapabilities;
+ private HashMap<Long, List<GenericConstraint>> fragmentGenericRequires;
+ // Flag specifying whether this bundle is resolvable
+ private boolean resolvable = true;
+ // Internal resolver state for this bundle
+ private int state = UNRESOLVED;
+ private boolean uninstalled = false;
+ private final ResolverImpl resolver;
+ private boolean newFragmentExports;
+ private boolean newFragmentCapabilities;
+
+ ResolverBundle(BundleDescription bundle, ResolverImpl resolver) {
+ super(bundle);
+ this.bundleID = new Long(bundle.getBundleId());
+ this.resolver = resolver;
+ initialize(bundle.isResolved());
+ }
+
+ void initialize(boolean useSelectedExports) {
+ if (getBundleDescription().getHost() != null) {
+ host = new BundleConstraint(this, getBundleDescription().getHost());
+ exports = new ResolverExport[0];
+ imports = new ResolverImport[0];
+ requires = new BundleConstraint[0];
+ GenericSpecification[] requirements = getBundleDescription().getGenericRequires();
+ List<GenericConstraint> constraints = new ArrayList<GenericConstraint>();
+ for (GenericSpecification requirement : requirements) {
+ if (StateImpl.OSGI_EE_NAMESPACE.equals(requirement.getType()))
+ constraints.add(new GenericConstraint(this, requirement));
+ }
+ genericReqiures = constraints.toArray(new GenericConstraint[constraints.size()]);
+ GenericDescription[] capabilities = getBundleDescription().getGenericCapabilities();
+ GenericCapability identity = null;
+ for (GenericDescription capability : capabilities) {
+ if (IdentityNamespace.IDENTITY_NAMESPACE.equals(capability.getType())) {
+ identity = new GenericCapability(this, capability);
+ break;
+ }
+ }
+
+ genericCapabilities = identity == null ? new GenericCapability[0] : new GenericCapability[] {identity};
+ return;
+ }
+
+ ImportPackageSpecification[] actualImports = getBundleDescription().getImportPackages();
+ // Reorder imports so that optionals are at the end so that we wire statics before optionals
+ List<ResolverImport> importList = new ArrayList<ResolverImport>(actualImports.length);
+ for (int i = actualImports.length - 1; i >= 0; i--)
+ if (ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(actualImports[i].getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ importList.add(new ResolverImport(this, actualImports[i]));
+ else
+ importList.add(0, new ResolverImport(this, actualImports[i]));
+ imports = importList.toArray(new ResolverImport[importList.size()]);
+
+ ExportPackageDescription[] actualExports = useSelectedExports ? getBundleDescription().getSelectedExports() : getBundleDescription().getExportPackages();
+ exports = new ResolverExport[actualExports.length];
+ for (int i = 0; i < actualExports.length; i++)
+ exports[i] = new ResolverExport(this, actualExports[i]);
+
+ BundleSpecification[] actualRequires = getBundleDescription().getRequiredBundles();
+ requires = new BundleConstraint[actualRequires.length];
+ for (int i = 0; i < requires.length; i++)
+ requires[i] = new BundleConstraint(this, actualRequires[i]);
+
+ GenericSpecification[] actualGenericRequires = getBundleDescription().getGenericRequires();
+ genericReqiures = new GenericConstraint[actualGenericRequires.length];
+ for (int i = 0; i < genericReqiures.length; i++)
+ genericReqiures[i] = new GenericConstraint(this, actualGenericRequires[i]);
+
+ GenericDescription[] actualCapabilities = useSelectedExports ? getBundleDescription().getSelectedGenericCapabilities() : getBundleDescription().getGenericCapabilities();
+ genericCapabilities = new GenericCapability[actualCapabilities.length];
+ for (int i = 0; i < genericCapabilities.length; i++)
+ genericCapabilities[i] = new GenericCapability(this, actualCapabilities[i]);
+
+ fragments = null;
+ fragmentExports = null;
+ fragmentImports = null;
+ fragmentRequires = null;
+ fragmentGenericCapabilities = null;
+ fragmentGenericRequires = null;
+ }
+
+ ResolverExport getExport(String name) {
+ ResolverExport[] allExports = getExports(name);
+ return allExports.length == 0 ? null : allExports[0];
+ }
+
+ ResolverExport[] getExports(String name) {
+ List<ResolverExport> results = new ArrayList<ResolverExport>(1); // rare to have more than one
+ // it is faster to ask the VersionHashMap for this package name and then compare the exporter to this
+ List<ResolverExport> resolverExports = resolver.getResolverExports().get(name);
+ for (ResolverExport export : resolverExports)
+ if (export.getExporter() == this)
+ results.add(export);
+ return results.toArray(new ResolverExport[results.size()]);
+ }
+
+ void clearWires() {
+ ResolverImport[] allImports = getImportPackages();
+ for (int i = 0; i < allImports.length; i++)
+ allImports[i].clearPossibleSuppliers();
+
+ if (host != null)
+ host.clearPossibleSuppliers();
+
+ BundleConstraint[] allRequires = getRequires();
+ for (int i = 0; i < allRequires.length; i++)
+ allRequires[i].clearPossibleSuppliers();
+
+ GenericConstraint[] allGenericRequires = getGenericRequires();
+ for (int i = 0; i < allGenericRequires.length; i++)
+ allGenericRequires[i].clearPossibleSuppliers();
+
+ ResolverExport[] allExports = getExportPackages();
+ for (int i = 0; i < allExports.length; i++)
+ allExports[i].setSubstitute(null);
+ }
+
+ boolean isResolved() {
+ return getState() == ResolverBundle.RESOLVED;
+ }
+
+ boolean isFragment() {
+ return host != null;
+ }
+
+ int getState() {
+ return state;
+ }
+
+ void setState(int state) {
+ this.state = state;
+ }
+
+ private <T> List<T> getAll(T[] hostEntries, Map<Long, List<T>> fragmentMap) {
+ List<T> result = new ArrayList<T>(hostEntries.length);
+ for (T entry : hostEntries)
+ result.add(entry);
+ for (ResolverBundle fragment : fragments) {
+ List<T> fragEntries = fragmentMap.get(fragment.bundleID);
+ if (fragEntries != null)
+ result.addAll(fragEntries);
+ }
+ return result;
+ }
+
+ ResolverImport[] getImportPackages() {
+ if (isFragment() || fragments == null || fragments.size() == 0)
+ return imports;
+ List<ResolverImport> result = getAll(imports, fragmentImports);
+ return result.toArray(new ResolverImport[result.size()]);
+ }
+
+ ResolverExport[] getExportPackages() {
+ if (isFragment() || fragments == null || fragments.size() == 0)
+ return exports;
+ List<ResolverExport> result = getAll(exports, fragmentExports);
+ return result.toArray(new ResolverExport[result.size()]);
+ }
+
+ ResolverExport[] getSelectedExports() {
+ return getExports(true);
+ }
+
+ ResolverExport[] getSubstitutedExports() {
+ return getExports(false);
+ }
+
+ private ResolverExport[] getExports(boolean selected) {
+ ResolverExport[] results = getExportPackages();
+ int removedExports = 0;
+ for (int i = 0; i < results.length; i++)
+ if (selected ? results[i].getSubstitute() != null : results[i].getSubstitute() == null)
+ removedExports++;
+ if (removedExports == 0)
+ return results;
+ ResolverExport[] selectedExports = new ResolverExport[results.length - removedExports];
+ int index = 0;
+ for (int i = 0; i < results.length; i++) {
+ if (selected ? results[i].getSubstitute() != null : results[i].getSubstitute() == null)
+ continue;
+ selectedExports[index] = results[i];
+ index++;
+ }
+ return selectedExports;
+ }
+
+ BundleConstraint getHost() {
+ return host;
+ }
+
+ GenericCapability[] getGenericCapabilities() {
+ if (isFragment() || fragments == null || fragments.size() == 0)
+ return genericCapabilities;
+ List<GenericCapability> result = getAll(genericCapabilities, fragmentGenericCapabilities);
+ return result.toArray(new GenericCapability[result.size()]);
+ }
+
+ BundleConstraint[] getRequires() {
+ if (isFragment() || fragments == null || fragments.size() == 0)
+ return requires;
+ List<BundleConstraint> result = getAll(requires, fragmentRequires);
+ return result.toArray(new BundleConstraint[result.size()]);
+ }
+
+ GenericConstraint[] getGenericRequires() {
+ if (isFragment() || fragments == null || fragments.size() == 0)
+ return genericReqiures;
+ List<GenericConstraint> result = getAll(genericReqiures, fragmentGenericRequires);
+ return result.toArray(new GenericConstraint[result.size()]);
+ }
+
+ BundleConstraint getRequire(String name) {
+ BundleConstraint[] allRequires = getRequires();
+ for (int i = 0; i < allRequires.length; i++)
+ if (allRequires[i].getVersionConstraint().getName().equals(name))
+ return allRequires[i];
+ return null;
+ }
+
+ public BundleDescription getBundleDescription() {
+ return (BundleDescription) getBaseDescription();
+ }
+
+ public ResolverBundle getResolverBundle() {
+ return this;
+ }
+
+ ResolverImport getImport(String name) {
+ ResolverImport[] allImports = getImportPackages();
+ for (int i = 0; i < allImports.length; i++) {
+ if (allImports[i].getName().equals(name)) {
+ return allImports[i];
+ }
+ }
+ return null;
+ }
+
+ public String toString() {
+ return "[" + getBundleDescription() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private void initFragments() {
+ if (fragments == null)
+ fragments = new ArrayList<ResolverBundle>(1);
+ if (fragmentExports == null)
+ fragmentExports = new HashMap<Long, List<ResolverExport>>(1);
+ if (fragmentImports == null)
+ fragmentImports = new HashMap<Long, List<ResolverImport>>(1);
+ if (fragmentRequires == null)
+ fragmentRequires = new HashMap<Long, List<BundleConstraint>>(1);
+ if (fragmentGenericCapabilities == null)
+ fragmentGenericCapabilities = new HashMap<Long, List<GenericCapability>>(1);
+ if (fragmentGenericRequires == null)
+ fragmentGenericRequires = new HashMap<Long, List<GenericConstraint>>(1);
+ }
+
+ private boolean isImported(String packageName) {
+ ResolverImport[] allImports = getImportPackages();
+ for (int i = 0; i < allImports.length; i++)
+ if (packageName.equals(allImports[i].getName()))
+ return true;
+
+ return false;
+ }
+
+ private boolean isRequired(String bundleName) {
+ return getRequire(bundleName) != null;
+ }
+
+ void attachFragment(ResolverBundle fragment, boolean dynamicAttach) {
+ if (isFragment())
+ return; // cannot attach to fragments;
+ if (!getBundleDescription().attachFragments() || (isResolved() && !getBundleDescription().dynamicFragments()))
+ return; // host is restricting attachment
+ if (fragment.getHost().getNumPossibleSuppliers() > 0 && !((HostSpecification) fragment.getHost().getVersionConstraint()).isMultiHost())
+ return; // fragment is restricting attachment
+
+ ImportPackageSpecification[] newImports = fragment.getBundleDescription().getImportPackages();
+ BundleSpecification[] newRequires = fragment.getBundleDescription().getRequiredBundles();
+ ExportPackageDescription[] newExports = fragment.getBundleDescription().getExportPackages();
+ GenericDescription[] newGenericCapabilities = fragment.getBundleDescription().getGenericCapabilities();
+ GenericSpecification[] newGenericRequires = fragment.getBundleDescription().getGenericRequires();
+
+ // if this is not during initialization then check if constraints conflict
+ if (dynamicAttach && constraintsConflict(fragment.getBundleDescription(), newImports, newRequires, newGenericRequires))
+ return; // do not allow fragments with conflicting constraints
+ if (isResolved() && newExports.length > 0)
+ fragment.setNewFragmentExports(true);
+
+ initFragments();
+ // need to make sure there is not already another version of this fragment
+ // already attached to this host
+ for (Iterator<ResolverBundle> iFragments = fragments.iterator(); iFragments.hasNext();) {
+ ResolverBundle existingFragment = iFragments.next();
+ String bsn = existingFragment.getName();
+ if (bsn != null && bsn.equals(fragment.getName()))
+ return;
+ }
+ if (fragments.contains(fragment))
+ return;
+ fragments.add(fragment);
+ fragment.getHost().addPossibleSupplier(this);
+
+ if (newImports.length > 0) {
+ ArrayList<ResolverImport> hostImports = new ArrayList<ResolverImport>(newImports.length);
+ for (int i = 0; i < newImports.length; i++)
+ if (!isImported(newImports[i].getName()))
+ hostImports.add(new ResolverImport(this, newImports[i]));
+ fragmentImports.put(fragment.bundleID, hostImports);
+ }
+
+ if (newRequires.length > 0) {
+ ArrayList<BundleConstraint> hostRequires = new ArrayList<BundleConstraint>(newRequires.length);
+ for (int i = 0; i < newRequires.length; i++)
+ if (!isRequired(newRequires[i].getName()))
+ hostRequires.add(new BundleConstraint(this, newRequires[i]));
+ fragmentRequires.put(fragment.bundleID, hostRequires);
+ }
+
+ if (newGenericRequires.length > 0) {
+ ArrayList<GenericConstraint> hostGenericRequires = new ArrayList<GenericConstraint>(newGenericRequires.length);
+ for (int i = 0; i < newGenericRequires.length; i++) {
+ // only add namespaces that are not osgi.ee
+ if (!StateImpl.OSGI_EE_NAMESPACE.equals(newGenericRequires[i].getType()))
+ hostGenericRequires.add(new GenericConstraint(this, newGenericRequires[i]));
+ }
+ if (!hostGenericRequires.isEmpty())
+ fragmentGenericRequires.put(fragment.bundleID, hostGenericRequires);
+ }
+
+ ArrayList<ResolverExport> hostExports = new ArrayList<ResolverExport>(newExports.length);
+ if (newExports.length > 0 && dynamicAttach) {
+ for (int i = 0; i < newExports.length; i++) {
+ ResolverExport currentExports[] = getExports(newExports[i].getName());
+ boolean foundEquivalent = false;
+ for (int j = 0; j < currentExports.length && !foundEquivalent; j++) {
+ if (equivalentExports(currentExports[j], newExports[i]))
+ foundEquivalent = true;
+ }
+ if (!foundEquivalent) {
+ ExportPackageDescription hostExport = new ExportPackageDescriptionImpl(getBundleDescription(), newExports[i]);
+ hostExports.add(new ResolverExport(this, hostExport));
+ }
+ }
+ fragmentExports.put(fragment.bundleID, hostExports);
+ }
+
+ List<GenericCapability> hostCapabilities = new ArrayList<GenericCapability>(newGenericCapabilities.length);
+ if (newGenericCapabilities.length > 0 && dynamicAttach) {
+ for (GenericDescription capability : newGenericCapabilities) {
+ if (!IdentityNamespace.IDENTITY_NAMESPACE.equals(capability.getType())) {
+ GenericDescription hostCapabililty = new GenericDescriptionImpl(getBundleDescription(), capability);
+ hostCapabilities.add(new GenericCapability(this, hostCapabililty));
+ }
+ }
+ if (hostCapabilities.size() > 0) {
+ fragmentGenericCapabilities.put(fragment.bundleID, hostCapabilities);
+ if (isResolved())
+ fragment.setNewFragmentCapabilities(true);
+ }
+ }
+ if (dynamicAttach) {
+ resolver.getResolverExports().put(hostExports.toArray(new ResolverExport[hostExports.size()]));
+ resolver.addGenerics(hostCapabilities.toArray(new GenericCapability[hostCapabilities.size()]));
+ }
+ }
+
+ private boolean equivalentExports(ResolverExport existingExport, ExportPackageDescription newDescription) {
+ ExportPackageDescription existingDescription = existingExport.getExportPackageDescription();
+ if (!existingDescription.getName().equals(newDescription.getName()))
+ return false;
+ if (!existingDescription.getVersion().equals(newDescription.getVersion()))
+ return false;
+ if (!equivalentMaps(existingDescription.getAttributes(), newDescription.getAttributes(), true))
+ return false;
+ if (!equivalentMaps(existingDescription.getDirectives(), newDescription.getDirectives(), true))
+ return false;
+ return true;
+ }
+
+ public static boolean equivalentMaps(Map<String, Object> existingDirectives, Map<String, Object> newDirectives, boolean exactMatch) {
+ if (existingDirectives == null && newDirectives == null)
+ return true;
+ if (existingDirectives == null ? newDirectives != null : newDirectives == null)
+ return false;
+ if (exactMatch && existingDirectives.size() != newDirectives.size())
+ return false;
+ for (Iterator<Entry<String, Object>> entries = existingDirectives.entrySet().iterator(); entries.hasNext();) {
+ Entry<String, Object> entry = entries.next();
+ Object newValue = newDirectives.get(entry.getKey());
+ if (newValue == null || entry.getValue().getClass() != newValue.getClass())
+ return false;
+ if (newValue instanceof String[]) {
+ if (!Arrays.equals((Object[]) entry.getValue(), (Object[]) newValue))
+ return false;
+ } else if (!entry.getValue().equals(newValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean constraintsConflict(BundleDescription fragment, ImportPackageSpecification[] newImports, BundleSpecification[] newRequires, GenericSpecification[] newGenericRequires) {
+ // this method iterates over all additional constraints from a fragment
+ // if the host is resolved then the fragment is not allowed to add new constraints;
+ // if the host is resolved and it already has a constraint of the same name then ensure the supplier satisfies the fragment's constraint
+ boolean result = false;
+ for (int i = 0; i < newImports.length; i++) {
+ ResolverImport hostImport = getImport(newImports[i].getName());
+ ResolverExport resolvedExport = (ResolverExport) (hostImport == null ? null : hostImport.getSelectedSupplier());
+ if (importPackageConflict(resolvedExport, newImports[i])) {
+ result = true;
+ resolver.getState().addResolverError(fragment, ResolverError.FRAGMENT_CONFLICT, newImports[i].toString(), newImports[i]);
+ }
+ }
+ for (int i = 0; i < newRequires.length; i++) {
+ BundleConstraint hostRequire = getRequire(newRequires[i].getName());
+ ResolverBundle resolvedRequire = (ResolverBundle) (hostRequire == null ? null : hostRequire.getSelectedSupplier());
+ if ((resolvedRequire == null && isResolved()) || (resolvedRequire != null && !newRequires[i].isSatisfiedBy(resolvedRequire.getBundleDescription()))) {
+ result = true;
+ resolver.getState().addResolverError(fragment, ResolverError.FRAGMENT_CONFLICT, newRequires[i].toString(), newRequires[i]);
+ }
+ }
+ // generic constraints cannot conflict;
+ // only check that a fragment does not add generic constraints to an already resolved host
+ if (isResolved() && newGenericRequires != null) {
+ for (GenericSpecification genericSpecification : newGenericRequires) {
+ if (!StateImpl.OSGI_EE_NAMESPACE.equals(genericSpecification.getType())) {
+ result = true;
+ resolver.getState().addResolverError(fragment, ResolverError.FRAGMENT_CONFLICT, genericSpecification.toString(), genericSpecification);
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean importPackageConflict(ResolverExport resolvedExport, ImportPackageSpecification newImport) {
+ if (resolvedExport == null)
+ return isResolved();
+ return !((ImportPackageSpecificationImpl) newImport).isSatisfiedBy(resolvedExport.getExportPackageDescription(), false);
+ }
+
+ private void setNewFragmentExports(boolean newFragmentExports) {
+ this.newFragmentExports = newFragmentExports;
+ }
+
+ boolean isNewFragmentExports() {
+ return newFragmentExports;
+ }
+
+ private void setNewFragmentCapabilities(boolean newFragmentCapabilities) {
+ this.newFragmentCapabilities = newFragmentCapabilities;
+ }
+
+ boolean isNewFragmentCapabilities() {
+ return newFragmentCapabilities;
+ }
+
+ public void detachFromHosts() {
+ if (!isFragment()) {
+ return;
+ }
+ VersionSupplier[] hosts = getHost().getPossibleSuppliers();
+ if (hosts == null) {
+ return;
+ }
+ for (VersionSupplier possibleHost : hosts) {
+ ((ResolverBundle) possibleHost).detachFragment(this, null);
+ }
+ }
+
+ void detachFragment(ResolverBundle fragment, ResolverConstraint reason) {
+ if (isFragment())
+ return;
+ initFragments();
+
+ // must save off old imports and requires before we remove the fragment;
+ // this will be used to merge the constraints of the same name from the remaining fragments
+ ResolverImport[] oldImports = getImportPackages();
+ BundleConstraint[] oldRequires = getRequires();
+ if (!fragments.remove(fragment))
+ return;
+
+ fragment.setNewFragmentExports(false);
+ fragment.setNewFragmentCapabilities(false);
+ fragment.getHost().removePossibleSupplier(this);
+ fragmentImports.remove(fragment.bundleID);
+ fragmentRequires.remove(fragment.bundleID);
+ List<ResolverExport> removedExports = fragmentExports.remove(fragment.bundleID);
+ fragmentGenericRequires.remove(fragment.bundleID);
+ List<GenericCapability> removedCapabilities = fragmentGenericCapabilities.remove(fragment.bundleID);
+ if (reason != null) {
+ // the fragment is being detached because one of its imports or requires cannot be resolved;
+ // we need to check the remaining fragment constraints to make sure they do not have
+ // the same unresolved constraint.
+ // bug 353103: must make a snapshot to avoid ConcurrentModificationException
+ ResolverBundle[] remainingFrags = fragments.toArray(new ResolverBundle[fragments.size()]);
+ for (ResolverBundle remainingFrag : remainingFrags) {
+ List<ResolverImport> additionalImports = new ArrayList<ResolverImport>(0);
+ List<BundleConstraint> additionalRequires = new ArrayList<BundleConstraint>(0);
+ if (hasUnresolvedConstraint(reason, fragment, remainingFrag, oldImports, oldRequires, additionalImports, additionalRequires))
+ continue;
+ // merge back the additional imports or requires which the detached fragment has in common with the remaining fragment
+ if (additionalImports.size() > 0) {
+ List<ResolverImport> remainingImports = fragmentImports.get(remainingFrag.bundleID);
+ if (remainingImports == null)
+ fragmentImports.put(remainingFrag.bundleID, additionalImports);
+ else
+ remainingImports.addAll(additionalImports);
+ }
+ if (additionalRequires.size() > 0) {
+ List<BundleConstraint> remainingRequires = fragmentRequires.get(remainingFrag.bundleID);
+ if (remainingRequires == null)
+ fragmentRequires.put(remainingFrag.bundleID, additionalRequires);
+ else
+ remainingRequires.addAll(additionalRequires);
+ }
+ }
+ }
+ ResolverExport[] results = removedExports == null ? new ResolverExport[0] : removedExports.toArray(new ResolverExport[removedExports.size()]);
+ for (int i = 0; i < results.length; i++)
+ // TODO this is a hack; need to figure out how to indicate that a fragment export is no longer attached
+ results[i].setSubstitute(results[i]);
+ resolver.getResolverExports().remove(results);
+ if (removedCapabilities != null)
+ resolver.removeGenerics(removedCapabilities.toArray(new GenericCapability[removedCapabilities.size()]));
+ }
+
+ private boolean hasUnresolvedConstraint(ResolverConstraint reason, ResolverBundle detachedFragment, ResolverBundle remainingFragment, ResolverImport[] oldImports, BundleConstraint[] oldRequires, List<ResolverImport> additionalImports, List<BundleConstraint> additionalRequires) {
+ ImportPackageSpecification[] remainingFragImports = remainingFragment.getBundleDescription().getImportPackages();
+ BundleSpecification[] remainingFragRequires = remainingFragment.getBundleDescription().getRequiredBundles();
+ VersionConstraint[] constraints;
+ if (reason instanceof ResolverImport)
+ constraints = remainingFragImports;
+ else
+ constraints = remainingFragRequires;
+ for (int i = 0; i < constraints.length; i++)
+ if (reason.getName().equals(constraints[i].getName())) {
+ detachFragment(remainingFragment, reason);
+ return true;
+ }
+ for (int i = 0; i < oldImports.length; i++) {
+ if (oldImports[i].getVersionConstraint().getBundle() != detachedFragment.getBundleDescription())
+ continue; // the constraint is not from the detached fragment
+ for (int j = 0; j < remainingFragImports.length; j++) {
+ if (oldImports[i].getName().equals(remainingFragImports[j].getName())) {
+ // same constraint, must reuse the constraint object but swap out the fragment info
+ additionalImports.add(oldImports[i]);
+ oldImports[i].setVersionConstraint(remainingFragImports[j]);
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < oldRequires.length; i++) {
+ if (oldRequires[i].getVersionConstraint().getBundle() != detachedFragment.getBundleDescription())
+ continue; // the constraint is not from the detached fragment
+ for (int j = 0; j < remainingFragRequires.length; j++) {
+ if (oldRequires[i].getName().equals(remainingFragRequires[j].getName())) {
+ // same constraint, must reuse the constraint object but swap out the fragment info
+ additionalRequires.add(oldRequires[i]);
+ oldRequires[i].setVersionConstraint(remainingFragRequires[j]);
+ break;
+ }
+ }
+ }
+ return false;
+ }
+
+ void detachAllFragments() {
+ if (fragments == null)
+ return;
+ ResolverBundle[] allFragments = fragments.toArray(new ResolverBundle[fragments.size()]);
+ for (int i = 0; i < allFragments.length; i++)
+ detachFragment(allFragments[i], null);
+ fragments = null;
+ }
+
+ boolean isResolvable() {
+ return resolvable;
+ }
+
+ void setResolvable(boolean resolvable) {
+ this.resolvable = resolvable;
+ }
+
+ void addExport(ResolverExport re) {
+ ResolverExport[] newExports = new ResolverExport[exports.length + 1];
+ for (int i = 0; i < exports.length; i++)
+ newExports[i] = exports[i];
+ newExports[exports.length] = re;
+ exports = newExports;
+ }
+
+ ResolverImpl getResolver() {
+ return resolver;
+ }
+
+ ResolverBundle[] getFragments() {
+ return fragments == null ? new ResolverBundle[0] : (ResolverBundle[]) fragments.toArray(new ResolverBundle[fragments.size()]);
+ }
+
+ /*
+ * This is used to sort bundles by BSN. This is needed to fix bug 174930
+ * If both BSNs are null then 0 is returned
+ * If this BSN is null the 1 is returned
+ * If the other BSN is null then -1 is returned
+ * otherwise String.compareTo is used
+ */
+ public int compareTo(ResolverBundle o) {
+ String bsn = getName();
+ String otherBsn = o.getName();
+ if (bsn == null)
+ return otherBsn == null ? 0 : 1;
+ return otherBsn == null ? -1 : bsn.compareTo(otherBsn);
+ }
+
+ void setUninstalled() {
+ uninstalled = true;
+ }
+
+ boolean isUninstalled() {
+ return uninstalled;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverConstraint.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverConstraint.java
new file mode 100644
index 000000000..759898a70
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverConstraint.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2011 IBM Corporation and others. All rights reserved. This
+ * program and the accompanying materials are made available under the terms of
+ * the Eclipse Public License v1.0 which accompanies this distribution, and is
+ * available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+import org.osgi.framework.wiring.BundleRequirement;
+
+/*
+ * A companion to VersionConstraint from the state used while resolving
+ */
+public abstract class ResolverConstraint {
+ final protected ResolverBundle bundle;
+ protected VersionConstraint constraint;
+ private BundleRequirement requrement;
+ private VersionSupplier[] possibleSuppliers;
+ private int selectedSupplierIndex = 0;
+
+ ResolverConstraint(ResolverBundle bundle, VersionConstraint constraint) {
+ this.bundle = bundle;
+ this.constraint = constraint;
+ this.requrement = constraint.getRequirement();
+ }
+
+ // returns the Resolver bundle requiring the ResolverConstraint
+ ResolverBundle getBundle() {
+ return bundle;
+ }
+
+ // returns the BundleDescription requiring the ResolverConstraint
+ BundleDescription getBundleDescription() {
+ return bundle.getBundleDescription();
+ }
+
+ // returns whether this constraint is from an attached fragment
+ boolean isFromFragment() {
+ return constraint.getBundle().getHost() != null;
+ }
+
+ // Same as VersionConstraint but does additinal permission checks
+ boolean isSatisfiedBy(VersionSupplier vs) {
+ if (vs.getResolverBundle().isUninstalled() || !bundle.getResolver().getPermissionChecker().checkPermission(constraint, vs.getBaseDescription()))
+ return false;
+ return vs.getSubstitute() == null && constraint.isSatisfiedBy(vs.getBaseDescription());
+ }
+
+ // returns the companion VersionConstraint object from the State
+ VersionConstraint getVersionConstraint() {
+ return constraint;
+ }
+
+ // returns the name of this constraint
+ public String getName() {
+ return constraint.getName();
+ }
+
+ public String toString() {
+ return constraint.toString();
+ }
+
+ // returns whether this constraint is optional
+ abstract boolean isOptional();
+
+ void addPossibleSupplier(VersionSupplier supplier) {
+ if (supplier == null)
+ return;
+ // we hope multiple suppliers are rare so do simple array expansion here.
+ if (possibleSuppliers == null) {
+ possibleSuppliers = new VersionSupplier[] {supplier};
+ return;
+ }
+ VersionSupplier[] newSuppliers = new VersionSupplier[possibleSuppliers.length + 1];
+ System.arraycopy(possibleSuppliers, 0, newSuppliers, 0, possibleSuppliers.length);
+ newSuppliers[possibleSuppliers.length] = supplier;
+ possibleSuppliers = newSuppliers;
+ }
+
+ public void removePossibleSupplier(VersionSupplier supplier) {
+ if (possibleSuppliers == null || supplier == null)
+ return;
+ int index = -1;
+ for (int i = 0; i < possibleSuppliers.length; i++) {
+ if (possibleSuppliers[i] == supplier) {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0) {
+ if (possibleSuppliers.length == 1) {
+ possibleSuppliers = null;
+ return;
+ }
+ VersionSupplier[] newSuppliers = new VersionSupplier[possibleSuppliers.length - 1];
+ System.arraycopy(possibleSuppliers, 0, newSuppliers, 0, index);
+ if (index < possibleSuppliers.length - 1)
+ System.arraycopy(possibleSuppliers, index + 1, newSuppliers, index, possibleSuppliers.length - index - 1);
+ possibleSuppliers = newSuppliers;
+ }
+ }
+
+ int getNumPossibleSuppliers() {
+ if (possibleSuppliers == null)
+ return 0;
+ return possibleSuppliers.length;
+ }
+
+ boolean selectNextSupplier() {
+ if (possibleSuppliers == null || selectedSupplierIndex >= possibleSuppliers.length)
+ return false;
+ selectedSupplierIndex += 1;
+ return selectedSupplierIndex < possibleSuppliers.length;
+ }
+
+ VersionSupplier getSelectedSupplier() {
+ if (possibleSuppliers == null || selectedSupplierIndex >= possibleSuppliers.length)
+ return null;
+ return possibleSuppliers[selectedSupplierIndex];
+ }
+
+ void setSelectedSupplier(int selectedSupplier) {
+ this.selectedSupplierIndex = selectedSupplier;
+ }
+
+ int getSelectedSupplierIndex() {
+ return this.selectedSupplierIndex;
+ }
+
+ VersionSupplier[] getPossibleSuppliers() {
+ return possibleSuppliers;
+ }
+
+ void clearPossibleSuppliers() {
+ possibleSuppliers = null;
+ selectedSupplierIndex = 0;
+ }
+
+ void setVersionConstraint(VersionConstraint constraint) {
+ this.constraint = constraint;
+ this.requrement = constraint.getRequirement();
+ }
+
+ BundleRequirement getRequirement() {
+ return requrement;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverExport.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverExport.java
new file mode 100644
index 000000000..7aab84b87
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverExport.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.osgi.framework.Constants;
+
+/*
+ * A companion to ExportPackageDescription from the state used while resolving.
+ */
+public class ResolverExport extends VersionSupplier {
+ private ResolverBundle resolverBundle;
+
+ ResolverExport(ResolverBundle resolverBundle, ExportPackageDescription epd) {
+ super(epd);
+ this.resolverBundle = resolverBundle;
+ }
+
+ public ExportPackageDescription getExportPackageDescription() {
+ return (ExportPackageDescription) base;
+ }
+
+ public BundleDescription getBundleDescription() {
+ return getExportPackageDescription().getExporter();
+ }
+
+ ResolverBundle getExporter() {
+ return resolverBundle;
+ }
+
+ ResolverBundle getResolverBundle() {
+ return getExporter();
+ }
+
+ String[] getUsesDirective() {
+ return (String[]) getExportPackageDescription().getDirective(Constants.USES_DIRECTIVE);
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java
new file mode 100644
index 000000000..6a97c2159
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -0,0 +1,2243 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2012 IBM Corporation and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ ******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.internal.framework.FilterImpl;
+
+import org.eclipse.osgi.internal.debug.Debug;
+import org.eclipse.osgi.internal.debug.FrameworkDebugOptions;
+
+import java.security.AccessController;
+import java.util.*;
+import org.eclipse.osgi.framework.util.SecureAction;
+import org.eclipse.osgi.internal.baseadaptor.ArrayMap;
+import org.eclipse.osgi.internal.module.GroupingChecker.PackageRoots;
+import org.eclipse.osgi.internal.resolver.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ResolverImpl implements Resolver {
+ // Debug fields
+ private static final String RESOLVER = EquinoxContainer.NAME + "/resolver"; //$NON-NLS-1$
+ private static final String OPTION_DEBUG = RESOLVER + "/debug";//$NON-NLS-1$
+ private static final String OPTION_WIRING = RESOLVER + "/wiring"; //$NON-NLS-1$
+ private static final String OPTION_IMPORTS = RESOLVER + "/imports"; //$NON-NLS-1$
+ private static final String OPTION_REQUIRES = RESOLVER + "/requires"; //$NON-NLS-1$
+ private static final String OPTION_GENERICS = RESOLVER + "/generics"; //$NON-NLS-1$
+ private static final String OPTION_USES = RESOLVER + "/uses"; //$NON-NLS-1$
+ private static final String OPTION_CYCLES = RESOLVER + "/cycles"; //$NON-NLS-1$
+ public static boolean DEBUG = false;
+ public static boolean DEBUG_WIRING = false;
+ public static boolean DEBUG_IMPORTS = false;
+ public static boolean DEBUG_REQUIRES = false;
+ public static boolean DEBUG_GENERICS = false;
+ public static boolean DEBUG_USES = false;
+ public static boolean DEBUG_CYCLES = false;
+ private static int MAX_MULTIPLE_SUPPLIERS_MERGE = 10;
+ private static int MAX_USES_TIME_BASE = 30000; // 30 seconds
+ private static int MAX_USES_TIME_LIMIT = 90000; // 90 seconds
+ private static final String USES_TIMEOUT_PROP = "osgi.usesTimeout"; //$NON-NLS-1$
+ private static final String MULTIPLE_SUPPLIERS_LIMIT_PROP = "osgi.usesLimit"; //$NON-NLS-1$
+ static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
+
+ private String[][] CURRENT_EES;
+ private ResolverHook hook;
+
+ // The State associated with this resolver
+ private State state;
+ // Used to check permissions for import/export, provide/require, host/fragment
+ private final PermissionChecker permissionChecker;
+ // Set of bundles that are pending removal
+ private MappedList<Long, BundleDescription> removalPending = new MappedList<Long, BundleDescription>();
+ // Indicates whether this resolver has been initialized
+ private boolean initialized = false;
+
+ // Repository for exports
+ private VersionHashMap<ResolverExport> resolverExports = null;
+ // Repository for bundles
+ private VersionHashMap<ResolverBundle> resolverBundles = null;
+ // Repository for generics
+ private Map<String, VersionHashMap<GenericCapability>> resolverGenerics = null;
+ // List of unresolved bundles
+ private HashSet<ResolverBundle> unresolvedBundles = null;
+ // Keys are BundleDescriptions, values are ResolverBundles
+ private HashMap<BundleDescription, ResolverBundle> bundleMapping = null;
+ private GroupingChecker groupingChecker;
+ private Comparator<BaseDescription> selectionPolicy;
+ private boolean developmentMode = false;
+ private boolean usesCalculationTimeout = false;
+ private long usesTimeout = -1;
+ private int usesMultipleSuppliersLimit;
+ private volatile CompositeResolveHelperRegistry compositeHelpers;
+
+ public ResolverImpl(boolean checkPermissions) {
+ this.permissionChecker = new PermissionChecker(checkPermissions, this);
+ }
+
+ PermissionChecker getPermissionChecker() {
+ return permissionChecker;
+ }
+
+ // Initializes the resolver
+ private void initialize() {
+ resolverExports = new VersionHashMap<ResolverExport>(this);
+ resolverBundles = new VersionHashMap<ResolverBundle>(this);
+ resolverGenerics = new HashMap<String, VersionHashMap<GenericCapability>>();
+ unresolvedBundles = new HashSet<ResolverBundle>();
+ bundleMapping = new HashMap<BundleDescription, ResolverBundle>();
+ BundleDescription[] bundles = state.getBundles();
+ groupingChecker = new GroupingChecker();
+
+ ArrayList<ResolverBundle> fragmentBundles = new ArrayList<ResolverBundle>();
+ // Add each bundle to the resolver's internal state
+ for (int i = 0; i < bundles.length; i++)
+ initResolverBundle(bundles[i], fragmentBundles, false);
+ // Add each removal pending bundle to the resolver's internal state
+ List<BundleDescription> removedBundles = removalPending.getAllValues();
+ for (BundleDescription removed : removedBundles)
+ initResolverBundle(removed, fragmentBundles, true);
+ // Iterate over the resolved fragments and attach them to their hosts
+ for (Iterator<ResolverBundle> iter = fragmentBundles.iterator(); iter.hasNext();) {
+ ResolverBundle fragment = iter.next();
+ BundleDescription[] hosts = ((HostSpecification) fragment.getHost().getVersionConstraint()).getHosts();
+ for (int i = 0; i < hosts.length; i++) {
+ ResolverBundle host = bundleMapping.get(hosts[i]);
+ if (host != null)
+ // Do not add fragment exports here because they would have been added by the host above.
+ host.attachFragment(fragment, false);
+ }
+ }
+ rewireBundles(); // Reconstruct wirings
+ setDebugOptions();
+ initialized = true;
+ }
+
+ private void initResolverBundle(BundleDescription bundleDesc, ArrayList<ResolverBundle> fragmentBundles, boolean pending) {
+ ResolverBundle bundle = new ResolverBundle(bundleDesc, this);
+ bundleMapping.put(bundleDesc, bundle);
+ if (!pending || bundleDesc.isResolved()) {
+ resolverExports.put(bundle.getExportPackages());
+ resolverBundles.put(bundle.getName(), bundle);
+ addGenerics(bundle.getGenericCapabilities());
+ }
+ if (bundleDesc.isResolved()) {
+ bundle.setState(ResolverBundle.RESOLVED);
+ if (bundleDesc.getHost() != null)
+ fragmentBundles.add(bundle);
+ } else {
+ if (!pending)
+ unresolvedBundles.add(bundle);
+ }
+ }
+
+ // Re-wire previously resolved bundles
+ private void rewireBundles() {
+ List<ResolverBundle> visited = new ArrayList<ResolverBundle>(bundleMapping.size());
+ for (ResolverBundle rb : bundleMapping.values()) {
+ if (!rb.getBundleDescription().isResolved())
+ continue;
+ rewireBundle(rb, visited);
+ }
+ }
+
+ private void rewireBundle(ResolverBundle rb, List<ResolverBundle> visited) {
+ if (visited.contains(rb))
+ return;
+ visited.add(rb);
+ // Wire requires to bundles
+ BundleConstraint[] requires = rb.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ rewireRequire(requires[i], visited);
+ }
+ // Wire imports to exports
+ ResolverImport[] imports = rb.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ rewireImport(imports[i], visited);
+ }
+ // Wire generics
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++)
+ rewireGeneric(genericRequires[i], visited);
+ }
+
+ private void rewireGeneric(GenericConstraint constraint, List<ResolverBundle> visited) {
+ if (constraint.getSelectedSupplier() != null)
+ return;
+ GenericDescription[] suppliers = ((GenericSpecification) constraint.getVersionConstraint()).getSuppliers();
+ if (suppliers == null)
+ return;
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(constraint.getNameSpace());
+ if (namespace == null) {
+ System.err.println("Could not find matching capability for " + constraint.getVersionConstraint()); //$NON-NLS-1$
+ // TODO log error!!
+ return;
+ }
+ String constraintName = constraint.getName();
+ List<GenericCapability> matches = constraintName == null ? namespace.get(constraintName) : namespace.getAllValues();
+ for (GenericCapability match : matches) {
+ for (GenericDescription supplier : suppliers)
+ if (match.getBaseDescription() == supplier)
+ constraint.addPossibleSupplier(match);
+ }
+ VersionSupplier[] matchingCapabilities = constraint.getPossibleSuppliers();
+ if (matchingCapabilities != null)
+ for (int i = 0; i < matchingCapabilities.length; i++)
+ rewireBundle(matchingCapabilities[i].getResolverBundle(), visited);
+ }
+
+ private void rewireRequire(BundleConstraint req, List<ResolverBundle> visited) {
+ if (req.getSelectedSupplier() != null)
+ return;
+ ResolverBundle matchingBundle = bundleMapping.get(req.getVersionConstraint().getSupplier());
+ req.addPossibleSupplier(matchingBundle);
+ if (matchingBundle == null && !req.isOptional()) {
+ System.err.println("Could not find matching bundle for " + req.getVersionConstraint()); //$NON-NLS-1$
+ // TODO log error!!
+ }
+ if (matchingBundle != null) {
+ rewireBundle(matchingBundle, visited);
+ }
+ }
+
+ private void rewireImport(ResolverImport imp, List<ResolverBundle> visited) {
+ if (imp.isDynamic() || imp.getSelectedSupplier() != null)
+ return;
+ // Re-wire 'imp'
+ ResolverExport matchingExport = null;
+ ExportPackageDescription importSupplier = (ExportPackageDescription) imp.getVersionConstraint().getSupplier();
+ ResolverBundle exporter = importSupplier == null ? null : (ResolverBundle) bundleMapping.get(importSupplier.getExporter());
+ List<ResolverExport> matches = resolverExports.get(imp.getName());
+ for (ResolverExport export : matches) {
+ if (export.getExporter() == exporter && importSupplier == export.getExportPackageDescription()) {
+ matchingExport = export;
+ break;
+ }
+ }
+ imp.addPossibleSupplier(matchingExport);
+ // If we still have a null wire and it's not optional, then we have an error
+ if (imp.getSelectedSupplier() == null && !imp.isOptional()) {
+ System.err.println("Could not find matching export for " + imp.getVersionConstraint()); //$NON-NLS-1$
+ // TODO log error!!
+ }
+ if (imp.getSelectedSupplier() != null) {
+ rewireBundle(((ResolverExport) imp.getSelectedSupplier()).getExporter(), visited);
+ }
+ }
+
+ // Checks a bundle to make sure it is valid. If this method returns false for
+ // a given bundle, then that bundle will not even be considered for resolution
+ private boolean isResolvable(ResolverBundle bundle, Dictionary<Object, Object>[] platformProperties, Collection<ResolverBundle> hookDisabled) {
+ BundleDescription bundleDesc = bundle.getBundleDescription();
+
+ // check if the bundle is a hook disabled bundle
+ if (hookDisabled.contains(bundle)) {
+ state.addResolverError(bundleDesc, ResolverError.DISABLED_BUNDLE, "Resolver hook disabled bundle.", null); //$NON-NLS-1$
+ return false;
+ }
+ // check to see if the bundle is disabled
+ DisabledInfo[] disabledInfos = state.getDisabledInfos(bundleDesc);
+ if (disabledInfos.length > 0) {
+ StringBuffer message = new StringBuffer();
+ for (int i = 0; i < disabledInfos.length; i++) {
+ if (i > 0)
+ message.append(' ');
+ message.append('\"').append(disabledInfos[i].getPolicyName()).append(':').append(disabledInfos[i].getMessage()).append('\"');
+ }
+ state.addResolverError(bundleDesc, ResolverError.DISABLED_BUNDLE, message.toString(), null);
+ return false; // fail because we are disable
+ }
+
+ // check the required execution environment
+ String[] ees = bundleDesc.getExecutionEnvironments();
+ boolean matchedEE = ees.length == 0;
+ if (!matchedEE)
+ for (int i = 0; i < ees.length && !matchedEE; i++)
+ for (int j = 0; j < CURRENT_EES.length && !matchedEE; j++)
+ for (int k = 0; k < CURRENT_EES[j].length && !matchedEE; k++)
+ if (CURRENT_EES[j][k].equals(ees[i])) {
+ ((BundleDescriptionImpl) bundleDesc).setEquinoxEE(j);
+ matchedEE = true;
+ }
+ if (!matchedEE) {
+ StringBuffer bundleEE = new StringBuffer(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT.length() + 20);
+ bundleEE.append(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT).append(": "); //$NON-NLS-1$
+ for (int i = 0; i < ees.length; i++) {
+ if (i > 0)
+ bundleEE.append(","); //$NON-NLS-1$
+ bundleEE.append(ees[i]);
+ }
+ state.addResolverError(bundleDesc, ResolverError.MISSING_EXECUTION_ENVIRONMENT, bundleEE.toString(), null);
+ return false;
+ }
+
+ // check the native code specification
+ NativeCodeSpecification nativeCode = bundleDesc.getNativeCodeSpecification();
+ if (nativeCode != null) {
+ NativeCodeDescription[] nativeCodeSuppliers = nativeCode.getPossibleSuppliers();
+ NativeCodeDescription highestRanked = null;
+ for (int i = 0; i < nativeCodeSuppliers.length; i++)
+ if (nativeCode.isSatisfiedBy(nativeCodeSuppliers[i]) && (highestRanked == null || highestRanked.compareTo(nativeCodeSuppliers[i]) < 0))
+ highestRanked = nativeCodeSuppliers[i];
+ if (highestRanked == null) {
+ if (!nativeCode.isOptional()) {
+ state.addResolverError(bundleDesc, ResolverError.NO_NATIVECODE_MATCH, nativeCode.toString(), nativeCode);
+ return false;
+ }
+ } else {
+ if (highestRanked.hasInvalidNativePaths()) {
+ state.addResolverError(bundleDesc, ResolverError.INVALID_NATIVECODE_PATHS, highestRanked.toString(), nativeCode);
+ return false;
+ }
+ }
+ state.resolveConstraint(nativeCode, highestRanked);
+ }
+
+ // check the platform filter
+ String platformFilter = bundleDesc.getPlatformFilter();
+ if (platformFilter == null)
+ return true;
+ if (platformProperties == null)
+ return false;
+ try {
+ Filter filter = FilterImpl.newInstance(platformFilter);
+ for (int i = 0; i < platformProperties.length; i++) {
+ // using matchCase here in case of duplicate case invarient keys (bug 180817)
+ @SuppressWarnings("rawtypes")
+ Dictionary props = platformProperties[i];
+ if (filter.matchCase(props))
+ return true;
+ }
+ } catch (InvalidSyntaxException e) {
+ // return false below
+ }
+ state.addResolverError(bundleDesc, ResolverError.PLATFORM_FILTER, platformFilter, null);
+ return false;
+ }
+
+ // Attach fragment to its host
+ private void attachFragment(ResolverBundle bundle, Collection<String> processedFragments) {
+ if (processedFragments.contains(bundle.getName()))
+ return;
+ processedFragments.add(bundle.getName());
+ // we want to attach multiple versions of the same fragment
+ // from highest version to lowest to give the higher versions first pick
+ // of the available host bundles.
+ List<ResolverBundle> fragments = resolverBundles.get(bundle.getName());
+ for (ResolverBundle fragment : fragments) {
+ if (!fragment.isResolved())
+ attachFragment0(fragment);
+ }
+ }
+
+ private void attachFragment0(ResolverBundle bundle) {
+ if (!bundle.isFragment() || !bundle.isResolvable())
+ return;
+ bundle.clearWires();
+ if (!resolveOSGiEE(bundle))
+ return;
+ // no need to select singletons now; it will be done when we select the rest of the singleton bundles (bug 152042)
+ // find all available hosts to attach to.
+ boolean foundMatch = false;
+ BundleConstraint hostConstraint = bundle.getHost();
+ long timestamp;
+ List<ResolverBundle> candidates;
+ do {
+ timestamp = state.getTimeStamp();
+ List<ResolverBundle> hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
+ candidates = new ArrayList<ResolverBundle>(hosts);
+ List<BundleCapability> hostCapabilities = new ArrayList<BundleCapability>(hosts.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<ResolverBundle> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ ResolverBundle host = iCandidates.next();
+ if (!host.isResolvable() || !host.getBundleDescription().attachFragments() || !hostConstraint.isSatisfiedBy(host)) {
+ iCandidates.remove();
+ } else {
+ List<BundleCapability> h = host.getBundleDescription().getDeclaredCapabilities(BundleRevision.HOST_NAMESPACE);
+ // the bundle must have 1 host capability.
+ hostCapabilities.add(h.get(0));
+ }
+ }
+
+ if (hook != null)
+ hook.filterMatches(hostConstraint.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, ResolverBundle>(hostCapabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ // we are left with only candidates that satisfy the host constraint
+ for (ResolverBundle host : candidates) {
+ foundMatch = true;
+ host.attachFragment(bundle, true);
+ }
+ if (!foundMatch)
+ state.addResolverError(bundle.getBundleDescription(), ResolverError.MISSING_FRAGMENT_HOST, bundle.getHost().getVersionConstraint().toString(), bundle.getHost().getVersionConstraint());
+
+ }
+
+ private boolean resolveOSGiEE(ResolverBundle bundle) {
+ GenericConstraint[] requirements = bundle.getGenericRequires();
+ for (GenericConstraint requirement : requirements) {
+ if (!(StateImpl.OSGI_EE_NAMESPACE.equals(requirement.getNameSpace()) || requirement.isEffective()))
+ continue;
+ {
+ if (!resolveGenericReq(requirement, new ArrayList<ResolverBundle>(0))) {
+ if (DEBUG || DEBUG_GENERICS)
+ ResolverImpl.log("** GENERICS " + requirement.getVersionConstraint().getName() + "[" + requirement.getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ state.addResolverError(requirement.getVersionConstraint().getBundle(), ResolverError.MISSING_GENERIC_CAPABILITY, requirement.getVersionConstraint().toString(), requirement.getVersionConstraint());
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ return false;
+ }
+ } else {
+ VersionSupplier supplier = requirement.getSelectedSupplier();
+ Integer ee = supplier == null ? null : (Integer) ((GenericDescription) supplier.getBaseDescription()).getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (ee != null && ((BundleDescriptionImpl) bundle.getBaseDescription()).getEquinoxEE() < 0)
+ ((BundleDescriptionImpl) bundle.getBundleDescription()).setEquinoxEE(ee);
+ }
+ }
+ }
+ return true;
+ }
+
+ public synchronized void resolve(BundleDescription[] reRefresh, Dictionary<Object, Object>[] platformProperties) {
+ if (DEBUG)
+ ResolverImpl.log("*** BEGIN RESOLUTION ***"); //$NON-NLS-1$
+ if (state == null)
+ throw new IllegalStateException("RESOLVER_NO_STATE"); //$NON-NLS-1$
+
+ if (!initialized)
+ initialize();
+ hook = (state instanceof StateImpl) ? ((StateImpl) state).getResolverHook() : null;
+ try {
+ // set developmentMode each resolution
+ developmentMode = platformProperties.length == 0 ? false : StateImpl.DEVELOPMENT_MODE.equals(platformProperties[0].get(StateImpl.OSGI_RESOLVER_MODE));
+ // set uses timeout each resolution
+ usesTimeout = getUsesTimeout(platformProperties);
+ // set limit for constraints with multiple suppliers each resolution
+ usesMultipleSuppliersLimit = getMultipleSuppliersLimit(platformProperties);
+ reRefresh = addDevConstraints(reRefresh);
+ // Unresolve all the supplied bundles and their dependents
+ if (reRefresh != null)
+ for (int i = 0; i < reRefresh.length; i++) {
+ ResolverBundle rb = bundleMapping.get(reRefresh[i]);
+ if (rb != null)
+ unresolveBundle(rb, false);
+ }
+ // reorder exports and bundles after unresolving the bundles
+ resolverExports.reorder();
+ resolverBundles.reorder();
+ reorderGenerics();
+ // always get the latest EEs
+ getCurrentEEs(platformProperties);
+ boolean resolveOptional = platformProperties.length == 0 ? false : "true".equals(platformProperties[0].get("osgi.resolveOptional")); //$NON-NLS-1$//$NON-NLS-2$
+ ResolverBundle[] currentlyResolved = null;
+ if (resolveOptional) {
+ BundleDescription[] resolvedBundles = state.getResolvedBundles();
+ currentlyResolved = new ResolverBundle[resolvedBundles.length];
+ for (int i = 0; i < resolvedBundles.length; i++)
+ currentlyResolved[i] = bundleMapping.get(resolvedBundles[i]);
+ }
+ // attempt to resolve all unresolved bundles
+ @SuppressWarnings("unchecked")
+ Collection<ResolverBundle> hookDisabled = Collections.EMPTY_LIST;
+ if (hook != null) {
+ List<ResolverBundle> resolvableBundles = new ArrayList<ResolverBundle>(unresolvedBundles);
+ List<BundleRevision> resolvableRevisions = new ArrayList<BundleRevision>(resolvableBundles.size());
+ for (ResolverBundle bundle : resolvableBundles)
+ resolvableRevisions.add(bundle.getBundleDescription());
+ ArrayMap<BundleRevision, ResolverBundle> resolvable = new ArrayMap<BundleRevision, ResolverBundle>(resolvableRevisions, resolvableBundles);
+ int size = resolvableBundles.size();
+ hook.filterResolvable(resolvable);
+ if (resolvable.size() < size) {
+ hookDisabled = new ArrayList<ResolverBundle>(unresolvedBundles);
+ hookDisabled.removeAll(resolvableBundles);
+ }
+ }
+
+ usesCalculationTimeout = false;
+
+ List<ResolverBundle> toResolve = new ArrayList<ResolverBundle>(unresolvedBundles);
+ // first resolve the system bundle to allow osgi.ee capabilities to be resolved
+ List<ResolverBundle> unresolvedSystemBundles = new ArrayList<ResolverBundle>(1);
+ String systemBSN = getSystemBundle();
+ for (Iterator<ResolverBundle> iToResolve = toResolve.iterator(); iToResolve.hasNext();) {
+ ResolverBundle rb = iToResolve.next();
+ String symbolicName = rb.getName();
+ if (symbolicName != null && symbolicName.equals(systemBSN)) {
+ unresolvedSystemBundles.add(rb);
+ iToResolve.remove();
+ }
+ }
+ if (!unresolvedSystemBundles.isEmpty())
+ resolveBundles(unresolvedSystemBundles.toArray(new ResolverBundle[unresolvedSystemBundles.size()]), platformProperties, hookDisabled);
+
+ // Now resolve the rest
+ resolveBundles(toResolve.toArray(new ResolverBundle[toResolve.size()]), platformProperties, hookDisabled);
+
+ @SuppressWarnings("unchecked")
+ Collection<ResolverBundle> optionalResolved = resolveOptional ? resolveOptionalConstraints(currentlyResolved) : Collections.EMPTY_LIST;
+ ResolverHook current = hook;
+ if (current != null) {
+ hook = null;
+ current.end();
+ }
+
+ // set the resolved status of the bundles in the State
+ // Note this must be done after calling end above in case end throws errors
+ stateResolveBundles(bundleMapping.values().toArray(new ResolverBundle[bundleMapping.size()]));
+
+ for (ResolverBundle bundle : optionalResolved) {
+ state.resolveBundle(bundle.getBundleDescription(), false, null, null, null, null, null, null, null, null);
+ stateResolveBundle(bundle);
+ }
+ // reorder exports and bundles after resolving the bundles
+ resolverExports.reorder();
+ resolverBundles.reorder();
+ reorderGenerics();
+ if (resolveOptional)
+ resolveOptionalConstraints(currentlyResolved);
+ if (DEBUG)
+ ResolverImpl.log("*** END RESOLUTION ***"); //$NON-NLS-1$
+ } finally {
+ if (hook != null)
+ hook.end(); // need to make sure end is always called
+ hook = null;
+ }
+ }
+
+ private long getUsesTimeout(Dictionary<Object, Object>[] platformProperties) {
+ try {
+ Object timeout = platformProperties.length == 0 ? null : platformProperties[0].get(USES_TIMEOUT_PROP);
+ if (timeout != null) {
+ long temp = Long.parseLong(timeout.toString());
+ if (temp < 0) {
+ return -1;
+ } else if (temp == 0) {
+ return Long.MAX_VALUE;
+ } else {
+ return temp;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // nothing;
+ }
+ return -1;
+ }
+
+ private int getMultipleSuppliersLimit(Dictionary<Object, Object>[] platformProperties) {
+ try {
+ Object limit = platformProperties.length == 0 ? null : platformProperties[0].get(MULTIPLE_SUPPLIERS_LIMIT_PROP);
+ if (limit != null) {
+ int temp = Integer.parseInt(limit.toString());
+ if (temp < 0) {
+ return MAX_MULTIPLE_SUPPLIERS_MERGE;
+ } else if (temp == 0) {
+ return Integer.MAX_VALUE;
+ } else {
+ return temp;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // nothing;
+ }
+ return MAX_MULTIPLE_SUPPLIERS_MERGE;
+ }
+
+ private BundleDescription[] addDevConstraints(BundleDescription[] reRefresh) {
+ if (!developmentMode)
+ return reRefresh; // we don't care about this unless we are in development mode
+ // when in develoment mode we need to reRefresh hosts of unresolved fragments that add new constraints
+ // and reRefresh and unresolved bundles that have dependents
+ Set<BundleDescription> additionalRefresh = new HashSet<BundleDescription>();
+ ResolverBundle[] unresolved = unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
+ for (int i = 0; i < unresolved.length; i++) {
+ addUnresolvedWithDependents(unresolved[i], additionalRefresh);
+ addHostsFromFragmentConstraints(unresolved[i], additionalRefresh);
+ }
+ if (additionalRefresh.size() == 0)
+ return reRefresh; // no new bundles found to refresh
+ // add the original reRefresh bundles to the set
+ if (reRefresh != null)
+ for (int i = 0; i < reRefresh.length; i++)
+ additionalRefresh.add(reRefresh[i]);
+ return additionalRefresh.toArray(new BundleDescription[additionalRefresh.size()]);
+ }
+
+ private void addUnresolvedWithDependents(ResolverBundle unresolved, Set<BundleDescription> additionalRefresh) {
+ BundleDescription[] dependents = unresolved.getBundleDescription().getDependents();
+ if (dependents.length > 0)
+ additionalRefresh.add(unresolved.getBundleDescription());
+ }
+
+ private void addHostsFromFragmentConstraints(ResolverBundle unresolved, Set<BundleDescription> additionalRefresh) {
+ if (!unresolved.isFragment())
+ return;
+ ImportPackageSpecification[] newImports = unresolved.getBundleDescription().getImportPackages();
+ BundleSpecification[] newRequires = unresolved.getBundleDescription().getRequiredBundles();
+ if (newImports.length == 0 && newRequires.length == 0)
+ return; // the fragment does not have its own constraints
+ BundleConstraint hostConstraint = unresolved.getHost();
+ List<ResolverBundle> hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
+ for (ResolverBundle host : hosts)
+ if (hostConstraint.isSatisfiedBy(host) && host.isResolved())
+ // we found a host that is resolved;
+ // add it to the set of bundle to refresh so we can ensure this fragment is allowed to resolve
+ additionalRefresh.add(host.getBundleDescription());
+
+ }
+
+ private Collection<ResolverBundle> resolveOptionalConstraints(ResolverBundle[] bundles) {
+ Collection<ResolverBundle> result = new ArrayList<ResolverBundle>();
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundles[i] != null && resolveOptionalConstraints(bundles[i])) {
+ result.add(bundles[i]);
+ }
+ }
+ return result;
+ }
+
+ // TODO this does not do proper uses constraint verification.
+ private boolean resolveOptionalConstraints(ResolverBundle bundle) {
+ BundleConstraint[] requires = bundle.getRequires();
+ List<ResolverBundle> cycle = new ArrayList<ResolverBundle>();
+ boolean resolvedOptional = false;
+ for (int i = 0; i < requires.length; i++)
+ if (requires[i].isOptional() && requires[i].getSelectedSupplier() == null) {
+ cycle.clear();
+ resolveRequire(requires[i], cycle);
+ if (requires[i].getSelectedSupplier() != null)
+ resolvedOptional = true;
+ }
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++)
+ if (imports[i].isOptional() && imports[i].getSelectedSupplier() == null) {
+ cycle.clear();
+ resolveImport(imports[i], cycle);
+ if (imports[i].getSelectedSupplier() != null)
+ resolvedOptional = true;
+ }
+ return resolvedOptional;
+ }
+
+ private void getCurrentEEs(Dictionary<Object, Object>[] platformProperties) {
+ CURRENT_EES = new String[platformProperties.length][];
+ for (int i = 0; i < platformProperties.length; i++) {
+ String eeSpecs = (String) platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
+ CURRENT_EES[i] = ManifestElement.getArrayFromList(eeSpecs, ","); //$NON-NLS-1$
+ }
+ }
+
+ private void resolveBundles(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties, Collection<ResolverBundle> hookDisabled) {
+
+ // First check that all the meta-data is valid for each unresolved bundle
+ // This will reset the resolvable flag for each bundle
+ for (ResolverBundle bundle : bundles) {
+ state.removeResolverErrors(bundle.getBundleDescription());
+ // if in development mode then make all bundles resolvable
+ // we still want to call isResolvable here to populate any possible ResolverErrors for the bundle
+ bundle.setResolvable(isResolvable(bundle, platformProperties, hookDisabled) || developmentMode);
+ }
+ selectSingletons(bundles);
+ resolveBundles0(bundles, platformProperties);
+ if (DEBUG_WIRING)
+ printWirings();
+ }
+
+ private void selectSingletons(ResolverBundle[] bundles) {
+ if (developmentMode)
+ return; // want all singletons to resolve in devmode
+ Map<String, Collection<ResolverBundle>> selectedSingletons = new HashMap<String, Collection<ResolverBundle>>(bundles.length);
+ for (ResolverBundle bundle : bundles) {
+ if (!bundle.getBundleDescription().isSingleton() || !bundle.isResolvable())
+ continue;
+ String bsn = bundle.getName();
+ Collection<ResolverBundle> selected = selectedSingletons.get(bsn);
+ if (selected != null)
+ continue; // already processed the bsn
+ selected = new ArrayList<ResolverBundle>(1);
+ selectedSingletons.put(bsn, selected);
+
+ List<ResolverBundle> sameBSN = resolverBundles.get(bsn);
+ if (sameBSN.size() < 2) {
+ selected.add(bundle);
+ continue;
+ }
+ // prime selected with resolved singleton bundles
+ for (ResolverBundle singleton : sameBSN) {
+ if (singleton.getBundleDescription().isSingleton() && singleton.getBundleDescription().isResolved())
+ selected.add(singleton);
+ }
+ // get the collision map for the BSN
+ Map<ResolverBundle, Collection<ResolverBundle>> collisionMap = getCollisionMap(sameBSN);
+ // process the collision map
+ for (ResolverBundle singleton : sameBSN) {
+ if (selected.contains(singleton))
+ continue; // no need to process resolved bundles
+ Collection<ResolverBundle> collisions = collisionMap.get(singleton);
+ if (collisions == null || !singleton.isResolvable())
+ continue; // not a singleton or not resolvable
+ Collection<ResolverBundle> pickOneToResolve = new ArrayList<ResolverBundle>();
+ for (ResolverBundle collision : collisions) {
+ if (selected.contains(collision)) {
+ // Must fail since there is already a selected bundle which is a collision of the singleton bundle
+ singleton.setResolvable(false);
+ state.addResolverError(singleton.getBundleDescription(), ResolverError.SINGLETON_SELECTION, collision.getBundleDescription().toString(), null);
+ break;
+ }
+ if (!pickOneToResolve.contains(collision))
+ pickOneToResolve.add(collision);
+ }
+ // need to make sure the bundle does not collide from the POV of another entry
+ for (Map.Entry<ResolverBundle, Collection<ResolverBundle>> collisionEntry : collisionMap.entrySet()) {
+ if (collisionEntry.getKey() != singleton && collisionEntry.getValue().contains(singleton)) {
+ if (selected.contains(collisionEntry.getKey())) {
+ // Must fail since there is already a selected bundle for which the singleton bundle is a collision
+ singleton.setResolvable(false);
+ state.addResolverError(singleton.getBundleDescription(), ResolverError.SINGLETON_SELECTION, collisionEntry.getKey().getBundleDescription().toString(), null);
+ break;
+ }
+ if (!pickOneToResolve.contains(collisionEntry.getKey()))
+ pickOneToResolve.add(collisionEntry.getKey());
+ }
+ }
+ if (singleton.isResolvable()) {
+ pickOneToResolve.add(singleton);
+ selected.add(pickOneToResolve(pickOneToResolve));
+ }
+ }
+ }
+ }
+
+ private ResolverBundle pickOneToResolve(Collection<ResolverBundle> pickOneToResolve) {
+ ResolverBundle selectedVersion = null;
+ for (ResolverBundle singleton : pickOneToResolve) {
+ if (selectedVersion == null)
+ selectedVersion = singleton;
+ boolean higherVersion = selectionPolicy != null ? selectionPolicy.compare(selectedVersion.getBundleDescription(), singleton.getBundleDescription()) > 0 : selectedVersion.getVersion().compareTo(singleton.getVersion()) < 0;
+ if (higherVersion)
+ selectedVersion = singleton;
+ }
+
+ for (ResolverBundle singleton : pickOneToResolve) {
+ if (singleton != selectedVersion) {
+ singleton.setResolvable(false);
+ state.addResolverError(singleton.getBundleDescription(), ResolverError.SINGLETON_SELECTION, selectedVersion.getBundleDescription().toString(), null);
+ }
+ }
+ return selectedVersion;
+ }
+
+ private Map<ResolverBundle, Collection<ResolverBundle>> getCollisionMap(List<ResolverBundle> sameBSN) {
+ Map<ResolverBundle, Collection<ResolverBundle>> result = new HashMap<ResolverBundle, Collection<ResolverBundle>>();
+ for (ResolverBundle singleton : sameBSN) {
+ if (!singleton.getBundleDescription().isSingleton() || !singleton.isResolvable())
+ continue; // ignore non-singleton and non-resolvable
+ List<ResolverBundle> collisionCandidates = new ArrayList<ResolverBundle>(sameBSN.size() - 1);
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(sameBSN.size() - 1);
+ for (ResolverBundle collision : sameBSN) {
+ if (collision == singleton || !collision.getBundleDescription().isSingleton() || !collision.isResolvable())
+ continue; // Ignore the bundle we are checking and non-singletons and non-resolvable
+ collisionCandidates.add(collision);
+ capabilities.add(getIdentity(collision));
+ }
+ if (hook != null)
+ hook.filterSingletonCollisions(getIdentity(singleton), asCapabilities(new ArrayMap<BundleCapability, ResolverBundle>(capabilities, collisionCandidates)));
+ result.put(singleton, collisionCandidates);
+ }
+ return result;
+ }
+
+ private BundleCapability getIdentity(ResolverBundle bundle) {
+ List<BundleCapability> identities = bundle.getBundleDescription().getDeclaredCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+ return identities.size() == 1 ? identities.get(0) : bundle.getCapability();
+ }
+
+ private void resolveBundles0(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ if (developmentMode)
+ // need to sort bundles to keep consistent order for fragment attachment (bug 174930)
+ Arrays.sort(bundles);
+ // First attach all fragments to the matching hosts
+ Collection<String> processedFragments = new HashSet<String>(bundles.length);
+ for (int i = 0; i < bundles.length; i++)
+ attachFragment(bundles[i], processedFragments);
+
+ // Lists of cyclic dependencies recording during resolving
+ List<ResolverBundle> cycle = new ArrayList<ResolverBundle>(1); // start small
+ // Attempt to resolve all unresolved bundles
+ for (int i = 0; i < bundles.length; i++) {
+ if (DEBUG)
+ ResolverImpl.log("** RESOLVING " + bundles[i] + " **"); //$NON-NLS-1$ //$NON-NLS-2$
+ cycle.clear();
+ resolveBundle(bundles[i], cycle);
+ // Check for any bundles involved in a cycle.
+ // if any bundles in the cycle are not resolved then we need to resolve the resolvable ones
+ checkCycle(cycle);
+ }
+ // Resolve all fragments that are still attached to at least one host.
+ if (unresolvedBundles.size() > 0) {
+ ResolverBundle[] unresolved = unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
+ for (int i = 0; i < unresolved.length; i++)
+ resolveFragment(unresolved[i]);
+ }
+ checkUsesConstraints(bundles, platformProperties);
+ checkComposites(bundles, platformProperties);
+ }
+
+ private void checkComposites(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ CompositeResolveHelperRegistry helpers = getCompositeHelpers();
+ if (helpers == null)
+ return;
+ Set<ResolverBundle> exclude = null;
+ for (int i = 0; i < bundles.length; i++) {
+ CompositeResolveHelper helper = helpers.getCompositeResolveHelper(bundles[i].getBundleDescription());
+ if (helper == null)
+ continue;
+ if (!bundles[i].isResolved())
+ continue;
+ if (!helper.giveExports(getExportsWiredTo(bundles[i], null))) {
+ state.addResolverError(bundles[i].getBundleDescription(), ResolverError.DISABLED_BUNDLE, null, null);
+ bundles[i].setResolvable(false);
+ // We pass false for keepFragmentsAttached because we need to redo the attachments (bug 272561)
+ setBundleUnresolved(bundles[i], false, false);
+ if (exclude == null)
+ exclude = new HashSet<ResolverBundle>(1);
+ exclude.add(bundles[i]);
+ }
+ }
+ reResolveBundles(exclude, bundles, platformProperties);
+ }
+
+ private void checkUsesConstraints(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ List<ResolverConstraint> conflictingConstraints = findBestCombination(bundles, platformProperties);
+ if (conflictingConstraints == null)
+ return;
+ Set<ResolverBundle> conflictedBundles = null;
+ for (ResolverConstraint conflict : conflictingConstraints) {
+ if (conflict.isOptional()) {
+ conflict.clearPossibleSuppliers();
+ continue;
+ }
+ if (conflictedBundles == null)
+ conflictedBundles = new HashSet<ResolverBundle>(conflictingConstraints.size());
+ ResolverBundle conflictedBundle;
+ if (conflict.isFromFragment())
+ conflictedBundle = bundleMapping.get(conflict.getVersionConstraint().getBundle());
+ else
+ conflictedBundle = conflict.getBundle();
+ if (conflictedBundle != null) {
+ if (DEBUG_USES)
+ System.out.println("Found conflicting constraint: " + conflict + " in bundle " + conflictedBundle); //$NON-NLS-1$//$NON-NLS-2$
+ conflictedBundles.add(conflictedBundle);
+ int type = conflict instanceof ResolverImport ? ResolverError.IMPORT_PACKAGE_USES_CONFLICT : ResolverError.REQUIRE_BUNDLE_USES_CONFLICT;
+ state.addResolverError(conflictedBundle.getBundleDescription(), type, conflict.getVersionConstraint().toString(), conflict.getVersionConstraint());
+ conflictedBundle.setResolvable(false);
+ // We pass false for keepFragmentsAttached because we need to redo the attachments (bug 272561)
+ setBundleUnresolved(conflictedBundle, false, false);
+ }
+ }
+ reResolveBundles(conflictedBundles, bundles, platformProperties);
+ }
+
+ private void reResolveBundles(Set<ResolverBundle> exclude, ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ if (exclude == null || exclude.size() == 0)
+ return;
+ List<ResolverBundle> remainingUnresolved = new ArrayList<ResolverBundle>();
+ for (int i = 0; i < bundles.length; i++) {
+ if (!exclude.contains(bundles[i])) {
+ // We pass false for keepFragmentsAttached because we need to redo the attachments (bug 272561)
+ setBundleUnresolved(bundles[i], false, false);
+ remainingUnresolved.add(bundles[i]);
+ }
+ }
+ resolveBundles0(remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties);
+ }
+
+ private List<ResolverConstraint> findBestCombination(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ Object usesMode = platformProperties.length == 0 ? null : platformProperties[0].get("osgi.resolver.usesMode"); //$NON-NLS-1$
+ if (usesMode == null)
+ usesMode = secureAction.getProperty("osgi.resolver.usesMode"); //$NON-NLS-1$
+ if ("ignore".equals(usesMode) || developmentMode) //$NON-NLS-1$
+ return null;
+ Set<String> bundleConstraints = new HashSet<String>();
+ Set<String> packageConstraints = new HashSet<String>();
+ // first try out the initial selections
+ List<ResolverConstraint> initialConflicts = getConflicts(bundles, packageConstraints, bundleConstraints);
+ if (initialConflicts == null || "tryFirst".equals(usesMode) || usesCalculationTimeout) { //$NON-NLS-1$
+ groupingChecker.clear();
+ // the first combination have no conflicts or
+ // we only are trying the first combination or
+ // we have timed out the calculation; return without iterating over all combinations
+ return initialConflicts;
+ }
+ ResolverConstraint[][] multipleSuppliers = getMultipleSuppliers(bundles, packageConstraints, bundleConstraints);
+ List<ResolverConstraint> conflicts = null;
+ int[] bestCombination = new int[multipleSuppliers.length];
+ conflicts = findBestCombination(bundles, multipleSuppliers, bestCombination, initialConflicts);
+ if (DEBUG_USES) {
+ System.out.print("Best combination found: "); //$NON-NLS-1$
+ printCombination(bestCombination);
+ }
+ for (int i = 0; i < bestCombination.length; i++) {
+ for (int j = 0; j < multipleSuppliers[i].length; j++) {
+ ResolverConstraint constraint = multipleSuppliers[i][j];
+ constraint.setSelectedSupplier(bestCombination[i]);
+ // sanity check to make sure we did not just get wired to our own dropped export
+ VersionSupplier selectedSupplier = constraint.getSelectedSupplier();
+ if (selectedSupplier != null)
+ selectedSupplier.setSubstitute(null);
+ }
+ }
+
+ // do not need to keep uses data in memory
+ groupingChecker.clear();
+ return conflicts;
+ }
+
+ private int[] getCombination(ResolverConstraint[][] multipleSuppliers, int[] combination) {
+ for (int i = 0; i < combination.length; i++)
+ combination[i] = multipleSuppliers[i][0].getSelectedSupplierIndex();
+ return combination;
+ }
+
+ private List<ResolverConstraint> findBestCombination(ResolverBundle[] bundles, ResolverConstraint[][] multipleSuppliers, int[] bestCombination, List<ResolverConstraint> bestConflicts) {
+ // now iterate over every possible combination until either zero conflicts are found
+ // or we have run out of combinations
+ // if all combinations are tried then return the combination with the lowest number of conflicts
+ long initialTime = System.currentTimeMillis();
+ long timeLimit;
+ if (usesTimeout < 0)
+ timeLimit = Math.min(MAX_USES_TIME_BASE + (bundles.length * 30), MAX_USES_TIME_LIMIT);
+ else
+ timeLimit = usesTimeout;
+
+ int bestConflictCount = getConflictCount(bestConflicts);
+ ResolverBundle[] bestConflictBundles = getConflictedBundles(bestConflicts);
+ while (bestConflictCount != 0 && getNextCombination(multipleSuppliers)) {
+ if ((System.currentTimeMillis() - initialTime) > timeLimit) {
+ if (DEBUG_USES)
+ System.out.println("Uses constraint check has timedout. Using the best solution found so far."); //$NON-NLS-1$
+ usesCalculationTimeout = true;
+ break;
+ }
+ if (DEBUG_USES)
+ printCombination(getCombination(multipleSuppliers, new int[multipleSuppliers.length]));
+ // first count the conflicts for the bundles with conflicts from the best combination
+ // this significantly reduces the time it takes to populate the GroupingChecker for cases where
+ // the combination is no better.
+ List<ResolverConstraint> conflicts = getConflicts(bestConflictBundles, null, null);
+ int conflictCount = getConflictCount(conflicts);
+ if (conflictCount >= bestConflictCount) {
+ if (DEBUG_USES)
+ System.out.println("Combination is not better that current best: " + conflictCount + ">=" + bestConflictCount); //$NON-NLS-1$ //$NON-NLS-2$
+ // no need to test the other bundles;
+ // this combination is no better for the bundles which conflict with the current best combination
+ continue;
+ }
+ // this combination improves upon the conflicts for the bundles which conflict with the current best combination;
+ // do an complete conflict count
+ conflicts = getConflicts(bundles, null, null);
+ conflictCount = getConflictCount(conflicts);
+ if (conflictCount < bestConflictCount) {
+ // this combination is better that the current best combination; save this combination as the current best
+ bestConflictCount = conflictCount;
+ bestConflicts = conflicts;
+ getCombination(multipleSuppliers, bestCombination);
+ bestConflictBundles = getConflictedBundles(bestConflicts);
+ if (DEBUG_USES)
+ System.out.println("Combination selected as current best: number of conflicts: " + bestConflictCount); //$NON-NLS-1$
+ } else if (DEBUG_USES) {
+ System.out.println("Combination is not better that current best: " + conflictCount + ">=" + bestConflictCount); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ return bestConflicts;
+ }
+
+ private void printCombination(int[] curCombination) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ for (int i = 0; i < curCombination.length; i++) {
+ sb.append(curCombination[i]);
+ if (i < curCombination.length - 1)
+ sb.append(',');
+ }
+ sb.append(']');
+ System.out.println(sb.toString());
+ }
+
+ private ResolverBundle[] getConflictedBundles(List<ResolverConstraint> bestConflicts) {
+ if (bestConflicts == null)
+ return new ResolverBundle[0];
+ List<ResolverBundle> conflictedBundles = new ArrayList<ResolverBundle>(bestConflicts.size());
+ for (ResolverConstraint constraint : bestConflicts)
+ if (!conflictedBundles.contains(constraint.getBundle()))
+ conflictedBundles.add(constraint.getBundle());
+ return conflictedBundles.toArray(new ResolverBundle[conflictedBundles.size()]);
+ }
+
+ private boolean getNextCombination(ResolverConstraint[][] multipleSuppliers) {
+ int current = 0;
+ while (current < multipleSuppliers.length) {
+ if (multipleSuppliers[current][0].selectNextSupplier()) {
+ for (int i = 1; i < multipleSuppliers[current].length; i++)
+ multipleSuppliers[current][i].selectNextSupplier();
+ return true; // the current slot has a next supplier
+ }
+ for (int i = 0; i < multipleSuppliers[current].length; i++)
+ multipleSuppliers[current][i].setSelectedSupplier(0); // reset the current slot
+ current++; // move to the next slot
+ }
+ return false;
+ }
+
+ // only count non-optional conflicts
+ private int getConflictCount(List<ResolverConstraint> conflicts) {
+ if (conflicts == null || conflicts.size() == 0)
+ return 0;
+ int result = 0;
+ for (ResolverConstraint constraint : conflicts)
+ if (!constraint.isOptional())
+ result += 1;
+ return result;
+ }
+
+ private List<ResolverConstraint> getConflicts(ResolverBundle[] bundles, Set<String> packageConstraints, Set<String> bundleConstraints) {
+ groupingChecker.clear();
+ List<ResolverConstraint> conflicts = null;
+ for (int i = 0; i < bundles.length; i++)
+ conflicts = addConflicts(bundles[i], packageConstraints, bundleConstraints, conflicts);
+ return conflicts;
+ }
+
+ private List<ResolverConstraint> addConflicts(ResolverBundle bundle, Set<String> packageConstraints, Set<String> bundleConstraints, List<ResolverConstraint> conflicts) {
+ BundleConstraint[] requires = bundle.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ ResolverBundle selectedSupplier = (ResolverBundle) requires[i].getSelectedSupplier();
+ PackageRoots[][] conflict = selectedSupplier == null ? null : groupingChecker.isConsistent(bundle, selectedSupplier);
+ if (conflict != null) {
+ addConflictNames(conflict, packageConstraints, bundleConstraints);
+ if (conflicts == null)
+ conflicts = new ArrayList<ResolverConstraint>(1);
+ conflicts.add(requires[i]);
+ }
+ }
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ ResolverExport selectedSupplier = (ResolverExport) imports[i].getSelectedSupplier();
+ PackageRoots[][] conflict = selectedSupplier == null ? null : groupingChecker.isConsistent(bundle, selectedSupplier);
+ if (conflict != null) {
+ addConflictNames(conflict, packageConstraints, bundleConstraints);
+ if (conflicts == null)
+ conflicts = new ArrayList<ResolverConstraint>(1);
+ conflicts.add(imports[i]);
+ }
+ }
+
+ GenericConstraint[] genericRequires = bundle.getGenericRequires();
+ for (GenericConstraint capabilityRequirement : genericRequires) {
+ VersionSupplier[] suppliers = capabilityRequirement.getMatchingCapabilities();
+ if (suppliers == null)
+ continue;
+ for (VersionSupplier supplier : suppliers) {
+ PackageRoots[][] conflict = groupingChecker.isConsistent(bundle, (GenericCapability) supplier);
+ if (conflict != null) {
+ addConflictNames(conflict, packageConstraints, bundleConstraints);
+ if (conflicts == null)
+ conflicts = new ArrayList<ResolverConstraint>(1);
+ conflicts.add(capabilityRequirement);
+ }
+ }
+ }
+ return conflicts;
+ }
+
+ // records the conflict names we can use to scope down the list of multiple suppliers
+ private void addConflictNames(PackageRoots[][] conflict, Set<String> packageConstraints, Set<String> bundleConstraints) {
+ if (packageConstraints == null || bundleConstraints == null)
+ return;
+ for (int i = 0; i < conflict.length; i++) {
+ packageConstraints.add(conflict[i][0].getName());
+ packageConstraints.add(conflict[i][1].getName());
+ ResolverExport[] exports0 = conflict[i][0].getRoots();
+ if (exports0 != null)
+ for (int j = 0; j < exports0.length; j++) {
+ ResolverBundle exporter = exports0[j].getExporter();
+ if (exporter != null && exporter.getName() != null)
+ bundleConstraints.add(exporter.getName());
+ }
+ ResolverExport[] exports1 = conflict[i][1].getRoots();
+ if (exports1 != null)
+ for (int j = 0; j < exports1.length; j++) {
+ ResolverBundle exporter = exports1[j].getExporter();
+ if (exporter != null && exporter.getName() != null)
+ bundleConstraints.add(exporter.getName());
+ }
+ }
+ }
+
+ // get a list of resolver constraints that have multiple suppliers
+ // a 2 demensional array is used each entry is a list of identical constraints that have identical suppliers.
+ private ResolverConstraint[][] getMultipleSuppliers(ResolverBundle[] bundles, Set<String> packageConstraints, Set<String> bundleConstraints) {
+ List<ResolverImport> multipleImportSupplierList = new ArrayList<ResolverImport>(1);
+ List<BundleConstraint> multipleRequireSupplierList = new ArrayList<BundleConstraint>(1);
+ List<GenericConstraint> multipleGenericSupplierList = new ArrayList<GenericConstraint>(1);
+ for (ResolverBundle bundle : bundles) {
+ BundleConstraint[] requires = bundle.getRequires();
+ for (BundleConstraint require : requires)
+ if (require.getNumPossibleSuppliers() > 1)
+ multipleRequireSupplierList.add(require);
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (ResolverImport importPkg : imports) {
+ if (importPkg.getNumPossibleSuppliers() > 1) {
+ Integer eeProfile = (Integer) ((ResolverExport) importPkg.getSelectedSupplier()).getExportPackageDescription().getDirective(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (eeProfile.intValue() < 0) {
+ // this is a normal package; always add it
+ multipleImportSupplierList.add(importPkg);
+ } else {
+ // this is a system bundle export
+ // If other exporters of this package also require the system bundle
+ // then this package does not need to be added to the mix
+ // this is an optimization for bundles like org.eclipse.xerces
+ // that export lots of packages also exported by the system bundle on J2SE 1.4
+ VersionSupplier[] suppliers = importPkg.getPossibleSuppliers();
+ for (int suppliersIndex = 1; suppliersIndex < suppliers.length; suppliersIndex++) {
+ Integer ee = (Integer) ((ResolverExport) suppliers[suppliersIndex]).getExportPackageDescription().getDirective(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (ee.intValue() >= 0)
+ continue;
+ if (((ResolverExport) suppliers[suppliersIndex]).getExporter().getRequire(getSystemBundle()) == null)
+ if (((ResolverExport) suppliers[suppliersIndex]).getExporter().getRequire(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) == null) {
+ multipleImportSupplierList.add(importPkg);
+ break;
+ }
+ }
+ }
+ }
+ }
+ GenericConstraint[] genericRequires = bundle.getGenericRequires();
+ for (GenericConstraint genericRequire : genericRequires)
+ if (genericRequire.getNumPossibleSuppliers() > 1 && genericRequire.supplierHasUses())
+ multipleGenericSupplierList.add(genericRequire);
+ }
+ List<ResolverConstraint[]> results = new ArrayList<ResolverConstraint[]>();
+ if (multipleImportSupplierList.size() + multipleRequireSupplierList.size() + multipleGenericSupplierList.size() > usesMultipleSuppliersLimit) {
+ // we have hit a max on the multiple suppliers in the lists without merging.
+ // first merge the identical constraints that have identical suppliers
+ Map<String, List<List<ResolverConstraint>>> multipleImportSupplierMaps = new HashMap<String, List<List<ResolverConstraint>>>();
+ for (ResolverImport importPkg : multipleImportSupplierList)
+ addMutipleSupplierConstraint(multipleImportSupplierMaps, importPkg, importPkg.getName());
+ Map<String, List<List<ResolverConstraint>>> multipleRequireSupplierMaps = new HashMap<String, List<List<ResolverConstraint>>>();
+ for (BundleConstraint requireBundle : multipleRequireSupplierList)
+ addMutipleSupplierConstraint(multipleRequireSupplierMaps, requireBundle, requireBundle.getName());
+ Map<String, List<List<ResolverConstraint>>> multipleGenericSupplierMaps = new HashMap<String, List<List<ResolverConstraint>>>();
+ for (GenericConstraint genericRequire : multipleGenericSupplierList)
+ addMutipleSupplierConstraint(multipleGenericSupplierMaps, genericRequire, genericRequire.getNameSpace());
+ addMergedSuppliers(results, multipleImportSupplierMaps);
+ addMergedSuppliers(results, multipleRequireSupplierMaps);
+ addMergedSuppliers(results, multipleGenericSupplierMaps);
+ // check the results to see if we have reduced the number enough
+ if (results.size() > usesMultipleSuppliersLimit && packageConstraints != null && bundleConstraints != null) {
+ // we still have too big of a list; filter out constraints that are not in conflict
+ List<ResolverConstraint[]> tooBig = results;
+ results = new ArrayList<ResolverConstraint[]>();
+ for (ResolverConstraint[] constraints : tooBig) {
+ ResolverConstraint constraint = constraints.length > 0 ? constraints[0] : null;
+ if (constraint instanceof ResolverImport) {
+ if (packageConstraints.contains(constraint.getName()))
+ results.add(constraints);
+ } else if (constraint instanceof BundleConstraint) {
+ if (bundleConstraints.contains(constraint.getName()))
+ results.add(constraints);
+ }
+ }
+ }
+ } else {
+ // the size is acceptable; just copy the lists as-is
+ for (ResolverConstraint constraint : multipleImportSupplierList)
+ results.add(new ResolverConstraint[] {constraint});
+ for (ResolverConstraint constraint : multipleRequireSupplierList)
+ results.add(new ResolverConstraint[] {constraint});
+ for (ResolverConstraint constraint : multipleGenericSupplierList)
+ results.add(new ResolverConstraint[] {constraint});
+
+ }
+ return results.toArray(new ResolverConstraint[results.size()][]);
+ }
+
+ String getSystemBundle() {
+ Dictionary<?, ?>[] platformProperties = state.getPlatformProperties();
+ String systemBundle = platformProperties.length == 0 ? null : (String) platformProperties[0].get(StateImpl.STATE_SYSTEM_BUNDLE);
+ if (systemBundle == null)
+ systemBundle = EquinoxContainer.NAME;
+ return systemBundle;
+ }
+
+ private void addMergedSuppliers(List<ResolverConstraint[]> mergedSuppliers, Map<String, List<List<ResolverConstraint>>> constraints) {
+ for (List<List<ResolverConstraint>> mergedConstraintLists : constraints.values()) {
+ for (List<ResolverConstraint> constraintList : mergedConstraintLists) {
+ mergedSuppliers.add(constraintList.toArray(new ResolverConstraint[constraintList.size()]));
+ }
+ }
+ }
+
+ private void addMutipleSupplierConstraint(Map<String, List<List<ResolverConstraint>>> constraints, ResolverConstraint constraint, String key) {
+ List<List<ResolverConstraint>> mergedConstraintLists = constraints.get(key);
+ if (mergedConstraintLists == null) {
+ mergedConstraintLists = new ArrayList<List<ResolverConstraint>>(0);
+ List<ResolverConstraint> constraintList = new ArrayList<ResolverConstraint>(1);
+ constraintList.add(constraint);
+ mergedConstraintLists.add(constraintList);
+ constraints.put(key, mergedConstraintLists);
+ return;
+ }
+ for (List<ResolverConstraint> constraintList : mergedConstraintLists) {
+ ResolverConstraint mergedConstraint = constraintList.get(0);
+ VersionSupplier[] suppliers1 = constraint.getPossibleSuppliers();
+ VersionSupplier[] suppliers2 = mergedConstraint.getPossibleSuppliers();
+ if (suppliers1.length != suppliers2.length)
+ continue;
+ for (int i = 0; i < suppliers1.length; i++)
+ if (suppliers1[i] != suppliers2[i])
+ continue;
+ constraintList.add(constraint);
+ return;
+ }
+ List<ResolverConstraint> constraintList = new ArrayList<ResolverConstraint>(1);
+ constraintList.add(constraint);
+ mergedConstraintLists.add(constraintList);
+ }
+
+ private void checkCycle(List<ResolverBundle> cycle) {
+ int cycleSize = cycle.size();
+ if (cycleSize == 0)
+ return;
+ cycleLoop: for (Iterator<ResolverBundle> iCycle = cycle.iterator(); iCycle.hasNext();) {
+ ResolverBundle cycleBundle = iCycle.next();
+ if (!cycleBundle.isResolvable()) {
+ iCycle.remove(); // remove this bundle from the list of bundles that need re-resolved
+ continue cycleLoop;
+ }
+ // Check that we haven't wired to any dropped exports
+ ResolverImport[] imports = cycleBundle.getImportPackages();
+ for (int j = 0; j < imports.length; j++) {
+ // check for dropped exports
+ while (imports[j].getSelectedSupplier() != null) {
+ ResolverExport importSupplier = (ResolverExport) imports[j].getSelectedSupplier();
+ if (importSupplier.getSubstitute() != null)
+ imports[j].selectNextSupplier();
+ else
+ break;
+ }
+ if (!imports[j].isDynamic() && !imports[j].isOptional() && imports[j].getSelectedSupplier() == null) {
+ cycleBundle.setResolvable(false);
+ state.addResolverError(imports[j].getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, imports[j].getVersionConstraint().toString(), imports[j].getVersionConstraint());
+ iCycle.remove();
+ continue cycleLoop;
+ }
+ }
+ }
+ if (cycle.size() != cycleSize) {
+ //we removed an un-resolvable bundle; must re-resolve remaining cycle
+ for (int i = 0; i < cycle.size(); i++) {
+ ResolverBundle cycleBundle = cycle.get(i);
+ cycleBundle.clearWires();
+ }
+ List<ResolverBundle> innerCycle = new ArrayList<ResolverBundle>(cycle.size());
+ for (int i = 0; i < cycle.size(); i++)
+ resolveBundle(cycle.get(i), innerCycle);
+ checkCycle(innerCycle);
+ } else {
+ for (int i = 0; i < cycle.size(); i++) {
+ if (DEBUG || DEBUG_CYCLES)
+ ResolverImpl.log("Pushing " + cycle.get(i) + " to RESOLVED"); //$NON-NLS-1$ //$NON-NLS-2$
+ setBundleResolved(cycle.get(i));
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ static Collection<BundleCapability> asCapabilities(Collection<? extends BundleCapability> capabilities) {
+ return (Collection<BundleCapability>) capabilities;
+ }
+
+ private void resolveFragment(ResolverBundle fragment) {
+ if (!fragment.isFragment())
+ return;
+ if (fragment.getHost().getNumPossibleSuppliers() > 0)
+ if (!developmentMode || state.getResolverErrors(fragment.getBundleDescription()).length == 0)
+ setBundleResolved(fragment);
+ }
+
+ // This method will attempt to resolve the supplied bundle and any bundles that it is dependent on
+ private boolean resolveBundle(ResolverBundle bundle, List<ResolverBundle> cycle) {
+ if (bundle.isFragment())
+ return false;
+ if (!bundle.isResolvable()) {
+ if (DEBUG)
+ ResolverImpl.log(" - " + bundle + " is unresolvable"); //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ }
+ switch (bundle.getState()) {
+ case ResolverBundle.RESOLVED :
+ // 'bundle' is already resolved so just return
+ if (DEBUG)
+ ResolverImpl.log(" - " + bundle + " already resolved"); //$NON-NLS-1$ //$NON-NLS-2$
+ return true;
+ case ResolverBundle.UNRESOLVED :
+ // 'bundle' is UNRESOLVED so move to RESOLVING
+ bundle.clearWires();
+ setBundleResolving(bundle);
+ break;
+ case ResolverBundle.RESOLVING :
+ if (cycle.contains(bundle))
+ return true;
+ break;
+ default :
+ break;
+ }
+
+ boolean failed = false;
+
+ if (!failed) {
+ GenericConstraint[] genericRequires = bundle.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++) {
+ if (genericRequires[i].isEffective()) {
+ if (!resolveGenericReq(genericRequires[i], cycle)) {
+ if (DEBUG || DEBUG_GENERICS)
+ ResolverImpl.log("** GENERICS " + genericRequires[i].getVersionConstraint().getName() + "[" + genericRequires[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ state.addResolverError(genericRequires[i].getVersionConstraint().getBundle(), ResolverError.MISSING_GENERIC_CAPABILITY, genericRequires[i].getVersionConstraint().toString(), genericRequires[i].getVersionConstraint());
+ if (genericRequires[i].isFromFragment()) {
+ if (!developmentMode) // only detach fragments when not in devmode
+ bundle.detachFragment(bundleMapping.get(genericRequires[i].getVersionConstraint().getBundle()), null);
+ continue;
+ }
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ failed = true;
+ break;
+ }
+ } else {
+ if (StateImpl.OSGI_EE_NAMESPACE.equals(genericRequires[i].getNameSpace())) {
+ VersionSupplier supplier = genericRequires[i].getSelectedSupplier();
+ Integer ee = supplier == null ? null : (Integer) ((GenericDescription) supplier.getBaseDescription()).getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (ee != null && ((BundleDescriptionImpl) bundle.getBaseDescription()).getEquinoxEE() < 0)
+ ((BundleDescriptionImpl) bundle.getBundleDescription()).setEquinoxEE(ee);
+ }
+ }
+ }
+ }
+ }
+
+ if (!failed) {
+ // Iterate thru required bundles of 'bundle' trying to find matching bundles.
+ BundleConstraint[] requires = bundle.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ if (!resolveRequire(requires[i], cycle)) {
+ if (DEBUG || DEBUG_REQUIRES)
+ ResolverImpl.log("** REQUIRE " + requires[i].getVersionConstraint().getName() + "[" + requires[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ state.addResolverError(requires[i].getVersionConstraint().getBundle(), ResolverError.MISSING_REQUIRE_BUNDLE, requires[i].getVersionConstraint().toString(), requires[i].getVersionConstraint());
+ // If the require has failed to resolve and it is from a fragment, then remove the fragment from the host
+ if (requires[i].isFromFragment()) {
+ if (!developmentMode) // only detach fragments when not in devmode
+ bundle.detachFragment(bundleMapping.get(requires[i].getVersionConstraint().getBundle()), requires[i]);
+ continue;
+ }
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ failed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!failed) {
+ // Iterate thru imports of 'bundle' trying to find matching exports.
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ // Only resolve non-dynamic imports here
+ if (!imports[i].isDynamic() && !resolveImport(imports[i], cycle)) {
+ if (DEBUG || DEBUG_IMPORTS)
+ ResolverImpl.log("** IMPORT " + imports[i].getName() + "[" + imports[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ // If the import has failed to resolve and it is from a fragment, then remove the fragment from the host
+ state.addResolverError(imports[i].getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, imports[i].getVersionConstraint().toString(), imports[i].getVersionConstraint());
+ if (imports[i].isFromFragment()) {
+ if (!developmentMode) // only detach fragments when not in devmode
+ bundle.detachFragment(bundleMapping.get(imports[i].getVersionConstraint().getBundle()), imports[i]);
+ continue;
+ }
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ failed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // check that fragment constraints are met by the constraints that got resolved to the host
+ checkFragmentConstraints(bundle);
+
+ // do some extra checking when in development mode to see if other resolver error occurred
+ if (developmentMode && !failed && state.getResolverErrors(bundle.getBundleDescription()).length > 0)
+ failed = true;
+
+ // Need to check that all mandatory imports are wired. If they are then
+ // set the bundle RESOLVED, otherwise set it back to UNRESOLVED
+ if (failed) {
+ setBundleUnresolved(bundle, false, developmentMode);
+ if (DEBUG)
+ ResolverImpl.log(bundle + " NOT RESOLVED"); //$NON-NLS-1$
+ } else if (!cycle.contains(bundle)) {
+ setBundleResolved(bundle);
+ if (DEBUG)
+ ResolverImpl.log(bundle + " RESOLVED"); //$NON-NLS-1$
+ }
+
+ if (bundle.getState() == ResolverBundle.UNRESOLVED)
+ bundle.setResolvable(false); // Set it to unresolvable so we don't attempt to resolve it again in this round
+
+ return bundle.getState() != ResolverBundle.UNRESOLVED;
+ }
+
+ private void checkFragmentConstraints(ResolverBundle bundle) {
+ // get all currently attached fragments and ensure that any constraints
+ // they have do not conflict with the constraints resolved to by the host
+ ResolverBundle[] fragments = bundle.getFragments();
+ for (int i = 0; i < fragments.length; i++) {
+ BundleDescription fragment = fragments[i].getBundleDescription();
+ if (bundle.constraintsConflict(fragment, fragment.getImportPackages(), fragment.getRequiredBundles(), fragment.getGenericRequires()) && !developmentMode)
+ // found some conflicts; detach the fragment
+ bundle.detachFragment(fragments[i], null);
+ }
+ }
+
+ private boolean resolveGenericReq(GenericConstraint constraint, List<ResolverBundle> cycle) {
+ if (DEBUG_GENERICS)
+ ResolverImpl.log("Trying to resolve: " + constraint.getBundle() + ", " + constraint.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$
+ VersionSupplier matchingCapability = constraint.getSelectedSupplier();
+ if (matchingCapability != null) {
+ if (!cycle.contains(constraint.getBundle())) {
+ cycle.add(constraint.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("generic cycle: " + constraint.getBundle() + " -> " + constraint.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (DEBUG_GENERICS)
+ ResolverImpl.log(" - already wired"); //$NON-NLS-1$
+ return true; // Already wired (due to grouping dependencies) so just return
+ }
+ List<GenericCapability> candidates;
+ long timestamp;
+ do {
+ timestamp = state.getTimeStamp();
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(constraint.getNameSpace());
+ String name = constraint.getName();
+ List<GenericCapability> capabilities;
+ if (namespace == null)
+ capabilities = Collections.EMPTY_LIST;
+ else
+ capabilities = name == null || name.indexOf('*') >= 0 ? namespace.getAllValues() : namespace.get(name);
+ candidates = new ArrayList<GenericCapability>(capabilities);
+ List<BundleCapability> genCapabilities = new ArrayList<BundleCapability>(candidates.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<GenericCapability> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ GenericCapability capability = iCandidates.next();
+ if (!constraint.isSatisfiedBy(capability)) {
+ iCandidates.remove();
+ } else {
+ genCapabilities.add(capability.getCapability());
+ }
+ }
+ if (hook != null)
+ hook.filterMatches(constraint.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, GenericCapability>(genCapabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ boolean result = false;
+ // We are left with only capabilities that satisfy the constraint.
+ for (GenericCapability capability : candidates) {
+ if (DEBUG_GENERICS)
+ ResolverImpl.log("CHECKING GENERICS: " + capability.getBaseDescription()); //$NON-NLS-1$
+
+ // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
+ constraint.addPossibleSupplier(capability); // Wire to the capability
+ if (constraint.getBundle() == capability.getResolverBundle()) {
+ result = true; // Wired to ourselves
+ continue;
+ }
+ VersionSupplier[] capabilityHosts = capability.getResolverBundle().isFragment() ? capability.getResolverBundle().getHost().getPossibleSuppliers() : new ResolverBundle[] {capability.getResolverBundle()};
+ boolean foundResolvedMatch = false;
+ for (int i = 0; capabilityHosts != null && i < capabilityHosts.length; i++) {
+ ResolverBundle capabilitySupplier = capabilityHosts[i].getResolverBundle();
+ if (capabilitySupplier == constraint.getBundle()) {
+ // the capability is from a fragment attached to this host do not recursively resolve the host again
+ foundResolvedMatch = true;
+ continue;
+ }
+ boolean successfulResolve = false;
+ if (capabilitySupplier.getState() != ResolverBundle.RESOLVED) {
+ // only attempt to resolve the supplier if not osgi.ee name space
+ if (!StateImpl.OSGI_EE_NAMESPACE.equals(constraint.getNameSpace()))
+ successfulResolve = resolveBundle(capabilitySupplier, cycle);
+ }
+
+ // if in dev mode then allow a constraint to resolve to an unresolved bundle
+ if (capabilitySupplier.getState() == ResolverBundle.RESOLVED || (successfulResolve || developmentMode)) {
+ foundResolvedMatch |= !capability.getResolverBundle().isFragment() ? true : capability.getResolverBundle().getHost().getPossibleSuppliers() != null;
+ // Check cyclic dependencies
+ if (capabilitySupplier.getState() == ResolverBundle.RESOLVING)
+ if (!cycle.contains(capabilitySupplier))
+ cycle.add(capabilitySupplier);
+ }
+ }
+ if (!foundResolvedMatch) {
+ constraint.removePossibleSupplier(capability);
+ continue; // constraint hasn't resolved
+ }
+ if (DEBUG_GENERICS)
+ ResolverImpl.log("Found match: " + capability.getBaseDescription() + ". Wiring"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = true;
+ }
+ return result ? true : constraint.isOptional() || constraint.isFromRequiredEE();
+ }
+
+ // Resolve the supplied import. Returns true if the import can be resolved, false otherwise
+ private boolean resolveRequire(BundleConstraint req, List<ResolverBundle> cycle) {
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log("Trying to resolve: " + req.getBundle() + ", " + req.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$
+ if (req.getSelectedSupplier() != null) {
+ // Check for unrecorded cyclic dependency
+ if (!cycle.contains(req.getBundle())) {
+ cycle.add(req.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("require-bundle cycle: " + req.getBundle() + " -> " + req.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log(" - already wired"); //$NON-NLS-1$
+ return true; // Already wired (due to grouping dependencies) so just return
+ }
+ List<ResolverBundle> candidates;
+ long timestamp;
+ do {
+ timestamp = state.getTimeStamp();
+ List<ResolverBundle> bundles = resolverBundles.get(req.getVersionConstraint().getName());
+ candidates = new ArrayList<ResolverBundle>(bundles);
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(candidates.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<ResolverBundle> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ ResolverBundle bundle = iCandidates.next();
+ if (!req.isSatisfiedBy(bundle)) {
+ iCandidates.remove();
+ } else {
+ capabilities.add(bundle.getCapability());
+ }
+ }
+ if (hook != null)
+ hook.filterMatches(req.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, ResolverBundle>(capabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ // We are left with only capabilities that satisfy the require bundle.
+ boolean result = false;
+ for (ResolverBundle bundle : candidates) {
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log("CHECKING: " + bundle.getBundleDescription()); //$NON-NLS-1$
+
+ // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
+ req.addPossibleSupplier(bundle);
+ if (req.getBundle() != bundle) {
+ // if in dev mode then allow a constraint to resolve to an unresolved bundle
+ if (bundle.getState() != ResolverBundle.RESOLVED && !resolveBundle(bundle, cycle) && !developmentMode) {
+ req.removePossibleSupplier(bundle);
+ continue; // Bundle hasn't resolved
+ }
+ }
+ // Check cyclic dependencies
+ if (req.getBundle() != bundle) {
+ if (bundle.getState() == ResolverBundle.RESOLVING)
+ // If the bundle is RESOLVING, we have a cyclic dependency
+ if (!cycle.contains(req.getBundle())) {
+ cycle.add(req.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("require-bundle cycle: " + req.getBundle() + " -> " + req.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log("Found match: " + bundle.getBundleDescription() + ". Wiring"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = true;
+ }
+
+ if (result || req.isOptional())
+ return true; // If the req is optional then just return true
+
+ return false;
+ }
+
+ // Resolve the supplied import. Returns true if the import can be resolved, false otherwise
+ private boolean resolveImport(ResolverImport imp, List<ResolverBundle> cycle) {
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("Trying to resolve: " + imp.getBundle() + ", " + imp.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+ if (imp.getSelectedSupplier() != null) {
+ // Check for unrecorded cyclic dependency
+ if (!cycle.contains(imp.getBundle())) {
+ cycle.add(imp.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("import-package cycle: " + imp.getBundle() + " -> " + imp.getSelectedSupplier() + " from " + imp.getSelectedSupplier().getBundleDescription()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log(" - already wired"); //$NON-NLS-1$
+ return true; // Already wired (due to grouping dependencies) so just return
+ }
+ boolean result = false;
+ ResolverExport[] substitutableExps = imp.getBundle().getExports(imp.getName());
+ long timestamp;
+ List<ResolverExport> candidates;
+ do {
+ timestamp = state.getTimeStamp();
+ List<ResolverExport> exports = resolverExports.get(imp.getName());
+ candidates = new ArrayList<ResolverExport>(exports);
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(candidates.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<ResolverExport> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ ResolverExport export = iCandidates.next();
+ if (!imp.isSatisfiedBy(export)) {
+ iCandidates.remove();
+ } else {
+ capabilities.add(export.getCapability());
+ }
+ }
+ if (hook != null)
+ hook.filterMatches(imp.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, ResolverExport>(capabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ // We are left with only capabilities that satisfy the import.
+ for (ResolverExport export : candidates) {
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("CHECKING: " + export.getExporter().getBundleDescription() + ", " + export.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+
+ int originalState = export.getExporter().getState();
+ if (imp.isDynamic() && originalState != ResolverBundle.RESOLVED)
+ continue; // Must not attempt to resolve an exporter when dynamic
+ if (imp.getSelectedSupplier() != null && ((ResolverExport) imp.getSelectedSupplier()).getExporter() == imp.getBundle())
+ break; // We wired to ourselves; nobody else matters
+ // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
+ imp.addPossibleSupplier(export);
+ if (imp.getBundle() != export.getExporter()) {
+ for (int j = 0; j < substitutableExps.length; j++)
+ if (substitutableExps[j].getSubstitute() == null)
+ substitutableExps[j].setSubstitute(export); // Import wins, drop export
+ // if in dev mode then allow a constraint to resolve to an unresolved bundle
+ if ((originalState != ResolverBundle.RESOLVED && !resolveBundle(export.getExporter(), cycle) && !developmentMode) || export.getSubstitute() != null) {
+ // remove the possible supplier
+ imp.removePossibleSupplier(export);
+ // add back the exports of this package from the importer
+ if (imp.getSelectedSupplier() == null)
+ for (int j = 0; j < substitutableExps.length; j++)
+ if (substitutableExps[j].getSubstitute() == export)
+ substitutableExps[j].setSubstitute(null);
+ continue; // Bundle hasn't resolved || export has not been selected and is unavailable
+ }
+ } else if (export.getSubstitute() != null)
+ continue; // we already found a possible import that satisifies us; our export is dropped
+
+ // Record any cyclic dependencies
+ if (imp.getBundle() != export.getExporter())
+ if (export.getExporter().getState() == ResolverBundle.RESOLVING) {
+ // If the exporter is RESOLVING, we have a cyclic dependency
+ if (!cycle.contains(imp.getBundle())) {
+ cycle.add(imp.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("import-package cycle: " + imp.getBundle() + " -> " + imp.getSelectedSupplier() + " from " + imp.getSelectedSupplier().getBundleDescription()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("Found match: " + export.getExporter() + ". Wiring " + imp.getBundle() + ":" + imp.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ result = true;
+ }
+
+ if (result)
+ return true;
+ if (imp.isOptional())
+ return true; // If the import is optional then just return true
+ if (substitutableExps.length > 0 && substitutableExps[0].getSubstitute() == null)
+ return true; // If we still have an export that is not substituted return true
+ return false;
+ }
+
+ // Move a bundle to UNRESOLVED
+ private void setBundleUnresolved(ResolverBundle bundle, boolean removed, boolean keepFragsAttached) {
+ if (bundle.getState() == ResolverBundle.UNRESOLVED && !developmentMode)
+ // in this case there is nothing more to do
+ return;
+ // Note that when in dev mode we only want to force the fragment detach if asked to;
+ // this would be done only when forcing a dependency chain to unresolve from unresolveBundle method
+ if (removed || !keepFragsAttached) {
+ // Force the initialization of the bundle, its exports and its capabilities. This is needed to force proper attachment of fragments.
+ resolverExports.remove(bundle.getExportPackages());
+ removeGenerics(bundle.getGenericCapabilities());
+ bundle.detachAllFragments();
+ bundle.detachFromHosts();
+ bundle.initialize(false);
+ if (!removed) {
+ // add back the available exports/capabilities
+ resolverExports.put(bundle.getExportPackages());
+ addGenerics(bundle.getGenericCapabilities());
+ }
+ }
+ // TODO unresolvedBundles should be a set; for now only need to do a contains check in devMode.
+ if (!removed && (!developmentMode || !unresolvedBundles.contains(bundle)))
+ unresolvedBundles.add(bundle);
+ bundle.setState(ResolverBundle.UNRESOLVED);
+ }
+
+ // Move a bundle to RESOLVED
+ private void setBundleResolved(ResolverBundle bundle) {
+ if (bundle.getState() == ResolverBundle.RESOLVED)
+ return;
+ unresolvedBundles.remove(bundle);
+ bundle.setState(ResolverBundle.RESOLVED);
+ }
+
+ // Move a bundle to RESOLVING
+ private void setBundleResolving(ResolverBundle bundle) {
+ if (bundle.getState() == ResolverBundle.RESOLVING)
+ return;
+ unresolvedBundles.remove(bundle);
+ bundle.setState(ResolverBundle.RESOLVING);
+ }
+
+ // Resolves the bundles in the State
+ private void stateResolveBundles(ResolverBundle[] resolvedBundles) {
+ for (int i = 0; i < resolvedBundles.length; i++) {
+ if (!resolvedBundles[i].getBundleDescription().isResolved())
+ stateResolveBundle(resolvedBundles[i]);
+ }
+ }
+
+ private void stateResolveConstraints(ResolverBundle rb) {
+ ResolverImport[] imports = rb.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ ResolverExport export = (ResolverExport) imports[i].getSelectedSupplier();
+ BaseDescription supplier = export == null ? null : export.getExportPackageDescription();
+ state.resolveConstraint(imports[i].getVersionConstraint(), supplier);
+ }
+ BundleConstraint[] requires = rb.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ ResolverBundle bundle = (ResolverBundle) requires[i].getSelectedSupplier();
+ BaseDescription supplier = bundle == null ? null : bundle.getBundleDescription();
+ state.resolveConstraint(requires[i].getVersionConstraint(), supplier);
+ }
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++) {
+ VersionSupplier[] matchingCapabilities = genericRequires[i].getMatchingCapabilities();
+ if (matchingCapabilities == null)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), null);
+ else
+ for (int j = 0; j < matchingCapabilities.length; j++)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), matchingCapabilities[j].getBaseDescription());
+ }
+ }
+
+ private void stateResolveFragConstraints(ResolverBundle rb) {
+ ResolverBundle host = (ResolverBundle) rb.getHost().getSelectedSupplier();
+ ImportPackageSpecification[] imports = rb.getBundleDescription().getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ ResolverImport hostImport = host == null ? null : host.getImport(imports[i].getName());
+ ResolverExport export = (ResolverExport) (hostImport == null ? null : hostImport.getSelectedSupplier());
+ BaseDescription supplier = export == null ? null : export.getExportPackageDescription();
+ state.resolveConstraint(imports[i], supplier);
+ }
+ BundleSpecification[] requires = rb.getBundleDescription().getRequiredBundles();
+ for (int i = 0; i < requires.length; i++) {
+ BundleConstraint hostRequire = host == null ? null : host.getRequire(requires[i].getName());
+ ResolverBundle bundle = (ResolverBundle) (hostRequire == null ? null : hostRequire.getSelectedSupplier());
+ BaseDescription supplier = bundle == null ? null : bundle.getBundleDescription();
+ state.resolveConstraint(requires[i], supplier);
+ }
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++) {
+ VersionSupplier[] matchingCapabilities = genericRequires[i].getMatchingCapabilities();
+ if (matchingCapabilities == null)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), null);
+ else
+ for (int j = 0; j < matchingCapabilities.length; j++)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), matchingCapabilities[j].getBaseDescription());
+ }
+ }
+
+ private void stateResolveBundle(ResolverBundle rb) {
+ // if in dev mode then we want to tell the state about the constraints we were able to resolve
+ if (!rb.isResolved() && !developmentMode)
+ return;
+ if (rb.isFragment())
+ stateResolveFragConstraints(rb);
+ else
+ stateResolveConstraints(rb);
+
+ // Build up the state wires
+ Map<String, List<StateWire>> stateWires = new HashMap<String, List<StateWire>>();
+
+ // Gather selected exports
+ ResolverExport[] exports = rb.getSelectedExports();
+ List<ExportPackageDescription> selectedExports = new ArrayList<ExportPackageDescription>(exports.length);
+ for (int i = 0; i < exports.length; i++) {
+ if (permissionChecker.checkPackagePermission(exports[i].getExportPackageDescription()))
+ selectedExports.add(exports[i].getExportPackageDescription());
+ }
+ ExportPackageDescription[] selectedExportsArray = selectedExports.toArray(new ExportPackageDescription[selectedExports.size()]);
+
+ // Gather substitute exports
+ ResolverExport[] substituted = rb.getSubstitutedExports();
+ List<ExportPackageDescription> substitutedExports = new ArrayList<ExportPackageDescription>(substituted.length);
+ for (int i = 0; i < substituted.length; i++) {
+ substitutedExports.add(substituted[i].getExportPackageDescription());
+ }
+ ExportPackageDescription[] substitutedExportsArray = substitutedExports.toArray(new ExportPackageDescription[substitutedExports.size()]);
+
+ // Gather exports that have been wired to
+ ExportPackageDescription[] exportsWiredToArray = getExportsWiredTo(rb, stateWires);
+
+ // Gather bundles that have been wired to
+ BundleConstraint[] requires = rb.getRequires();
+ List<BundleDescription> bundlesWiredTo = new ArrayList<BundleDescription>(requires.length);
+ List<StateWire> requireWires = new ArrayList<StateWire>(requires.length);
+ for (int i = 0; i < requires.length; i++)
+ if (requires[i].getSelectedSupplier() != null) {
+ BundleDescription supplier = (BundleDescription) requires[i].getSelectedSupplier().getBaseDescription();
+ bundlesWiredTo.add(supplier);
+ StateWire requireWire = newStateWire(rb.getBundleDescription(), requires[i].getVersionConstraint(), supplier, supplier);
+ requireWires.add(requireWire);
+ }
+ BundleDescription[] bundlesWiredToArray = bundlesWiredTo.toArray(new BundleDescription[bundlesWiredTo.size()]);
+ if (!requireWires.isEmpty())
+ stateWires.put(BundleRevision.BUNDLE_NAMESPACE, requireWires);
+
+ GenericCapability[] capabilities = rb.getGenericCapabilities();
+ List<GenericDescription> selectedCapabilities = new ArrayList<GenericDescription>(capabilities.length);
+ for (GenericCapability capability : capabilities)
+ if (capability.isEffective() && permissionChecker.checkCapabilityPermission(capability.getGenericDescription()))
+ selectedCapabilities.add(capability.getGenericDescription());
+ GenericDescription[] selectedCapabilitiesArray = selectedCapabilities.toArray(new GenericDescription[selectedCapabilities.size()]);
+
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ List<GenericDescription> resolvedGenericRequires = new ArrayList<GenericDescription>(genericRequires.length);
+ for (GenericConstraint genericConstraint : genericRequires) {
+ VersionSupplier[] matching = genericConstraint.getMatchingCapabilities();
+ if (matching != null)
+ for (VersionSupplier capability : matching) {
+ GenericDescription supplier = ((GenericCapability) capability).getGenericDescription();
+ resolvedGenericRequires.add(supplier);
+ StateWire genericWire = newStateWire(rb.getBundleDescription(), genericConstraint.getVersionConstraint(), supplier.getSupplier(), supplier);
+ List<StateWire> genericWires = stateWires.get(genericConstraint.getNameSpace());
+ if (genericWires == null) {
+ genericWires = new ArrayList<StateWire>();
+ stateWires.put(genericConstraint.getNameSpace(), genericWires);
+ }
+ genericWires.add(genericWire);
+ }
+ }
+ GenericDescription[] capabilitiesWiredToArray = resolvedGenericRequires.toArray(new GenericDescription[resolvedGenericRequires.size()]);
+
+ BundleDescription[] hostBundles = null;
+ if (rb.isFragment()) {
+ VersionSupplier[] matchingBundles = rb.getHost().getPossibleSuppliers();
+ if (matchingBundles != null && matchingBundles.length > 0) {
+ hostBundles = new BundleDescription[matchingBundles.length];
+ List<StateWire> hostWires = new ArrayList<StateWire>(matchingBundles.length);
+ stateWires.put(BundleRevision.HOST_NAMESPACE, hostWires);
+ for (int i = 0; i < matchingBundles.length; i++) {
+ hostBundles[i] = matchingBundles[i].getBundleDescription();
+ StateWire hostWire = newStateWire(rb.getBundleDescription(), rb.getHost().getVersionConstraint(), hostBundles[i], hostBundles[i]);
+ hostWires.add(hostWire);
+ if (hostBundles[i].isResolved()) {
+ ExportPackageDescription[] newSelectedExports = null;
+ GenericDescription[] newSelectedCapabilities = null;
+ if (rb.isNewFragmentExports()) {
+ // update the host's set of selected exports
+ ResolverExport[] hostExports = ((ResolverBundle) matchingBundles[i]).getSelectedExports();
+ newSelectedExports = new ExportPackageDescription[hostExports.length];
+ for (int j = 0; j < hostExports.length; j++)
+ newSelectedExports[j] = hostExports[j].getExportPackageDescription();
+ }
+ if (rb.isNewFragmentCapabilities()) {
+ // update the host's set of selected capabilities
+ GenericCapability[] hostCapabilities = ((ResolverBundle) matchingBundles[i]).getGenericCapabilities();
+ newSelectedCapabilities = new GenericDescription[hostCapabilities.length];
+ for (int j = 0; j < hostCapabilities.length; j++)
+ newSelectedCapabilities[j] = hostCapabilities[j].getGenericDescription();
+ }
+ if (newSelectedCapabilities != null || newSelectedExports != null) {
+ if (newSelectedCapabilities == null)
+ newSelectedCapabilities = hostBundles[i].getSelectedGenericCapabilities();
+ if (newSelectedExports == null)
+ newSelectedExports = hostBundles[i].getSelectedExports();
+ state.resolveBundle(hostBundles[i], true, null, newSelectedExports, hostBundles[i].getSubstitutedExports(), newSelectedCapabilities, hostBundles[i].getResolvedRequires(), hostBundles[i].getResolvedImports(), hostBundles[i].getResolvedGenericRequires(), ((BundleDescriptionImpl) hostBundles[i]).getWires());
+ }
+ }
+ }
+ }
+ }
+
+ // Resolve the bundle in the state
+ state.resolveBundle(rb.getBundleDescription(), rb.isResolved(), hostBundles, selectedExportsArray, substitutedExportsArray, selectedCapabilitiesArray, bundlesWiredToArray, exportsWiredToArray, capabilitiesWiredToArray, stateWires);
+ }
+
+ private static ExportPackageDescription[] getExportsWiredTo(ResolverBundle rb, Map<String, List<StateWire>> stateWires) {
+ // Gather exports that have been wired to
+ ResolverImport[] imports = rb.getImportPackages();
+ List<ExportPackageDescription> exportsWiredTo = new ArrayList<ExportPackageDescription>(imports.length);
+ List<StateWire> importWires = new ArrayList<StateWire>(imports.length);
+ for (int i = 0; i < imports.length; i++)
+ if (imports[i].getSelectedSupplier() != null) {
+ ExportPackageDescription supplier = (ExportPackageDescription) imports[i].getSelectedSupplier().getBaseDescription();
+ exportsWiredTo.add(supplier);
+ StateWire wire = newStateWire(rb.getBundleDescription(), imports[i].getVersionConstraint(), supplier.getExporter(), supplier);
+ importWires.add(wire);
+ }
+ if (stateWires != null && !importWires.isEmpty())
+ stateWires.put(BundleRevision.PACKAGE_NAMESPACE, importWires);
+ return exportsWiredTo.toArray(new ExportPackageDescription[exportsWiredTo.size()]);
+ }
+
+ private static StateWire newStateWire(BundleDescription requirementHost, VersionConstraint declaredRequirement, BundleDescription capabilityHost, BaseDescription declaredCapability) {
+ BaseDescription fragDeclared = ((BaseDescriptionImpl) declaredCapability).getFragmentDeclaration();
+ declaredCapability = fragDeclared != null ? fragDeclared : declaredCapability;
+ return new StateWire(requirementHost, declaredRequirement, capabilityHost, declaredCapability);
+ }
+
+ // Resolve dynamic import
+ public synchronized ExportPackageDescription resolveDynamicImport(BundleDescription importingBundle, String requestedPackage) {
+ if (state == null)
+ throw new IllegalStateException("RESOLVER_NO_STATE"); //$NON-NLS-1$
+
+ // Make sure the resolver is initialized
+ if (!initialized)
+ initialize();
+ hook = (state instanceof StateImpl) ? ((StateImpl) state).getResolverHook() : null;
+ try {
+ ResolverBundle rb = bundleMapping.get(importingBundle);
+ if (rb.getExport(requestedPackage) != null)
+ return null; // do not allow dynamic wires for packages which this bundle exports
+ ResolverImport[] resolverImports = rb.getImportPackages();
+ // Check through the ResolverImports of this bundle.
+ // If there is a matching one then pass it into resolveImport()
+ for (int j = 0; j < resolverImports.length; j++) {
+ // Make sure it is a dynamic import
+ if (!resolverImports[j].isDynamic())
+ continue;
+ // Resolve the import
+ ExportPackageDescription supplier = resolveDynamicImport(resolverImports[j], requestedPackage);
+ if (supplier != null)
+ return supplier;
+ }
+ // look for packages added dynamically
+ ImportPackageSpecification[] addedDynamicImports = importingBundle.getAddedDynamicImportPackages();
+ for (ImportPackageSpecification addedDynamicImport : addedDynamicImports) {
+ ResolverImport newImport = new ResolverImport(rb, addedDynamicImport);
+ ExportPackageDescription supplier = resolveDynamicImport(newImport, requestedPackage);
+ if (supplier != null)
+ return supplier;
+ }
+
+ if (DEBUG || DEBUG_IMPORTS)
+ ResolverImpl.log("Failed to resolve dynamic import: " + requestedPackage); //$NON-NLS-1$
+ return null; // Couldn't resolve the import, so return null
+ } finally {
+ hook = null;
+ }
+ }
+
+ private void addStateWire(BundleDescription importingBundle, VersionConstraint requirement, BundleDescription capabilityHost, ExportPackageDescription capability) {
+ Map<String, List<StateWire>> wires = ((BundleDescriptionImpl) importingBundle).getWires();
+ List<StateWire> imports = wires.get(BundleRevision.PACKAGE_NAMESPACE);
+ if (imports == null) {
+ imports = new ArrayList<StateWire>();
+ wires.put(BundleRevision.PACKAGE_NAMESPACE, imports);
+ }
+ imports.add(newStateWire(importingBundle, requirement, capabilityHost, capability));
+ }
+
+ private ExportPackageDescription resolveDynamicImport(ResolverImport dynamicImport, String requestedPackage) {
+ String importName = dynamicImport.getName();
+ // If the import uses a wildcard, then temporarily replace this with the requested package
+ if (importName.equals("*") || //$NON-NLS-1$
+ (importName.endsWith(".*") && requestedPackage.startsWith(importName.substring(0, importName.length() - 1)))) { //$NON-NLS-1$
+ dynamicImport.setName(requestedPackage);
+ }
+ try {
+ // Resolve the import
+ if (!requestedPackage.equals(dynamicImport.getName()))
+ return null;
+
+ if (resolveImport(dynamicImport, new ArrayList<ResolverBundle>())) {
+ // populate the grouping checker with current imports
+ groupingChecker.populateRoots(dynamicImport.getBundle());
+ while (dynamicImport.getSelectedSupplier() != null) {
+ if (groupingChecker.isDynamicConsistent(dynamicImport.getBundle(), (ResolverExport) dynamicImport.getSelectedSupplier()) != null) {
+ dynamicImport.selectNextSupplier(); // not consistent; try the next
+ } else {
+ // If the import resolved then return it's matching export
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("Resolved dynamic import: " + dynamicImport.getBundle() + ":" + dynamicImport.getName() + " -> " + ((ResolverExport) dynamicImport.getSelectedSupplier()).getExporter() + ":" + requestedPackage); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+ // now that we have an export to wire to; populate the roots for that package for the bundle
+ ResolverExport export = (ResolverExport) dynamicImport.getSelectedSupplier();
+ groupingChecker.populateRoots(dynamicImport.getBundle(), export);
+
+ ExportPackageDescription supplier = export.getExportPackageDescription();
+ if (supplier != null)
+ addStateWire(dynamicImport.getBundleDescription(), dynamicImport.getVersionConstraint(), supplier.getExporter(), supplier);
+ return supplier;
+ }
+ }
+ dynamicImport.clearPossibleSuppliers();
+ }
+ } finally {
+ // If it is a wildcard import then clear the wire, so other
+ // exported packages can be found for it
+ if (importName.endsWith("*")) //$NON-NLS-1$
+ dynamicImport.clearPossibleSuppliers();
+ // Reset the import package name
+ dynamicImport.setName(null);
+ }
+ return null;
+ }
+
+ public void bundleAdded(BundleDescription bundle) {
+ if (!initialized)
+ return;
+
+ if (bundleMapping.get(bundle) != null)
+ return; // this description already exists in the resolver
+ ResolverBundle rb = new ResolverBundle(bundle, this);
+ bundleMapping.put(bundle, rb);
+ unresolvedBundles.add(rb);
+ resolverExports.put(rb.getExportPackages());
+ resolverBundles.put(rb.getName(), rb);
+ addGenerics(rb.getGenericCapabilities());
+ if (hook != null && rb.isFragment()) {
+ attachFragment0(rb);
+ }
+ }
+
+ public void bundleRemoved(BundleDescription bundle, boolean pending) {
+ ResolverBundle rb = initialized ? (ResolverBundle) bundleMapping.get(bundle) : null;
+ if (rb != null)
+ rb.setUninstalled();
+ internalBundleRemoved(bundle, pending);
+ }
+
+ private void internalBundleRemoved(BundleDescription bundle, boolean pending) {
+ // check if there are any dependants
+ if (pending)
+ removalPending.put(new Long(bundle.getBundleId()), bundle);
+ if (!initialized)
+ return;
+ ResolverBundle rb = bundleMapping.get(bundle);
+ if (rb == null)
+ return;
+
+ if (!pending) {
+ bundleMapping.remove(bundle);
+ groupingChecker.clear(rb);
+ }
+ if (!pending || !bundle.isResolved()) {
+ resolverExports.remove(rb.getExportPackages());
+ resolverBundles.remove(rb);
+ removeGenerics(rb.getGenericCapabilities());
+ }
+ unresolvedBundles.remove(rb);
+ }
+
+ private void unresolveBundle(ResolverBundle bundle, boolean removed) {
+ if (bundle == null)
+ return;
+ // check the removed list if unresolving then remove from the removed list
+ List<BundleDescription> removedBundles = removalPending.remove(new Long(bundle.getBundleDescription().getBundleId()));
+ for (BundleDescription removedDesc : removedBundles) {
+ ResolverBundle re = bundleMapping.get(removedDesc);
+ unresolveBundle(re, true);
+ state.removeBundleComplete(removedDesc);
+ resolverExports.remove(re.getExportPackages());
+ resolverBundles.remove(re);
+ removeGenerics(re.getGenericCapabilities());
+ bundleMapping.remove(removedDesc);
+ groupingChecker.clear(re);
+ // the bundle is removed
+ if (removedDesc == bundle.getBundleDescription())
+ removed = true;
+ }
+
+ if (!bundle.getBundleDescription().isResolved() && !developmentMode)
+ return;
+ CompositeResolveHelperRegistry currentLinks = compositeHelpers;
+ if (currentLinks != null) {
+ CompositeResolveHelper helper = currentLinks.getCompositeResolveHelper(bundle.getBundleDescription());
+ if (helper != null)
+ helper.giveExports(null);
+ }
+ // if not removed then add to the list of unresolvedBundles,
+ // passing false for devmode because we need all fragments detached
+ setBundleUnresolved(bundle, removed, false);
+ // Get bundles dependent on 'bundle'
+ BundleDescription[] dependents = bundle.getBundleDescription().getDependents();
+ state.resolveBundle(bundle.getBundleDescription(), false, null, null, null, null, null, null, null, null);
+ // Unresolve dependents of 'bundle'
+ for (int i = 0; i < dependents.length; i++)
+ unresolveBundle(bundleMapping.get(dependents[i]), false);
+ }
+
+ public void bundleUpdated(BundleDescription newDescription, BundleDescription existingDescription, boolean pending) {
+ internalBundleRemoved(existingDescription, pending);
+ bundleAdded(newDescription);
+ }
+
+ public void flush() {
+ resolverExports = null;
+ resolverBundles = null;
+ resolverGenerics = null;
+ unresolvedBundles = null;
+ bundleMapping = null;
+ List<BundleDescription> removed = removalPending.getAllValues();
+ for (BundleDescription removedDesc : removed)
+ state.removeBundleComplete(removedDesc);
+ removalPending.clear();
+ initialized = false;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public void setState(State newState) {
+ if (this.state != null) {
+ throw new IllegalStateException("Cannot change the State of a Resolver"); //$NON-NLS-1$
+ }
+ state = newState;
+ flush();
+ }
+
+ private void setDebugOptions() {
+ FrameworkDebugOptions options = null; //FrameworkDebugOptions.getDefault();
+ // may be null if debugging is not enabled
+ if (options == null)
+ return;
+ DEBUG = options.getBooleanOption(OPTION_DEBUG, false);
+ DEBUG_WIRING = options.getBooleanOption(OPTION_WIRING, false);
+ DEBUG_IMPORTS = options.getBooleanOption(OPTION_IMPORTS, false);
+ DEBUG_REQUIRES = options.getBooleanOption(OPTION_REQUIRES, false);
+ DEBUG_GENERICS = options.getBooleanOption(OPTION_GENERICS, false);
+ DEBUG_USES = options.getBooleanOption(OPTION_USES, false);
+ DEBUG_CYCLES = options.getBooleanOption(OPTION_CYCLES, false);
+ }
+
+ // LOGGING METHODS
+ private void printWirings() {
+ ResolverImpl.log("****** Result Wirings ******"); //$NON-NLS-1$
+ List<ResolverBundle> bundles = resolverBundles.getAllValues();
+ for (ResolverBundle rb : bundles) {
+ if (rb.getBundleDescription().isResolved()) {
+ continue;
+ }
+ ResolverImpl.log(" * WIRING for " + rb); //$NON-NLS-1$
+ // Require bundles
+ BundleConstraint[] requireBundles = rb.getRequires();
+ if (requireBundles.length == 0) {
+ ResolverImpl.log(" (r) no requires"); //$NON-NLS-1$
+ } else {
+ for (int i = 0; i < requireBundles.length; i++) {
+ if (requireBundles[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (r) " + rb.getBundleDescription() + " -> NULL!!!"); //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ ResolverImpl.log(" (r) " + rb.getBundleDescription() + " -> " + requireBundles[i].getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ // Hosts
+ BundleConstraint hostSpec = rb.getHost();
+ if (hostSpec != null) {
+ VersionSupplier[] hosts = hostSpec.getPossibleSuppliers();
+ if (hosts != null)
+ for (int i = 0; i < hosts.length; i++) {
+ ResolverImpl.log(" (h) " + rb.getBundleDescription() + " -> " + hosts[i].getBundleDescription()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ // Imports
+ ResolverImport[] imports = rb.getImportPackages();
+ if (imports.length == 0) {
+ ResolverImpl.log(" (w) no imports"); //$NON-NLS-1$
+ continue;
+ }
+ for (int i = 0; i < imports.length; i++) {
+ if (imports[i].isDynamic() && imports[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> DYNAMIC"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else if (imports[i].isOptional() && imports[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> OPTIONAL (could not be wired)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else if (imports[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> NULL!!!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ ((ResolverExport) imports[i].getSelectedSupplier()).getExporter() + ":" + imports[i].getSelectedSupplier().getName()); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ static void log(String message) {
+ Debug.println(message);
+ }
+
+ VersionHashMap<ResolverExport> getResolverExports() {
+ return resolverExports;
+ }
+
+ public void setSelectionPolicy(Comparator<BaseDescription> selectionPolicy) {
+ this.selectionPolicy = selectionPolicy;
+ }
+
+ public Comparator<BaseDescription> getSelectionPolicy() {
+ return selectionPolicy;
+ }
+
+ public void setCompositeResolveHelperRegistry(CompositeResolveHelperRegistry compositeHelpers) {
+ this.compositeHelpers = compositeHelpers;
+ }
+
+ CompositeResolveHelperRegistry getCompositeHelpers() {
+ return compositeHelpers;
+ }
+
+ private void reorderGenerics() {
+ for (VersionHashMap<GenericCapability> namespace : resolverGenerics.values())
+ namespace.reorder();
+ }
+
+ void removeGenerics(GenericCapability[] generics) {
+ for (GenericCapability capability : generics) {
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(capability.getGenericDescription().getType());
+ if (namespace != null)
+ namespace.remove(capability);
+ }
+ }
+
+ void addGenerics(GenericCapability[] generics) {
+ for (GenericCapability capability : generics) {
+ if (!capability.isEffective())
+ continue;
+ String type = capability.getGenericDescription().getType();
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(type);
+ if (namespace == null) {
+ namespace = new VersionHashMap<GenericCapability>(this);
+ resolverGenerics.put(type, namespace);
+ }
+ namespace.put(capability.getName(), capability);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImport.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImport.java
new file mode 100644
index 000000000..848841ba6
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImport.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.osgi.framework.Constants;
+
+/*
+ * A companion to ImportPackageSpecification from the state used while resolving
+ */
+public class ResolverImport extends ResolverConstraint {
+ // only used for dynamic imports
+ private String name;
+
+ ResolverImport(ResolverBundle bundle, ImportPackageSpecification ips) {
+ super(bundle, ips);
+ }
+
+ boolean isOptional() {
+ return ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(((ImportPackageSpecification) constraint).getDirective(Constants.RESOLUTION_DIRECTIVE));
+ }
+
+ boolean isDynamic() {
+ return ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(((ImportPackageSpecification) constraint).getDirective(Constants.RESOLUTION_DIRECTIVE));
+ }
+
+ public String getName() {
+ if (name != null)
+ return name; // return the required package set for a dynamic import
+ return super.getName();
+ }
+
+ // used for dynamic import package when wildcards are used
+ void setName(String requestedPackage) {
+ this.name = requestedPackage;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionHashMap.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionHashMap.java
new file mode 100644
index 000000000..b27b03876
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionHashMap.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import java.util.*;
+
+public class VersionHashMap<V extends VersionSupplier> extends MappedList<String, V> implements Comparator<V> {
+ private final ResolverImpl resolver;
+ private final boolean preferSystemPackages;
+
+ public VersionHashMap(ResolverImpl resolver) {
+ this.resolver = resolver;
+ Dictionary<?, ?>[] allProperties = resolver.getState().getPlatformProperties();
+ Object preferSystem = allProperties.length == 0 ? "true" : allProperties[0].get("osgi.resolver.preferSystemPackages"); //$NON-NLS-1$//$NON-NLS-2$
+ if (preferSystem == null)
+ preferSystem = "true"; //$NON-NLS-1$
+ preferSystemPackages = Boolean.valueOf(preferSystem.toString()).booleanValue();
+ }
+
+ // assumes existing array is sorted
+ // finds the index where to insert the new value
+ protected int insertionIndex(List<V> existing, V value) {
+ int index = existing.size();
+ if (compare(existing.get(existing.size() - 1), value) > 0) {
+ index = Collections.binarySearch(existing, value, this);
+
+ if (index < 0)
+ index = -index - 1;
+ }
+ return index;
+ }
+
+ public void put(V[] versionSuppliers) {
+ for (int i = 0; i < versionSuppliers.length; i++)
+ put(versionSuppliers[i].getName(), versionSuppliers[i]);
+ }
+
+ public boolean contains(V vs) {
+ return contains(vs, false) != null;
+ }
+
+ private V contains(V vs, boolean remove) {
+ List<V> existing = internal.get(vs.getName());
+ if (existing == null)
+ return null;
+ int index = existing.indexOf(vs);
+ if (index >= 0) {
+ if (remove) {
+ existing.remove(index);
+ if (existing.size() == 0)
+ internal.remove(vs.getName());
+ }
+ return vs;
+ }
+ return null;
+ }
+
+ public V remove(V toBeRemoved) {
+ return contains(toBeRemoved, true);
+ }
+
+ public void remove(V[] versionSuppliers) {
+ for (int i = 0; i < versionSuppliers.length; i++)
+ remove(versionSuppliers[i]);
+ }
+
+ // Once we have resolved bundles, we need to make sure that version suppliers
+ // from the resolved bundles are ahead of those from unresolved bundles
+ void reorder() {
+ for (Iterator<List<V>> it = internal.values().iterator(); it.hasNext();) {
+ List<V> existing = it.next();
+ if (existing.size() > 1)
+ Collections.sort(existing, this);
+ }
+ }
+
+ // Compares two VersionSuppliers for descending ordered sorts.
+ // The VersionSuppliers are sorted by the following priorities
+ // First the resolution status of the supplying bundle.
+ // Second is the supplier version.
+ // Third is the bundle id of the supplying bundle.
+ public int compare(V vs1, V vs2) {
+ // if the selection policy is set then use that
+ if (resolver.getSelectionPolicy() != null)
+ return resolver.getSelectionPolicy().compare(vs1.getBaseDescription(), vs2.getBaseDescription());
+ if (preferSystemPackages) {
+ String systemBundle = resolver.getSystemBundle();
+ if (systemBundle.equals(vs1.getBundleDescription().getSymbolicName()) && !systemBundle.equals(vs2.getBundleDescription().getSymbolicName()))
+ return -1;
+ else if (!systemBundle.equals(vs1.getBundleDescription().getSymbolicName()) && systemBundle.equals(vs2.getBundleDescription().getSymbolicName()))
+ return 1;
+ }
+ if (vs1.getBundleDescription().isResolved() != vs2.getBundleDescription().isResolved())
+ return vs1.getBundleDescription().isResolved() ? -1 : 1;
+ int versionCompare = -(vs1.getVersion().compareTo(vs2.getVersion()));
+ if (versionCompare != 0)
+ return versionCompare;
+ return vs1.getBundleDescription().getBundleId() <= vs2.getBundleDescription().getBundleId() ? -1 : 1;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionSupplier.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionSupplier.java
new file mode 100644
index 000000000..03c94b021
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/VersionSupplier.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+
+/*
+ * A companion to BaseDescription from the state used while resolving.
+ */
+public abstract class VersionSupplier {
+ final protected BaseDescription base;
+ final private BundleCapability capability;
+ private VersionSupplier substitute;
+
+ VersionSupplier(BaseDescription base) {
+ this.base = base;
+ this.capability = base.getCapability();
+ }
+
+ public Version getVersion() {
+ return base.getVersion();
+ }
+
+ public String getName() {
+ return base.getName();
+ }
+
+ public BaseDescription getBaseDescription() {
+ return base;
+ }
+
+ // returns the version supplier that has been substituted for this version supplier
+ VersionSupplier getSubstitute() {
+ return substitute;
+ }
+
+ // sets the dropped status. This should only be called by the VersionHashMap
+ // when VersionSuppliers are removed
+ void setSubstitute(VersionSupplier substitute) {
+ this.substitute = substitute;
+ }
+
+ /*
+ * returns the BundleDescription which supplies this VersionSupplier
+ */
+ abstract public BundleDescription getBundleDescription();
+
+ /*
+ * returns the ResolverBundle which supplies this VersionSupplier
+ */
+ abstract ResolverBundle getResolverBundle();
+
+ public String toString() {
+ return base.toString();
+ }
+
+ BundleCapability getCapability() {
+ return capability;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java
new file mode 100644
index 000000000..60d3f2b30
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import java.util.Map.Entry;
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public abstract class BaseDescriptionImpl implements BaseDescription {
+
+ protected final Object monitor = new Object();
+
+ private volatile String name;
+
+ private volatile Version version;
+
+ private volatile Object userObject;
+
+ public String getName() {
+ return name;
+ }
+
+ public Version getVersion() {
+ synchronized (this.monitor) {
+ if (version == null)
+ return Version.emptyVersion;
+ return version;
+ }
+ }
+
+ protected void setName(String name) {
+ this.name = name;
+ }
+
+ protected void setVersion(Version version) {
+ this.version = version;
+ }
+
+ static <V> String toString(Map<String, V> map, boolean directives) {
+ if (map.size() == 0)
+ return ""; //$NON-NLS-1$
+ String assignment = directives ? ":=" : "="; //$NON-NLS-1$//$NON-NLS-2$
+ Set<Entry<String, V>> set = map.entrySet();
+ StringBuffer sb = new StringBuffer();
+ for (Entry<String, V> entry : set) {
+ sb.append("; "); //$NON-NLS-1$
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof List) {
+ @SuppressWarnings("unchecked")
+ List<Object> list = (List<Object>) value;
+ if (list.size() == 0)
+ continue;
+ Object component = list.get(0);
+ String className = component.getClass().getName();
+ String type = className.substring(className.lastIndexOf('.') + 1);
+ sb.append(key).append(':').append("List<").append(type).append(">").append(assignment).append('"'); //$NON-NLS-1$ //$NON-NLS-2$
+ for (Object object : list)
+ sb.append(object).append(',');
+ sb.setLength(sb.length() - 1);
+ sb.append('"');
+ } else {
+ String type = ""; //$NON-NLS-1$
+ if (!(value instanceof String)) {
+ String className = value.getClass().getName();
+ type = ":" + className.substring(className.lastIndexOf('.') + 1); //$NON-NLS-1$
+ }
+ sb.append(key).append(type).append(assignment).append('"').append(value).append('"');
+ }
+ }
+ return sb.toString();
+ }
+
+ String getInternalNameSpace() {
+ return null;
+ }
+
+ public BaseDescription getFragmentDeclaration() {
+ return null;
+ }
+
+ public BundleCapability getCapability() {
+ return getCapability(null);
+ }
+
+ BundleCapability getCapability(String namespace) {
+ BaseDescriptionImpl fragmentDeclaration = (BaseDescriptionImpl) getFragmentDeclaration();
+ if (fragmentDeclaration != null)
+ return fragmentDeclaration.getCapability(namespace);
+ if (namespace == null)
+ namespace = getInternalNameSpace();
+ if (namespace == null)
+ return null;
+ return new BaseCapability(namespace);
+ }
+
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ public void setUserObject(Object userObject) {
+ this.userObject = userObject;
+ }
+
+ class BaseCapability implements BundleCapability {
+ private final String namespace;
+
+ public BaseCapability(String namespace) {
+ super();
+ this.namespace = namespace;
+ }
+
+ public BundleRevision getRevision() {
+ return getSupplier();
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public Map<String, String> getDirectives() {
+ return getDeclaredDirectives();
+ }
+
+ public Map<String, Object> getAttributes() {
+ Map<String, Object> attrs = getDeclaredAttributes();
+ String internalName = BaseDescriptionImpl.this.getInternalNameSpace();
+ if (namespace.equals(internalName))
+ return attrs;
+ // we are doing an alias, must remove internal Name and add alias
+ attrs = new HashMap<String, Object>(attrs);
+ Object nameValue = attrs.remove(internalName);
+ if (nameValue != null)
+ attrs.put(namespace, nameValue);
+ return Collections.unmodifiableMap(attrs);
+ }
+
+ public int hashCode() {
+ return System.identityHashCode(BaseDescriptionImpl.this);
+ }
+
+ protected BaseDescriptionImpl getBaseDescription() {
+ return BaseDescriptionImpl.this;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof BaseCapability))
+ return false;
+ return (((BaseCapability) obj).getBaseDescription() == BaseDescriptionImpl.this) && namespace.equals(((BaseCapability) obj).getNamespace());
+ }
+
+ public String toString() {
+ return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true);
+ }
+
+ public BundleRevision getResource() {
+ return getRevision();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java
new file mode 100644
index 000000000..448e37dbb
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.BundleDelta;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+
+final class BundleDeltaImpl implements BundleDelta {
+
+ private volatile BundleDescription bundleDescription;
+ private volatile int type;
+
+ public BundleDeltaImpl(BundleDescription bundleDescription) {
+ this(bundleDescription, 0);
+ }
+
+ public BundleDeltaImpl(BundleDescription bundleDescription, int type) {
+ this.bundleDescription = bundleDescription;
+ this.type = type;
+ }
+
+ public BundleDescription getBundle() {
+ return bundleDescription;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ protected void setBundle(BundleDescription bundleDescription) {
+ this.bundleDescription = bundleDescription;
+ }
+
+ protected void setType(int type) {
+ this.type = type;
+ }
+
+ public String toString() {
+ return bundleDescription.getSymbolicName() + '_' + bundleDescription.getVersion() + " (" + toTypeString(type) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private static String toTypeString(int type) {
+ StringBuffer typeStr = new StringBuffer();
+ if ((type & BundleDelta.ADDED) != 0)
+ typeStr.append("ADDED,"); //$NON-NLS-1$
+ if ((type & BundleDelta.REMOVED) != 0)
+ typeStr.append("REMOVED,"); //$NON-NLS-1$
+ if ((type & BundleDelta.RESOLVED) != 0)
+ typeStr.append("RESOLVED,"); //$NON-NLS-1$
+ if ((type & BundleDelta.UNRESOLVED) != 0)
+ typeStr.append("UNRESOLVED,"); //$NON-NLS-1$
+ if ((type & BundleDelta.LINKAGE_CHANGED) != 0)
+ typeStr.append("LINKAGE_CHANGED,"); //$NON-NLS-1$
+ if ((type & BundleDelta.UPDATED) != 0)
+ typeStr.append("UPDATED,"); //$NON-NLS-1$
+ if ((type & BundleDelta.REMOVAL_PENDING) != 0)
+ typeStr.append("REMOVAL_PENDING,"); //$NON-NLS-1$
+ if ((type & BundleDelta.REMOVAL_COMPLETE) != 0)
+ typeStr.append("REMOVAL_COMPLETE,"); //$NON-NLS-1$
+ if (typeStr.length() > 0)
+ typeStr.deleteCharAt(typeStr.length() - 1);
+ return typeStr.toString();
+ }
+
+ public int compareTo(BundleDelta obj) {
+ long idcomp = getBundle().getBundleId() - obj.getBundle().getBundleId();
+ return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0);
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
new file mode 100644
index 000000000..ee6cdb91c
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
@@ -0,0 +1,1244 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522 and 255520)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.framework.util.KeyedElement;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+import org.osgi.framework.wiring.*;
+import org.osgi.resource.*;
+
+public final class BundleDescriptionImpl extends BaseDescriptionImpl implements BundleDescription, KeyedElement {
+ static final String[] EMPTY_STRING = new String[0];
+ static final ImportPackageSpecification[] EMPTY_IMPORTS = new ImportPackageSpecification[0];
+ static final BundleSpecification[] EMPTY_BUNDLESPECS = new BundleSpecification[0];
+ static final ExportPackageDescription[] EMPTY_EXPORTS = new ExportPackageDescription[0];
+ static final BundleDescription[] EMPTY_BUNDLEDESCS = new BundleDescription[0];
+ static final GenericSpecification[] EMPTY_GENERICSPECS = new GenericSpecification[0];
+ static final GenericDescription[] EMPTY_GENERICDESCS = new GenericDescription[0];
+ static final RuntimePermission GET_CLASSLOADER_PERM = new RuntimePermission("getClassLoader"); //$NON-NLS-1$
+
+ static final int RESOLVED = 0x01;
+ static final int SINGLETON = 0x02;
+ static final int REMOVAL_PENDING = 0x04;
+ static final int FULLY_LOADED = 0x08;
+ static final int LAZY_LOADED = 0x10;
+ static final int HAS_DYNAMICIMPORT = 0x20;
+ static final int ATTACH_FRAGMENTS = 0x40;
+ static final int DYNAMIC_FRAGMENTS = 0x80;
+
+ // set to fully loaded and allow dynamic fragments by default
+ private volatile int stateBits = FULLY_LOADED | ATTACH_FRAGMENTS | DYNAMIC_FRAGMENTS;
+
+ private volatile long bundleId = -1;
+ volatile HostSpecification host; //null if the bundle is not a fragment. volatile to allow unsynchronized checks for null
+ private volatile StateImpl containingState;
+
+ private volatile int lazyDataOffset = -1;
+ private volatile int lazyDataSize = -1;
+
+ private List<BundleDescription> dependencies;
+ private List<BundleDescription> dependents;
+ private String[] mandatory;
+ private Map<String, Object> attributes;
+ private Map<String, String> arbitraryDirectives;
+
+ private volatile LazyData lazyData;
+ private volatile int equinox_ee = -1;
+
+ private DescriptionWiring bundleWiring;
+
+ public BundleDescriptionImpl() {
+ //
+ }
+
+ public long getBundleId() {
+ return bundleId;
+ }
+
+ public String getSymbolicName() {
+ return getName();
+ }
+
+ public BundleDescription getSupplier() {
+ return this;
+ }
+
+ public String getLocation() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.location;
+ }
+ }
+
+ public String getPlatformFilter() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.platformFilter;
+ }
+ }
+
+ public String[] getExecutionEnvironments() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.executionEnvironments == null)
+ return EMPTY_STRING;
+ return currentData.executionEnvironments;
+ }
+ }
+
+ public ImportPackageSpecification[] getImportPackages() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.importPackages == null)
+ return EMPTY_IMPORTS;
+ return currentData.importPackages;
+ }
+ }
+
+ public ImportPackageSpecification[] getAddedDynamicImportPackages() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.addedDynamicImports == null)
+ return EMPTY_IMPORTS;
+ return currentData.addedDynamicImports.toArray(new ImportPackageSpecification[currentData.addedDynamicImports.size()]);
+ }
+ }
+
+ public BundleSpecification[] getRequiredBundles() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.requiredBundles == null)
+ return EMPTY_BUNDLESPECS;
+ return currentData.requiredBundles;
+ }
+ }
+
+ public GenericSpecification[] getGenericRequires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.genericRequires == null)
+ return EMPTY_GENERICSPECS;
+ return currentData.genericRequires;
+ }
+ }
+
+ public GenericDescription[] getGenericCapabilities() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.genericCapabilities == null)
+ return EMPTY_GENERICDESCS;
+ return currentData.genericCapabilities;
+ }
+ }
+
+ public NativeCodeSpecification getNativeCodeSpecification() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.nativeCode;
+ }
+ }
+
+ public ExportPackageDescription[] getExportPackages() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.exportPackages == null ? EMPTY_EXPORTS : currentData.exportPackages;
+ }
+ }
+
+ public boolean isResolved() {
+ return (stateBits & RESOLVED) != 0;
+ }
+
+ public State getContainingState() {
+ return containingState;
+ }
+
+ public BundleDescription[] getFragments() {
+ if (host != null)
+ return EMPTY_BUNDLEDESCS;
+ StateImpl currentState = (StateImpl) getContainingState();
+ if (currentState == null)
+ throw new IllegalStateException("BundleDescription does not belong to a state."); //$NON-NLS-1$
+ return currentState.getFragments(this);
+ }
+
+ public HostSpecification getHost() {
+ return host;
+ }
+
+ public boolean isSingleton() {
+ return (stateBits & SINGLETON) != 0;
+ }
+
+ public boolean isRemovalPending() {
+ return (stateBits & REMOVAL_PENDING) != 0;
+ }
+
+ public boolean hasDynamicImports() {
+ return (stateBits & HAS_DYNAMICIMPORT) != 0;
+ }
+
+ public boolean attachFragments() {
+ return (stateBits & ATTACH_FRAGMENTS) != 0;
+ }
+
+ public boolean dynamicFragments() {
+ return (stateBits & DYNAMIC_FRAGMENTS) != 0;
+ }
+
+ public ExportPackageDescription[] getSelectedExports() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.selectedExports == null)
+ return EMPTY_EXPORTS;
+ return currentData.selectedExports;
+ }
+ }
+
+ public GenericDescription[] getSelectedGenericCapabilities() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.selectedCapabilities == null)
+ return EMPTY_GENERICDESCS;
+ return currentData.selectedCapabilities;
+ }
+ }
+
+ public ExportPackageDescription[] getSubstitutedExports() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.substitutedExports == null)
+ return EMPTY_EXPORTS;
+ return currentData.substitutedExports;
+ }
+ }
+
+ public BundleDescription[] getResolvedRequires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.resolvedRequires == null)
+ return EMPTY_BUNDLEDESCS;
+ return currentData.resolvedRequires;
+ }
+ }
+
+ public ExportPackageDescription[] getResolvedImports() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.resolvedImports == null)
+ return EMPTY_EXPORTS;
+ return currentData.resolvedImports;
+ }
+ }
+
+ public GenericDescription[] getResolvedGenericRequires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.resolvedCapabilities == null)
+ return EMPTY_GENERICDESCS;
+ return currentData.resolvedCapabilities;
+ }
+ }
+
+ public Map<String, List<StateWire>> getWires() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.stateWires == null) {
+ currentData.stateWires = new HashMap<String, List<StateWire>>(0);
+ }
+ return currentData.stateWires;
+ }
+ }
+
+ Map<String, List<StateWire>> getWiresInternal() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.stateWires;
+ }
+ }
+
+ protected void setBundleId(long bundleId) {
+ this.bundleId = bundleId;
+ }
+
+ protected void setSymbolicName(String symbolicName) {
+ setName(symbolicName);
+ }
+
+ protected void setLocation(String location) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.location = location;
+ }
+ }
+
+ protected void setPlatformFilter(String platformFilter) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.platformFilter = platformFilter;
+ }
+ }
+
+ protected void setExecutionEnvironments(String[] executionEnvironments) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.executionEnvironments = executionEnvironments;
+ }
+ }
+
+ protected void setExportPackages(ExportPackageDescription[] exportPackages) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.exportPackages = exportPackages;
+ if (exportPackages != null) {
+ for (int i = 0; i < exportPackages.length; i++) {
+ ((ExportPackageDescriptionImpl) exportPackages[i]).setExporter(this);
+ }
+ }
+ }
+ }
+
+ protected void setImportPackages(ImportPackageSpecification[] importPackages) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.importPackages = importPackages;
+ if (importPackages != null) {
+ for (int i = 0; i < importPackages.length; i++) {
+ ((ImportPackageSpecificationImpl) importPackages[i]).setBundle(this);
+ if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(importPackages[i].getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ stateBits |= HAS_DYNAMICIMPORT;
+ }
+ }
+ }
+ }
+
+ protected void setRequiredBundles(BundleSpecification[] requiredBundles) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.requiredBundles = requiredBundles;
+ if (requiredBundles != null)
+ for (int i = 0; i < requiredBundles.length; i++) {
+ ((VersionConstraintImpl) requiredBundles[i]).setBundle(this);
+ }
+ }
+ }
+
+ protected void setGenericCapabilities(GenericDescription[] genericCapabilities) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.genericCapabilities = genericCapabilities;
+ if (genericCapabilities != null)
+ for (int i = 0; i < genericCapabilities.length; i++)
+ ((GenericDescriptionImpl) genericCapabilities[i]).setSupplier(this);
+ }
+ }
+
+ protected void setGenericRequires(GenericSpecification[] genericRequires) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.genericRequires = genericRequires;
+ if (genericRequires != null)
+ for (int i = 0; i < genericRequires.length; i++)
+ ((VersionConstraintImpl) genericRequires[i]).setBundle(this);
+ }
+ }
+
+ protected void setNativeCodeSpecification(NativeCodeSpecification nativeCode) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.nativeCode = nativeCode;
+ if (nativeCode != null) {
+ ((NativeCodeSpecificationImpl) nativeCode).setBundle(this);
+ NativeCodeDescription[] suppliers = nativeCode.getPossibleSuppliers();
+ if (suppliers != null)
+ for (int i = 0; i < suppliers.length; i++)
+ ((NativeCodeDescriptionImpl) suppliers[i]).setSupplier(this);
+ }
+ }
+ }
+
+ protected int getStateBits() {
+ return stateBits;
+ }
+
+ protected void setStateBit(int stateBit, boolean on) {
+ synchronized (this.monitor) {
+ if (on) {
+ stateBits |= stateBit;
+ } else {
+ stateBits &= ~stateBit;
+ if (stateBit == RESOLVED) {
+ if (bundleWiring != null)
+ bundleWiring.invalidate();
+ bundleWiring = null;
+ }
+ }
+ }
+ }
+
+ protected void setContainingState(State value) {
+ synchronized (this.monitor) {
+ containingState = (StateImpl) value;
+ if (containingState != null && containingState.getReader() != null) {
+ if (containingState.getReader().isLazyLoaded())
+ stateBits |= LAZY_LOADED;
+ else
+ stateBits &= ~LAZY_LOADED;
+ } else {
+ stateBits &= ~LAZY_LOADED;
+ }
+ }
+ }
+
+ protected void setHost(HostSpecification host) {
+ synchronized (this.monitor) {
+ this.host = host;
+ if (host != null) {
+ ((VersionConstraintImpl) host).setBundle(this);
+ }
+ }
+ }
+
+ protected void setLazyLoaded(boolean lazyLoad) {
+ loadLazyData();
+ synchronized (this.monitor) {
+ if (lazyLoad)
+ stateBits |= LAZY_LOADED;
+ else
+ stateBits &= ~LAZY_LOADED;
+ }
+ }
+
+ protected void setSelectedExports(ExportPackageDescription[] selectedExports) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.selectedExports = selectedExports;
+ if (selectedExports != null) {
+ for (int i = 0; i < selectedExports.length; i++) {
+ ((ExportPackageDescriptionImpl) selectedExports[i]).setExporter(this);
+ }
+ }
+ }
+ }
+
+ protected void setSelectedCapabilities(GenericDescription[] selectedCapabilities) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.selectedCapabilities = selectedCapabilities;
+ if (selectedCapabilities != null) {
+ for (GenericDescription capability : selectedCapabilities) {
+ ((GenericDescriptionImpl) capability).setSupplier(this);
+ }
+ }
+ }
+ }
+
+ protected void setSubstitutedExports(ExportPackageDescription[] substitutedExports) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.substitutedExports = substitutedExports;
+ }
+ }
+
+ protected void setResolvedImports(ExportPackageDescription[] resolvedImports) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.resolvedImports = resolvedImports;
+ }
+ }
+
+ protected void setResolvedRequires(BundleDescription[] resolvedRequires) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.resolvedRequires = resolvedRequires;
+ }
+ }
+
+ protected void setResolvedCapabilities(GenericDescription[] resolvedCapabilities) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.resolvedCapabilities = resolvedCapabilities;
+ }
+ }
+
+ protected void setStateWires(Map<String, List<StateWire>> stateWires) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.stateWires = stateWires;
+ }
+ }
+
+ void clearAddedDynamicImportPackages() {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.addedDynamicImports = null;
+ }
+ }
+
+ public String toString() {
+ if (getSymbolicName() == null)
+ return "[" + getBundleId() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ return getSymbolicName() + "_" + getVersion(); //$NON-NLS-1$
+ }
+
+ public Object getKey() {
+ return new Long(bundleId);
+ }
+
+ public boolean compare(KeyedElement other) {
+ if (!(other instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl otherBundleDescription = (BundleDescriptionImpl) other;
+ return bundleId == otherBundleDescription.bundleId;
+ }
+
+ public int getKeyHashCode() {
+ return (int) (bundleId ^ (bundleId >>> 32));
+ }
+
+ /* TODO Determine if we need more than just Object ID type of hashcode.
+ public int hashCode() {
+ if (getSymbolicName() == null)
+ return (int) (bundleId % Integer.MAX_VALUE);
+ return (int) ((bundleId * (getSymbolicName().hashCode())) % Integer.MAX_VALUE);
+ }
+ */
+
+ protected void removeDependencies() {
+ synchronized (this.monitor) {
+ if (dependencies == null)
+ return;
+ Iterator<BundleDescription> iter = dependencies.iterator();
+ while (iter.hasNext()) {
+ ((BundleDescriptionImpl) iter.next()).removeDependent(this);
+ }
+ dependencies = null;
+ }
+ }
+
+ protected void addDependencies(BaseDescription[] newDependencies, boolean checkDups) {
+ synchronized (this.monitor) {
+ if (newDependencies == null)
+ return;
+ if (!checkDups && dependencies == null)
+ dependencies = new ArrayList<BundleDescription>(newDependencies.length);
+ for (int i = 0; i < newDependencies.length; i++) {
+ addDependency((BaseDescriptionImpl) newDependencies[i], checkDups);
+ }
+ }
+ }
+
+ protected void addDependency(BaseDescriptionImpl dependency, boolean checkDups) {
+ synchronized (this.monitor) {
+ BundleDescriptionImpl bundle = (BundleDescriptionImpl) dependency.getSupplier();
+ if (bundle == this)
+ return;
+ if (dependencies == null)
+ dependencies = new ArrayList<BundleDescription>(10);
+ if (!checkDups || !dependencies.contains(bundle)) {
+ bundle.addDependent(this);
+ dependencies.add(bundle);
+ }
+ }
+ }
+
+ /*
+ * Gets all the bundle dependencies as a result of import-package or require-bundle.
+ * Self and fragment bundles are removed.
+ */
+ List<BundleDescription> getBundleDependencies() {
+ synchronized (this.monitor) {
+ if (dependencies == null)
+ return new ArrayList<BundleDescription>(0);
+ ArrayList<BundleDescription> required = new ArrayList<BundleDescription>(dependencies.size());
+ for (Iterator<BundleDescription> iter = dependencies.iterator(); iter.hasNext();) {
+ BundleDescription dep = iter.next();
+ if (dep != this && dep.getHost() == null)
+ required.add(dep);
+ }
+ return required;
+ }
+ }
+
+ protected void addDependent(BundleDescription dependent) {
+ synchronized (this.monitor) {
+ if (dependents == null)
+ dependents = new ArrayList<BundleDescription>(10);
+ // no need to check for duplicates here; this is only called in addDepenency which already checks for dups.
+ dependents.add(dependent);
+ }
+ }
+
+ protected void removeDependent(BundleDescription dependent) {
+ synchronized (this.monitor) {
+ if (dependents == null)
+ return;
+ dependents.remove(dependent);
+ }
+ }
+
+ public BundleDescription[] getDependents() {
+ synchronized (this.monitor) {
+ if (dependents == null)
+ return EMPTY_BUNDLEDESCS;
+ return dependents.toArray(new BundleDescription[dependents.size()]);
+ }
+ }
+
+ boolean hasDependents() {
+ synchronized (this.monitor) {
+ return dependents == null ? false : dependents.size() > 0;
+ }
+ }
+
+ void setFullyLoaded(boolean fullyLoaded) {
+ synchronized (this.monitor) {
+ if (fullyLoaded) {
+ stateBits |= FULLY_LOADED;
+ } else {
+ stateBits &= ~FULLY_LOADED;
+ }
+ }
+ }
+
+ boolean isFullyLoaded() {
+ return (stateBits & FULLY_LOADED) != 0;
+ }
+
+ void setLazyDataOffset(int lazyDataOffset) {
+ this.lazyDataOffset = lazyDataOffset;
+ }
+
+ int getLazyDataOffset() {
+ return this.lazyDataOffset;
+ }
+
+ void setLazyDataSize(int lazyDataSize) {
+ this.lazyDataSize = lazyDataSize;
+ }
+
+ int getLazyDataSize() {
+ return this.lazyDataSize;
+ }
+
+ // DO NOT call while holding this.monitor
+ private LazyData loadLazyData() {
+ // TODO add back if ee min 1.2 adds holdsLock method
+ //if (Thread.holdsLock(this.monitor)) {
+ // throw new IllegalStateException("Should not call fullyLoad() holding monitor."); //$NON-NLS-1$
+ //}
+ if ((stateBits & LAZY_LOADED) == 0)
+ return this.lazyData;
+
+ StateImpl currentState = (StateImpl) getContainingState();
+ StateReader reader = currentState == null ? null : currentState.getReader();
+ if (reader == null)
+ throw new IllegalStateException("No valid reader for the bundle description"); //$NON-NLS-1$
+
+ synchronized (currentState.monitor) {
+ if (isFullyLoaded()) {
+ reader.setAccessedFlag(true); // set reader accessed flag
+ return this.lazyData;
+ }
+ try {
+ reader.fullyLoad(this);
+ return this.lazyData;
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e); // TODO not sure what to do here!!
+ }
+ }
+ }
+
+ void addDynamicResolvedImport(ExportPackageDescriptionImpl result) {
+ synchronized (this.monitor) {
+ // mark the dependency
+ addDependency(result, true);
+ // add the export to the list of the resolvedImports
+ checkLazyData();
+ if (lazyData.resolvedImports == null) {
+ lazyData.resolvedImports = new ExportPackageDescription[] {result};
+ return;
+ }
+ ExportPackageDescription[] newImports = new ExportPackageDescription[lazyData.resolvedImports.length + 1];
+ System.arraycopy(lazyData.resolvedImports, 0, newImports, 0, lazyData.resolvedImports.length);
+ newImports[newImports.length - 1] = result;
+ lazyData.resolvedImports = newImports;
+ }
+ }
+
+ void addDynamicImportPackages(ImportPackageSpecification[] dynamicImport) {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ if (currentData.addedDynamicImports == null)
+ currentData.addedDynamicImports = new ArrayList<ImportPackageSpecification>();
+ for (ImportPackageSpecification addImport : dynamicImport) {
+ if (!ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(addImport.getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ throw new IllegalArgumentException("Import must be a dynamic import."); //$NON-NLS-1$
+ }
+ adding: for (ImportPackageSpecification addImport : dynamicImport) {
+ for (ImportPackageSpecification currentImport : currentData.addedDynamicImports) {
+ if (equalImports(addImport, currentImport))
+ continue adding;
+ }
+ ((ImportPackageSpecificationImpl) addImport).setBundle(this);
+ currentData.addedDynamicImports.add(addImport);
+ }
+ }
+ }
+
+ private boolean equalImports(ImportPackageSpecification addImport, ImportPackageSpecification currentImport) {
+ if (!isEqual(addImport.getName(), currentImport.getName()))
+ return false;
+ if (!isEqual(addImport.getVersionRange(), currentImport.getVersionRange()))
+ return false;
+ if (!isEqual(addImport.getBundleSymbolicName(), currentImport.getBundleSymbolicName()))
+ return false;
+ if (!isEqual(addImport.getBundleVersionRange(), currentImport.getBundleVersionRange()))
+ return false;
+ return isEqual(addImport.getAttributes(), currentImport.getAttributes());
+ }
+
+ private boolean isEqual(Object o1, Object o2) {
+ return (o1 == null) ? o2 == null : o1.equals(o2);
+ }
+
+ void unload() {
+ StateImpl currentState = (StateImpl) getContainingState();
+ StateReader reader = currentState == null ? null : currentState.getReader();
+ if (reader == null)
+ throw new IllegalStateException("BundleDescription does not belong to a reader."); //$NON-NLS-1$
+ synchronized (currentState.monitor) {
+ if ((stateBits & LAZY_LOADED) == 0)
+ return;
+ if (!isFullyLoaded())
+ return;
+ synchronized (this.monitor) {
+ setFullyLoaded(false);
+ lazyData = null;
+ }
+ }
+ }
+
+ void setDynamicStamps(Map<String, Long> dynamicStamps) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ lazyData.dynamicStamps = dynamicStamps;
+ }
+ }
+
+ void setDynamicStamp(String requestedPackage, Long timestamp) {
+ synchronized (this.monitor) {
+ checkLazyData();
+ if (lazyData.dynamicStamps == null) {
+ if (timestamp == null)
+ return;
+ lazyData.dynamicStamps = new HashMap<String, Long>();
+ }
+ if (timestamp == null)
+ lazyData.dynamicStamps.remove(requestedPackage);
+ else
+ lazyData.dynamicStamps.put(requestedPackage, timestamp);
+ }
+ }
+
+ long getDynamicStamp(String requestedPackage) {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ Long stamp = currentData.dynamicStamps == null ? null : (Long) currentData.dynamicStamps.get(requestedPackage);
+ return stamp == null ? 0 : stamp.longValue();
+ }
+ }
+
+ Map<String, Long> getDynamicStamps() {
+ LazyData currentData = loadLazyData();
+ synchronized (this.monitor) {
+ return currentData.dynamicStamps;
+ }
+ }
+
+ public void setEquinoxEE(int equinox_ee) {
+ this.equinox_ee = equinox_ee;
+ }
+
+ public int getEquinoxEE() {
+ return equinox_ee;
+ }
+
+ private void checkLazyData() {
+ if (lazyData == null)
+ lazyData = new LazyData();
+ }
+
+ final class LazyData {
+ String location;
+ String platformFilter;
+
+ BundleSpecification[] requiredBundles;
+ ExportPackageDescription[] exportPackages;
+ ImportPackageSpecification[] importPackages;
+ GenericDescription[] genericCapabilities;
+ GenericSpecification[] genericRequires;
+ NativeCodeSpecification nativeCode;
+
+ ExportPackageDescription[] selectedExports;
+ GenericDescription[] selectedCapabilities;
+ BundleDescription[] resolvedRequires;
+ ExportPackageDescription[] resolvedImports;
+ GenericDescription[] resolvedCapabilities;
+ ExportPackageDescription[] substitutedExports;
+ String[] executionEnvironments;
+
+ Map<String, Long> dynamicStamps;
+ Map<String, List<StateWire>> stateWires;
+ // Note that this is not persisted in the state cache
+ List<ImportPackageSpecification> addedDynamicImports;
+ }
+
+ public Map<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map<String, ?> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map<String, Object>) attributes;
+ }
+ }
+
+ Object getDirective(String key) {
+ synchronized (this.monitor) {
+ if (Constants.MANDATORY_DIRECTIVE.equals(key))
+ return mandatory;
+ if (Constants.SINGLETON_DIRECTIVE.equals(key))
+ return isSingleton() ? Boolean.TRUE : Boolean.FALSE;
+ if (Constants.FRAGMENT_ATTACHMENT_DIRECTIVE.equals(key)) {
+ if (!attachFragments())
+ return Constants.FRAGMENT_ATTACHMENT_NEVER;
+ if (dynamicFragments())
+ return Constants.FRAGMENT_ATTACHMENT_ALWAYS;
+ return Constants.FRAGMENT_ATTACHMENT_RESOLVETIME;
+ }
+ }
+ return null;
+ }
+
+ void setDirective(String key, Object value) {
+ // only pay attention to mandatory directive for now; others are set with setState method
+ if (Constants.MANDATORY_DIRECTIVE.equals(key))
+ mandatory = (String[]) value;
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map<String, String>) directives;
+ }
+ }
+
+ Map<String, String> getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ public Map<String, String> getDeclaredDirectives() {
+ Map<String, String> result = new HashMap<String, String>(2);
+ Map<String, String> arbitrary = getArbitraryDirectives();
+ if (arbitrary != null)
+ result.putAll(arbitrary);
+ if (!attachFragments()) {
+ result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_NEVER);
+ } else {
+ if (dynamicFragments())
+ result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_ALWAYS);
+ else
+ result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_RESOLVETIME);
+ }
+ if (isSingleton())
+ result.put(Constants.SINGLETON_DIRECTIVE, Boolean.TRUE.toString());
+ String[] mandatoryDirective = (String[]) getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (mandatoryDirective != null)
+ result.put(Constants.MANDATORY_DIRECTIVE, ExportPackageDescriptionImpl.toString(mandatoryDirective));
+ return Collections.unmodifiableMap(result);
+ }
+
+ public Map<String, Object> getDeclaredAttributes() {
+ Map<String, Object> result = new HashMap<String, Object>(1);
+ synchronized (this.monitor) {
+ if (attributes != null)
+ result.putAll(attributes);
+ }
+ result.put(BundleRevision.BUNDLE_NAMESPACE, getName());
+ result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, getVersion());
+ return Collections.unmodifiableMap(result);
+ }
+
+ public List<BundleRequirement> getDeclaredRequirements(String namespace) {
+ List<BundleRequirement> result = new ArrayList<BundleRequirement>();
+ if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) {
+ BundleSpecification[] requires = getRequiredBundles();
+ for (BundleSpecification require : requires) {
+ result.add(require.getRequirement());
+ }
+ }
+ if (host != null && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) {
+ result.add(host.getRequirement());
+ }
+ if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
+ ImportPackageSpecification[] imports = getImportPackages();
+ for (ImportPackageSpecification importPkg : imports)
+ result.add(importPkg.getRequirement());
+ }
+ GenericSpecification[] genericSpecifications = getGenericRequires();
+ for (GenericSpecification requirement : genericSpecifications) {
+ if (namespace == null || namespace.equals(requirement.getType()))
+ result.add(requirement.getRequirement());
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ public List<BundleCapability> getDeclaredCapabilities(String namespace) {
+ List<BundleCapability> result = new ArrayList<BundleCapability>();
+ if (host == null) {
+ if (getSymbolicName() != null) {
+ if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) {
+ result.add(BundleDescriptionImpl.this.getCapability());
+ }
+ if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) {
+ result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE));
+ }
+ }
+
+ } else {
+ // may need to have a osgi.wiring.fragment capability
+ }
+ if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
+ ExportPackageDescription[] exports = getExportPackages();
+ for (ExportPackageDescription exportPkg : exports)
+ result.add(exportPkg.getCapability());
+ }
+ GenericDescription[] genericCapabilities = getGenericCapabilities();
+ for (GenericDescription capabilitiy : genericCapabilities) {
+ if (namespace == null || namespace.equals(capabilitiy.getType()))
+ result.add(capabilitiy.getCapability());
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ public int getTypes() {
+ return getHost() != null ? BundleRevision.TYPE_FRAGMENT : 0;
+ }
+
+ public Bundle getBundle() {
+ Object ref = getUserObject();
+ if (ref instanceof BundleReference)
+ return ((BundleReference) ref).getBundle();
+ return null;
+ }
+
+ String getInternalNameSpace() {
+ return BundleRevision.BUNDLE_NAMESPACE;
+ }
+
+ public BundleWiring getWiring() {
+ synchronized (this.monitor) {
+ if (bundleWiring != null || !isResolved())
+ return bundleWiring;
+ return bundleWiring = new DescriptionWiring();
+ }
+ }
+
+ static class BundleWireImpl implements BundleWire {
+ private final BundleCapability capability;
+ private final BundleWiring provider;
+ private final BundleRequirement requirement;
+ private final BundleWiring requirer;
+
+ public BundleWireImpl(StateWire wire) {
+ VersionConstraint declaredRequirement = wire.getDeclaredRequirement();
+ if (declaredRequirement instanceof HostSpecification)
+ this.capability = ((BaseDescriptionImpl) wire.getDeclaredCapability()).getCapability(BundleRevision.HOST_NAMESPACE);
+ else
+ this.capability = wire.getDeclaredCapability().getCapability();
+ this.provider = wire.getCapabilityHost().getWiring();
+ this.requirement = declaredRequirement.getRequirement();
+ this.requirer = wire.getRequirementHost().getWiring();
+ }
+
+ public BundleCapability getCapability() {
+ return capability;
+ }
+
+ public BundleRequirement getRequirement() {
+ return requirement;
+ }
+
+ public BundleWiring getProviderWiring() {
+ return provider;
+ }
+
+ public BundleWiring getRequirerWiring() {
+ return requirer;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashcode = 31 + capability.hashCode();
+ hashcode = hashcode * 31 + requirement.hashCode();
+ hashcode = hashcode * 31 + provider.hashCode();
+ hashcode = hashcode * 31 + requirer.hashCode();
+ return hashcode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof BundleWireImpl))
+ return false;
+ BundleWireImpl other = (BundleWireImpl) obj;
+ return capability.equals(other.getCapability()) && requirement.equals(other.getRequirement()) && provider.equals(other.getProviderWiring()) && requirer.equals(other.getRequirerWiring());
+ }
+
+ public String toString() {
+ return getRequirement() + " -> " + getCapability(); //$NON-NLS-1$
+ }
+
+ public BundleRevision getProvider() {
+ return provider.getRevision();
+ }
+
+ public BundleRevision getRequirer() {
+ return requirer.getRevision();
+ }
+ }
+
+ /**
+ * Coerce the generic type of a list from List<BundleWire>
+ * to List<Wire>
+ * @param l List to be coerced.
+ * @return l coerced to List<Wire>
+ */
+ @SuppressWarnings("unchecked")
+ static List<Wire> asListWire(List<? extends Wire> l) {
+ return (List<Wire>) l;
+ }
+
+ /**
+ * Coerce the generic type of a list from List<BundleCapability>
+ * to List<Capability>
+ * @param l List to be coerced.
+ * @return l coerced to List<Capability>
+ */
+ @SuppressWarnings("unchecked")
+ static List<Capability> asListCapability(List<? extends Capability> l) {
+ return (List<Capability>) l;
+ }
+
+ /**
+ * Coerce the generic type of a list from List<BundleRequirement>
+ * to List<Requirement>
+ * @param l List to be coerced.
+ * @return l coerced to List<Requirement>
+ */
+ @SuppressWarnings("unchecked")
+ static List<Requirement> asListRequirement(List<? extends Requirement> l) {
+ return (List<Requirement>) l;
+ }
+
+ // Note that description wiring are identity equality based
+ class DescriptionWiring implements BundleWiring {
+ private volatile boolean valid = true;
+
+ public Bundle getBundle() {
+ return BundleDescriptionImpl.this.getBundle();
+ }
+
+ public boolean isInUse() {
+ return valid && (isCurrent() || BundleDescriptionImpl.this.hasDependents());
+ }
+
+ void invalidate() {
+ valid = false;
+ }
+
+ public boolean isCurrent() {
+ return valid && !BundleDescriptionImpl.this.isRemovalPending();
+ }
+
+ public List<BundleCapability> getCapabilities(String namespace) {
+ if (!isInUse())
+ return null;
+ List<BundleCapability> result = new ArrayList<BundleCapability>();
+ GenericDescription[] genericCapabilities = getSelectedGenericCapabilities();
+ for (GenericDescription capabilitiy : genericCapabilities) {
+ if (namespace == null || namespace.equals(capabilitiy.getType()))
+ result.add(capabilitiy.getCapability());
+ }
+ if (host != null)
+ return result;
+ if (getSymbolicName() != null) {
+ if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) {
+ result.add(BundleDescriptionImpl.this.getCapability());
+ }
+ if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) {
+ result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE));
+ }
+ }
+ if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
+ ExportPackageDescription[] exports = getSelectedExports();
+ for (ExportPackageDescription exportPkg : exports)
+ result.add(exportPkg.getCapability());
+ }
+ return result;
+ }
+
+ public List<Capability> getResourceCapabilities(String namespace) {
+ return asListCapability(getCapabilities(namespace));
+ }
+
+ public List<BundleRequirement> getRequirements(String namespace) {
+ List<BundleWire> requiredWires = getRequiredWires(namespace);
+ if (requiredWires == null)
+ // happens if not in use
+ return null;
+ List<BundleRequirement> requirements = new ArrayList<BundleRequirement>(requiredWires.size());
+ for (BundleWire wire : requiredWires) {
+ if (!requirements.contains(wire.getRequirement()))
+ requirements.add(wire.getRequirement());
+ }
+ // get dynamic imports
+ if (getHost() == null && (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace))) {
+ // TODO need to handle fragments that add dynamic imports
+ if (hasDynamicImports()) {
+ ImportPackageSpecification[] imports = getImportPackages();
+ for (ImportPackageSpecification impPackage : imports) {
+ if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(impPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) {
+ BundleRequirement req = impPackage.getRequirement();
+ if (!requirements.contains(req))
+ requirements.add(req);
+ }
+ }
+ }
+ ImportPackageSpecification[] addedDynamic = getAddedDynamicImportPackages();
+ for (ImportPackageSpecification dynamicImport : addedDynamic) {
+ BundleRequirement req = dynamicImport.getRequirement();
+ if (!requirements.contains(req))
+ requirements.add(req);
+ }
+ }
+ return requirements;
+ }
+
+ public List<Requirement> getResourceRequirements(String namespace) {
+ return asListRequirement(getRequirements(namespace));
+ }
+
+ public List<BundleWire> getProvidedWires(String namespace) {
+ if (!isInUse())
+ return null;
+ BundleDescription[] dependentBundles = getDependents();
+ List<BundleWire> unorderedResult = new ArrayList<BundleWire>();
+ for (BundleDescription dependent : dependentBundles) {
+ List<BundleWire> dependentWires = dependent.getWiring().getRequiredWires(namespace);
+ if (dependentWires != null)
+ for (BundleWire bundleWire : dependentWires) {
+ if (bundleWire.getProviderWiring() == this)
+ unorderedResult.add(bundleWire);
+ }
+ }
+ List<BundleWire> orderedResult = new ArrayList<BundleWire>(unorderedResult.size());
+ List<BundleCapability> capabilities = getCapabilities(namespace);
+ for (BundleCapability capability : capabilities) {
+ for (Iterator<BundleWire> wires = unorderedResult.iterator(); wires.hasNext();) {
+ BundleWire wire = wires.next();
+ if (wire.getCapability().equals(capability)) {
+ wires.remove();
+ orderedResult.add(wire);
+ }
+ }
+ }
+ return orderedResult;
+ }
+
+ public List<Wire> getProvidedResourceWires(String namespace) {
+ return asListWire(getProvidedWires(namespace));
+ }
+
+ public List<BundleWire> getRequiredWires(String namespace) {
+ if (!isInUse())
+ return null;
+ @SuppressWarnings("unchecked")
+ List<BundleWire> result = Collections.EMPTY_LIST;
+ Map<String, List<StateWire>> wireMap = getWires();
+ if (namespace == null) {
+ result = new ArrayList<BundleWire>();
+ for (List<StateWire> wires : wireMap.values()) {
+ for (StateWire wire : wires) {
+ result.add(new BundleWireImpl(wire));
+ }
+ }
+ return result;
+ }
+ List<StateWire> wires = wireMap.get(namespace);
+ if (wires == null)
+ return result;
+ result = new ArrayList<BundleWire>(wires.size());
+ for (StateWire wire : wires) {
+ result.add(new BundleWireImpl(wire));
+ }
+ return result;
+ }
+
+ public List<Wire> getRequiredResourceWires(String namespace) {
+ return asListWire(getRequiredWires(namespace));
+ }
+
+ public BundleRevision getRevision() {
+ return BundleDescriptionImpl.this;
+ }
+
+ public BundleRevision getResource() {
+ return getRevision();
+ }
+
+ public ClassLoader getClassLoader() {
+ throw new UnsupportedOperationException();
+ }
+
+
+ public List<URL> findEntries(String path, String filePattern, int options) {
+ return null;
+ }
+
+ public Collection<String> listResources(String path, String filePattern, int options) {
+ return null;
+ }
+
+ public String toString() {
+ return BundleDescriptionImpl.this.toString();
+ }
+ }
+
+ public List<Capability> getCapabilities(String namespace) {
+ return asListCapability(getDeclaredCapabilities(namespace));
+ }
+
+ public List<Requirement> getRequirements(String namespace) {
+ return asListRequirement(getDeclaredRequirements(namespace));
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java
new file mode 100644
index 000000000..6946f4d7e
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class BundleSpecificationImpl extends VersionConstraintImpl implements BundleSpecification {
+ private boolean exported;
+ private boolean optional;
+ private Map<String, Object> attributes;
+ private Map<String, String> arbitraryDirectives;
+
+ protected void setExported(boolean exported) {
+ synchronized (this.monitor) {
+ this.exported = exported;
+ }
+ }
+
+ protected void setOptional(boolean optional) {
+ synchronized (this.monitor) {
+ this.optional = optional;
+ }
+ }
+
+ public boolean isExported() {
+ synchronized (this.monitor) {
+ return exported;
+ }
+ }
+
+ public boolean isOptional() {
+ synchronized (this.monitor) {
+ return optional;
+ }
+ }
+
+ Map<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map<String, ?> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map<String, Object>) attributes;
+ }
+ }
+
+ Map<String, String> getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map<String, String>) directives;
+ }
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl candidate = (BundleDescriptionImpl) supplier;
+ if (candidate.getHost() != null)
+ return false;
+ Map<String, ?> requiredAttrs = getAttributes();
+ if (requiredAttrs != null) {
+ Map<String, ?> prividerAttrs = candidate.getAttributes();
+ if (prividerAttrs == null)
+ return false;
+ for (String key : requiredAttrs.keySet()) {
+ Object requiredValue = requiredAttrs.get(key);
+ Object prividedValue = prividerAttrs.get(key);
+ if (prividedValue == null || !requiredValue.equals(prividedValue))
+ return false;
+ }
+ }
+ String[] mandatory = (String[]) candidate.getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (!hasMandatoryAttributes(mandatory))
+ return false;
+ if (getName() != null && getName().equals(candidate.getSymbolicName()) && (getVersionRange() == null || getVersionRange().isIncluded(candidate.getVersion())))
+ return true;
+ return false;
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ if (mandatory != null) {
+ Map<String, ?> requiredAttrs = getAttributes();
+ for (String key : mandatory) {
+ if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key))
+ continue; // has a default value of 0.0.0
+ if (requiredAttrs == null || requiredAttrs.get(key) == null)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public String toString() {
+ return "Require-Bundle: " + getName() + "; bundle-version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ protected Map<String, String> getInternalDirectives() {
+ Map<String, String> result = new HashMap<String, String>(2);
+ synchronized (this.monitor) {
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ if (exported)
+ result.put(Constants.VISIBILITY_DIRECTIVE, Constants.VISIBILITY_REEXPORT);
+ if (optional)
+ result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
+ result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
+ return result;
+ }
+ }
+
+ private String createFilterDirective() {
+ StringBuffer filter = new StringBuffer();
+ filter.append("(&"); //$NON-NLS-1$
+ synchronized (this.monitor) {
+ addFilterAttribute(filter, BundleRevision.BUNDLE_NAMESPACE, getName());
+ VersionRange range = getVersionRange();
+ if (range != null && range != VersionRange.emptyRange)
+ addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, range);
+ if (attributes != null)
+ addFilterAttributes(filter, attributes);
+ }
+ filter.append(')');
+ return filter.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Map<String, Object> getInteralAttributes() {
+ return Collections.EMPTY_MAP;
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return BundleRevision.BUNDLE_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java
new file mode 100644
index 000000000..3f354c3ea
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements ExportPackageDescription {
+ public static final String EQUINOX_EE = "x-equinox-ee"; //$NON-NLS-1$
+ private static final Integer EQUINOX_EE_DEFAULT = new Integer(-1);
+ private String[] uses;
+ private Map<String, Object> attributes;
+ private Map<String, String> arbitraryDirectives;
+ private volatile BundleDescription exporter;
+ private String exclude;
+ private String include;
+ private String[] friends;
+ private String[] mandatory;
+ private Boolean internal = Boolean.FALSE;
+ private int equinox_ee = -1;
+ private ExportPackageDescription fragmentDeclaration = null;
+
+ public ExportPackageDescriptionImpl() {
+ super();
+ }
+
+ public ExportPackageDescriptionImpl(BundleDescription host, ExportPackageDescription fragmentDeclaration) {
+ setName(fragmentDeclaration.getName());
+ setVersion(fragmentDeclaration.getVersion());
+ setDirectives(fragmentDeclaration.getDirectives());
+ setArbitraryDirectives(((ExportPackageDescriptionImpl) fragmentDeclaration).getArbitraryDirectives());
+ setAttributes(fragmentDeclaration.getAttributes());
+ setExporter(host);
+ this.fragmentDeclaration = fragmentDeclaration;
+ }
+
+ public Map<String, Object> getDirectives() {
+ synchronized (this.monitor) {
+ Map<String, Object> result = new HashMap<String, Object>(7);
+ if (uses != null)
+ result.put(Constants.USES_DIRECTIVE, uses);
+ if (exclude != null)
+ result.put(Constants.EXCLUDE_DIRECTIVE, exclude);
+ if (include != null)
+ result.put(Constants.INCLUDE_DIRECTIVE, include);
+ if (mandatory != null)
+ result.put(Constants.MANDATORY_DIRECTIVE, mandatory);
+ if (friends != null)
+ result.put(StateImpl.FRIENDS_DIRECTIVE, friends);
+ result.put(StateImpl.INTERNAL_DIRECTIVE, internal);
+ result.put(EQUINOX_EE, equinox_ee == -1 ? EQUINOX_EE_DEFAULT : new Integer(equinox_ee));
+ return result;
+ }
+ }
+
+ public Map<String, String> getDeclaredDirectives() {
+ Map<String, String> result = new HashMap<String, String>(6);
+ synchronized (this.monitor) {
+ Map<String, String> arbitrary = getArbitraryDirectives();
+ if (arbitrary != null)
+ result.putAll(arbitrary);
+ if (uses != null)
+ result.put(Constants.USES_DIRECTIVE, toString(uses));
+ if (exclude != null)
+ result.put(Constants.EXCLUDE_DIRECTIVE, exclude);
+ if (include != null)
+ result.put(Constants.INCLUDE_DIRECTIVE, include);
+ if (mandatory != null)
+ result.put(Constants.MANDATORY_DIRECTIVE, toString(mandatory));
+ if (friends != null)
+ result.put(StateImpl.FRIENDS_DIRECTIVE, toString(friends));
+ if (internal != null)
+ result.put(StateImpl.INTERNAL_DIRECTIVE, internal.toString());
+ return Collections.unmodifiableMap(result);
+ }
+ }
+
+ public Map<String, Object> getDeclaredAttributes() {
+ Map<String, Object> result = new HashMap<String, Object>(2);
+ synchronized (this.monitor) {
+ if (attributes != null)
+ result.putAll(attributes);
+ result.put(BundleRevision.PACKAGE_NAMESPACE, getName());
+ result.put(Constants.VERSION_ATTRIBUTE, getVersion());
+ Version bundleVersion = getSupplier().getVersion();
+ if (bundleVersion != null)
+ result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
+ String symbolicName = getSupplier().getSymbolicName();
+ if (symbolicName != null) {
+ if (symbolicName.equals(EquinoxContainer.NAME))
+ result.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Arrays.asList(Constants.SYSTEM_BUNDLE_SYMBOLICNAME, symbolicName));
+ else
+ result.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName);
+ }
+ return Collections.unmodifiableMap(result);
+ }
+ }
+
+ static String toString(String[] list) {
+ StringBuffer buffer = new StringBuffer();
+ for (String string : list)
+ buffer.append(string).append(',');
+ if (buffer.length() > 0)
+ buffer.setLength(buffer.length() - 1);
+ return buffer.toString();
+ }
+
+ public Object getDirective(String key) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.USES_DIRECTIVE))
+ return uses;
+ if (key.equals(Constants.EXCLUDE_DIRECTIVE))
+ return exclude;
+ if (key.equals(Constants.INCLUDE_DIRECTIVE))
+ return include;
+ if (key.equals(Constants.MANDATORY_DIRECTIVE))
+ return mandatory;
+ if (key.equals(StateImpl.FRIENDS_DIRECTIVE))
+ return friends;
+ if (key.equals(StateImpl.INTERNAL_DIRECTIVE))
+ return internal;
+ if (key.equals(EQUINOX_EE))
+ return equinox_ee == -1 ? EQUINOX_EE_DEFAULT : new Integer(equinox_ee);
+ return null;
+ }
+ }
+
+ public Object setDirective(String key, Object value) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.USES_DIRECTIVE))
+ return uses = (String[]) value;
+ if (key.equals(Constants.EXCLUDE_DIRECTIVE))
+ return exclude = (String) value;
+ if (key.equals(Constants.INCLUDE_DIRECTIVE))
+ return include = (String) value;
+ if (key.equals(Constants.MANDATORY_DIRECTIVE))
+ return mandatory = (String[]) value;
+ if (key.equals(StateImpl.FRIENDS_DIRECTIVE))
+ return friends = (String[]) value;
+ if (key.equals(StateImpl.INTERNAL_DIRECTIVE))
+ return internal = (Boolean) value;
+ if (key.equals(EQUINOX_EE)) {
+ equinox_ee = ((Integer) value).intValue();
+ return value;
+ }
+ return null;
+ }
+ }
+
+ public void setDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ if (directives == null)
+ return;
+ uses = (String[]) directives.get(Constants.USES_DIRECTIVE);
+ exclude = (String) directives.get(Constants.EXCLUDE_DIRECTIVE);
+ include = (String) directives.get(Constants.INCLUDE_DIRECTIVE);
+ mandatory = (String[]) directives.get(Constants.MANDATORY_DIRECTIVE);
+ friends = (String[]) directives.get(StateImpl.FRIENDS_DIRECTIVE);
+ internal = (Boolean) directives.get(StateImpl.INTERNAL_DIRECTIVE);
+ equinox_ee = ((Integer) directives.get(EQUINOX_EE)).intValue();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map<String, String>) directives;
+ }
+ }
+
+ Map<String, String> getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ public Map<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ public BundleDescription getSupplier() {
+ return getExporter();
+ }
+
+ public BundleDescription getExporter() {
+ return exporter;
+ }
+
+ /**
+ * @deprecated
+ */
+ public boolean isRoot() {
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void setAttributes(Map<String, ?> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map<String, Object>) attributes;
+ }
+ }
+
+ protected void setExporter(BundleDescription exporter) {
+ this.exporter = exporter;
+ }
+
+ public BaseDescription getFragmentDeclaration() {
+ return fragmentDeclaration;
+ }
+
+ void setFragmentDeclaration(ExportPackageDescription fragmentDeclaration) {
+ this.fragmentDeclaration = fragmentDeclaration;
+ }
+
+ public String toString() {
+ return "Export-Package: " + getName() + "; version=\"" + getVersion() + "\""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ String getInternalNameSpace() {
+ return BundleRevision.PACKAGE_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java
new file mode 100644
index 000000000..9e438f033
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+public class GenericDescriptionImpl extends BaseDescriptionImpl implements GenericDescription {
+ private Dictionary<String, Object> attributes;
+ private volatile BundleDescription supplier;
+ private volatile String type = GenericDescription.DEFAULT_TYPE;
+ private Map<String, String> directives;
+ private GenericDescription fragmentDeclaration;
+
+ public GenericDescriptionImpl() {
+ super();
+ }
+
+ public GenericDescriptionImpl(BundleDescription host, GenericDescription fragmentDeclaration) {
+ setType(fragmentDeclaration.getType());
+ Dictionary<String, Object> origAttrs = fragmentDeclaration.getAttributes();
+ if (origAttrs != null) {
+ Hashtable<String, Object> copyAttrs = new Hashtable<String, Object>();
+ for (Enumeration<String> keys = origAttrs.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ copyAttrs.put(key, origAttrs.get(key));
+ }
+ setAttributes(copyAttrs);
+ }
+ Map<String, String> origDirectives = fragmentDeclaration.getDeclaredDirectives();
+ Map<String, String> copyDirectives = new HashMap<String, String>(origDirectives);
+ setDirectives(copyDirectives);
+ setSupplier(host);
+ this.fragmentDeclaration = fragmentDeclaration;
+ }
+
+ public Dictionary<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ public BundleDescription getSupplier() {
+ return supplier;
+ }
+
+ void setAttributes(Dictionary<String, Object> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = attributes;
+ }
+ }
+
+ void setDirectives(Map<String, String> directives) {
+ synchronized (this.monitor) {
+ this.directives = directives;
+ }
+ }
+
+ void setSupplier(BundleDescription supplier) {
+ this.supplier = supplier;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(Constants.PROVIDE_CAPABILITY).append(": ").append(getType()); //$NON-NLS-1$
+ Map<String, Object> attrs = getDeclaredAttributes();
+ sb.append(toString(attrs, false));
+ return sb.toString();
+ }
+
+ /**
+ * @deprecated
+ */
+ public String getName() {
+ synchronized (this.monitor) {
+ Object name = attributes != null ? attributes.get(getType()) : null;
+ return name instanceof String ? (String) name : null;
+ }
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ void setType(String type) {
+ if (type == null || type.equals(GenericDescription.DEFAULT_TYPE))
+ this.type = GenericDescription.DEFAULT_TYPE;
+ else
+ this.type = type;
+ }
+
+ /**
+ * @deprecated
+ */
+ public Version getVersion() {
+ Object version = attributes != null ? attributes.get(Constants.VERSION_ATTRIBUTE) : null;
+ return version instanceof Version ? (Version) version : super.getVersion();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, String> getDeclaredDirectives() {
+ synchronized (this.monitor) {
+ if (directives == null)
+ return Collections.EMPTY_MAP;
+ return Collections.unmodifiableMap(directives);
+ }
+ }
+
+ public Map<String, Object> getDeclaredAttributes() {
+ synchronized (this.monitor) {
+ Map<String, Object> result = new HashMap<String, Object>(5);
+ if (attributes != null)
+ for (Enumeration<String> keys = attributes.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ Object value = attributes.get(key);
+ if (value instanceof List)
+ value = Collections.unmodifiableList((List<Object>) value);
+ result.put(key, value);
+ }
+ return Collections.unmodifiableMap(result);
+ }
+ }
+
+ String getInternalNameSpace() {
+ return getType();
+ }
+
+ public BaseDescription getFragmentDeclaration() {
+ return fragmentDeclaration;
+ }
+
+ void setFragmentDeclaration(GenericDescription fragmentDeclaration) {
+ this.fragmentDeclaration = fragmentDeclaration;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java
new file mode 100644
index 000000000..f0a30cbd2
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.internal.framework.FilterImpl;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+import org.osgi.resource.Namespace;
+
+public class GenericSpecificationImpl extends VersionConstraintImpl implements GenericSpecification {
+ private Filter matchingFilter;
+ private String type = GenericDescription.DEFAULT_TYPE;
+ private int resolution = 0;
+ private GenericDescription[] suppliers;
+ private Map<String, Object> attributes;
+ private Map<String, String> arbitraryDirectives;
+ /*
+ * Indicates that a generic constraint was from converting the BREE header
+ */
+ public static final int RESOLUTION_FROM_BREE = 0x04;
+
+ public String getMatchingFilter() {
+ synchronized (this.monitor) {
+ return matchingFilter == null ? null : matchingFilter.toString();
+ }
+ }
+
+ void setMatchingFilter(String matchingFilter, boolean matchName) throws InvalidSyntaxException {
+ synchronized (this.monitor) {
+ String name = getName();
+ if (matchName && name != null && !"*".equals(name)) { //$NON-NLS-1$
+ String nameFilter = "(" + getType() + "=" + getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ matchingFilter = matchingFilter == null ? nameFilter : "(&" + nameFilter + matchingFilter + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ this.matchingFilter = matchingFilter == null ? null : FilterImpl.newInstance(matchingFilter);
+ }
+ }
+
+ void setMatchingFilter(Filter matchingFilter) {
+ synchronized (this.monitor) {
+ this.matchingFilter = matchingFilter;
+ }
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof GenericDescription))
+ return false;
+ GenericDescription candidate = (GenericDescription) supplier;
+ if (!getType().equals(candidate.getType()))
+ return false;
+ // Note that names and versions are only matched by including them in the filter
+ return matchingFilter == null || matchingFilter.match(candidate.getAttributes());
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ return true;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(Constants.REQUIRE_CAPABILITY).append(": ").append(getType()); //$NON-NLS-1$
+ if (matchingFilter != null)
+ sb.append("; filter=\"").append(getMatchingFilter()).append('"'); //$NON-NLS-1$
+ return sb.toString();
+ }
+
+ public String getType() {
+ synchronized (this.monitor) {
+ return type;
+ }
+ }
+
+ void setType(String type) {
+ synchronized (this.monitor) {
+ if (type == null || type.equals(GenericDescription.DEFAULT_TYPE))
+ this.type = GenericDescription.DEFAULT_TYPE;
+ else
+ this.type = type;
+ }
+ }
+
+ public int getResolution() {
+ synchronized (this.monitor) {
+ return resolution;
+ }
+ }
+
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return suppliers != null && suppliers.length > 0;
+ }
+ }
+
+ void setResolution(int resolution) {
+ synchronized (this.monitor) {
+ this.resolution = resolution;
+ }
+ }
+
+ public BaseDescription getSupplier() {
+ synchronized (this.monitor) {
+ return suppliers == null || suppliers.length == 0 ? null : suppliers[0];
+ }
+ }
+
+ protected void setSupplier(BaseDescription supplier) {
+ synchronized (this.monitor) {
+ if (supplier == null) {
+ suppliers = null;
+ return;
+ }
+ int len = suppliers == null ? 0 : suppliers.length;
+ GenericDescription[] temp = new GenericDescription[len + 1];
+ if (suppliers != null)
+ System.arraycopy(suppliers, 0, temp, 0, len);
+ temp[len] = (GenericDescription) supplier;
+ suppliers = temp;
+ }
+ }
+
+ public GenericDescription[] getSuppliers() {
+ synchronized (this.monitor) {
+ return suppliers;
+ }
+ }
+
+ void setSupplers(GenericDescription[] suppliers) {
+ synchronized (this.monitor) {
+ this.suppliers = suppliers;
+ }
+ }
+
+ Map<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map<String, ?> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map<String, Object>) attributes;
+ }
+ }
+
+ Map<String, String> getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map<String, String>) directives;
+ }
+ }
+
+ @Override
+ protected Map<String, String> getInternalDirectives() {
+ Map<String, String> result = new HashMap<String, String>(2);
+ synchronized (this.monitor) {
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ if ((resolution & GenericSpecification.RESOLUTION_OPTIONAL) != 0)
+ result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
+ if ((resolution & GenericSpecification.RESOLUTION_MULTIPLE) != 0)
+ result.put(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE, Namespace.CARDINALITY_MULTIPLE);
+ if (matchingFilter != null) {
+ result.put(Constants.FILTER_DIRECTIVE, matchingFilter.toString());
+ }
+ }
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Map<String, Object> getInteralAttributes() {
+ synchronized (this.monitor) {
+ return attributes == null ? Collections.EMPTY_MAP : new HashMap<String, Object>(attributes);
+ }
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return getType();
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java
new file mode 100644
index 000000000..2f6d77841
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class HostSpecificationImpl extends VersionConstraintImpl implements HostSpecification {
+
+ private BundleDescription[] hosts;
+ private boolean multihost = false;
+ private Map<String, Object> attributes;
+ private Map<String, String> arbitraryDirectives;
+
+ Map<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setAttributes(Map<String, ?> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map<String, Object>) attributes;
+ }
+ }
+
+ Map<String, String> getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map<String, String>) directives;
+ }
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl candidate = (BundleDescriptionImpl) supplier;
+ if (candidate.getHost() != null)
+ return false;
+ Map<String, ?> requiredAttrs = getAttributes();
+ if (requiredAttrs != null) {
+ Map<String, ?> prividerAttrs = candidate.getAttributes();
+ if (prividerAttrs == null)
+ return false;
+ for (String key : requiredAttrs.keySet()) {
+ Object requiredValue = requiredAttrs.get(key);
+ Object prividedValue = prividerAttrs.get(key);
+ if (prividedValue == null || !requiredValue.equals(prividedValue))
+ return false;
+ }
+ }
+ String[] mandatory = (String[]) candidate.getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (!hasMandatoryAttributes(mandatory))
+ return false;
+ if (getName() != null && getName().equals(candidate.getSymbolicName()) && (getVersionRange() == null || getVersionRange().isIncluded(candidate.getVersion())))
+ return true;
+ return false;
+ }
+
+ public BundleDescription[] getHosts() {
+ synchronized (this.monitor) {
+ return hosts == null ? BundleDescriptionImpl.EMPTY_BUNDLEDESCS : hosts;
+ }
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ if (mandatory != null) {
+ Map<String, ?> requiredAttrs = getAttributes();
+ for (String key : mandatory) {
+ if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key))
+ continue; // has a default value of 0.0.0
+ if (requiredAttrs == null || requiredAttrs.get(key) == null)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return hosts != null && hosts.length > 0;
+ }
+ }
+
+ /*
+ * The resolve algorithm will call this method to set the hosts.
+ */
+ void setHosts(BundleDescription[] hosts) {
+ synchronized (this.monitor) {
+ this.hosts = hosts;
+ }
+ }
+
+ public String toString() {
+ return "Fragment-Host: " + getName() + "; bundle-version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ public BaseDescription getSupplier() {
+ synchronized (this.monitor) {
+ if (hosts == null || hosts.length == 0)
+ return null;
+ return hosts[0];
+ }
+ }
+
+ public boolean isMultiHost() {
+ synchronized (this.monitor) {
+ return multihost;
+ }
+ }
+
+ void setIsMultiHost(boolean multihost) {
+ synchronized (this.monitor) {
+ this.multihost = multihost;
+ }
+ }
+
+ @Override
+ protected Map<String, String> getInternalDirectives() {
+ Map<String, String> result = new HashMap<String, String>(2);
+ synchronized (this.monitor) {
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
+ return result;
+ }
+ }
+
+ private String createFilterDirective() {
+ StringBuffer filter = new StringBuffer();
+ filter.append("(&"); //$NON-NLS-1$
+ synchronized (this.monitor) {
+ addFilterAttribute(filter, BundleRevision.HOST_NAMESPACE, getName());
+ VersionRange range = getVersionRange();
+ if (range != null && range != VersionRange.emptyRange)
+ addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, range);
+ if (attributes != null)
+ addFilterAttributes(filter, attributes);
+ }
+ filter.append(')');
+ return filter.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Map<String, Object> getInteralAttributes() {
+ return Collections.EMPTY_MAP;
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return BundleRevision.HOST_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ImportPackageSpecificationImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ImportPackageSpecificationImpl.java
new file mode 100644
index 000000000..5e82d40da
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ImportPackageSpecificationImpl.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ImportPackageSpecificationImpl extends VersionConstraintImpl implements ImportPackageSpecification {
+ private String resolution = ImportPackageSpecification.RESOLUTION_STATIC; // the default is static
+ private String symbolicName;
+ private VersionRange bundleVersionRange;
+ private Map<String, Object> attributes;
+ private Map<String, String> arbitraryDirectives;
+
+ public Map<String, Object> getDirectives() {
+ synchronized (this.monitor) {
+ Map<String, Object> result = new HashMap<String, Object>(5);
+ if (resolution != null)
+ result.put(Constants.RESOLUTION_DIRECTIVE, resolution);
+ return result;
+ }
+ }
+
+ public Object getDirective(String key) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.RESOLUTION_DIRECTIVE))
+ return resolution;
+ return null;
+ }
+ }
+
+ Object setDirective(String key, Object value) {
+ synchronized (this.monitor) {
+ if (key.equals(Constants.RESOLUTION_DIRECTIVE))
+ return resolution = (String) value;
+ return null;
+ }
+ }
+
+ void setDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ if (directives == null)
+ return;
+ resolution = (String) directives.get(Constants.RESOLUTION_DIRECTIVE);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void setArbitraryDirectives(Map<String, ?> directives) {
+ synchronized (this.monitor) {
+ this.arbitraryDirectives = (Map<String, String>) directives;
+ }
+ }
+
+ Map<String, String> getArbitraryDirectives() {
+ synchronized (this.monitor) {
+ return arbitraryDirectives;
+ }
+ }
+
+ public String getBundleSymbolicName() {
+ synchronized (this.monitor) {
+ if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) {
+ StateImpl state = (StateImpl) getBundle().getContainingState();
+ return state == null ? EquinoxContainer.NAME : state.getSystemBundle();
+ }
+ return symbolicName;
+ }
+ }
+
+ public VersionRange getBundleVersionRange() {
+ synchronized (this.monitor) {
+ if (bundleVersionRange == null)
+ return VersionRange.emptyRange;
+ return bundleVersionRange;
+ }
+ }
+
+ public Map<String, Object> getAttributes() {
+ synchronized (this.monitor) {
+ return attributes;
+ }
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ return isSatisfiedBy(supplier, true);
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier, boolean checkEE) {
+ if (!(supplier instanceof ExportPackageDescription))
+ return false;
+ ExportPackageDescriptionImpl pkgDes = (ExportPackageDescriptionImpl) supplier;
+
+ // If we are in strict mode, check to see if the export specifies friends.
+ // If it does, are we one of the friends
+ String[] friends = (String[]) pkgDes.getDirective(StateImpl.FRIENDS_DIRECTIVE);
+ Boolean internal = (Boolean) pkgDes.getDirective(StateImpl.INTERNAL_DIRECTIVE);
+ if (internal.booleanValue() || friends != null) {
+ StateImpl state = (StateImpl) getBundle().getContainingState();
+ boolean strict = state == null ? false : state.inStrictMode();
+ if (strict) {
+ if (internal.booleanValue())
+ return false;
+ boolean found = false;
+ if (friends != null && getBundle().getSymbolicName() != null)
+ for (int i = 0; i < friends.length; i++)
+ if (getBundle().getSymbolicName().equals(friends[i]))
+ found = true;
+ if (!found)
+ return false;
+ }
+ }
+ String exporterSymbolicName = getBundleSymbolicName();
+ if (exporterSymbolicName != null) {
+ BundleDescription exporter = pkgDes.getExporter();
+ if (!exporterSymbolicName.equals(exporter.getSymbolicName()))
+ return false;
+ if (getBundleVersionRange() != null && !getBundleVersionRange().isIncluded(exporter.getVersion()))
+ return false;
+ }
+
+ String name = getName();
+ // shortcut '*'
+ // NOTE: wildcards are supported only in cases where this is a dynamic import
+ if (!"*".equals(name) && !(name.endsWith(".*") && pkgDes.getName().startsWith(name.substring(0, name.length() - 1))) && !pkgDes.getName().equals(name)) //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ if (getVersionRange() != null && !getVersionRange().isIncluded(pkgDes.getVersion()))
+ return false;
+
+ Map<String, ?> importAttrs = getAttributes();
+ if (importAttrs != null) {
+ Map<String, ?> exportAttrs = pkgDes.getAttributes();
+ if (exportAttrs == null)
+ return false;
+ for (Iterator<String> i = importAttrs.keySet().iterator(); i.hasNext();) {
+ String importKey = i.next();
+ Object importValue = importAttrs.get(importKey);
+ Object exportValue = exportAttrs.get(importKey);
+ if (exportValue == null || !importValue.equals(exportValue))
+ return false;
+ }
+ }
+ String[] mandatory = (String[]) pkgDes.getDirective(Constants.MANDATORY_DIRECTIVE);
+ if (!hasMandatoryAttributes(mandatory))
+ return false;
+ // finally check the ee index
+ if (!checkEE)
+ return true;
+ if (((BundleDescriptionImpl) getBundle()).getEquinoxEE() < 0)
+ return true;
+ int eeIndex = ((Integer) pkgDes.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue();
+ return eeIndex < 0 || eeIndex == ((BundleDescriptionImpl) getBundle()).getEquinoxEE();
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ if (mandatory != null) {
+ Map<String, ?> importAttrs = getAttributes();
+ for (int i = 0; i < mandatory.length; i++) {
+ if (Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE.equals(mandatory[i])) {
+ if (getBundleSymbolicName() == null)
+ return false;
+ } else if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(mandatory[i])) {
+ if (bundleVersionRange == null)
+ return false;
+ } else if (Constants.PACKAGE_SPECIFICATION_VERSION.equals(mandatory[i]) || Constants.VERSION_ATTRIBUTE.equals(mandatory[i])) {
+ if (getVersionRange() == null)
+ return false;
+ } else { // arbitrary attribute
+ if (importAttrs == null)
+ return false;
+ if (importAttrs.get(mandatory[i]) == null)
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ protected void setBundleSymbolicName(String symbolicName) {
+ synchronized (this.monitor) {
+ this.symbolicName = symbolicName;
+ }
+ }
+
+ protected void setBundleVersionRange(VersionRange bundleVersionRange) {
+ synchronized (this.monitor) {
+ this.bundleVersionRange = bundleVersionRange;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void setAttributes(Map<String, ?> attributes) {
+ synchronized (this.monitor) {
+ this.attributes = (Map<String, Object>) attributes;
+ }
+ }
+
+ public String toString() {
+ return "Import-Package: " + getName() + "; version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ protected Map<String, String> getInternalDirectives() {
+ synchronized (this.monitor) {
+ Map<String, String> result = new HashMap<String, String>(5);
+ if (arbitraryDirectives != null)
+ result.putAll(arbitraryDirectives);
+ if (resolution != null) {
+ if (ImportPackageSpecification.RESOLUTION_STATIC.equals(resolution))
+ result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_MANDATORY);
+ else
+ result.put(Constants.RESOLUTION_DIRECTIVE, resolution);
+ }
+ result.put(Constants.FILTER_DIRECTIVE, createFilterDirective());
+ return result;
+ }
+ }
+
+ private String createFilterDirective() {
+ StringBuffer filter = new StringBuffer();
+ filter.append("(&"); //$NON-NLS-1$
+ synchronized (this.monitor) {
+ addFilterAttribute(filter, BundleRevision.PACKAGE_NAMESPACE, getName(), false);
+ VersionRange range = getVersionRange();
+ if (range != null && range != VersionRange.emptyRange)
+ addFilterAttribute(filter, Constants.VERSION_ATTRIBUTE, range);
+ if (symbolicName != null)
+ addFilterAttribute(filter, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName);
+ if (bundleVersionRange != null)
+ addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersionRange);
+ if (attributes != null)
+ addFilterAttributes(filter, attributes);
+ }
+ filter.append(')');
+ return filter.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Map<String, Object> getInteralAttributes() {
+ return Collections.EMPTY_MAP;
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return BundleRevision.PACKAGE_NAMESPACE;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java
new file mode 100644
index 000000000..a9c183ec1
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.internal.framework.FilterImpl;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.osgi.framework.*;
+
+public class NativeCodeDescriptionImpl extends BaseDescriptionImpl implements NativeCodeDescription {
+ private static final VersionRange[] EMPTY_VERSIONRANGES = new VersionRange[0];
+
+ private volatile Filter filter;
+ private String[] languages;
+ private String[] nativePaths;
+ private String[] osNames;
+ private VersionRange[] osVersions;
+ private String[] processors;
+ private BundleDescription supplier;
+ private volatile boolean invalidNativePaths = false;
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public String[] getLanguages() {
+ synchronized (this.monitor) {
+ if (languages == null)
+ return BundleDescriptionImpl.EMPTY_STRING;
+ return languages;
+ }
+ }
+
+ public String[] getNativePaths() {
+ synchronized (this.monitor) {
+ if (nativePaths == null)
+ return BundleDescriptionImpl.EMPTY_STRING;
+ return nativePaths;
+ }
+ }
+
+ public String[] getOSNames() {
+ synchronized (this.monitor) {
+ if (osNames == null)
+ return BundleDescriptionImpl.EMPTY_STRING;
+ return osNames;
+ }
+ }
+
+ public VersionRange[] getOSVersions() {
+ synchronized (this.monitor) {
+ if (osVersions == null)
+ return EMPTY_VERSIONRANGES;
+ return osVersions;
+ }
+ }
+
+ public String[] getProcessors() {
+ synchronized (this.monitor) {
+ if (processors == null)
+ return BundleDescriptionImpl.EMPTY_STRING;
+ return processors;
+ }
+ }
+
+ public BundleDescription getSupplier() {
+ return supplier;
+ }
+
+ public int compareTo(NativeCodeDescription otherDesc) {
+ State containingState = getSupplier().getContainingState();
+ if (containingState == null)
+ return 0;
+ Dictionary<Object, Object>[] platformProps = containingState.getPlatformProperties();
+ Version osversion;
+ try {
+ osversion = Version.parseVersion((String) platformProps[0].get(Constants.FRAMEWORK_OS_VERSION));
+ } catch (Exception e) {
+ osversion = Version.emptyVersion;
+ }
+ VersionRange[] thisRanges = getOSVersions();
+ VersionRange[] otherRanges = otherDesc.getOSVersions();
+ Version thisHighest = getHighestVersionMatch(osversion, thisRanges);
+ Version otherHighest = getHighestVersionMatch(osversion, otherRanges);
+ if (thisHighest.compareTo(otherHighest) < 0)
+ return -1;
+ return (getLanguages().length == 0 ? 0 : 1) - (otherDesc.getLanguages().length == 0 ? 0 : 1);
+ }
+
+ public boolean hasInvalidNativePaths() {
+ return invalidNativePaths;
+ }
+
+ private Version getHighestVersionMatch(Version version, VersionRange[] ranges) {
+ Version highest = Version.emptyVersion;
+ for (int i = 0; i < ranges.length; i++) {
+ if (ranges[i].isIncluded(version) && highest.compareTo(ranges[i].getMinimum()) < 0)
+ highest = ranges[i].getMinimum();
+ }
+ return highest;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ String[] paths = getNativePaths();
+ for (int i = 0; i < paths.length; i++) {
+ if (i > 0) {
+ sb.append("; "); //$NON-NLS-1$
+ }
+ sb.append(paths[i]);
+ }
+
+ String[] procs = getProcessors();
+ for (int i = 0; i < procs.length; i++) {
+ sb.append("; "); //$NON-NLS-1$
+ sb.append(Constants.BUNDLE_NATIVECODE_PROCESSOR);
+ sb.append('=');
+ sb.append(procs[i]);
+ }
+
+ String[] oses = getOSNames();
+ for (int i = 0; i < oses.length; i++) {
+ sb.append("; "); //$NON-NLS-1$
+ sb.append(Constants.BUNDLE_NATIVECODE_OSNAME);
+ sb.append('=');
+ sb.append(oses[i]);
+ }
+
+ VersionRange[] osRanges = getOSVersions();
+ for (int i = 0; i < osRanges.length; i++) {
+ sb.append("; "); //$NON-NLS-1$
+ sb.append(Constants.BUNDLE_NATIVECODE_OSVERSION);
+ sb.append("=\""); //$NON-NLS-1$
+ sb.append(osRanges[i].toString());
+ sb.append('"');
+ }
+
+ String[] langs = getLanguages();
+ for (int i = 0; i < langs.length; i++) {
+ sb.append("; "); //$NON-NLS-1$
+ sb.append(Constants.BUNDLE_NATIVECODE_LANGUAGE);
+ sb.append('=');
+ sb.append(langs[i]);
+ }
+
+ Filter f = getFilter();
+ if (f != null) {
+ sb.append("; "); //$NON-NLS-1$
+ sb.append(Constants.SELECTION_FILTER_ATTRIBUTE);
+ sb.append("=\""); //$NON-NLS-1$
+ sb.append(f.toString());
+ sb.append('"');
+ }
+ return (sb.toString());
+ }
+
+ void setInvalidNativePaths(boolean invalidNativePaths) {
+ this.invalidNativePaths = invalidNativePaths;
+ }
+
+ void setOSNames(String[] osNames) {
+ synchronized (this.monitor) {
+ this.osNames = osNames;
+ }
+ }
+
+ void setOSVersions(VersionRange[] osVersions) {
+ synchronized (this.monitor) {
+ this.osVersions = osVersions;
+ }
+ }
+
+ void setFilter(String filter) throws InvalidSyntaxException {
+ this.filter = filter == null ? null : FilterImpl.newInstance(filter);
+ }
+
+ void setLanguages(String[] languages) {
+ synchronized (this.monitor) {
+ this.languages = languages;
+ }
+ }
+
+ void setNativePaths(String[] nativePaths) {
+ synchronized (this.monitor) {
+ this.nativePaths = nativePaths;
+ }
+ }
+
+ void setProcessors(String[] processors) {
+ synchronized (this.monitor) {
+ this.processors = processors;
+ }
+ }
+
+ void setSupplier(BundleDescription supplier) {
+ this.supplier = supplier;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, String> getDeclaredDirectives() {
+ return Collections.EMPTY_MAP;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> getDeclaredAttributes() {
+ return Collections.EMPTY_MAP;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeSpecificationImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeSpecificationImpl.java
new file mode 100644
index 000000000..883346331
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/NativeCodeSpecificationImpl.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.internal.framework.AliasMapper;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.Version;
+
+public class NativeCodeSpecificationImpl extends VersionConstraintImpl implements NativeCodeSpecification {
+ private static final NativeCodeDescription[] EMPTY_NATIVECODEDESCRIPTIONS = new NativeCodeDescription[0];
+ private static AliasMapper aliasMapper = new AliasMapper();
+ private NativeCodeDescription[] possibleSuppliers;
+ private boolean optional;
+
+ public NativeCodeDescription[] getPossibleSuppliers() {
+ synchronized (this.monitor) {
+ if (possibleSuppliers == null)
+ return EMPTY_NATIVECODEDESCRIPTIONS;
+ return possibleSuppliers;
+ }
+ }
+
+ void setPossibleSuppliers(NativeCodeDescription[] possibleSuppliers) {
+ synchronized (this.monitor) {
+ this.possibleSuppliers = possibleSuppliers;
+ }
+ }
+
+ public boolean isOptional() {
+ synchronized (this.monitor) {
+ return optional;
+ }
+ }
+
+ void setOptional(boolean optional) {
+ synchronized (this.monitor) {
+ this.optional = optional;
+ }
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ if (!(supplier instanceof NativeCodeDescription))
+ return false;
+ State containingState = getBundle().getContainingState();
+ if (containingState == null)
+ return false;
+ Dictionary<Object, Object>[] platformProps = containingState.getPlatformProperties();
+ NativeCodeDescription nativeSupplier = (NativeCodeDescription) supplier;
+ Filter filter = nativeSupplier.getFilter();
+ boolean match = false;
+ for (int i = 0; i < platformProps.length && !match; i++) {
+ @SuppressWarnings("rawtypes")
+ Dictionary props = platformProps[i];
+ if (filter != null && !filter.matchCase(props))
+ continue;
+ String[] osNames = nativeSupplier.getOSNames();
+ if (osNames.length == 0)
+ match = true;
+ else {
+ Object platformOS = platformProps[i].get(Constants.FRAMEWORK_OS_NAME);
+ Object aliasedPlatformOS = platformOS == null || !(platformOS instanceof String) ? platformOS : aliasMapper.aliasOSName((String) platformOS);
+ Object[] platformOSes;
+ if (aliasedPlatformOS instanceof Collection)
+ platformOSes = ((Collection<?>) aliasedPlatformOS).toArray();
+ else
+ platformOSes = aliasedPlatformOS == null ? new Object[0] : new Object[] {aliasedPlatformOS};
+ for (int j = 0; j < osNames.length && !match; j++) {
+ Object aliasedName = aliasMapper.aliasOSName(osNames[j]);
+ for (int k = 0; k < platformOSes.length; k++) {
+ if (aliasedName instanceof String) {
+ if (platformOSes[k].equals(aliasedName))
+ match = true;
+ } else {
+ for (Iterator<?> iAliases = ((Collection<?>) aliasedName).iterator(); iAliases.hasNext() && !match;)
+ if (platformOSes[k].equals(iAliases.next()))
+ match = true;
+ }
+ }
+ }
+ }
+ if (!match)
+ continue;
+ match = false;
+
+ String[] processors = nativeSupplier.getProcessors();
+ if (processors.length == 0)
+ match = true;
+ else {
+ Object platformProcessor = platformProps[i].get(Constants.FRAMEWORK_PROCESSOR);
+ Object aliasedPlatformProcessor = platformProcessor == null || !(platformProcessor instanceof String) ? platformProcessor : aliasMapper.aliasProcessor((String) platformProcessor);
+ if (aliasedPlatformProcessor != null)
+ for (int j = 0; j < processors.length && !match; j++) {
+ String aliasedProcessor = aliasMapper.aliasProcessor(processors[j]);
+ if (aliasedPlatformProcessor.equals(aliasedProcessor))
+ match = true;
+ }
+ }
+ if (!match)
+ return false;
+ match = false;
+
+ String[] languages = nativeSupplier.getLanguages();
+ if (languages.length == 0)
+ match = true;
+ else {
+ Object platformLanguage = platformProps[i].get(Constants.FRAMEWORK_LANGUAGE);
+ if (platformLanguage != null)
+ for (int j = 0; j < languages.length && !match; j++) {
+ if ((platformLanguage instanceof String) ? ((String) platformLanguage).equalsIgnoreCase(languages[j]) : platformLanguage.equals(languages[j]))
+ match = true;
+ }
+ }
+ if (!match)
+ return false;
+ match = false;
+
+ VersionRange[] osVersions = nativeSupplier.getOSVersions();
+ if (osVersions.length == 0 || platformProps[i].get(Constants.FRAMEWORK_OS_VERSION) == null)
+ match = true;
+ else {
+ Version osversion;
+ try {
+ osversion = Version.parseVersion((String) platformProps[i].get(Constants.FRAMEWORK_OS_VERSION));
+ } catch (Exception e) {
+ osversion = Version.emptyVersion;
+ }
+ for (int j = 0; j < osVersions.length && !match; j++) {
+ if (osVersions[j].isIncluded(osversion))
+ match = true;
+ }
+ }
+ }
+ return match;
+ }
+
+ @Override
+ protected boolean hasMandatoryAttributes(String[] mandatory) {
+ return true;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ NativeCodeDescription[] suppliers = getPossibleSuppliers();
+ for (int i = 0; i < suppliers.length; i++) {
+ if (i > 0)
+ sb.append(", "); //$NON-NLS-1$
+ sb.append(suppliers[i].toString());
+ }
+
+ return sb.toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Map<String, String> getInternalDirectives() {
+ return Collections.EMPTY_MAP;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Map<String, Object> getInteralAttributes() {
+ return Collections.EMPTY_MAP;
+ }
+
+ @Override
+ protected String getInternalNameSpace() {
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ReadOnlyState.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ReadOnlyState.java
new file mode 100644
index 000000000..d0bc359ae
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ReadOnlyState.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+
+public final class ReadOnlyState implements State {
+ private final State target;
+
+ public ReadOnlyState(State target) {
+ this.target = target;
+ }
+
+ public boolean addBundle(BundleDescription description) {
+ throw new UnsupportedOperationException();
+ }
+
+ public StateDelta compare(State state) throws BundleException {
+ return target.compare(state);
+ }
+
+ public BundleDescription getBundle(long id) {
+ return target.getBundle(id);
+ }
+
+ public BundleDescription getBundle(String symbolicName, Version version) {
+ return target.getBundle(symbolicName, version);
+ }
+
+ public BundleDescription getBundleByLocation(String location) {
+ return target.getBundleByLocation(location);
+ }
+
+ public BundleDescription[] getBundles() {
+ return target.getBundles();
+ }
+
+ public BundleDescription[] getBundles(String symbolicName) {
+ return target.getBundles(symbolicName);
+ }
+
+ public StateDelta getChanges() {
+ return target.getChanges();
+ }
+
+ public ExportPackageDescription[] getExportedPackages() {
+ return target.getExportedPackages();
+ }
+
+ public StateObjectFactory getFactory() {
+ return target.getFactory();
+ }
+
+ public BundleDescription[] getResolvedBundles() {
+ return target.getResolvedBundles();
+ }
+
+ public long getTimeStamp() {
+ return target.getTimeStamp();
+ }
+
+ public boolean isEmpty() {
+ return target.isEmpty();
+ }
+
+ public boolean isResolved() {
+ return target.isResolved();
+ }
+
+ public boolean removeBundle(BundleDescription bundle) {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription removeBundle(long bundleId) {
+ throw new UnsupportedOperationException();
+ }
+
+ public StateDelta resolve() {
+ throw new UnsupportedOperationException();
+ }
+
+ public StateDelta resolve(boolean incremental) {
+ throw new UnsupportedOperationException();
+ }
+
+ public StateDelta resolve(BundleDescription[] discard) {
+ throw new UnsupportedOperationException();
+ }
+
+ public StateDelta resolve(BundleDescription[] resolve, boolean discard) {
+ throw new UnsupportedOperationException();
+ }
+
+ @SuppressWarnings("deprecation")
+ public void setOverrides(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean updateBundle(BundleDescription newDescription) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void resolveConstraint(VersionConstraint constraint, BaseDescription supplier) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @deprecated
+ */
+ public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @deprecated
+ */
+ public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] host, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolveImports) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedRequirements) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeBundleComplete(BundleDescription bundle) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Resolver getResolver() {
+ return null;
+ }
+
+ public void setResolver(Resolver value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean setPlatformProperties(Dictionary<?, ?> platformProperties) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean setPlatformProperties(Dictionary<?, ?> platformProperties[]) {
+ throw new UnsupportedOperationException();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Dictionary[] getPlatformProperties() {
+ return target.getPlatformProperties();
+ }
+
+ public ExportPackageDescription linkDynamicImport(BundleDescription importingBundle, String requestedPackage) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setTimeStamp(long timeStamp) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ExportPackageDescription[] getSystemPackages() {
+ return target.getSystemPackages();
+ }
+
+ public void addResolverError(BundleDescription bundle, int type, String data, VersionConstraint unsatisfied) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ResolverError[] getResolverErrors(BundleDescription bundle) {
+ return target.getResolverErrors(bundle);
+ }
+
+ public void removeResolverErrors(BundleDescription bundle) {
+ throw new UnsupportedOperationException();
+ }
+
+ public StateHelper getStateHelper() {
+ return StateHelperImpl.getInstance();
+ }
+
+ public long getHighestBundleId() {
+ return target.getHighestBundleId();
+ }
+
+ public void setNativePathsInvalid(NativeCodeDescription nativeCodeDescription, boolean hasInvalidPaths) {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription[] getDisabledBundles() {
+ return target.getDisabledBundles();
+ }
+
+ public void addDisabledInfo(DisabledInfo disabledInfo) {
+ throw new UnsupportedOperationException();
+ }
+
+ public DisabledInfo[] getDisabledInfos(BundleDescription bundle) {
+ return target.getDisabledInfos(bundle);
+ }
+
+ public DisabledInfo getDisabledInfo(BundleDescription bundle, String policyName) {
+ return target.getDisabledInfo(bundle, policyName);
+ }
+
+ public void removeDisabledInfo(DisabledInfo disabledInfo) {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription[] getRemovalPending() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Collection<BundleDescription> getDependencyClosure(Collection<BundleDescription> bundles) {
+ return target.getDependencyClosure(bundles);
+ }
+
+ public void addDynamicImportPackages(BundleDescription importingBundle, ImportPackageSpecification[] dynamicImports) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setResolverHookFactory(ResolverHookFactory hookFactory) {
+ // do nothing
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java
new file mode 100644
index 000000000..e9131afb3
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.NLS;
+
+public final class ResolverErrorImpl implements ResolverError {
+ private final BundleDescriptionImpl bundle;
+ private final int type;
+ private final String data;
+ private final VersionConstraint unsatisfied;
+
+ public ResolverErrorImpl(BundleDescriptionImpl bundle, int type, String data, VersionConstraint unsatisfied) {
+ this.bundle = bundle;
+ this.data = data;
+ this.type = type;
+ this.unsatisfied = unsatisfied;
+ }
+
+ public BundleDescription getBundle() {
+ return bundle;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public VersionConstraint getUnsatisfiedConstraint() {
+ return unsatisfied;
+ }
+
+ public String toString() {
+ switch (getType()) {
+ case ResolverError.EXPORT_PACKAGE_PERMISSION :
+ case ResolverError.IMPORT_PACKAGE_PERMISSION :
+ case ResolverError.REQUIRE_BUNDLE_PERMISSION :
+ case ResolverError.PROVIDE_BUNDLE_PERMISSION :
+ case ResolverError.FRAGMENT_BUNDLE_PERMISSION :
+ case ResolverError.HOST_BUNDLE_PERMISSION :
+ case ResolverError.REQUIRE_CAPABILITY_PERMISSION :
+ case ResolverError.PROVIDE_CAPABILITY_PERMISSION :
+ return NLS.bind(StateMsg.RES_ERROR_MISSING_PERMISSION, getData());
+ case ResolverError.MISSING_IMPORT_PACKAGE :
+ case ResolverError.MISSING_REQUIRE_BUNDLE :
+ case ResolverError.MISSING_FRAGMENT_HOST :
+ case ResolverError.MISSING_EXECUTION_ENVIRONMENT :
+ case ResolverError.MISSING_GENERIC_CAPABILITY :
+ return NLS.bind(StateMsg.RES_ERROR_MISSING_CONSTRAINT, getData());
+ case ResolverError.FRAGMENT_CONFLICT :
+ return NLS.bind(StateMsg.RES_ERROR_FRAGMENT_CONFLICT, getData());
+ case ResolverError.IMPORT_PACKAGE_USES_CONFLICT :
+ case ResolverError.REQUIRE_BUNDLE_USES_CONFLICT :
+ return NLS.bind(StateMsg.RES_ERROR_USES_CONFLICT, getData());
+ case ResolverError.SINGLETON_SELECTION :
+ return NLS.bind(StateMsg.RES_ERROR_SINGLETON_CONFLICT, getData());
+ case ResolverError.PLATFORM_FILTER :
+ return NLS.bind(StateMsg.RES_ERROR_PLATFORM_FILTER, getData());
+ case ResolverError.NO_NATIVECODE_MATCH :
+ return NLS.bind(StateMsg.RES_ERROR_NO_NATIVECODE_MATCH, getData());
+ case ResolverError.INVALID_NATIVECODE_PATHS :
+ return NLS.bind(StateMsg.RES_ERROR_NATIVECODE_PATH_INVALID, getData());
+ case ResolverError.DISABLED_BUNDLE :
+ return NLS.bind(StateMsg.RES_ERROR_DISABLEDBUNDLE, getData());
+ default :
+ return StateMsg.RES_ERROR_UNKNOWN;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateBuilder.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
new file mode 100644
index 000000000..332eedb48
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
@@ -0,0 +1,913 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.internal.framework.FilterImpl;
+
+import java.lang.reflect.Constructor;
+import java.util.*;
+import org.eclipse.osgi.framework.internal.core.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Namespace;
+
+/**
+ * This class builds bundle description objects from manifests
+ */
+public class StateBuilder {
+ private static final String[] DEFINED_EXPORT_PACKAGE_DIRECTIVES = {Constants.USES_DIRECTIVE, Constants.INCLUDE_DIRECTIVE, Constants.EXCLUDE_DIRECTIVE, StateImpl.FRIENDS_DIRECTIVE, StateImpl.INTERNAL_DIRECTIVE, Constants.MANDATORY_DIRECTIVE};
+ private static final String[] DEFINED_IMPORT_PACKAGE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE};
+ private static final String[] DEFINED_PACKAGE_MATCHING_ATTRS = {Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION, Constants.VERSION_ATTRIBUTE};
+ private static final String[] DEFINED_REQUIRE_BUNDLE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.VISIBILITY_DIRECTIVE};
+ private static final String[] DEFINED_FRAGMENT_HOST_DIRECTIVES = {Constants.EXTENSION_DIRECTIVE};
+ static final String[] DEFINED_BSN_DIRECTIVES = {Constants.SINGLETON_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.MANDATORY_DIRECTIVE};
+ static final String[] DEFINED_BSN_MATCHING_ATTRS = {Constants.BUNDLE_VERSION_ATTRIBUTE, StateImpl.OPTIONAL_ATTRIBUTE, StateImpl.REPROVIDE_ATTRIBUTE};
+ private static final String[] DEFINED_REQUIRE_CAPABILITY_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.FILTER_DIRECTIVE, Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE};
+ private static final String[] DEFINED_REQUIRE_CAPABILITY_ATTRS = {};
+ private static final String[] DEFINED_OSGI_VALIDATE_HEADERS = {Constants.IMPORT_PACKAGE, Constants.DYNAMICIMPORT_PACKAGE, Constants.EXPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.BUNDLE_SYMBOLICNAME, Constants.REQUIRE_BUNDLE};
+ static final String GENERIC_REQUIRE = "Eclipse-GenericRequire"; //$NON-NLS-1$
+ static final String GENERIC_CAPABILITY = "Eclipse-GenericCapability"; //$NON-NLS-1$
+
+ private static final String ATTR_TYPE_STRING = "string"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_VERSION = "version"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_URI = "uri"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_LONG = "long"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_DOUBLE = "double"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_SET = "set"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_LIST = "List"; //$NON-NLS-1$
+ private static final String OPTIONAL_ATTR = "optional"; //$NON-NLS-1$
+ private static final String MULTIPLE_ATTR = "multiple"; //$NON-NLS-1$
+ private static final String TRUE = "true"; //$NON-NLS-1$
+
+ static BundleDescription createBundleDescription(StateImpl state, Dictionary<String, String> manifest, String location) throws BundleException {
+ BundleDescriptionImpl result = new BundleDescriptionImpl();
+ String manifestVersionHeader = manifest.get(Constants.BUNDLE_MANIFESTVERSION);
+ boolean jreBundle = "true".equals(manifest.get(StateImpl.Eclipse_JREBUNDLE)); //$NON-NLS-1$
+ int manifestVersion = 1;
+ if (manifestVersionHeader != null)
+ manifestVersion = Integer.parseInt(manifestVersionHeader);
+ if (manifestVersion >= 2)
+ validateHeaders(manifest, jreBundle);
+
+ // retrieve the symbolic-name and the singleton status
+ String symbolicNameHeader = manifest.get(Constants.BUNDLE_SYMBOLICNAME);
+ if (symbolicNameHeader != null) {
+ ManifestElement[] symbolicNameElements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicNameHeader);
+ if (symbolicNameElements.length > 0) {
+ ManifestElement bsnElement = symbolicNameElements[0];
+ result.setSymbolicName(bsnElement.getValue());
+ String singleton = bsnElement.getDirective(Constants.SINGLETON_DIRECTIVE);
+ if (singleton == null) // TODO this is for backward compatibility; need to check manifest version < 2 to allow this after everyone has converted to new syntax
+ singleton = bsnElement.getAttribute(Constants.SINGLETON_DIRECTIVE);
+ result.setStateBit(BundleDescriptionImpl.SINGLETON, "true".equals(singleton)); //$NON-NLS-1$
+ String fragmentAttachment = bsnElement.getDirective(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+ if (fragmentAttachment != null) {
+ if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_RESOLVETIME)) {
+ result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, true);
+ result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false);
+ } else if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
+ result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, false);
+ result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false);
+ }
+ }
+ result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(bsnElement.getDirective(Constants.MANDATORY_DIRECTIVE)));
+ result.setAttributes(getAttributes(bsnElement, DEFINED_BSN_MATCHING_ATTRS));
+ result.setArbitraryDirectives(getDirectives(bsnElement, DEFINED_BSN_DIRECTIVES));
+ }
+ }
+ // retrieve other headers
+ String version = manifest.get(Constants.BUNDLE_VERSION);
+ try {
+ result.setVersion((version != null) ? Version.parseVersion(version) : Version.emptyVersion);
+ } catch (IllegalArgumentException ex) {
+ if (manifestVersion >= 2) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_VERSION, version);
+ throw new BundleException(message + " : " + ex.getMessage(), BundleException.MANIFEST_ERROR, ex); //$NON-NLS-1$
+ }
+ // prior to R4 the Bundle-Version header was not interpreted by the Framework;
+ // must not fail for old R3 style bundles
+ }
+ result.setLocation(location);
+ result.setPlatformFilter(manifest.get(StateImpl.ECLIPSE_PLATFORMFILTER));
+ String[] brees = ManifestElement.getArrayFromList(manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));
+ result.setExecutionEnvironments(brees);
+ ManifestElement[] host = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, manifest.get(Constants.FRAGMENT_HOST));
+ if (host != null)
+ result.setHost(createHostSpecification(host[0], state));
+ ManifestElement[] exports = ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, manifest.get(Constants.EXPORT_PACKAGE));
+ ManifestElement[] provides = ManifestElement.parseHeader(StateImpl.PROVIDE_PACKAGE, manifest.get(StateImpl.PROVIDE_PACKAGE));
+ boolean strict = state != null && state.inStrictMode();
+ List<String> providedExports = new ArrayList<String>(provides == null ? 0 : provides.length);
+ result.setExportPackages(createExportPackages(exports, provides, providedExports, strict));
+ ManifestElement[] imports = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, manifest.get(Constants.IMPORT_PACKAGE));
+ ManifestElement[] dynamicImports = ManifestElement.parseHeader(Constants.DYNAMICIMPORT_PACKAGE, manifest.get(Constants.DYNAMICIMPORT_PACKAGE));
+ result.setImportPackages(createImportPackages(result.getExportPackages(), providedExports, imports, dynamicImports, manifestVersion));
+ ManifestElement[] requires = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, manifest.get(Constants.REQUIRE_BUNDLE));
+ result.setRequiredBundles(createRequiredBundles(requires));
+ String[][] genericAliases = getGenericAliases(state);
+ ManifestElement[] genericRequires = getGenericRequires(manifest, genericAliases);
+ ManifestElement[] osgiRequires = ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, manifest.get(Constants.REQUIRE_CAPABILITY));
+ result.setGenericRequires(createGenericRequires(genericRequires, osgiRequires, brees));
+ ManifestElement[] genericCapabilities = getGenericCapabilities(manifest, genericAliases);
+ ManifestElement[] osgiCapabilities = ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, manifest.get(Constants.PROVIDE_CAPABILITY));
+ result.setGenericCapabilities(createGenericCapabilities(genericCapabilities, osgiCapabilities, result));
+ ManifestElement[] nativeCode = ManifestElement.parseHeader(Constants.BUNDLE_NATIVECODE, manifest.get(Constants.BUNDLE_NATIVECODE));
+ result.setNativeCodeSpecification(createNativeCode(nativeCode));
+ return result;
+ }
+
+ private static ManifestElement[] getGenericRequires(Dictionary<String, String> manifest, String[][] genericAliases) throws BundleException {
+ ManifestElement[] genericRequires = ManifestElement.parseHeader(GENERIC_REQUIRE, manifest.get(GENERIC_REQUIRE));
+ List<ManifestElement> aliasList = null;
+ if (genericAliases.length > 0) {
+ aliasList = new ArrayList<ManifestElement>(genericRequires == null ? 0 : genericRequires.length);
+ for (int i = 0; i < genericAliases.length; i++) {
+ ManifestElement[] aliasReqs = ManifestElement.parseHeader(genericAliases[i][1], manifest.get(genericAliases[i][1]));
+ if (aliasReqs == null)
+ continue;
+ for (int j = 0; j < aliasReqs.length; j++) {
+ StringBuffer strBuf = new StringBuffer();
+ strBuf.append(aliasReqs[j].getValue()).append(':').append(genericAliases[i][2]);
+ String filter = aliasReqs[j].getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE);
+ if (filter != null)
+ strBuf.append("; ").append(Constants.SELECTION_FILTER_ATTRIBUTE).append(filter).append("=\"").append(filter).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ ManifestElement[] withType = ManifestElement.parseHeader(genericAliases[i][1], strBuf.toString());
+ aliasList.add(withType[0]);
+ }
+ }
+ }
+ if (aliasList == null || aliasList.size() == 0)
+ return genericRequires;
+ if (genericRequires != null)
+ for (int i = 0; i < genericRequires.length; i++)
+ aliasList.add(genericRequires[i]);
+ return aliasList.toArray(new ManifestElement[aliasList.size()]);
+ }
+
+ private static ManifestElement[] getGenericCapabilities(Dictionary<String, String> manifest, String[][] genericAliases) throws BundleException {
+ ManifestElement[] genericCapabilities = ManifestElement.parseHeader(GENERIC_CAPABILITY, manifest.get(GENERIC_CAPABILITY));
+ List<ManifestElement> aliasList = null;
+ if (genericAliases.length > 0) {
+ aliasList = new ArrayList<ManifestElement>(genericCapabilities == null ? 0 : genericCapabilities.length);
+ for (int i = 0; i < genericAliases.length; i++) {
+ ManifestElement[] aliasCapabilities = ManifestElement.parseHeader(genericAliases[i][0], manifest.get(genericAliases[i][0]));
+ if (aliasCapabilities == null)
+ continue;
+ for (int j = 0; j < aliasCapabilities.length; j++) {
+ StringBuffer strBuf = new StringBuffer();
+ strBuf.append(aliasCapabilities[j].getValue()).append(':').append(genericAliases[i][2]);
+ for (Enumeration<String> keys = aliasCapabilities[j].getKeys(); keys != null && keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ strBuf.append("; ").append(key).append("=\"").append(aliasCapabilities[j].getAttribute(key)).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ ManifestElement[] withTypes = ManifestElement.parseHeader(genericAliases[i][0], strBuf.toString());
+ aliasList.add(withTypes[0]);
+ }
+ }
+ }
+ if (aliasList == null || aliasList.size() == 0)
+ return genericCapabilities;
+ if (genericCapabilities != null)
+ for (int i = 0; i < genericCapabilities.length; i++)
+ aliasList.add(genericCapabilities[i]);
+ return aliasList.toArray(new ManifestElement[aliasList.size()]);
+ }
+
+ private static String[][] getGenericAliases(StateImpl state) {
+ String genericAliasesProp = getPlatformProperty(state, "osgi.genericAliases"); //$NON-NLS-1$
+ if (genericAliasesProp == null)
+ return new String[0][0];
+ String[] aliases = ManifestElement.getArrayFromList(genericAliasesProp, ","); //$NON-NLS-1$
+ String[][] result = new String[aliases.length][];
+ for (int i = 0; i < aliases.length; i++)
+ result[i] = ManifestElement.getArrayFromList(aliases[i], ":"); //$NON-NLS-1$
+ return result;
+ }
+
+ private static String getPlatformProperty(State state, String key) {
+ Dictionary<Object, Object>[] platformProps = state == null ? null : state.getPlatformProperties();
+ return platformProps == null || platformProps.length == 0 ? null : (String) platformProps[0].get(key);
+ }
+
+ private static void validateHeaders(Dictionary<String, String> manifest, boolean jreBundle) throws BundleException {
+ for (int i = 0; i < DEFINED_OSGI_VALIDATE_HEADERS.length; i++) {
+ String header = manifest.get(DEFINED_OSGI_VALIDATE_HEADERS[i]);
+ if (header != null) {
+ ManifestElement[] elements = ManifestElement.parseHeader(DEFINED_OSGI_VALIDATE_HEADERS[i], header);
+ checkForDuplicateDirectivesAttributes(DEFINED_OSGI_VALIDATE_HEADERS[i], elements);
+ if (DEFINED_OSGI_VALIDATE_HEADERS[i] == Constants.IMPORT_PACKAGE)
+ checkImportExportSyntax(DEFINED_OSGI_VALIDATE_HEADERS[i], elements, false, false, jreBundle);
+ if (DEFINED_OSGI_VALIDATE_HEADERS[i] == Constants.DYNAMICIMPORT_PACKAGE)
+ checkImportExportSyntax(DEFINED_OSGI_VALIDATE_HEADERS[i], elements, false, true, jreBundle);
+ if (DEFINED_OSGI_VALIDATE_HEADERS[i] == Constants.EXPORT_PACKAGE)
+ checkImportExportSyntax(DEFINED_OSGI_VALIDATE_HEADERS[i], elements, true, false, jreBundle);
+ if (DEFINED_OSGI_VALIDATE_HEADERS[i] == Constants.FRAGMENT_HOST)
+ checkExtensionBundle(DEFINED_OSGI_VALIDATE_HEADERS[i], elements);
+ } else if (DEFINED_OSGI_VALIDATE_HEADERS[i] == Constants.BUNDLE_SYMBOLICNAME) {
+ throw new BundleException(NLS.bind(StateMsg.HEADER_REQUIRED, Constants.BUNDLE_SYMBOLICNAME), BundleException.MANIFEST_ERROR);
+ }
+ }
+ }
+
+ private static BundleSpecification[] createRequiredBundles(ManifestElement[] specs) {
+ if (specs == null)
+ return null;
+ BundleSpecification[] result = new BundleSpecification[specs.length];
+ for (int i = 0; i < specs.length; i++)
+ result[i] = createRequiredBundle(specs[i]);
+ return result;
+ }
+
+ static BundleSpecification createRequiredBundle(ManifestElement spec) {
+ BundleSpecificationImpl result = new BundleSpecificationImpl();
+ result.setName(spec.getValue());
+ result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
+ result.setExported(Constants.VISIBILITY_REEXPORT.equals(spec.getDirective(Constants.VISIBILITY_DIRECTIVE)) || "true".equals(spec.getAttribute(StateImpl.REPROVIDE_ATTRIBUTE))); //$NON-NLS-1$
+ result.setOptional(Constants.RESOLUTION_OPTIONAL.equals(spec.getDirective(Constants.RESOLUTION_DIRECTIVE)) || "true".equals(spec.getAttribute(StateImpl.OPTIONAL_ATTRIBUTE))); //$NON-NLS-1$
+ result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS));
+ result.setArbitraryDirectives(getDirectives(spec, DEFINED_REQUIRE_BUNDLE_DIRECTIVES));
+ return result;
+ }
+
+ private static ImportPackageSpecification[] createImportPackages(ExportPackageDescription[] exported, List<String> providedExports, ManifestElement[] imported, ManifestElement[] dynamicImported, int manifestVersion) {
+ List<ImportPackageSpecification> allImports = null;
+ if (manifestVersion < 2) {
+ // add implicit imports for each exported package if manifest verions is less than 2.
+ if (exported.length == 0 && imported == null && dynamicImported == null)
+ return null;
+ allImports = new ArrayList<ImportPackageSpecification>(exported.length + (imported == null ? 0 : imported.length));
+ for (int i = 0; i < exported.length; i++) {
+ if (providedExports.contains(exported[i].getName()))
+ continue;
+ ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
+ result.setName(exported[i].getName());
+ result.setVersionRange(getVersionRange(exported[i].getVersion().toString()));
+ result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_STATIC);
+ allImports.add(result);
+ }
+ } else {
+ allImports = new ArrayList<ImportPackageSpecification>(imported == null ? 0 : imported.length);
+ }
+
+ // add dynamics first so they will get overriden by static imports if
+ // the same package is dyanamically imported and statically imported.
+ if (dynamicImported != null)
+ for (int i = 0; i < dynamicImported.length; i++)
+ addImportPackages(dynamicImported[i], allImports, manifestVersion, true);
+ if (imported != null)
+ for (int i = 0; i < imported.length; i++)
+ addImportPackages(imported[i], allImports, manifestVersion, false);
+ return allImports.toArray(new ImportPackageSpecification[allImports.size()]);
+ }
+
+ public static void addImportPackages(ManifestElement importPackage, List<ImportPackageSpecification> allImports, int manifestVersion, boolean dynamic) {
+ String[] importNames = importPackage.getValueComponents();
+ for (int i = 0; i < importNames.length; i++) {
+ // do not allow for multiple imports of same package of manifest version < 2
+ if (manifestVersion < 2) {
+ Iterator<ImportPackageSpecification> iter = allImports.iterator();
+ while (iter.hasNext())
+ if (importNames[i].equals(iter.next().getName()))
+ iter.remove();
+ }
+
+ ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
+ result.setName(importNames[i]);
+ // set common attributes for both dynamic and static imports
+ String versionString = importPackage.getAttribute(Constants.VERSION_ATTRIBUTE);
+ if (versionString == null) // specification-version aliases to version
+ versionString = importPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
+ result.setVersionRange(getVersionRange(versionString));
+ result.setBundleSymbolicName(importPackage.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
+ result.setBundleVersionRange(getVersionRange(importPackage.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
+ // only set the matching attributes if manifest version >= 2
+ if (manifestVersion >= 2)
+ result.setAttributes(getAttributes(importPackage, DEFINED_PACKAGE_MATCHING_ATTRS));
+
+ if (dynamic)
+ result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_DYNAMIC);
+ else
+ result.setDirective(Constants.RESOLUTION_DIRECTIVE, getResolution(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE)));
+ result.setArbitraryDirectives(getDirectives(importPackage, DEFINED_IMPORT_PACKAGE_DIRECTIVES));
+
+ allImports.add(result);
+ }
+ }
+
+ private static String getResolution(String resolution) {
+ String result = ImportPackageSpecification.RESOLUTION_STATIC;
+ if (Constants.RESOLUTION_OPTIONAL.equals(resolution) || ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(resolution))
+ result = resolution;
+ return result;
+ }
+
+ static ExportPackageDescription[] createExportPackages(ManifestElement[] exported, ManifestElement[] provides, List<String> providedExports, boolean strict) {
+ int numExports = (exported == null ? 0 : exported.length) + (provides == null ? 0 : provides.length);
+ if (numExports == 0)
+ return null;
+ List<ExportPackageDescription> allExports = new ArrayList<ExportPackageDescription>(numExports);
+ if (exported != null)
+ for (int i = 0; i < exported.length; i++)
+ addExportPackages(exported[i], allExports, strict);
+ if (provides != null)
+ addProvidePackages(provides, allExports, providedExports);
+ return allExports.toArray(new ExportPackageDescription[allExports.size()]);
+ }
+
+ static void addExportPackages(ManifestElement exportPackage, List<ExportPackageDescription> allExports, boolean strict) {
+ String[] exportNames = exportPackage.getValueComponents();
+ for (int i = 0; i < exportNames.length; i++) {
+ // if we are in strict mode and the package is marked as internal, skip it.
+ if (strict && "true".equals(exportPackage.getDirective(StateImpl.INTERNAL_DIRECTIVE))) //$NON-NLS-1$
+ continue;
+ ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl();
+ result.setName(exportNames[i]);
+ String versionString = exportPackage.getAttribute(Constants.VERSION_ATTRIBUTE);
+ if (versionString == null) // specification-version aliases to version
+ versionString = exportPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if (versionString != null)
+ result.setVersion(Version.parseVersion(versionString));
+ result.setDirective(Constants.USES_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.USES_DIRECTIVE)));
+ result.setDirective(Constants.INCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.INCLUDE_DIRECTIVE));
+ result.setDirective(Constants.EXCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.EXCLUDE_DIRECTIVE));
+ result.setDirective(StateImpl.FRIENDS_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(StateImpl.FRIENDS_DIRECTIVE)));
+ result.setDirective(StateImpl.INTERNAL_DIRECTIVE, Boolean.valueOf(exportPackage.getDirective(StateImpl.INTERNAL_DIRECTIVE)));
+ result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.MANDATORY_DIRECTIVE)));
+ result.setAttributes(getAttributes(exportPackage, DEFINED_PACKAGE_MATCHING_ATTRS));
+ result.setArbitraryDirectives(getDirectives(exportPackage, DEFINED_EXPORT_PACKAGE_DIRECTIVES));
+ allExports.add(result);
+ }
+ }
+
+ private static void addProvidePackages(ManifestElement[] provides, List<ExportPackageDescription> allExports, List<String> providedExports) {
+ ExportPackageDescription[] currentExports = allExports.toArray(new ExportPackageDescription[allExports.size()]);
+ for (int i = 0; i < provides.length; i++) {
+ boolean duplicate = false;
+ for (int j = 0; j < currentExports.length; j++)
+ if (provides[i].getValue().equals(currentExports[j].getName())) {
+ duplicate = true;
+ break;
+ }
+ if (!duplicate) {
+ ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl();
+ result.setName(provides[i].getValue());
+ allExports.add(result);
+ }
+ providedExports.add(provides[i].getValue());
+ }
+ }
+
+ static Map<String, String> getDirectives(ManifestElement element, String[] definedDirectives) {
+ Enumeration<String> keys = element.getDirectiveKeys();
+ if (keys == null)
+ return null;
+ Map<String, String> arbitraryDirectives = null;
+ keyloop: while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ for (String definedDirective : definedDirectives) {
+ if (definedDirective.equals(key))
+ continue keyloop;
+ }
+ if (arbitraryDirectives == null)
+ arbitraryDirectives = new HashMap<String, String>();
+ arbitraryDirectives.put(key, element.getDirective(key));
+ }
+ return arbitraryDirectives;
+ }
+
+ static Map<String, Object> getAttributes(ManifestElement element, String[] definedAttrs) {
+ Enumeration<String> keys = element.getKeys();
+ Map<String, Object> arbitraryAttrs = null;
+ if (keys == null)
+ return null;
+ while (keys.hasMoreElements()) {
+ boolean definedAttr = false;
+ String key = keys.nextElement();
+ for (int i = 0; i < definedAttrs.length; i++) {
+ if (definedAttrs[i].equals(key)) {
+ definedAttr = true;
+ break;
+ }
+ }
+ String value = element.getAttribute(key);
+ int colonIndex = key.indexOf(':');
+ String type = ATTR_TYPE_STRING;
+ if (colonIndex > 0) {
+ type = key.substring(colonIndex + 1).trim();
+ key = key.substring(0, colonIndex).trim();
+ }
+ if (!definedAttr) {
+ if (arbitraryAttrs == null)
+ arbitraryAttrs = new HashMap<String, Object>();
+ arbitraryAttrs.put(key, convertValue(type, value));
+ }
+ }
+ return arbitraryAttrs;
+ }
+
+ private static Object convertValue(String type, String value) {
+
+ if (ATTR_TYPE_STRING.equalsIgnoreCase(type))
+ return value;
+
+ String trimmed = value.trim();
+ if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type))
+ return new Double(trimmed);
+ else if (ATTR_TYPE_LONG.equalsIgnoreCase(type))
+ return new Long(trimmed);
+ else if (ATTR_TYPE_URI.equalsIgnoreCase(type))
+ try {
+ Class<?> uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$
+ Constructor<?> constructor = uriClazz.getConstructor(new Class[] {String.class});
+ return constructor.newInstance(new Object[] {trimmed});
+ } catch (ClassNotFoundException e) {
+ // oh well cannot support; just use string
+ return value;
+ } catch (RuntimeException e) { // got some reflection exception
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ else if (ATTR_TYPE_VERSION.equalsIgnoreCase(type))
+ return new Version(trimmed);
+ else if (ATTR_TYPE_SET.equalsIgnoreCase(type))
+ return ManifestElement.getArrayFromList(trimmed, ","); //$NON-NLS-1$
+
+ // assume list type, anything else will throw an exception
+ Tokenizer listTokenizer = new Tokenizer(type);
+ String listType = listTokenizer.getToken("<"); //$NON-NLS-1$
+ if (!ATTR_TYPE_LIST.equalsIgnoreCase(listType))
+ throw new RuntimeException("Unsupported type: " + type); //$NON-NLS-1$
+ char c = listTokenizer.getChar();
+ String componentType = ATTR_TYPE_STRING;
+ if (c == '<') {
+ componentType = listTokenizer.getToken(">"); //$NON-NLS-1$
+ if (listTokenizer.getChar() != '>')
+ throw new RuntimeException("Invalid type, missing ending '>' : " + type); //$NON-NLS-1$
+ }
+ List<String> tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$
+ List<Object> components = new ArrayList<Object>();
+ for (String component : tokens) {
+ components.add(convertValue(componentType, component));
+ }
+ return components;
+ }
+
+ static HostSpecification createHostSpecification(ManifestElement spec, State state) {
+ if (spec == null)
+ return null;
+ HostSpecificationImpl result = new HostSpecificationImpl();
+ result.setName(spec.getValue());
+ result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
+ String multiple = spec.getDirective("multiple-hosts"); //$NON-NLS-1$
+ if (multiple == null)
+ multiple = getPlatformProperty(state, "osgi.support.multipleHosts"); //$NON-NLS-1$
+ result.setIsMultiHost("true".equals(multiple)); //$NON-NLS-1$
+ result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS));
+ result.setArbitraryDirectives(getDirectives(spec, DEFINED_FRAGMENT_HOST_DIRECTIVES));
+ return result;
+ }
+
+ private static GenericSpecification[] createGenericRequires(ManifestElement[] equinoxRequires, ManifestElement[] osgiRequires, String[] brees) throws BundleException {
+ List<GenericSpecification> result = createEquinoxRequires(equinoxRequires);
+ result = createOSGiRequires(osgiRequires, result);
+ result = convertBREEs(brees, result);
+ return result == null ? null : result.toArray(new GenericSpecification[result.size()]);
+ }
+
+ static List<GenericSpecification> convertBREEs(String[] brees, List<GenericSpecification> result) throws BundleException {
+ if (brees == null || brees.length == 0)
+ return result;
+ if (result == null)
+ result = new ArrayList<GenericSpecification>(brees.length);
+ List<String> breeFilters = new ArrayList<String>();
+ for (String bree : brees)
+ breeFilters.add(createOSGiEERequirementFilter(bree));
+ String filterSpec;
+ if (breeFilters.size() == 1) {
+ filterSpec = breeFilters.get(0);
+ } else {
+ StringBuffer filterBuf = new StringBuffer("(|"); //$NON-NLS-1$
+ for (String breeFilter : breeFilters) {
+ filterBuf.append(breeFilter);
+ }
+ filterSpec = filterBuf.append(")").toString(); //$NON-NLS-1$
+ }
+ GenericSpecificationImpl spec = new GenericSpecificationImpl();
+ spec.setResolution(GenericSpecificationImpl.RESOLUTION_FROM_BREE);
+ spec.setType(StateImpl.OSGI_EE_NAMESPACE);
+ try {
+ FilterImpl filter = FilterImpl.newInstance(filterSpec);
+ spec.setMatchingFilter(filter);
+ String name = filter.getPrimaryKeyValue(spec.getType());
+ if (name != null)
+ spec.setName(name);
+ } catch (InvalidSyntaxException e) {
+ throw new BundleException("Error converting required execution environment.", e); //$NON-NLS-1$
+ }
+ result.add(spec);
+ return result;
+ }
+
+ private static String createOSGiEERequirementFilter(String bree) throws BundleException {
+ String[] nameVersion = getOSGiEENameVersion(bree);
+ String eeName = nameVersion[0];
+ String v = nameVersion[1];
+ String filterSpec;
+ if (v == null)
+ filterSpec = "(osgi.ee=" + eeName + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ else
+ filterSpec = "(&(osgi.ee=" + eeName + ")(version=" + v + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ try {
+ // do a sanity check
+ FilterImpl.newInstance(filterSpec);
+ } catch (InvalidSyntaxException e) {
+ filterSpec = "(osgi.ee=" + bree + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ try {
+ // do another sanity check
+ FilterImpl.newInstance(filterSpec);
+ } catch (InvalidSyntaxException e1) {
+ throw new BundleException("Error converting required execution environment.", e1); //$NON-NLS-1$
+ }
+ }
+ return filterSpec;
+ }
+
+ static String[] getOSGiEENameVersion(String bree) {
+ String ee1 = null;
+ String ee2 = null;
+ String v1 = null;
+ String v2 = null;
+ int separator = bree.indexOf('/');
+ if (separator <= 0 || separator == bree.length() - 1) {
+ ee1 = bree;
+ } else {
+ ee1 = bree.substring(0, separator);
+ ee2 = bree.substring(separator + 1);
+ }
+ int v1idx = ee1.indexOf('-');
+ if (v1idx > 0 && v1idx < ee1.length() - 1) {
+ // check for > 0 to avoid EEs starting with -
+ // check for < len - 1 to avoid ending with -
+ try {
+ v1 = ee1.substring(v1idx + 1);
+ // sanity check version format
+ Version.parseVersion(v1);
+ ee1 = ee1.substring(0, v1idx);
+ } catch (IllegalArgumentException e) {
+ v1 = null;
+ }
+ }
+
+ int v2idx = ee2 == null ? -1 : ee2.indexOf('-');
+ if (v2idx > 0 && v2idx < ee2.length() - 1) {
+ // check for > 0 to avoid EEs starting with -
+ // check for < len - 1 to avoid ending with -
+ try {
+ v2 = ee2.substring(v2idx + 1);
+ Version.parseVersion(v2);
+ ee2 = ee2.substring(0, v2idx);
+ } catch (IllegalArgumentException e) {
+ v2 = null;
+ }
+ }
+
+ if (v1 == null)
+ v1 = v2;
+ if (v1 != null && v2 != null && !v1.equals(v2)) {
+ ee1 = bree;
+ ee2 = null;
+ v1 = null;
+ v2 = null;
+ }
+ if ("J2SE".equals(ee1)) //$NON-NLS-1$
+ ee1 = "JavaSE"; //$NON-NLS-1$
+ if ("J2SE".equals(ee2)) //$NON-NLS-1$
+ ee2 = "JavaSE"; //$NON-NLS-1$
+
+ String eeName = ee1 + (ee2 == null ? "" : '/' + ee2); //$NON-NLS-1$
+
+ return new String[] {eeName, v1};
+ }
+
+ static List<GenericSpecification> createOSGiRequires(ManifestElement[] osgiRequires, List<GenericSpecification> result) throws BundleException {
+ if (osgiRequires == null)
+ return result;
+ if (result == null)
+ result = new ArrayList<GenericSpecification>();
+ for (ManifestElement element : osgiRequires) {
+ String[] namespaces = element.getValueComponents();
+ for (String namespace : namespaces) {
+ GenericSpecificationImpl spec = new GenericSpecificationImpl();
+ spec.setType(namespace);
+ String filterSpec = element.getDirective(Constants.FILTER_DIRECTIVE);
+ if (filterSpec != null) {
+ try {
+ FilterImpl filter = FilterImpl.newInstance(filterSpec);
+ spec.setMatchingFilter(filter);
+ String name = filter.getPrimaryKeyValue(namespace);
+ if (name != null)
+ spec.setName(name);
+ } catch (InvalidSyntaxException e) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.REQUIRE_CAPABILITY, element.toString());
+ throw new BundleException(message + " : filter", BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
+ }
+ }
+ String resolutionDirective = element.getDirective(Constants.RESOLUTION_DIRECTIVE);
+ int resolution = 0;
+ if (Constants.RESOLUTION_OPTIONAL.equals(resolutionDirective))
+ resolution |= GenericSpecification.RESOLUTION_OPTIONAL;
+ String cardinality = element.getDirective(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE);
+ if (Namespace.CARDINALITY_MULTIPLE.equals(cardinality))
+ resolution |= GenericSpecification.RESOLUTION_MULTIPLE;
+ spec.setResolution(resolution);
+ spec.setAttributes(getAttributes(element, DEFINED_REQUIRE_CAPABILITY_ATTRS));
+ spec.setArbitraryDirectives(getDirectives(element, DEFINED_REQUIRE_CAPABILITY_DIRECTIVES));
+ result.add(spec);
+ }
+ }
+ return result;
+ }
+
+ private static List<GenericSpecification> createEquinoxRequires(ManifestElement[] equinoxRequires) throws BundleException {
+ if (equinoxRequires == null)
+ return null;
+ ArrayList<GenericSpecification> results = new ArrayList<GenericSpecification>(equinoxRequires.length);
+ for (int i = 0; i < equinoxRequires.length; i++) {
+ String[] genericNames = equinoxRequires[i].getValueComponents();
+ for (int j = 0; j < genericNames.length; j++) {
+ GenericSpecificationImpl spec = new GenericSpecificationImpl();
+ int colonIdx = genericNames[j].indexOf(':');
+ if (colonIdx > 0) {
+ spec.setName(genericNames[j].substring(0, colonIdx));
+ spec.setType(genericNames[j].substring(colonIdx + 1));
+ } else
+ spec.setName(genericNames[j]);
+ try {
+ spec.setMatchingFilter(equinoxRequires[i].getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE), true);
+ } catch (InvalidSyntaxException e) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, GENERIC_REQUIRE, equinoxRequires[i].toString());
+ throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
+ }
+ String optional = equinoxRequires[i].getAttribute(OPTIONAL_ATTR);
+ String multiple = equinoxRequires[i].getAttribute(MULTIPLE_ATTR);
+ int resolution = 0;
+ if (TRUE.equals(optional))
+ resolution |= GenericSpecification.RESOLUTION_OPTIONAL;
+ if (TRUE.equals(multiple))
+ resolution |= GenericSpecification.RESOLUTION_MULTIPLE;
+ spec.setResolution(resolution);
+ results.add(spec);
+ }
+ }
+ return results;
+ }
+
+ private static GenericDescription[] createGenericCapabilities(ManifestElement[] equinoxCapabilities, ManifestElement[] osgiCapabilities, BundleDescription description) throws BundleException {
+ List<GenericDescription> result = createEquinoxCapabilities(equinoxCapabilities);
+ result = createOSGiCapabilities(osgiCapabilities, result, description);
+ return result == null ? null : result.toArray(new GenericDescription[result.size()]);
+ }
+
+ static List<GenericDescription> createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, BundleDescription description) throws BundleException {
+ if (result == null)
+ result = new ArrayList<GenericDescription>(osgiCapabilities == null ? 1 : osgiCapabilities.length + 1);
+ // Always have an osgi.identity capability if there is a symbolic name.
+ GenericDescription osgiIdentity = createOsgiIdentityCapability(description);
+ if (osgiIdentity != null)
+ // always add the capability to the front
+ result.add(0, osgiIdentity);
+ return createOSGiCapabilities(osgiCapabilities, result, (Integer) null);
+ }
+
+ static List<GenericDescription> createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, Integer profileIndex) throws BundleException {
+ if (osgiCapabilities == null)
+ return result;
+ if (result == null)
+ result = new ArrayList<GenericDescription>(osgiCapabilities.length);
+
+ for (ManifestElement element : osgiCapabilities) {
+ String[] namespaces = element.getValueComponents();
+ for (String namespace : namespaces) {
+ if (IdentityNamespace.IDENTITY_NAMESPACE.equals(namespace))
+ throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$
+
+ GenericDescriptionImpl desc = new GenericDescriptionImpl();
+ desc.setType(namespace);
+ Map<String, Object> mapAttrs = getAttributes(element, new String[0]);
+ if (profileIndex != null)
+ mapAttrs.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex);
+ Dictionary<String, Object> attrs = mapAttrs == null ? new Hashtable<String, Object>() : new Hashtable<String, Object>(mapAttrs);
+ desc.setAttributes(attrs);
+ Map<String, String> directives = new HashMap<String, String>();
+ Enumeration<String> keys = element.getDirectiveKeys();
+ if (keys != null)
+ for (keys = element.getDirectiveKeys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ directives.put(key, element.getDirective(key));
+ }
+ desc.setDirectives(directives);
+ result.add(desc);
+ }
+ }
+ return result;
+ }
+
+ private static List<GenericDescription> createEquinoxCapabilities(ManifestElement[] equinoxCapabilities) throws BundleException {
+ if (equinoxCapabilities == null)
+ return null;
+ ArrayList<GenericDescription> results = new ArrayList<GenericDescription>(equinoxCapabilities.length);
+ for (int i = 0; i < equinoxCapabilities.length; i++) {
+ String[] genericNames = equinoxCapabilities[i].getValueComponents();
+ for (int j = 0; j < genericNames.length; j++) {
+ GenericDescriptionImpl desc = new GenericDescriptionImpl();
+ String name = genericNames[j];
+ int colonIdx = genericNames[j].indexOf(':');
+ if (colonIdx > 0) {
+ name = genericNames[j].substring(0, colonIdx);
+ desc.setType(genericNames[j].substring(colonIdx + 1));
+ if (IdentityNamespace.IDENTITY_NAMESPACE.equals(desc.getType()))
+ throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ Map<String, Object> mapAttrs = getAttributes(equinoxCapabilities[i], new String[] {Constants.VERSION_ATTRIBUTE});
+ Dictionary<String, Object> attrs = mapAttrs == null ? new Hashtable<String, Object>() : new Hashtable<String, Object>(mapAttrs);
+ attrs.put(desc.getType(), name);
+ String versionString = equinoxCapabilities[i].getAttribute(Constants.VERSION_ATTRIBUTE);
+ if (versionString != null)
+ attrs.put(Constants.VERSION_ATTRIBUTE, Version.parseVersion(versionString));
+ desc.setAttributes(attrs);
+ results.add(desc);
+ }
+ }
+ return results;
+ }
+
+ private static NativeCodeSpecification createNativeCode(ManifestElement[] nativeCode) throws BundleException {
+ if (nativeCode == null)
+ return null;
+ NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl();
+ result.setName(Constants.BUNDLE_NATIVECODE);
+ int length = nativeCode.length;
+ if (length > 0 && nativeCode[length - 1].getValue().equals("*")) { //$NON-NLS-1$
+ result.setOptional(true);
+ length--;
+ }
+ NativeCodeDescriptionImpl[] suppliers = new NativeCodeDescriptionImpl[length];
+ for (int i = 0; i < length; i++) {
+ suppliers[i] = createNativeCodeDescription(nativeCode[i]);
+ }
+ result.setPossibleSuppliers(suppliers);
+ return result;
+ }
+
+ private static NativeCodeDescriptionImpl createNativeCodeDescription(ManifestElement manifestElement) throws BundleException {
+ NativeCodeDescriptionImpl result = new NativeCodeDescriptionImpl();
+ result.setName(Constants.BUNDLE_NATIVECODE);
+ result.setNativePaths(manifestElement.getValueComponents());
+ result.setOSNames(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_OSNAME));
+ result.setProcessors(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_PROCESSOR));
+ result.setOSVersions(createVersionRanges(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_OSVERSION)));
+ result.setLanguages(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_LANGUAGE));
+ try {
+ result.setFilter(manifestElement.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE));
+ } catch (InvalidSyntaxException e) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_NATIVECODE, manifestElement.toString());
+ throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
+ }
+ return result;
+ }
+
+ private static VersionRange[] createVersionRanges(String[] ranges) {
+ if (ranges == null)
+ return null;
+ VersionRange[] result = new VersionRange[ranges.length];
+ for (int i = 0; i < result.length; i++)
+ result[i] = new VersionRange(ranges[i]);
+ return result;
+ }
+
+ private static VersionRange getVersionRange(String versionRange) {
+ if (versionRange == null)
+ return null;
+ return new VersionRange(versionRange);
+ }
+
+ public static void checkImportExportSyntax(String headerKey, ManifestElement[] elements, boolean export, boolean dynamic, boolean jreBundle) throws BundleException {
+ if (elements == null)
+ return;
+ int length = elements.length;
+ Set<String> packages = new HashSet<String>(length);
+ for (int i = 0; i < length; i++) {
+ // check for duplicate imports
+ String[] packageNames = elements[i].getValueComponents();
+ for (int j = 0; j < packageNames.length; j++) {
+ if (!export && !dynamic && packages.contains(packageNames[j])) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
+ throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_DUPLICATES, packageNames[j]), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ // check for java.*
+ if (!jreBundle && packageNames[j].startsWith("java.")) { //$NON-NLS-1$
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
+ throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_JAVA, packageNames[j]), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ packages.add(packageNames[j]);
+ }
+ // check for version/specification version mismatch
+ String version = elements[i].getAttribute(Constants.VERSION_ATTRIBUTE);
+ if (version != null) {
+ String specVersion = elements[i].getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if (specVersion != null && !specVersion.equals(version))
+ throw new BundleException(NLS.bind(StateMsg.HEADER_VERSION_ERROR, Constants.VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION), BundleException.MANIFEST_ERROR);
+ }
+ // check for bundle-symbolic-name and bundle-verion attibures
+ // (failure)
+ if (export) {
+ if (elements[i].getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE) != null) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
+ throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ if (elements[i].getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) != null) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
+ throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ private static void checkForDuplicateDirectivesAttributes(String headerKey, ManifestElement[] elements) throws BundleException {
+ // check for duplicate directives
+ for (int i = 0; i < elements.length; i++) {
+ Enumeration<String> directiveKeys = elements[i].getDirectiveKeys();
+ if (directiveKeys != null) {
+ while (directiveKeys.hasMoreElements()) {
+ String key = directiveKeys.nextElement();
+ String[] directives = elements[i].getDirectives(key);
+ if (directives.length > 1) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
+ throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_DIRECTIVE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ }
+ }
+ Enumeration<String> attrKeys = elements[i].getKeys();
+ if (attrKeys != null) {
+ while (attrKeys.hasMoreElements()) {
+ String key = attrKeys.nextElement();
+ String[] attrs = elements[i].getAttributes(key);
+ if (attrs.length > 1) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString());
+ throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_ATTRIBUTE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ }
+
+ private static void checkExtensionBundle(String headerKey, ManifestElement[] elements) throws BundleException {
+ if (elements.length == 0 || elements[0].getDirective(Constants.EXTENSION_DIRECTIVE) == null)
+ return;
+ String hostName = elements[0].getValue();
+ // XXX: The extension bundle check is done against system.bundle and org.eclipse.osgi
+ if (!hostName.equals(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) && !hostName.equals(EquinoxContainer.NAME)) {
+ String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[0].toString());
+ throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXTENSION_ERROR, hostName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$
+ }
+ }
+
+ static GenericDescription createOsgiIdentityCapability(BundleDescription description) {
+ if (description.getSymbolicName() == null)
+ return null;
+ GenericDescriptionImpl result = new GenericDescriptionImpl();
+ result.setType(IdentityNamespace.IDENTITY_NAMESPACE);
+ Dictionary<String, Object> attributes = new Hashtable<String, Object>(description.getDeclaredAttributes());
+ // remove osgi.wiring.bundle and bundle-version attributes
+ attributes.remove(BundleNamespace.BUNDLE_NAMESPACE);
+ attributes.remove(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ attributes.put(IdentityNamespace.IDENTITY_NAMESPACE, description.getSymbolicName());
+ attributes.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, description.getHost() == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT);
+ attributes.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, description.getVersion());
+ result.setAttributes(attributes);
+ Map<String, String> directives = new HashMap<String, String>(description.getDeclaredDirectives());
+ // remove defaults directive values
+ if (!description.isSingleton())
+ directives.remove(Constants.SINGLETON_DIRECTIVE);
+ if (description.attachFragments() && description.dynamicFragments())
+ directives.remove(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+ result.setDirectives(directives);
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java
new file mode 100644
index 000000000..966a0224d
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+
+/**
+ * This class is threadsafe.
+ *
+ */
+final class StateDeltaImpl implements StateDelta {
+
+ private final State state;
+
+ private final Map<BundleDescription, BundleDelta> changes = new HashMap<BundleDescription, BundleDelta>();
+ private ResolverHookException error;
+
+ public StateDeltaImpl(State state) {
+ this.state = state;
+ }
+
+ public BundleDelta[] getChanges() {
+ synchronized (this.changes) {
+ return changes.values().toArray(new BundleDelta[changes.size()]);
+ }
+ }
+
+ public BundleDelta[] getChanges(int mask, boolean exact) {
+ synchronized (this.changes) {
+ List<BundleDelta> result = new ArrayList<BundleDelta>();
+ for (Iterator<BundleDelta> changesIter = changes.values().iterator(); changesIter.hasNext();) {
+ BundleDelta change = changesIter.next();
+ if (mask == change.getType() || (!exact && (change.getType() & mask) != 0))
+ result.add(change);
+ }
+ return result.toArray(new BundleDelta[result.size()]);
+ }
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public ResolverHookException getResovlerHookException() {
+ return error;
+ }
+
+ void setResolverHookException(ResolverHookException error) {
+ this.error = error;
+ }
+
+ void recordBundleAdded(BundleDescriptionImpl added) {
+ synchronized (this.changes) {
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(added);
+ if (change == null) {
+ changes.put(added, new BundleDeltaImpl(added, BundleDelta.ADDED));
+ return;
+ }
+ if (change.getType() == BundleDelta.REMOVED) {
+ changes.remove(added);
+ return;
+ }
+ int newType = change.getType();
+ if ((newType & BundleDelta.REMOVED) != 0)
+ newType &= ~BundleDelta.REMOVED;
+ change.setType(newType | BundleDelta.ADDED);
+ change.setBundle(added);
+ }
+ }
+
+ void recordBundleUpdated(BundleDescriptionImpl updated) {
+ synchronized (this.changes) {
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(updated);
+ if (change == null) {
+ changes.put(updated, new BundleDeltaImpl(updated, BundleDelta.UPDATED));
+ return;
+ }
+ if ((change.getType() & (BundleDelta.ADDED | BundleDelta.REMOVED)) != 0)
+ return;
+ change.setType(change.getType() | BundleDelta.UPDATED);
+ change.setBundle(updated);
+ }
+ }
+
+ void recordBundleRemoved(BundleDescriptionImpl removed) {
+ synchronized (this.changes) {
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(removed);
+ if (change == null) {
+ changes.put(removed, new BundleDeltaImpl(removed, BundleDelta.REMOVED));
+ return;
+ }
+ if (change.getType() == BundleDelta.ADDED) {
+ changes.remove(removed);
+ return;
+ }
+ int newType = change.getType();
+ if ((newType & BundleDelta.ADDED) != 0)
+ newType &= ~BundleDelta.ADDED;
+ change.setType(newType | BundleDelta.REMOVED);
+ }
+ }
+
+ void recordBundleRemovalPending(BundleDescriptionImpl removed) {
+ synchronized (this.changes) {
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(removed);
+ if (change == null) {
+ changes.put(removed, new BundleDeltaImpl(removed, BundleDelta.REMOVAL_PENDING));
+ return;
+ }
+ int newType = change.getType();
+ if ((newType & BundleDelta.REMOVAL_COMPLETE) != 0)
+ newType &= ~BundleDelta.REMOVAL_COMPLETE;
+ change.setType(newType | BundleDelta.REMOVAL_PENDING);
+ }
+ }
+
+ void recordBundleRemovalComplete(BundleDescriptionImpl removed) {
+ synchronized (this.changes) {
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(removed);
+ if (change == null) {
+ changes.put(removed, new BundleDeltaImpl(removed, BundleDelta.REMOVAL_COMPLETE));
+ return;
+ }
+ int newType = change.getType();
+ if ((newType & BundleDelta.REMOVAL_PENDING) != 0)
+ newType &= ~BundleDelta.REMOVAL_PENDING;
+ change.setType(newType | BundleDelta.REMOVAL_COMPLETE);
+ }
+ }
+
+ void recordBundleResolved(BundleDescriptionImpl resolved, boolean result) {
+ synchronized (this.changes) {
+ if (resolved.isResolved() == result)
+ return; // do not record anything if nothing has changed
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(resolved);
+ int newType = result ? BundleDelta.RESOLVED : BundleDelta.UNRESOLVED;
+ if (change == null) {
+ change = new BundleDeltaImpl(resolved, newType);
+ changes.put(resolved, change);
+ return;
+ }
+ // new type will have only one of RESOLVED|UNRESOLVED bits set
+ newType = newType | (change.getType() & ~(BundleDelta.RESOLVED | BundleDelta.UNRESOLVED));
+ change.setType(newType);
+ change.setBundle(resolved);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java
new file mode 100644
index 000000000..e6cc72a7e
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java
@@ -0,0 +1,646 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.internal.baseadaptor.ArrayMap;
+import org.eclipse.osgi.internal.container.ComputeNodeOrder;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+/**
+ * An implementation for the StateHelper API. Access to this implementation is
+ * provided by the PlatformAdmin. Since this helper is a general facility for
+ * state manipulation, it should not be tied to any implementation details.
+ */
+public final class StateHelperImpl implements StateHelper {
+ private static final StateHelper instance = new StateHelperImpl();
+
+ /**
+ * @see StateHelper
+ */
+ public BundleDescription[] getDependentBundles(BundleDescription[] bundles) {
+ if (bundles == null || bundles.length == 0)
+ return new BundleDescription[0];
+
+ Set<BundleDescription> reachable = new HashSet<BundleDescription>(bundles.length);
+ for (int i = 0; i < bundles.length; i++) {
+ if (!bundles[i].isResolved())
+ continue;
+ addDependentBundles(bundles[i], reachable);
+ }
+ return reachable.toArray(new BundleDescription[reachable.size()]);
+ }
+
+ private void addDependentBundles(BundleDescription bundle, Set<BundleDescription> reachable) {
+ if (reachable.contains(bundle))
+ return;
+ reachable.add(bundle);
+ BundleDescription[] dependents = bundle.getDependents();
+ for (int i = 0; i < dependents.length; i++)
+ addDependentBundles(dependents[i], reachable);
+ }
+
+ public BundleDescription[] getPrerequisites(BundleDescription[] bundles) {
+ if (bundles == null || bundles.length == 0)
+ return new BundleDescription[0];
+ Set<BundleDescription> reachable = new HashSet<BundleDescription>(bundles.length);
+ for (int i = 0; i < bundles.length; i++)
+ addPrerequisites(bundles[i], reachable);
+ return reachable.toArray(new BundleDescription[reachable.size()]);
+ }
+
+ private void addPrerequisites(BundleDescription bundle, Set<BundleDescription> reachable) {
+ if (reachable.contains(bundle))
+ return;
+ reachable.add(bundle);
+ List<BundleDescription> depList = ((BundleDescriptionImpl) bundle).getBundleDependencies();
+ BundleDescription[] dependencies = depList.toArray(new BundleDescription[depList.size()]);
+ for (int i = 0; i < dependencies.length; i++)
+ addPrerequisites(dependencies[i], reachable);
+ }
+
+ private Map<String, List<ExportPackageDescription>> getExportedPackageMap(State state) {
+ Map<String, List<ExportPackageDescription>> result = new HashMap<String, List<ExportPackageDescription>>();
+ BundleDescription[] bundles = state.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ ExportPackageDescription[] packages = bundles[i].getExportPackages();
+ for (int j = 0; j < packages.length; j++) {
+ ExportPackageDescription description = packages[j];
+ List<ExportPackageDescription> exports = result.get(description.getName());
+ if (exports == null) {
+ exports = new ArrayList<ExportPackageDescription>();
+ result.put(description.getName(), exports);
+ }
+ exports.add(description);
+ }
+ }
+ return result;
+ }
+
+ private Map<String, List<GenericDescription>> getGenericsMap(State state, boolean resolved) {
+ Map<String, List<GenericDescription>> result = new HashMap<String, List<GenericDescription>>();
+ BundleDescription[] bundles = state.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ if (resolved && !bundles[i].isResolved())
+ continue; // discard unresolved bundles
+ GenericDescription[] generics = bundles[i].getGenericCapabilities();
+ for (int j = 0; j < generics.length; j++) {
+ GenericDescription description = generics[j];
+ List<GenericDescription> genericList = result.get(description.getName());
+ if (genericList == null) {
+ genericList = new ArrayList<GenericDescription>(1);
+ result.put(description.getName(), genericList);
+ }
+ genericList.add(description);
+ }
+ }
+ return result;
+ }
+
+ private VersionConstraint[] getUnsatisfiedLeaves(State state, BundleDescription[] bundles, ResolverHook hook) {
+ Map<String, List<ExportPackageDescription>> packages = getExportedPackageMap(state);
+ Map<String, List<GenericDescription>> generics = getGenericsMap(state, false);
+ Set<VersionConstraint> result = new HashSet<VersionConstraint>();
+ List<BundleDescription> bundleList = new ArrayList<BundleDescription>(bundles.length);
+ for (int i = 0; i < bundles.length; i++)
+ bundleList.add(bundles[i]);
+ for (int i = 0; i < bundleList.size(); i++) {
+ BundleDescription description = bundleList.get(i);
+ VersionConstraint[] constraints = getUnsatisfiedConstraints(description, hook);
+ for (int j = 0; j < constraints.length; j++) {
+ VersionConstraint constraint = constraints[j];
+ Collection<BaseDescription> satisfied = null;
+ if (constraint instanceof BundleSpecification || constraint instanceof HostSpecification) {
+ BundleDescription[] suppliers = state.getBundles(constraint.getName());
+ satisfied = getPossibleCandidates(constraint, suppliers, constraint instanceof HostSpecification ? BundleRevision.HOST_NAMESPACE : null, hook, false);
+ } else if (constraint instanceof ImportPackageSpecification) {
+ List<ExportPackageDescription> exports = packages.get(constraint.getName());
+ if (exports != null)
+ satisfied = getPossibleCandidates(constraint, exports.toArray(new BaseDescription[exports.size()]), null, hook, false);
+ } else if (constraint instanceof GenericSpecification) {
+ List<GenericDescription> genericSet = generics.get(constraint.getName());
+ if (genericSet != null)
+ satisfied = getPossibleCandidates(constraint, genericSet.toArray(new BaseDescription[genericSet.size()]), null, hook, false);
+ }
+ if (satisfied == null || satisfied.isEmpty()) {
+ result.add(constraint);
+ } else {
+ for (BaseDescription baseDescription : satisfied) {
+ if (!baseDescription.getSupplier().isResolved() && !bundleList.contains(baseDescription.getSupplier())) {
+ bundleList.add(baseDescription.getSupplier());
+ // TODO only add the first supplier that is not resolved;
+ // this is the previous behavior before the fix for bug 333071; should consider adding all unresolved
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result.toArray(new VersionConstraint[result.size()]);
+
+ }
+
+ public VersionConstraint[] getUnsatisfiedLeaves(BundleDescription[] bundles) {
+ if (bundles.length == 0)
+ return new VersionConstraint[0];
+ State state = bundles[0].getContainingState();
+ ResolverHook hook = beginHook(state, Arrays.asList((BundleRevision[]) bundles));
+ try {
+ return getUnsatisfiedLeaves(state, bundles, hook);
+ } finally {
+ if (hook != null)
+ hook.end();
+ }
+ }
+
+ private ResolverHook beginHook(State state, Collection<BundleRevision> triggers) {
+ if (!(state instanceof StateImpl))
+ return null;
+ ResolverHookFactory hookFactory = ((StateImpl) state).getResolverHookFactory();
+ return hookFactory == null ? null : hookFactory.begin(triggers);
+ }
+
+ /**
+ * @see StateHelper
+ */
+ public VersionConstraint[] getUnsatisfiedConstraints(BundleDescription bundle) {
+ ResolverHook hook = beginHook(bundle.getContainingState(), Arrays.asList(new BundleRevision[] {bundle}));
+ try {
+ return getUnsatisfiedConstraints(bundle, hook);
+ } finally {
+ if (hook != null)
+ hook.end();
+ }
+ }
+
+ private VersionConstraint[] getUnsatisfiedConstraints(BundleDescription bundle, ResolverHook hook) {
+ State containingState = bundle.getContainingState();
+ if (containingState == null)
+ // it is a bug in the client to call this method when not attached to a state
+ throw new IllegalStateException("Does not belong to a state"); //$NON-NLS-1$
+ List<VersionConstraint> unsatisfied = new ArrayList<VersionConstraint>();
+ HostSpecification host = bundle.getHost();
+ if (host != null)
+ if (!host.isResolved() && !isBundleConstraintResolvable(host, BundleRevision.HOST_NAMESPACE, hook))
+ unsatisfied.add(host);
+ BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
+ for (int i = 0; i < requiredBundles.length; i++)
+ if (!requiredBundles[i].isResolved() && !isBundleConstraintResolvable(requiredBundles[i], null, hook))
+ unsatisfied.add(requiredBundles[i]);
+ ImportPackageSpecification[] packages = bundle.getImportPackages();
+ for (int i = 0; i < packages.length; i++)
+ if (!packages[i].isResolved() && !isResolvable(packages[i], hook)) {
+ if (bundle.isResolved()) {
+ // if the bundle is resolved the check if the import is option.
+ // Here we assume that an unresolved mandatory import must have been dropped
+ // in favor of an export from the same bundle (bug 338240)
+ if (!ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(packages[i].getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ continue;
+ }
+ unsatisfied.add(packages[i]);
+ }
+ GenericSpecification[] generics = bundle.getGenericRequires();
+ for (int i = 0; i < generics.length; i++)
+ if (!generics[i].isResolved() && !isResolvable(generics[i], hook))
+ unsatisfied.add(generics[i]);
+ NativeCodeSpecification nativeCode = bundle.getNativeCodeSpecification();
+ if (nativeCode != null && !nativeCode.isResolved())
+ unsatisfied.add(nativeCode);
+ return unsatisfied.toArray(new VersionConstraint[unsatisfied.size()]);
+ }
+
+ private ArrayMap<BundleCapability, BaseDescription> asArrayMap(List<BaseDescription> descriptions, String namespace) {
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(descriptions.size());
+ for (BaseDescription description : descriptions)
+ capabilities.add(((BaseDescriptionImpl) description).getCapability(namespace));
+ return new ArrayMap<BundleCapability, BaseDescription>(capabilities, descriptions);
+ }
+
+ private List<BaseDescription> getPossibleCandidates(VersionConstraint constraint, BaseDescription[] descriptions, String namespace, ResolverHook hook, boolean resolved) {
+ List<BaseDescription> candidates = new ArrayList<BaseDescription>();
+ for (int i = 0; i < descriptions.length; i++)
+ if ((!resolved || descriptions[i].getSupplier().isResolved()) && constraint.isSatisfiedBy(descriptions[i]))
+ candidates.add(descriptions[i]);
+ if (hook != null)
+ hook.filterMatches(constraint.getRequirement(), asArrayMap(candidates, namespace));
+ return candidates;
+ }
+
+ /**
+ * @see StateHelper
+ */
+ public boolean isResolvable(ImportPackageSpecification constraint) {
+ ResolverHook hook = beginHook(constraint.getBundle().getContainingState(), Arrays.asList(new BundleRevision[] {constraint.getBundle()}));
+ try {
+ return isResolvable(constraint, hook);
+ } finally {
+ if (hook != null)
+ hook.end();
+ }
+ }
+
+ private boolean isResolvable(ImportPackageSpecification constraint, ResolverHook hook) {
+ ExportPackageDescription[] exports = constraint.getBundle().getContainingState().getExportedPackages();
+ return getPossibleCandidates(constraint, exports, null, hook, true).size() > 0;
+ }
+
+ private boolean isResolvable(GenericSpecification constraint, ResolverHook hook) {
+ Map<String, List<GenericDescription>> genericCapabilities = getGenericsMap(constraint.getBundle().getContainingState(), true);
+ List<GenericDescription> genericList = genericCapabilities.get(constraint.getName());
+ if (genericList == null)
+ return false;
+ return getPossibleCandidates(constraint, genericList.toArray(new BaseDescription[genericList.size()]), null, hook, true).size() > 0;
+ }
+
+ /**
+ * @see StateHelper
+ */
+ public boolean isResolvable(BundleSpecification specification) {
+ return isBundleConstraintResolvable(specification, null);
+ }
+
+ /**
+ * @see StateHelper
+ */
+ public boolean isResolvable(HostSpecification specification) {
+ return isBundleConstraintResolvable(specification, BundleRevision.HOST_NAMESPACE);
+ }
+
+ /*
+ * Returns whether a bundle specification/host specification can be resolved.
+ */
+ private boolean isBundleConstraintResolvable(VersionConstraint constraint, String namespace) {
+ ResolverHook hook = beginHook(constraint.getBundle().getContainingState(), Arrays.asList(new BundleRevision[] {constraint.getBundle()}));
+ try {
+ return isBundleConstraintResolvable(constraint, namespace, hook);
+ } finally {
+ if (hook != null)
+ hook.end();
+ }
+ }
+
+ private boolean isBundleConstraintResolvable(VersionConstraint constraint, String namespace, ResolverHook hook) {
+ BundleDescription[] availableBundles = constraint.getBundle().getContainingState().getBundles(constraint.getName());
+ return getPossibleCandidates(constraint, availableBundles, namespace, hook, true).size() > 0;
+ }
+
+ public Object[][] sortBundles(BundleDescription[] toSort) {
+ List<Object[]> references = new ArrayList<Object[]>(toSort.length);
+ for (int i = 0; i < toSort.length; i++)
+ if (toSort[i].isResolved())
+ buildReferences(toSort[i], references);
+ Object[][] cycles = ComputeNodeOrder.computeNodeOrder(toSort, references.toArray(new Object[references.size()][]));
+ if (cycles.length == 0)
+ return cycles;
+ // fix up host/fragment orders (bug 184127)
+ for (int i = 0; i < cycles.length; i++) {
+ for (int j = 0; j < cycles[i].length; j++) {
+ BundleDescription fragment = (BundleDescription) cycles[i][j];
+ if (fragment.getHost() == null)
+ continue;
+ BundleDescription host = (BundleDescription) fragment.getHost().getSupplier();
+ if (host == null)
+ continue;
+ fixFragmentOrder(host, fragment, toSort);
+ }
+ }
+ return cycles;
+ }
+
+ private void fixFragmentOrder(BundleDescription host, BundleDescription fragment, BundleDescription[] toSort) {
+ int hostIndex = -1;
+ int fragIndex = -1;
+ for (int i = 0; i < toSort.length && (hostIndex == -1 || fragIndex == -1); i++) {
+ if (toSort[i] == host)
+ hostIndex = i;
+ else if (toSort[i] == fragment)
+ fragIndex = i;
+ }
+ if (fragIndex > -1 && fragIndex < hostIndex) {
+ for (int i = fragIndex; i < hostIndex; i++)
+ toSort[i] = toSort[i + 1];
+ toSort[hostIndex] = fragment;
+ }
+ }
+
+ private void buildReferences(BundleDescription description, List<Object[]> references) {
+ HostSpecification host = description.getHost();
+ // it is a fragment
+ if (host != null) {
+ // just create a dependencies for non-payload requirements (osgi.wiring.host and osgi.ee)
+ if (host.getHosts() != null) {
+ BundleDescription[] hosts = host.getHosts();
+ for (int i = 0; i < hosts.length; i++)
+ if (hosts[i] != description)
+ references.add(new Object[] {description, hosts[i]});
+ }
+ GenericDescription[] genericDependencies = description.getResolvedGenericRequires();
+ for (GenericDescription dependency : genericDependencies) {
+ if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(dependency.getType())) {
+ references.add(new Object[] {description, dependency.getSupplier()});
+ }
+ }
+ } else {
+ // it is a host
+ buildReferences(description, ((BundleDescriptionImpl) description).getBundleDependencies(), references);
+ }
+ }
+
+ private void buildReferences(BundleDescription description, List<BundleDescription> dependencies, List<Object[]> references) {
+ for (Iterator<BundleDescription> iter = dependencies.iterator(); iter.hasNext();)
+ addReference(description, iter.next(), references);
+ }
+
+ private void addReference(BundleDescription description, BundleDescription reference, List<Object[]> references) {
+ // build the reference from the description
+ if (description == reference || reference == null)
+ return;
+ BundleDescription[] fragments = reference.getFragments();
+ for (int i = 0; i < fragments.length; i++) {
+ if (fragments[i].isResolved()) {
+ ExportPackageDescription[] exports = fragments[i].getExportPackages();
+ if (exports.length > 0)
+ references.add(new Object[] {description, fragments[i]});
+ }
+ }
+ references.add(new Object[] {description, reference});
+ }
+
+ public ExportPackageDescription[] getVisiblePackages(BundleDescription bundle) {
+ return getVisiblePackages(bundle, 0);
+ }
+
+ public ExportPackageDescription[] getVisiblePackages(BundleDescription bundle, int options) {
+ StateImpl state = (StateImpl) bundle.getContainingState();
+ boolean strict = false;
+ if (state != null)
+ strict = state.inStrictMode();
+ BundleDescription host = (BundleDescription) (bundle.getHost() == null ? bundle : bundle.getHost().getSupplier());
+ List<ExportPackageDescription> orderedPkgList = new ArrayList<ExportPackageDescription>(); // list of all ExportPackageDescriptions that are visible (ArrayList is used to keep order)
+ Set<ExportPackageDescription> pkgSet = new HashSet<ExportPackageDescription>();
+ Set<String> importList = new HashSet<String>(); // list of package names which are directly imported
+ // get the list of directly imported packages first.
+ ImportsHolder imports = new ImportsHolder(bundle, options);
+ for (int i = 0; i < imports.getSize(); i++) {
+ ExportPackageDescription pkgSupplier = imports.getSupplier(i);
+ if (pkgSupplier == null || pkgSupplier.getExporter() == host) // do not return the bundle'sr own imports
+ continue;
+ if (!isSystemExport(pkgSupplier, options) && !pkgSet.contains(pkgSupplier)) {
+ orderedPkgList.add(pkgSupplier);
+ pkgSet.add(pkgSupplier);
+ }
+ // get the sources of the required bundles of the exporter
+ BundleSpecification[] requires = pkgSupplier.getExporter().getRequiredBundles();
+ Set<BundleDescription> visited = new HashSet<BundleDescription>();
+ visited.add(bundle); // always add self to prevent recursing into self
+ Set<String> importNames = new HashSet<String>(1);
+ importNames.add(imports.getName(i));
+ for (int j = 0; j < requires.length; j++) {
+ BundleDescription bundleSupplier = (BundleDescription) requires[j].getSupplier();
+ if (bundleSupplier != null)
+ getPackages(bundleSupplier, bundle.getSymbolicName(), importList, orderedPkgList, pkgSet, visited, strict, importNames, options);
+ }
+ importList.add(imports.getName(i)); // be sure to add to direct import list
+
+ }
+ // now find all the packages that are visible from required bundles
+ RequiresHolder requires = new RequiresHolder(bundle, options);
+ Set<BundleDescription> visited = new HashSet<BundleDescription>(requires.getSize());
+ visited.add(bundle); // always add self to prevent recursing into self
+ for (int i = 0; i < requires.getSize(); i++) {
+ BundleDescription bundleSupplier = requires.getSupplier(i);
+ if (bundleSupplier != null)
+ getPackages(bundleSupplier, bundle.getSymbolicName(), importList, orderedPkgList, pkgSet, visited, strict, null, options);
+ }
+ return orderedPkgList.toArray(new ExportPackageDescription[orderedPkgList.size()]);
+ }
+
+ private void getPackages(BundleDescription requiredBundle, String symbolicName, Set<String> importList, List<ExportPackageDescription> orderedPkgList, Set<ExportPackageDescription> pkgSet, Set<BundleDescription> visited, boolean strict, Set<String> pkgNames, int options) {
+ if (visited.contains(requiredBundle))
+ return; // prevent duplicate entries and infinate loops incase of cycles
+ visited.add(requiredBundle);
+ // add all the exported packages from the required bundle; take x-friends into account.
+ ExportPackageDescription[] substitutedExports = requiredBundle.getSubstitutedExports();
+ ExportPackageDescription[] imports = requiredBundle.getResolvedImports();
+ Set<String> substituteNames = null; // a temporary set used to scope packages we get from getPackages
+ for (int i = 0; i < substitutedExports.length; i++) {
+ if ((pkgNames == null || pkgNames.contains(substitutedExports[i].getName()))) {
+ for (int j = 0; j < imports.length; j++) {
+ if (substitutedExports[i].getName().equals(imports[j].getName()) && !pkgSet.contains(imports[j])) {
+ if (substituteNames == null)
+ substituteNames = new HashSet<String>(1);
+ else
+ substituteNames.clear();
+ // substituteNames is a set of one package containing the single substitute we are trying to get the source for
+ substituteNames.add(substitutedExports[i].getName());
+ getPackages(imports[j].getSupplier(), symbolicName, importList, orderedPkgList, pkgSet, new HashSet<BundleDescription>(0), strict, substituteNames, options);
+ }
+ }
+ }
+ }
+ importList = substitutedExports.length == 0 ? importList : new HashSet<String>(importList);
+ for (int i = 0; i < substitutedExports.length; i++)
+ // we add the package name to the import list to prevent required bundles from adding more sources
+ importList.add(substitutedExports[i].getName());
+
+ ExportPackageDescription[] exports = requiredBundle.getSelectedExports();
+ HashSet<String> exportNames = new HashSet<String>(exports.length); // set is used to improve performance of duplicate check.
+ for (int i = 0; i < exports.length; i++)
+ if ((pkgNames == null || pkgNames.contains(exports[i].getName())) && !isSystemExport(exports[i], options) && isFriend(symbolicName, exports[i], strict) && !importList.contains(exports[i].getName()) && !pkgSet.contains(exports[i])) {
+ if (!exportNames.contains(exports[i].getName())) {
+ // only add the first export
+ orderedPkgList.add(exports[i]);
+ pkgSet.add(exports[i]);
+ exportNames.add(exports[i].getName());
+ }
+ }
+ // now look for exports from the required bundle.
+ RequiresHolder requiredBundles = new RequiresHolder(requiredBundle, options);
+ for (int i = 0; i < requiredBundles.getSize(); i++) {
+ if (requiredBundles.getSupplier(i) == null)
+ continue;
+ if (requiredBundles.isExported(i)) {
+ // looking for a specific package and that package is exported by this bundle or adding all packages from a reexported bundle
+ getPackages(requiredBundles.getSupplier(i), symbolicName, importList, orderedPkgList, pkgSet, visited, strict, pkgNames, options);
+ } else if (exportNames.size() > 0) {
+ // adding any exports from required bundles which we also export
+ Set<BundleDescription> tmpVisited = new HashSet<BundleDescription>();
+ getPackages(requiredBundles.getSupplier(i), symbolicName, importList, orderedPkgList, pkgSet, tmpVisited, strict, exportNames, options);
+ }
+ }
+ }
+
+ private boolean isSystemExport(ExportPackageDescription export, int options) {
+ if ((options & VISIBLE_INCLUDE_EE_PACKAGES) != 0)
+ return false;
+ return ((Integer) export.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() >= 0;
+ }
+
+ private boolean isFriend(String consumerBSN, ExportPackageDescription export, boolean strict) {
+ if (!strict)
+ return true; // ignore friends rules if not in strict mode
+ String[] friends = (String[]) export.getDirective(StateImpl.FRIENDS_DIRECTIVE);
+ if (friends == null)
+ return true; // no x-friends means it is wide open
+ for (int i = 0; i < friends.length; i++)
+ if (friends[i].equals(consumerBSN))
+ return true; // the consumer is a friend
+ return false;
+ }
+
+ public int getAccessCode(BundleDescription bundle, ExportPackageDescription export) {
+ if (((Boolean) export.getDirective(StateImpl.INTERNAL_DIRECTIVE)).booleanValue())
+ return ACCESS_DISCOURAGED;
+ if (!isFriend(bundle.getSymbolicName(), export, true)) // pass strict here so that x-friends is processed
+ return ACCESS_DISCOURAGED;
+ return ACCESS_ENCOURAGED;
+ }
+
+ public static StateHelper getInstance() {
+ return instance;
+ }
+
+}
+
+/*
+ * This class is used to encapsulate the import packages of a bundle used by getVisiblePackages(). If the method is called with the option
+ * VISIBLE_INCLUDE_ALL_HOST_WIRES, it uses resolved import packages to find all visible packages by a bundle. Called without this option,
+ * it uses imported packages instead of resolved imported packages and does not consider resolved dynamic imports.
+ * ImportsHolder serves to hide which of these is used, so that the body of getVisiblePackages() does not become full of checks.
+ *
+ */
+class ImportsHolder {
+ private final ImportPackageSpecification[] importedPackages;
+ private final ExportPackageDescription[] resolvedImports;
+ private final boolean isUsingResolved;
+
+ // Depending on the options used, either importedPackages or resolvedImports is initialize, but not both.
+ ImportsHolder(BundleDescription bundle, int options) {
+ isUsingResolved = (options & StateHelper.VISIBLE_INCLUDE_ALL_HOST_WIRES) != 0;
+ if (isUsingResolved) {
+ importedPackages = null;
+ resolvedImports = bundle.getResolvedImports();
+ } else {
+ importedPackages = bundle.getImportPackages();
+ resolvedImports = null;
+ }
+ }
+
+ ExportPackageDescription getSupplier(int index) {
+ if (isUsingResolved)
+ return resolvedImports[index];
+ return (ExportPackageDescription) importedPackages[index].getSupplier();
+ }
+
+ String getName(int index) {
+ if (isUsingResolved)
+ return resolvedImports[index].getName();
+ return importedPackages[index].getName();
+ }
+
+ int getSize() {
+ if (isUsingResolved)
+ return resolvedImports.length;
+ return importedPackages.length;
+ }
+}
+
+/*
+ * This class is used to encapsulate the required bundles by a bundle, used by getVisiblePackages(). If the method is called with the option
+ * VISIBLE_INCLUDE_ALL_HOST_WIRES, it uses resolved required bundles to find all visible packages by a bundle. Called without this option,
+ * it uses required bundles instead of resolved required bundles and does not consider the constraints from fragments.
+ * RequiresHolder serves to hide which of these is used.
+ */
+class RequiresHolder {
+ private final BundleSpecification[] requiredBundles;
+ private final BundleDescription[] resolvedRequires;
+ private final boolean isUsingResolved;
+ private final Map<BundleDescription, Boolean> resolvedBundlesExported;
+
+ // Depending on the options used, either requiredBundles or resolvedRequires is initialize, but not both.
+ RequiresHolder(BundleDescription bundle, int options) {
+ isUsingResolved = (options & StateHelper.VISIBLE_INCLUDE_ALL_HOST_WIRES) != 0;
+ if (isUsingResolved) {
+ requiredBundles = null;
+ resolvedBundlesExported = new HashMap<BundleDescription, Boolean>();
+ resolvedRequires = bundle.getResolvedRequires();
+ determineRequiresVisibility(bundle);
+ } else {
+ requiredBundles = bundle.getRequiredBundles();
+ resolvedBundlesExported = null;
+ resolvedRequires = null;
+ }
+ }
+
+ BundleDescription getSupplier(int index) {
+ if (isUsingResolved)
+ return resolvedRequires[index];
+ return (BundleDescription) requiredBundles[index].getSupplier();
+ }
+
+ boolean isExported(int index) {
+ if (isUsingResolved)
+ return resolvedBundlesExported.get(resolvedRequires[index]).booleanValue();
+ return requiredBundles[index].isExported();
+ }
+
+ int getSize() {
+ if (isUsingResolved)
+ return resolvedRequires.length;
+ return requiredBundles.length;
+ }
+
+ /*
+ * This method determines for all resolved required bundles if they are reexported.
+ * Fragment bundles are also considered.
+ */
+ private void determineRequiresVisibility(BundleDescription bundle) {
+ BundleSpecification[] required = bundle.getRequiredBundles();
+ Set<BundleDescription> resolved = new HashSet<BundleDescription>();
+
+ for (int i = 0; i < resolvedRequires.length; i++) {
+ resolved.add(resolvedRequires[i]);
+ }
+
+ // Get the visibility of all directly required bundles
+ for (int i = 0; i < required.length; i++) {
+ if (required[i].getSupplier() != null) {
+ resolvedBundlesExported.put((BundleDescription) required[i].getSupplier(), new Boolean(required[i].isExported()));
+ resolved.remove(required[i].getSupplier());
+ }
+ }
+
+ BundleDescription[] fragments = bundle.getFragments();
+
+ // Get the visibility of resolved required bundles, which come from fragments
+ if (resolved.size() > 0) {
+ for (int i = 0; i < fragments.length; i++) {
+ BundleSpecification[] fragmentRequiredBundles = fragments[i].getRequiredBundles();
+ for (int j = 0; j < fragmentRequiredBundles.length; j++) {
+ if (resolved.contains(fragmentRequiredBundles[j].getSupplier())) {
+ resolvedBundlesExported.put((BundleDescription) fragmentRequiredBundles[j].getSupplier(), new Boolean(fragmentRequiredBundles[j].isExported()));
+ resolved.remove(fragmentRequiredBundles[j].getSupplier());
+ }
+ }
+ if (resolved.size() == 0) {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateImpl.java
new file mode 100644
index 000000000..295605d4d
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateImpl.java
@@ -0,0 +1,1340 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.internal.framework.FilterImpl;
+
+import org.eclipse.osgi.internal.debug.Debug;
+import org.eclipse.osgi.internal.debug.FrameworkDebugOptions;
+
+import java.util.*;
+import org.eclipse.osgi.framework.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+import org.osgi.framework.wiring.BundleRevision;
+
+public abstract class StateImpl implements State {
+
+ public static final String ECLIPSE_PLATFORMFILTER = "Eclipse-PlatformFilter"; //$NON-NLS-1$
+ public static final String Eclipse_JREBUNDLE = "Eclipse-JREBundle"; //$NON-NLS-1$
+ /**
+ * Manifest Export-Package directive indicating that the exported package should only
+ * be made available when the resolver is not in strict mode.
+ */
+ public static final String INTERNAL_DIRECTIVE = "x-internal"; //$NON-NLS-1$
+
+ /**
+ * Manifest Export-Package directive indicating that the exported package should only
+ * be made available to friends of the exporting bundle.
+ */
+ public static final String FRIENDS_DIRECTIVE = "x-friends"; //$NON-NLS-1$
+
+ /**
+ * Manifest header (named &quot;Provide-Package&quot;)
+ * identifying the packages name
+ * provided to other bundles which require the bundle.
+ *
+ * <p>
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this header.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ * @deprecated
+ */
+ public final static String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$
+
+ /**
+ * Manifest header attribute (named &quot;reprovide&quot;)
+ * for Require-Bundle
+ * identifying that any packages that are provided
+ * by the required bundle must be reprovided by the requiring bundle.
+ * The default value is <tt>false</tt>.
+ * <p>
+ * The attribute value is encoded in the Require-Bundle manifest
+ * header like:
+ * <pre>
+ * Require-Bundle: com.acme.module.test; reprovide="true"
+ * </pre>
+ * <p>
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this attribute.
+ * @deprecated
+ */
+ public final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$
+
+ /**
+ * Manifest header attribute (named &quot;optional&quot;)
+ * for Require-Bundle
+ * identifying that a required bundle is optional and that
+ * the requiring bundle can be resolved if there is no
+ * suitable required bundle.
+ * The default value is <tt>false</tt>.
+ *
+ * <p>The attribute value is encoded in the Require-Bundle manifest
+ * header like:
+ * <pre>
+ * Require-Bundle: com.acme.module.test; optional="true"
+ * </pre>
+ * <p>
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this attribute.
+ * @since 1.3 <b>EXPERIMENTAL</b>
+ * @deprecated
+ */
+ public final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$
+
+ public static final String OSGI_RESOLVER_MODE = "osgi.resolverMode"; //$NON-NLS-1$
+ public static final String STRICT_MODE = "strict"; //$NON-NLS-1$
+ public static final String DEVELOPMENT_MODE = "development"; //$NON-NLS-1$
+
+ public static final String STATE_SYSTEM_BUNDLE = "osgi.system.bundle"; //$NON-NLS-1$
+
+ private static final String OSGI_OS = "osgi.os"; //$NON-NLS-1$
+ private static final String OSGI_WS = "osgi.ws"; //$NON-NLS-1$
+ private static final String OSGI_NL = "osgi.nl"; //$NON-NLS-1$
+ private static final String OSGI_ARCH = "osgi.arch"; //$NON-NLS-1$
+ public static final String[] PROPS = {OSGI_OS, OSGI_WS, OSGI_NL, OSGI_ARCH, Constants.FRAMEWORK_SYSTEMPACKAGES, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, OSGI_RESOLVER_MODE, Constants.FRAMEWORK_EXECUTIONENVIRONMENT, "osgi.resolveOptional", "osgi.genericAliases", Constants.FRAMEWORK_OS_NAME, Constants.FRAMEWORK_OS_VERSION, Constants.FRAMEWORK_PROCESSOR, Constants.FRAMEWORK_LANGUAGE, STATE_SYSTEM_BUNDLE, Constants.FRAMEWORK_SYSTEMCAPABILITIES, Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA}; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final DisabledInfo[] EMPTY_DISABLEDINFOS = new DisabledInfo[0];
+ public static final String OSGI_EE_NAMESPACE = "osgi.ee"; //$NON-NLS-1$
+
+ transient private Resolver resolver;
+ transient private StateDeltaImpl changes;
+ transient private boolean resolving = false;
+ transient private LinkedList<BundleDescription> removalPendings = new LinkedList<BundleDescription>();
+
+ private boolean resolved = true;
+ private long timeStamp = System.currentTimeMillis();
+ private final KeyedHashSet bundleDescriptions = new KeyedHashSet(false);
+ private final Map<BundleDescription, List<ResolverError>> resolverErrors = new HashMap<BundleDescription, List<ResolverError>>();
+ private StateObjectFactory factory;
+ private final KeyedHashSet resolvedBundles = new KeyedHashSet();
+ private final Map<BundleDescription, List<DisabledInfo>> disabledBundles = new HashMap<BundleDescription, List<DisabledInfo>>();
+ private boolean fullyLoaded = false;
+ private boolean dynamicCacheChanged = false;
+ // only used for lazy loading of BundleDescriptions
+ private StateReader reader;
+ @SuppressWarnings("unchecked")
+ private Dictionary<Object, Object>[] platformProperties = new Dictionary[] {new Hashtable<String, String>(PROPS.length)}; // Dictionary here because of Filter API
+ private long highestBundleId = -1;
+ private final Set<String> platformPropertyKeys = new HashSet<String>(PROPS.length);
+ private ResolverHookFactory hookFactory;
+ private ResolverHook hook;
+ private boolean developmentMode = false;
+
+ private static long cumulativeTime;
+
+ final Object monitor = new Object();
+
+ // to prevent extra-package instantiation
+ protected StateImpl() {
+ // always add the default platform property keys.
+ addPlatformPropertyKeys(PROPS);
+ }
+
+ public boolean addBundle(BundleDescription description) {
+ synchronized (this.monitor) {
+ if (!basicAddBundle(description))
+ return false;
+ String platformFilter = description.getPlatformFilter();
+ if (platformFilter != null) {
+ try {
+ // add any new platform filter propery keys this bundle is using
+ FilterImpl filter = FilterImpl.newInstance(platformFilter);
+ addPlatformPropertyKeys(filter.getAttributes());
+ } catch (InvalidSyntaxException e) {
+ // ignore this is handled in another place
+ }
+ }
+ NativeCodeSpecification nativeCode = description.getNativeCodeSpecification();
+ if (nativeCode != null) {
+ NativeCodeDescription[] suppliers = nativeCode.getPossibleSuppliers();
+ for (int i = 0; i < suppliers.length; i++) {
+ FilterImpl filter = (FilterImpl) suppliers[i].getFilter();
+ if (filter != null)
+ addPlatformPropertyKeys(filter.getAttributes());
+ }
+ }
+ resolved = false;
+ getDelta().recordBundleAdded((BundleDescriptionImpl) description);
+ if (getSystemBundle().equals(description.getSymbolicName()))
+ resetAllSystemCapabilities();
+ if (resolver != null)
+ resolver.bundleAdded(description);
+ updateTimeStamp();
+ return true;
+ }
+ }
+
+ public boolean updateBundle(BundleDescription newDescription) {
+ synchronized (this.monitor) {
+ BundleDescriptionImpl existing = (BundleDescriptionImpl) bundleDescriptions.get((BundleDescriptionImpl) newDescription);
+ if (existing == null)
+ return false;
+ if (!bundleDescriptions.remove(existing))
+ return false;
+ resolvedBundles.remove(existing);
+ List<DisabledInfo> infos = disabledBundles.remove(existing);
+ if (infos != null) {
+ List<DisabledInfo> newInfos = new ArrayList<DisabledInfo>(infos.size());
+ for (Iterator<DisabledInfo> iInfos = infos.iterator(); iInfos.hasNext();) {
+ DisabledInfo info = iInfos.next();
+ newInfos.add(new DisabledInfo(info.getPolicyName(), info.getMessage(), newDescription));
+ }
+ disabledBundles.put(newDescription, newInfos);
+ }
+ existing.setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true);
+ if (!basicAddBundle(newDescription))
+ return false;
+ resolved = false;
+ getDelta().recordBundleUpdated((BundleDescriptionImpl) newDescription);
+ if (getSystemBundle().equals(newDescription.getSymbolicName()))
+ resetAllSystemCapabilities();
+ if (resolver != null) {
+ boolean pending = isInUse(existing);
+ resolver.bundleUpdated(newDescription, existing, pending);
+ if (pending) {
+ getDelta().recordBundleRemovalPending(existing);
+ addRemovalPending(existing);
+ } else {
+ // an existing bundle has been updated with no dependents it can safely be unresolved now
+ try {
+ resolving = true;
+ resolverErrors.remove(existing);
+ resolveBundle(existing, false, null, null, null, null, null, null, null, null);
+ } finally {
+ resolving = false;
+ }
+ }
+ }
+ updateTimeStamp();
+ return true;
+ }
+ }
+
+ public BundleDescription removeBundle(long bundleId) {
+ synchronized (this.monitor) {
+ BundleDescription toRemove = getBundle(bundleId);
+ if (toRemove == null || !removeBundle(toRemove))
+ return null;
+ return toRemove;
+ }
+ }
+
+ public boolean removeBundle(BundleDescription toRemove) {
+ synchronized (this.monitor) {
+ toRemove = (BundleDescription) bundleDescriptions.get((KeyedElement) toRemove);
+ if (toRemove == null || !bundleDescriptions.remove((KeyedElement) toRemove))
+ return false;
+ resolvedBundles.remove((KeyedElement) toRemove);
+ disabledBundles.remove(toRemove);
+ resolved = false;
+ getDelta().recordBundleRemoved((BundleDescriptionImpl) toRemove);
+ ((BundleDescriptionImpl) toRemove).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true);
+ if (resolver != null) {
+ boolean pending = isInUse(toRemove);
+ resolver.bundleRemoved(toRemove, pending);
+ if (pending) {
+ getDelta().recordBundleRemovalPending((BundleDescriptionImpl) toRemove);
+ addRemovalPending(toRemove);
+ } else {
+ // a bundle has been removed with no dependents it can safely be unresolved now
+ try {
+ resolving = true;
+ resolverErrors.remove(toRemove);
+ resolveBundle(toRemove, false, null, null, null, null, null);
+ } finally {
+ resolving = false;
+ }
+ }
+ }
+ updateTimeStamp();
+ return true;
+ }
+ }
+
+ private boolean isInUse(BundleDescription bundle) {
+ return bundle.getDependents().length > 0;
+ }
+
+ public StateDelta getChanges() {
+ synchronized (this.monitor) {
+ return getDelta();
+ }
+ }
+
+ private StateDeltaImpl getDelta() {
+ if (changes == null)
+ changes = new StateDeltaImpl(this);
+ return changes;
+ }
+
+ public BundleDescription[] getBundles(String symbolicName) {
+ synchronized (this.monitor) {
+ if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName))
+ symbolicName = getSystemBundle();
+ final List<BundleDescription> bundles = new ArrayList<BundleDescription>();
+ for (Iterator<KeyedElement> iter = bundleDescriptions.iterator(); iter.hasNext();) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ if (symbolicName.equals(bundle.getSymbolicName()))
+ bundles.add(bundle);
+ }
+ return bundles.toArray(new BundleDescription[bundles.size()]);
+ }
+ }
+
+ public BundleDescription[] getBundles() {
+ synchronized (this.monitor) {
+ return (BundleDescription[]) bundleDescriptions.elements(new BundleDescription[bundleDescriptions.size()]);
+ }
+ }
+
+ public BundleDescription getBundle(long id) {
+ synchronized (this.monitor) {
+ BundleDescription result = (BundleDescription) bundleDescriptions.getByKey(new Long(id));
+ if (result != null)
+ return result;
+ // need to look in removal pending bundles;
+ for (Iterator<BundleDescription> iter = removalPendings.iterator(); iter.hasNext();) {
+ BundleDescription removedBundle = iter.next();
+ if (removedBundle.getBundleId() == id) // just return the first matching id
+ return removedBundle;
+ }
+ return null;
+ }
+ }
+
+ public BundleDescription getBundle(String name, Version version) {
+ synchronized (this.monitor) {
+ BundleDescription[] allBundles = getBundles(name);
+ if (allBundles.length == 1)
+ return version == null || allBundles[0].getVersion().equals(version) ? allBundles[0] : null;
+ if (allBundles.length == 0)
+ return null;
+ BundleDescription unresolvedFound = null;
+ BundleDescription resolvedFound = null;
+ for (int i = 0; i < allBundles.length; i++) {
+ BundleDescription current = allBundles[i];
+ BundleDescription base;
+
+ if (current.isResolved())
+ base = resolvedFound;
+ else
+ base = unresolvedFound;
+
+ if (version == null || current.getVersion().equals(version)) {
+ if (base != null && (base.getVersion().compareTo(current.getVersion()) <= 0 || base.getBundleId() > current.getBundleId())) {
+ if (base == resolvedFound)
+ resolvedFound = current;
+ else
+ unresolvedFound = current;
+ } else {
+ if (current.isResolved())
+ resolvedFound = current;
+ else
+ unresolvedFound = current;
+ }
+
+ }
+ }
+ if (resolvedFound != null)
+ return resolvedFound;
+ return unresolvedFound;
+ }
+ }
+
+ public long getTimeStamp() {
+ synchronized (this.monitor) {
+ return timeStamp;
+ }
+ }
+
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return resolved || isEmpty();
+ }
+ }
+
+ public void resolveConstraint(VersionConstraint constraint, BaseDescription supplier) {
+ ((VersionConstraintImpl) constraint).setSupplier(supplier);
+ }
+
+ /**
+ * @deprecated
+ */
+ public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) {
+ resolveBundle(bundle, status, hosts, selectedExports, null, resolvedRequires, resolvedImports);
+ }
+
+ /**
+ * @deprecated
+ */
+ public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) {
+ resolveBundle(bundle, status, hosts, selectedExports, substitutedExports, null, resolvedRequires, resolvedImports, null, null);
+ }
+
+ public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires) {
+ synchronized (this.monitor) {
+ if (!resolving)
+ throw new IllegalStateException(); // TODO need error message here!
+ BundleDescriptionImpl modifiable = (BundleDescriptionImpl) bundle;
+ // must record the change before setting the resolve state to
+ // accurately record if a change has happened.
+ getDelta().recordBundleResolved(modifiable, status);
+ // force the new resolution data to stay in memory; we will not read this from disk anymore
+ modifiable.setLazyLoaded(false);
+ modifiable.setStateBit(BundleDescriptionImpl.RESOLVED, status);
+ if (status) {
+ resolverErrors.remove(modifiable);
+ resolvedBundles.add(modifiable);
+ } else {
+ // remove the bundle from the resolved pool
+ resolvedBundles.remove(modifiable);
+ modifiable.removeDependencies();
+ }
+ // to support development mode we will resolveConstraints even if the resolve status == false
+ // we only do this if the resolved constraints are not null
+ if (selectedExports == null || resolvedRequires == null || resolvedImports == null)
+ unresolveConstraints(modifiable);
+ else
+ resolveConstraints(modifiable, hosts, selectedExports, substitutedExports, selectedCapabilities, resolvedRequires, resolvedImports, resolvedCapabilities, resolvedWires);
+ }
+ }
+
+ public void removeBundleComplete(BundleDescription bundle) {
+ synchronized (this.monitor) {
+ if (!resolving)
+ throw new IllegalStateException(); // TODO need error message here!
+ getDelta().recordBundleRemovalComplete((BundleDescriptionImpl) bundle);
+ removalPendings.remove(bundle);
+ }
+ }
+
+ private void resolveConstraints(BundleDescriptionImpl bundle, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires) {
+ HostSpecificationImpl hostSpec = (HostSpecificationImpl) bundle.getHost();
+ if (hostSpec != null) {
+ if (hosts != null) {
+ hostSpec.setHosts(hosts);
+ for (int i = 0; i < hosts.length; i++) {
+ ((BundleDescriptionImpl) hosts[i]).addDependency(bundle, true);
+ checkHostForSubstitutedExports((BundleDescriptionImpl) hosts[i], bundle);
+ }
+ }
+ }
+
+ bundle.setSelectedExports(selectedExports);
+ bundle.setResolvedRequires(resolvedRequires);
+ bundle.setResolvedImports(resolvedImports);
+ bundle.setSubstitutedExports(substitutedExports);
+ bundle.setSelectedCapabilities(selectedCapabilities);
+ bundle.setResolvedCapabilities(resolvedCapabilities);
+ bundle.setStateWires(resolvedWires);
+
+ bundle.addDependencies(hosts, true);
+ bundle.addDependencies(resolvedRequires, true);
+ bundle.addDependencies(resolvedImports, true);
+ bundle.addDependencies(resolvedCapabilities, true);
+ }
+
+ private void checkHostForSubstitutedExports(BundleDescriptionImpl host, BundleDescriptionImpl fragment) {
+ // TODO need to handle this case where a fragment has its own export substituted
+ // there are issues here because the order in which fragments are resolved is not always the same ...
+ }
+
+ private void unresolveConstraints(BundleDescriptionImpl bundle) {
+ HostSpecificationImpl host = (HostSpecificationImpl) bundle.getHost();
+ if (host != null)
+ host.setHosts(null);
+
+ bundle.setSelectedExports(null);
+ bundle.setResolvedImports(null);
+ bundle.setResolvedRequires(null);
+ bundle.setSubstitutedExports(null);
+ bundle.setSelectedCapabilities(null);
+ bundle.setResolvedCapabilities(null);
+ bundle.setStateWires(null);
+ bundle.clearAddedDynamicImportPackages();
+
+ // remove the constraint suppliers
+ NativeCodeSpecificationImpl nativeCode = (NativeCodeSpecificationImpl) bundle.getNativeCodeSpecification();
+ if (nativeCode != null)
+ nativeCode.setSupplier(null);
+ ImportPackageSpecification[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++)
+ ((ImportPackageSpecificationImpl) imports[i]).setSupplier(null);
+ BundleSpecification[] requires = bundle.getRequiredBundles();
+ for (int i = 0; i < requires.length; i++)
+ ((BundleSpecificationImpl) requires[i]).setSupplier(null);
+ GenericSpecification[] genericRequires = bundle.getGenericRequires();
+ if (genericRequires.length > 0)
+ for (int i = 0; i < genericRequires.length; i++)
+ ((GenericSpecificationImpl) genericRequires[i]).setSupplers(null);
+
+ bundle.removeDependencies();
+ }
+
+ private StateDelta resolve(boolean incremental, BundleDescription[] reResolve, BundleDescription[] triggers) {
+ fullyLoad();
+ synchronized (this.monitor) {
+ if (resolver == null)
+ throw new IllegalStateException("no resolver set"); //$NON-NLS-1$
+ if (resolving == true)
+ throw new IllegalStateException("An attempt to start a nested resolve process has been detected."); //$NON-NLS-1$
+ try {
+ resolving = true;
+ long start = 0;
+ if (!incremental) {
+ resolved = false;
+ reResolve = getBundles();
+ // need to get any removal pendings before flushing
+ if (removalPendings.size() > 0) {
+ BundleDescription[] removed = internalGetRemovalPending();
+ reResolve = mergeBundles(reResolve, removed);
+ }
+ flush(reResolve);
+ } else {
+ if (resolved && reResolve == null)
+ return new StateDeltaImpl(this);
+ if (developmentMode) {
+ // in dev mode we need to aggressively flush removal pendings
+ if (removalPendings.size() > 0) {
+ BundleDescription[] removed = internalGetRemovalPending();
+ reResolve = mergeBundles(reResolve, removed);
+ }
+ }
+ if (reResolve == null)
+ reResolve = internalGetRemovalPending();
+ if (triggers == null) {
+ Set<BundleDescription> triggerSet = new HashSet<BundleDescription>();
+ Collection<BundleDescription> closure = getDependencyClosure(Arrays.asList(reResolve));
+ for (BundleDescription toRefresh : closure) {
+ Bundle bundle = toRefresh.getBundle();
+ if (bundle != null && (bundle.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED | Bundle.RESOLVED)) == 0)
+ triggerSet.add(toRefresh);
+ }
+ triggers = triggerSet.toArray(new BundleDescription[triggerSet.size()]);
+ }
+ }
+ // use the Headers class to handle ignoring case while matching keys (bug 180817)
+ @SuppressWarnings("unchecked")
+ Headers<Object, Object>[] tmpPlatformProperties = new Headers[platformProperties.length];
+ for (int i = 0; i < platformProperties.length; i++) {
+ tmpPlatformProperties[i] = new Headers<Object, Object>(platformProperties[i].size());
+ for (Enumeration<Object> keys = platformProperties[i].keys(); keys.hasMoreElements();) {
+ Object key = keys.nextElement();
+ tmpPlatformProperties[i].put(key, platformProperties[i].get(key));
+ }
+ }
+
+ ResolverHookFactory currentFactory = hookFactory;
+ if (currentFactory != null) {
+ @SuppressWarnings("unchecked")
+ Collection<BundleRevision> triggerRevisions = Collections.unmodifiableCollection(triggers == null ? Collections.EMPTY_LIST : Arrays.asList((BundleRevision[]) triggers));
+ begin(triggerRevisions);
+ }
+ ResolverHookException error = null;
+ try {
+ resolver.resolve(reResolve, tmpPlatformProperties);
+ } catch (ResolverHookException e) {
+ error = e;
+ resolverErrors.clear();
+ }
+ resolved = removalPendings.size() == 0;
+
+ StateDeltaImpl savedChanges = changes == null ? new StateDeltaImpl(this) : changes;
+ savedChanges.setResolverHookException(error);
+ changes = new StateDeltaImpl(this);
+
+ if (savedChanges.getChanges().length > 0)
+ updateTimeStamp();
+ return savedChanges;
+ } finally {
+ resolving = false;
+ }
+ }
+ }
+
+ private BundleDescription[] mergeBundles(BundleDescription[] reResolve, BundleDescription[] removed) {
+ if (reResolve == null)
+ return removed; // just return all the removed bundles
+ if (reResolve.length == 0)
+ return reResolve; // if reResolve length==0 then we want to prevent pending removal
+ // merge in all removal pending bundles that are not already in the list
+ List<BundleDescription> result = new ArrayList<BundleDescription>(reResolve.length + removed.length);
+ for (int i = 0; i < reResolve.length; i++)
+ result.add(reResolve[i]);
+ for (int i = 0; i < removed.length; i++) {
+ boolean found = false;
+ for (int j = 0; j < reResolve.length; j++) {
+ if (removed[i] == reResolve[j]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ result.add(removed[i]);
+ }
+ return result.toArray(new BundleDescription[result.size()]);
+ }
+
+ private void flush(BundleDescription[] bundles) {
+ resolver.flush();
+ resolved = false;
+ resolverErrors.clear();
+ if (resolvedBundles.isEmpty())
+ return;
+ for (int i = 0; i < bundles.length; i++) {
+ resolveBundle(bundles[i], false, null, null, null, null, null);
+ }
+ resolvedBundles.clear();
+ }
+
+ public StateDelta resolve() {
+ return resolve(true, null, null);
+ }
+
+ public StateDelta resolve(boolean incremental) {
+ return resolve(incremental, null, null);
+ }
+
+ public StateDelta resolve(BundleDescription[] reResolve) {
+ return resolve(true, reResolve, null);
+ }
+
+ public StateDelta resolve(BundleDescription[] resolve, boolean discard) {
+ BundleDescription[] reResolve = discard ? resolve : new BundleDescription[0];
+ BundleDescription[] triggers = discard ? null : resolve;
+ return resolve(true, reResolve, triggers);
+ }
+
+ @SuppressWarnings("deprecation")
+ public void setOverrides(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setResolverHookFactory(ResolverHookFactory hookFactory) {
+ synchronized (this.monitor) {
+ if (this.hookFactory != null)
+ throw new IllegalStateException("Resolver hook factory is already set."); //$NON-NLS-1$
+ this.hookFactory = hookFactory;
+ }
+ }
+
+ private ResolverHook begin(Collection<BundleRevision> triggers) {
+ ResolverHookFactory current;
+ synchronized (this.monitor) {
+ current = this.hookFactory;
+ }
+ ResolverHook newHook = current.begin(triggers);
+ synchronized (this.monitor) {
+ this.hook = newHook;
+ }
+ return newHook;
+ }
+
+ ResolverHookFactory getResolverHookFactory() {
+ synchronized (this.monitor) {
+ return this.hookFactory;
+ }
+ }
+
+ public ResolverHook getResolverHook() {
+ synchronized (this.monitor) {
+ return this.hook;
+ }
+ }
+
+ public BundleDescription[] getResolvedBundles() {
+ synchronized (this.monitor) {
+ return (BundleDescription[]) resolvedBundles.elements(new BundleDescription[resolvedBundles.size()]);
+ }
+ }
+
+ public boolean isEmpty() {
+ synchronized (this.monitor) {
+ return bundleDescriptions.isEmpty();
+ }
+ }
+
+ void setResolved(boolean resolved) {
+ synchronized (this.monitor) {
+ this.resolved = resolved;
+ }
+ }
+
+ boolean basicAddBundle(BundleDescription description) {
+ synchronized (this.monitor) {
+ StateImpl origState = (StateImpl) description.getContainingState();
+ if (origState != null && origState != this) {
+ if (origState.removalPendings.contains(description))
+ throw new IllegalStateException(NLS.bind(StateMsg.BUNDLE_PENDING_REMOVE_STATE, description.toString()));
+ if (origState.getBundle(description.getBundleId()) == description)
+ throw new IllegalStateException(NLS.bind(StateMsg.BUNDLE_IN_OTHER_STATE, description.toString()));
+ }
+ ((BundleDescriptionImpl) description).setContainingState(this);
+ ((BundleDescriptionImpl) description).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, false);
+ if (bundleDescriptions.add((BundleDescriptionImpl) description)) {
+ if (description.getBundleId() > getHighestBundleId())
+ highestBundleId = description.getBundleId();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ void addResolvedBundle(BundleDescriptionImpl resolvedBundle) {
+ synchronized (this.monitor) {
+ resolvedBundles.add(resolvedBundle);
+ }
+ }
+
+ public ExportPackageDescription[] getExportedPackages() {
+ fullyLoad();
+ synchronized (this.monitor) {
+ List<ExportPackageDescription> allExportedPackages = new ArrayList<ExportPackageDescription>();
+ for (Iterator<KeyedElement> iter = resolvedBundles.iterator(); iter.hasNext();) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ ExportPackageDescription[] bundlePackages = bundle.getSelectedExports();
+ if (bundlePackages == null)
+ continue;
+ for (int i = 0; i < bundlePackages.length; i++)
+ allExportedPackages.add(bundlePackages[i]);
+ }
+ for (Iterator<BundleDescription> iter = removalPendings.iterator(); iter.hasNext();) {
+ BundleDescription bundle = iter.next();
+ ExportPackageDescription[] bundlePackages = bundle.getSelectedExports();
+ if (bundlePackages == null)
+ continue;
+ for (int i = 0; i < bundlePackages.length; i++)
+ allExportedPackages.add(bundlePackages[i]);
+ }
+ return allExportedPackages.toArray(new ExportPackageDescription[allExportedPackages.size()]);
+ }
+ }
+
+ BundleDescription[] getFragments(final BundleDescription host) {
+ final List<BundleDescription> fragments = new ArrayList<BundleDescription>();
+ for (Iterator<KeyedElement> iter = bundleDescriptions.iterator(); iter.hasNext();) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ HostSpecification hostSpec = bundle.getHost();
+
+ if (hostSpec != null) {
+ BundleDescription[] hosts = hostSpec.getHosts();
+ if (hosts != null)
+ for (int i = 0; i < hosts.length; i++)
+ if (hosts[i] == host) {
+ fragments.add(bundle);
+ break;
+ }
+ }
+ }
+ return fragments.toArray(new BundleDescription[fragments.size()]);
+ }
+
+ public void setTimeStamp(long newTimeStamp) {
+ synchronized (this.monitor) {
+ timeStamp = newTimeStamp;
+ }
+ }
+
+ private void updateTimeStamp() {
+ synchronized (this.monitor) {
+ if (getTimeStamp() == Long.MAX_VALUE)
+ setTimeStamp(0);
+ setTimeStamp(getTimeStamp() + 1);
+ }
+ }
+
+ public StateObjectFactory getFactory() {
+ return factory;
+ }
+
+ void setFactory(StateObjectFactory factory) {
+ this.factory = factory;
+ }
+
+ public BundleDescription getBundleByLocation(String location) {
+ synchronized (this.monitor) {
+ for (Iterator<KeyedElement> i = bundleDescriptions.iterator(); i.hasNext();) {
+ BundleDescription current = (BundleDescription) i.next();
+ if (location.equals(current.getLocation()))
+ return current;
+ }
+ return null;
+ }
+ }
+
+ public Resolver getResolver() {
+ synchronized (this.monitor) {
+ return resolver;
+ }
+ }
+
+ public void setResolver(Resolver newResolver) {
+ if (resolver == newResolver)
+ return;
+ if (resolver != null) {
+ Resolver oldResolver = resolver;
+ resolver = null;
+ oldResolver.setState(null);
+ }
+ synchronized (this.monitor) {
+ resolver = newResolver;
+ }
+ if (resolver == null)
+ return;
+ resolver.setState(this);
+ }
+
+ public boolean setPlatformProperties(Dictionary<?, ?> platformProperties) {
+ return setPlatformProperties(new Dictionary[] {platformProperties});
+ }
+
+ public boolean setPlatformProperties(Dictionary<?, ?>[] platformProperties) {
+ return setPlatformProperties(platformProperties, true);
+ }
+
+ synchronized boolean setPlatformProperties(Dictionary<?, ?>[] platformProperties, boolean resetSystemExports) {
+ if (platformProperties.length == 0)
+ throw new IllegalArgumentException();
+ // copy the properties for our use internally;
+ // only copy String and String[] values
+ @SuppressWarnings("unchecked")
+ Dictionary<Object, Object>[] newPlatformProperties = new Dictionary[platformProperties.length];
+ for (int i = 0; i < platformProperties.length; i++) {
+ newPlatformProperties[i] = new Hashtable<Object, Object>(platformProperties[i].size());
+ synchronized (platformProperties[i]) {
+ for (Enumeration<?> keys = platformProperties[i].keys(); keys.hasMoreElements();) {
+ Object key = keys.nextElement();
+ Object value = platformProperties[i].get(key);
+ newPlatformProperties[i].put(key, value);
+ }
+ }
+ // make sure the bundle native code osgi properties have decent defaults
+ if (newPlatformProperties[i].get(Constants.FRAMEWORK_OS_NAME) == null && newPlatformProperties[i].get(OSGI_OS) != null)
+ newPlatformProperties[i].put(Constants.FRAMEWORK_OS_NAME, newPlatformProperties[i].get(OSGI_OS));
+ if (newPlatformProperties[i].get(Constants.FRAMEWORK_PROCESSOR) == null && newPlatformProperties[i].get(OSGI_ARCH) != null)
+ newPlatformProperties[i].put(Constants.FRAMEWORK_PROCESSOR, newPlatformProperties[i].get(OSGI_ARCH));
+ if (newPlatformProperties[i].get(Constants.FRAMEWORK_LANGUAGE) == null && newPlatformProperties[i].get(OSGI_NL) instanceof String) {
+ String osgiNL = (String) newPlatformProperties[i].get(OSGI_NL);
+ int idx = osgiNL.indexOf('_');
+ if (idx >= 0)
+ osgiNL = osgiNL.substring(0, idx);
+ newPlatformProperties[i].put(Constants.FRAMEWORK_LANGUAGE, osgiNL);
+ }
+
+ }
+ boolean result = false;
+ boolean performResetSystemExports = false;
+ boolean performResetSystemCapabilities = false;
+ if (this.platformProperties.length != newPlatformProperties.length) {
+ result = true;
+ performResetSystemExports = true;
+ performResetSystemCapabilities = true;
+ } else {
+ // we need to see if any of the existing filter prop keys have changed
+ String[] keys = getPlatformPropertyKeys();
+ for (int i = 0; i < newPlatformProperties.length && !result; i++) {
+ result |= changedProps(this.platformProperties[i], newPlatformProperties[i], keys);
+ if (resetSystemExports) {
+ performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES));
+ performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA));
+ performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.SYSTEM_BUNDLE_SYMBOLICNAME), newPlatformProperties[i].get(Constants.SYSTEM_BUNDLE_SYMBOLICNAME));
+ performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES));
+ performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA));
+ performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT), newPlatformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
+ }
+ }
+ }
+ // always do a complete replacement of the properties in case new bundles are added that uses new filter props
+ this.platformProperties = newPlatformProperties;
+ if (performResetSystemExports)
+ resetSystemExports();
+ if (performResetSystemCapabilities)
+ resetSystemCapabilities();
+ developmentMode = this.platformProperties.length == 0 ? false : DEVELOPMENT_MODE.equals(this.platformProperties[0].get(OSGI_RESOLVER_MODE));
+ return result;
+ }
+
+ private void resetAllSystemCapabilities() {
+ resetSystemExports();
+ resetSystemCapabilities();
+ }
+
+ private void resetSystemExports() {
+ BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
+ for (int idx = 0; idx < systemBundles.length; idx++) {
+ BundleDescriptionImpl systemBundle = (BundleDescriptionImpl) systemBundles[idx];
+ ExportPackageDescription[] exports = systemBundle.getExportPackages();
+ List<ExportPackageDescription> newExports = new ArrayList<ExportPackageDescription>(exports.length);
+ for (int i = 0; i < exports.length; i++)
+ if (((Integer) exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() < 0)
+ newExports.add(exports[i]);
+ addSystemExports(newExports);
+ systemBundle.setExportPackages(newExports.toArray(new ExportPackageDescription[newExports.size()]));
+ }
+ }
+
+ private void addSystemExports(List<ExportPackageDescription> exports) {
+ for (int i = 0; i < platformProperties.length; i++)
+ try {
+ addSystemExports(exports, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES)), i);
+ addSystemExports(exports, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)), i);
+ } catch (BundleException e) {
+ // TODO consider throwing this...
+ }
+ }
+
+ private void addSystemExports(List<ExportPackageDescription> exports, ManifestElement[] elements, int index) {
+ if (elements == null)
+ return;
+ ExportPackageDescription[] systemExports = StateBuilder.createExportPackages(elements, null, null, false);
+ Integer profInx = new Integer(index);
+ for (int j = 0; j < systemExports.length; j++) {
+ ((ExportPackageDescriptionImpl) systemExports[j]).setDirective(ExportPackageDescriptionImpl.EQUINOX_EE, profInx);
+ exports.add(systemExports[j]);
+ }
+ }
+
+ private void resetSystemCapabilities() {
+ BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
+ for (BundleDescription systemBundle : systemBundles) {
+ GenericDescription[] capabilities = systemBundle.getGenericCapabilities();
+ List<GenericDescription> newCapabilities = new ArrayList<GenericDescription>(capabilities.length);
+ for (GenericDescription capability : capabilities) {
+ Object equinoxEEIndex = capability.getDeclaredAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (equinoxEEIndex == null)
+ newCapabilities.add(capability); // keep the built in ones.
+ }
+ // now add the externally defined ones
+ addSystemCapabilities(newCapabilities);
+ ((BundleDescriptionImpl) systemBundle).setGenericCapabilities(newCapabilities.toArray(new GenericDescription[newCapabilities.size()]));
+ }
+ }
+
+ private void addSystemCapabilities(List<GenericDescription> capabilities) {
+ for (int i = 0; i < platformProperties.length; i++)
+ try {
+ addSystemCapabilities(capabilities, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES)), i);
+ addSystemCapabilities(capabilities, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA)), i);
+ checkOSGiEE(capabilities, (String) platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT), i);
+ } catch (BundleException e) {
+ // TODO consider throwing this...
+ }
+ }
+
+ private void checkOSGiEE(List<GenericDescription> capabilities, String profileEE, Integer profileIndex) {
+ if (profileEE == null || profileEE.length() == 0)
+ return;
+ for (GenericDescription capability : capabilities) {
+ if (OSGI_EE_NAMESPACE.equals(capability.getType()) && profileIndex.equals(capability.getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE)))
+ return; // profile already specifies osgi.ee capabilities
+ }
+ Map<String, List<String>> eeVersions = new HashMap<String, List<String>>();
+ String[] ees = ManifestElement.getArrayFromList(profileEE);
+ for (String ee : ees) {
+ String[] eeNameVersion = StateBuilder.getOSGiEENameVersion(ee);
+
+ List<String> versions = eeVersions.get(eeNameVersion[0]);
+ if (versions == null) {
+ versions = new ArrayList<String>();
+ eeVersions.put(eeNameVersion[0], versions);
+ }
+ if (eeNameVersion[1] != null && !versions.contains(eeNameVersion[1]))
+ versions.add(eeNameVersion[1]);
+ }
+ for (Map.Entry<String, List<String>> eeVersion : eeVersions.entrySet()) {
+ GenericDescriptionImpl capability = new GenericDescriptionImpl();
+ capability.setType(OSGI_EE_NAMESPACE);
+ Dictionary<String, Object> attributes = new Hashtable<String, Object>();
+ attributes.put(capability.getType(), eeVersion.getKey());
+ if (eeVersion.getValue().size() > 0) {
+ List<Version> versions = new ArrayList<Version>(eeVersion.getValue().size());
+ for (String version : eeVersion.getValue()) {
+ versions.add(new Version(version));
+ }
+ attributes.put("version", versions); //$NON-NLS-1$
+ }
+ attributes.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex);
+ capability.setAttributes(attributes);
+ capabilities.add(capability);
+ }
+ }
+
+ private void addSystemCapabilities(List<GenericDescription> capabilities, ManifestElement[] elements, Integer profileIndex) {
+ try {
+ StateBuilder.createOSGiCapabilities(elements, capabilities, profileIndex);
+ } catch (BundleException e) {
+ throw new RuntimeException("Unexpected exception adding system capabilities.", e); //$NON-NLS-1$
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Dictionary[] getPlatformProperties() {
+ return platformProperties;
+ }
+
+ private boolean checkProp(Object origObj, Object newObj) {
+ if ((origObj == null && newObj != null) || (origObj != null && newObj == null))
+ return true;
+ if (origObj == null)
+ return false;
+ if (origObj.getClass() != newObj.getClass())
+ return true;
+ if (origObj instanceof String[]) {
+ String[] origProps = (String[]) origObj;
+ String[] newProps = (String[]) newObj;
+ if (origProps.length != newProps.length)
+ return true;
+ for (int i = 0; i < origProps.length; i++) {
+ if (!origProps[i].equals(newProps[i]))
+ return true;
+ }
+ return false;
+ }
+ return !origObj.equals(newObj);
+ }
+
+ private boolean changedProps(Dictionary<Object, Object> origProps, Dictionary<Object, Object> newProps, String[] keys) {
+ for (int i = 0; i < keys.length; i++) {
+ Object origProp = origProps.get(keys[i]);
+ Object newProp = newProps.get(keys[i]);
+ if (checkProp(origProp, newProp))
+ return true;
+ }
+ return false;
+ }
+
+ public String getSystemBundle() {
+ String symbolicName = null;
+ if (platformProperties != null && platformProperties.length > 0)
+ symbolicName = (String) platformProperties[0].get(STATE_SYSTEM_BUNDLE);
+ return symbolicName != null ? symbolicName : EquinoxContainer.NAME;
+ }
+
+ public BundleDescription[] getRemovalPending() {
+ synchronized (this.monitor) {
+ return removalPendings.toArray(new BundleDescription[removalPendings.size()]);
+ }
+ }
+
+ private void addRemovalPending(BundleDescription removed) {
+ synchronized (this.monitor) {
+ if (!removalPendings.contains(removed))
+ removalPendings.addFirst(removed);
+ }
+ }
+
+ public Collection<BundleDescription> getDependencyClosure(Collection<BundleDescription> bundles) {
+ BundleDescription[] removals = getRemovalPending();
+ Set<BundleDescription> result = new HashSet<BundleDescription>();
+ for (BundleDescription bundle : bundles) {
+ addDependents(bundle, result, removals);
+ }
+ return result;
+ }
+
+ private static void addDependents(BundleDescription bundle, Set<BundleDescription> result, BundleDescription[] removals) {
+ if (result.contains(bundle))
+ return; // avoid cycles
+ result.add(bundle);
+ BundleDescription[] dependents = bundle.getDependents();
+ for (BundleDescription dependent : dependents)
+ addDependents(dependent, result, removals);
+ // check if this is a removal pending
+ for (BundleDescription removed : removals) {
+ if (removed.getBundleId() == bundle.getBundleId())
+ addDependents(removed, result, removals);
+ }
+ }
+
+ /**
+ * Returns the latest versions BundleDescriptions which have old removal pending versions.
+ * @return the BundleDescriptions that have removal pending versions.
+ */
+ private BundleDescription[] internalGetRemovalPending() {
+ synchronized (this.monitor) {
+ Iterator<BundleDescription> removed = removalPendings.iterator();
+ BundleDescription[] result = new BundleDescription[removalPendings.size()];
+ int i = 0;
+ while (removed.hasNext())
+ // we return the latest version of the description if it is still contained in the state (bug 287636)
+ result[i++] = getBundle(removed.next().getBundleId());
+ return result;
+ }
+ }
+
+ public ExportPackageDescription linkDynamicImport(BundleDescription importingBundle, String requestedPackage) {
+ if (resolver == null)
+ throw new IllegalStateException("no resolver set"); //$NON-NLS-1$
+ BundleDescriptionImpl importer = (BundleDescriptionImpl) importingBundle;
+ if (importer.getDynamicStamp(requestedPackage) == getTimeStamp())
+ return null;
+ fullyLoad();
+ synchronized (this.monitor) {
+ ResolverHook currentHook = null;
+ try {
+ resolving = true;
+ ResolverHookFactory currentFactory = hookFactory;
+ if (currentFactory != null) {
+ Collection<BundleRevision> triggers = new ArrayList<BundleRevision>(1);
+ triggers.add(importingBundle);
+ triggers = Collections.unmodifiableCollection(triggers);
+ currentHook = begin(triggers);
+ }
+ // ask the resolver to resolve our dynamic import
+ ExportPackageDescriptionImpl result = (ExportPackageDescriptionImpl) resolver.resolveDynamicImport(importingBundle, requestedPackage);
+ if (result == null)
+ importer.setDynamicStamp(requestedPackage, new Long(getTimeStamp()));
+ else {
+ importer.setDynamicStamp(requestedPackage, null); // remove any cached timestamp
+ // need to add the result to the list of resolved imports
+ importer.addDynamicResolvedImport(result);
+ }
+ setDynamicCacheChanged(true);
+ return result;
+ } finally {
+ resolving = false;
+ if (currentHook != null)
+ currentHook.end();
+ }
+ }
+
+ }
+
+ public void addDynamicImportPackages(BundleDescription importingBundle, ImportPackageSpecification[] dynamicImports) {
+ synchronized (this.monitor) {
+ ((BundleDescriptionImpl) importingBundle).addDynamicImportPackages(dynamicImports);
+ setDynamicCacheChanged(true);
+ }
+ }
+
+ void setReader(StateReader reader) {
+ synchronized (this.monitor) {
+ this.reader = reader;
+ }
+ }
+
+ StateReader getReader() {
+ synchronized (this.monitor) {
+ return reader;
+ }
+ }
+
+ // not synchronized on this to prevent deadlock
+ public final void fullyLoad() {
+ synchronized (this.monitor) {
+ if (reader == null)
+ return;
+ if (fullyLoaded == true)
+ return;
+ if (reader.isLazyLoaded())
+ reader.fullyLoad();
+ fullyLoaded = true;
+ }
+ }
+
+ // not synchronized on this to prevent deadlock
+ public final boolean unloadLazyData(long checkStamp) {
+ // make sure no other thread is trying to unload or load
+ synchronized (this.monitor) {
+ if (checkStamp != getTimeStamp() || dynamicCacheChanged())
+ return false;
+ if (reader.getAccessedFlag()) {
+ reader.setAccessedFlag(false); // reset accessed flag
+ return true;
+ }
+ fullyLoaded = false;
+ BundleDescription[] bundles = getBundles();
+ for (int i = 0; i < bundles.length; i++)
+ ((BundleDescriptionImpl) bundles[i]).unload();
+ reader.flushLazyObjectCache();
+ resolver.flush();
+ return true;
+ }
+ }
+
+ public ExportPackageDescription[] getSystemPackages() {
+ synchronized (this.monitor) {
+ List<ExportPackageDescription> result = new ArrayList<ExportPackageDescription>();
+ BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
+ if (systemBundles.length > 0) {
+ BundleDescriptionImpl systemBundle = (BundleDescriptionImpl) systemBundles[0];
+ ExportPackageDescription[] exports = systemBundle.getExportPackages();
+ for (int i = 0; i < exports.length; i++)
+ if (((Integer) exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() >= 0)
+ result.add(exports[i]);
+ }
+ return result.toArray(new ExportPackageDescription[result.size()]);
+ }
+ }
+
+ boolean inStrictMode() {
+ synchronized (this.monitor) {
+ return STRICT_MODE.equals(getPlatformProperties()[0].get(OSGI_RESOLVER_MODE));
+ }
+ }
+
+ public ResolverError[] getResolverErrors(BundleDescription bundle) {
+ synchronized (this.monitor) {
+ if (bundle.isResolved())
+ return new ResolverError[0];
+ List<ResolverError> result = resolverErrors.get(bundle);
+ return result == null ? new ResolverError[0] : result.toArray(new ResolverError[result.size()]);
+ }
+ }
+
+ public void addResolverError(BundleDescription bundle, int type, String data, VersionConstraint unsatisfied) {
+ synchronized (this.monitor) {
+ if (!resolving)
+ throw new IllegalStateException(); // TODO need error message here!
+ List<ResolverError> errors = resolverErrors.get(bundle);
+ if (errors == null) {
+ errors = new ArrayList<ResolverError>(1);
+ resolverErrors.put(bundle, errors);
+ }
+ errors.add(new ResolverErrorImpl((BundleDescriptionImpl) bundle, type, data, unsatisfied));
+ }
+ }
+
+ public void removeResolverErrors(BundleDescription bundle) {
+ synchronized (this.monitor) {
+ if (!resolving)
+ throw new IllegalStateException(); // TODO need error message here!
+ resolverErrors.remove(bundle);
+ }
+ }
+
+ public boolean dynamicCacheChanged() {
+ synchronized (this.monitor) {
+ return dynamicCacheChanged;
+ }
+ }
+
+ void setDynamicCacheChanged(boolean dynamicCacheChanged) {
+ synchronized (this.monitor) {
+ this.dynamicCacheChanged = dynamicCacheChanged;
+ }
+ }
+
+ public StateHelper getStateHelper() {
+ return StateHelperImpl.getInstance();
+ }
+
+ void addPlatformPropertyKeys(String[] keys) {
+ synchronized (platformPropertyKeys) {
+ for (int i = 0; i < keys.length; i++)
+ if (!platformPropertyKeys.contains(keys[i]))
+ platformPropertyKeys.add(keys[i]);
+ }
+ }
+
+ String[] getPlatformPropertyKeys() {
+ synchronized (platformPropertyKeys) {
+ return platformPropertyKeys.toArray(new String[platformPropertyKeys.size()]);
+ }
+ }
+
+ public long getHighestBundleId() {
+ synchronized (this.monitor) {
+ return highestBundleId;
+ }
+ }
+
+ public void setNativePathsInvalid(NativeCodeDescription nativeCodeDescription, boolean hasInvalidNativePaths) {
+ ((NativeCodeDescriptionImpl) nativeCodeDescription).setInvalidNativePaths(hasInvalidNativePaths);
+ }
+
+ public BundleDescription[] getDisabledBundles() {
+ synchronized (this.monitor) {
+ return disabledBundles.keySet().toArray(new BundleDescription[0]);
+ }
+ }
+
+ public void addDisabledInfo(DisabledInfo disabledInfo) {
+ synchronized (this.monitor) {
+ if (getBundle(disabledInfo.getBundle().getBundleId()) != disabledInfo.getBundle())
+ throw new IllegalArgumentException(NLS.bind(StateMsg.BUNDLE_NOT_IN_STATE, disabledInfo.getBundle()));
+ List<DisabledInfo> currentInfos = disabledBundles.get(disabledInfo.getBundle());
+ if (currentInfos == null) {
+ currentInfos = new ArrayList<DisabledInfo>(1);
+ currentInfos.add(disabledInfo);
+ disabledBundles.put(disabledInfo.getBundle(), currentInfos);
+ } else {
+ Iterator<DisabledInfo> it = currentInfos.iterator();
+ while (it.hasNext()) {
+ DisabledInfo currentInfo = it.next();
+ if (disabledInfo.getPolicyName().equals(currentInfo.getPolicyName())) {
+ currentInfos.remove(currentInfo);
+ break;
+ }
+ }
+ currentInfos.add(disabledInfo);
+ }
+ updateTimeStamp();
+ }
+ }
+
+ public void removeDisabledInfo(DisabledInfo disabledInfo) {
+ synchronized (this.monitor) {
+ List<DisabledInfo> currentInfos = disabledBundles.get(disabledInfo.getBundle());
+ if ((currentInfos != null) && currentInfos.contains(disabledInfo)) {
+ currentInfos.remove(disabledInfo);
+ if (currentInfos.isEmpty()) {
+ disabledBundles.remove(disabledInfo.getBundle());
+ }
+ }
+ updateTimeStamp();
+ }
+ }
+
+ public DisabledInfo getDisabledInfo(BundleDescription bundle, String policyName) {
+ synchronized (this.monitor) {
+ List<DisabledInfo> currentInfos = disabledBundles.get(bundle);
+ if (currentInfos == null)
+ return null;
+ Iterator<DisabledInfo> it = currentInfos.iterator();
+ while (it.hasNext()) {
+ DisabledInfo currentInfo = it.next();
+ if (currentInfo.getPolicyName().equals(policyName)) {
+ return currentInfo;
+ }
+ }
+ return null;
+ }
+ }
+
+ public DisabledInfo[] getDisabledInfos(BundleDescription bundle) {
+ synchronized (this.monitor) {
+ List<DisabledInfo> currentInfos = disabledBundles.get(bundle);
+ return currentInfos == null ? EMPTY_DISABLEDINFOS : currentInfos.toArray(new DisabledInfo[currentInfos.size()]);
+ }
+ }
+
+ /*
+ * Used by StateWriter to get all the DisabledInfo objects to persist
+ */
+ DisabledInfo[] getDisabledInfos() {
+ List<DisabledInfo> results = new ArrayList<DisabledInfo>();
+ synchronized (this.monitor) {
+ for (Iterator<List<DisabledInfo>> allDisabledInfos = disabledBundles.values().iterator(); allDisabledInfos.hasNext();)
+ results.addAll(allDisabledInfos.next());
+ }
+ return results.toArray(new DisabledInfo[results.size()]);
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMessages.properties b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMessages.properties
new file mode 100644
index 000000000..acf9ffad8
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMessages.properties
@@ -0,0 +1,35 @@
+###############################################################################
+# Copyright (c) 2004, 2011 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+
+#State/Resolver Messages for EN locale
+BUNDLE_NOT_IN_STATE=The bundle is not in the state: {0}
+BUNDLE_IN_OTHER_STATE=The bundle belongs to another state: {0}
+BUNDLE_PENDING_REMOVE_STATE = The bundle is pending remove in another state: {0}
+
+HEADER_REQUIRED=The \"{0}\" header must be specified
+HEADER_PACKAGE_DUPLICATES=Cannot import a package more than once \"{0}\"
+HEADER_PACKAGE_JAVA=Cannot specify java.* packages in Import/Export headers \"{0}\"
+HEADER_VERSION_ERROR=The attributes \"{0}\" and \"{1}\" must match
+HEADER_EXPORT_ATTR_ERROR=Specifying \"{0}\" in the \"{1}\" header is not permitted
+HEADER_DIRECTIVE_DUPLICATES=Duplicate directives are not permitted \"{0}\"
+HEADER_ATTRIBUTE_DUPLICATES=Duplicate attributes are not permitted \"{0}\"
+HEADER_EXTENSION_ERROR=Extension bundle is not a fragment to the system bundle \"{0}\"
+
+RES_ERROR_DISABLEDBUNDLE=The bundle is disabled: {0}
+RES_ERROR_MISSING_PERMISSION=Missing Permission: {0}
+RES_ERROR_MISSING_CONSTRAINT=Missing Constraint: {0}
+RES_ERROR_FRAGMENT_CONFLICT=Constraints from the fragment conflict with the host: {0}
+RES_ERROR_USES_CONFLICT=Package uses conflict: {0}
+RES_ERROR_SINGLETON_CONFLICT=Another singleton version selected: {0}
+RES_ERROR_PLATFORM_FILTER=Platform filter did not match: {0}
+RES_ERROR_NO_NATIVECODE_MATCH=No match found for native code: {0}
+RES_ERROR_NATIVECODE_PATH_INVALID=The native code paths cannot be found: {0}
+RES_ERROR_UNKNOWN=Unknown resolution error
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMsg.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMsg.java
new file mode 100644
index 000000000..1aeb570ae
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateMsg.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.util.NLS;
+
+public class StateMsg extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.osgi.internal.resolver.StateMessages"; //$NON-NLS-1$
+
+ public static String BUNDLE_NOT_IN_STATE;
+ public static String BUNDLE_IN_OTHER_STATE;
+ public static String BUNDLE_PENDING_REMOVE_STATE;
+
+ public static String HEADER_REQUIRED;
+ public static String HEADER_PACKAGE_DUPLICATES;
+ public static String HEADER_PACKAGE_JAVA;
+ public static String HEADER_VERSION_ERROR;
+ public static String HEADER_EXPORT_ATTR_ERROR;
+ public static String HEADER_DIRECTIVE_DUPLICATES;
+ public static String HEADER_ATTRIBUTE_DUPLICATES;
+ public static String HEADER_EXTENSION_ERROR;
+
+ public static String RES_ERROR_DISABLEDBUNDLE;
+ public static String RES_ERROR_MISSING_PERMISSION;
+ public static String RES_ERROR_MISSING_CONSTRAINT;
+ public static String RES_ERROR_FRAGMENT_CONFLICT;
+ public static String RES_ERROR_USES_CONFLICT;
+ public static String RES_ERROR_SINGLETON_CONFLICT;
+ public static String RES_ERROR_PLATFORM_FILTER;
+ public static String RES_ERROR_NO_NATIVECODE_MATCH;
+ public static String RES_ERROR_NATIVECODE_PATH_INVALID;
+ public static String RES_ERROR_UNKNOWN;
+
+ static {
+ // initialize resource bundles
+ NLS.initializeMessages(BUNDLE_NAME, StateMsg.class);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
new file mode 100644
index 000000000..2c69a98b4
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
@@ -0,0 +1,581 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.osgi.internal.module.ResolverImpl;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.eclipse.osgi.storagemanager.StorageManager;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.*;
+
+public class StateObjectFactoryImpl implements StateObjectFactory {
+
+ /**
+ * @deprecated
+ */
+ public BundleDescription createBundleDescription(Dictionary<String, String> manifest, String location, long id) throws BundleException {
+ return createBundleDescription(null, manifest, location, id);
+ }
+
+ public BundleDescription createBundleDescription(State state, Dictionary<String, String> manifest, String location, long id) throws BundleException {
+ BundleDescriptionImpl result = (BundleDescriptionImpl) StateBuilder.createBundleDescription((StateImpl) state, manifest, location);
+ result.setBundleId(id);
+ return result;
+ }
+
+ /**
+ * @deprecated
+ */
+ public BundleDescription createBundleDescription(long id, String symbolicName, Version version, String location, BundleSpecification[] required, HostSpecification host, ImportPackageSpecification[] imports, ExportPackageDescription[] exports, String[] providedPackages, boolean singleton) {
+ return createBundleDescription(id, symbolicName, version, location, required, host, imports, exports, providedPackages, singleton, true, true, null, null, null, null);
+ }
+
+ /**
+ * @deprecated
+ */
+ public BundleDescription createBundleDescription(long id, String symbolicName, Version version, String location, BundleSpecification[] required, HostSpecification host, ImportPackageSpecification[] imports, ExportPackageDescription[] exports, String[] providedPackages, boolean singleton, boolean attachFragments, boolean dynamicFragments, String platformFilter, String executionEnvironment, GenericSpecification[] genericRequires, GenericDescription[] genericCapabilities) {
+ // bug 154137 we need to parse the executionEnvironment param; no need to check for null, ManifestElement does that for us.
+ return createBundleDescription(id, symbolicName, version, location, required, host, imports, exports, singleton, attachFragments, dynamicFragments, platformFilter, ManifestElement.getArrayFromList(executionEnvironment), genericRequires, genericCapabilities);
+ }
+
+ public BundleDescription createBundleDescription(long id, String symbolicName, Version version, String location, BundleSpecification[] required, HostSpecification host, ImportPackageSpecification[] imports, ExportPackageDescription[] exports, boolean singleton, boolean attachFragments, boolean dynamicFragments, String platformFilter, String[] executionEnvironments, GenericSpecification[] genericRequires, GenericDescription[] genericCapabilities) {
+ return createBundleDescription(id, symbolicName, version, location, required, host, imports, exports, singleton, attachFragments, dynamicFragments, platformFilter, executionEnvironments, genericRequires, genericCapabilities, null);
+ }
+
+ public BundleDescription createBundleDescription(long id, String symbolicName, Version version, String location, BundleSpecification[] required, HostSpecification host, ImportPackageSpecification[] imports, ExportPackageDescription[] exports, boolean singleton, boolean attachFragments, boolean dynamicFragments, String platformFilter, String[] executionEnvironments, GenericSpecification[] genericRequires, GenericDescription[] genericCapabilities, NativeCodeSpecification nativeCode) {
+ BundleDescriptionImpl bundle = new BundleDescriptionImpl();
+ bundle.setBundleId(id);
+ bundle.setSymbolicName(symbolicName);
+ bundle.setVersion(version);
+ bundle.setLocation(location);
+ bundle.setRequiredBundles(required);
+ bundle.setHost(host);
+ bundle.setImportPackages(imports);
+ bundle.setExportPackages(exports);
+ bundle.setStateBit(BundleDescriptionImpl.SINGLETON, singleton);
+ bundle.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, attachFragments);
+ bundle.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, dynamicFragments);
+ bundle.setPlatformFilter(platformFilter);
+ bundle.setExecutionEnvironments(executionEnvironments);
+ bundle.setGenericRequires(genericRequires);
+ bundle.setGenericCapabilities(genericCapabilities);
+ bundle.setNativeCodeSpecification(nativeCode);
+ return bundle;
+ }
+
+ public BundleDescription createBundleDescription(long id, String symbolicName, Version version, String location, BundleSpecification[] required, HostSpecification host, ImportPackageSpecification[] imports, ExportPackageDescription[] exports, String platformFilter, String[] executionEnvironments, GenericSpecification[] genericRequires, GenericDescription[] genericCapabilities, NativeCodeSpecification nativeCode) {
+ BundleDescriptionImpl bundle = new BundleDescriptionImpl();
+ bundle.setBundleId(id);
+
+ try {
+ ManifestElement[] symbolicNameElements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+ if (symbolicNameElements.length > 0) {
+ ManifestElement bsnElement = symbolicNameElements[0];
+ bundle.setSymbolicName(bsnElement.getValue());
+ bundle.setStateBit(BundleDescriptionImpl.SINGLETON, "true".equals(bsnElement.getDirective(Constants.SINGLETON_DIRECTIVE))); //$NON-NLS-1$
+ String fragmentAttachment = bsnElement.getDirective(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+ if (fragmentAttachment != null) {
+ if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_RESOLVETIME)) {
+ bundle.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, true);
+ bundle.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false);
+ } else if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
+ bundle.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, false);
+ bundle.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false);
+ }
+ }
+ bundle.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(bsnElement.getDirective(Constants.MANDATORY_DIRECTIVE)));
+ bundle.setAttributes(StateBuilder.getAttributes(bsnElement, StateBuilder.DEFINED_BSN_MATCHING_ATTRS));
+ bundle.setArbitraryDirectives(StateBuilder.getDirectives(bsnElement, StateBuilder.DEFINED_BSN_DIRECTIVES));
+ }
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Illegal symbolic name: " + symbolicName).initCause(e); //$NON-NLS-1$
+ }
+
+ bundle.setVersion(version);
+ bundle.setLocation(location);
+ bundle.setRequiredBundles(required);
+ bundle.setHost(host);
+ bundle.setImportPackages(imports);
+ bundle.setExportPackages(exports);
+ bundle.setPlatformFilter(platformFilter);
+ bundle.setExecutionEnvironments(executionEnvironments);
+ bundle.setGenericRequires(genericRequires);
+ GenericDescription[] includeIdentity = new GenericDescription[genericCapabilities == null ? 1 : genericCapabilities.length + 1];
+ if (genericCapabilities != null)
+ System.arraycopy(genericCapabilities, 0, includeIdentity, 1, genericCapabilities.length);
+ includeIdentity[0] = StateBuilder.createOsgiIdentityCapability(bundle);
+ bundle.setGenericCapabilities(includeIdentity);
+ bundle.setNativeCodeSpecification(nativeCode);
+ return bundle;
+ }
+
+ public BundleDescription createBundleDescription(BundleDescription original) {
+ BundleDescriptionImpl bundle = new BundleDescriptionImpl();
+ bundle.setBundleId(original.getBundleId());
+ bundle.setSymbolicName(original.getSymbolicName());
+ bundle.setVersion(original.getVersion());
+ bundle.setLocation(original.getLocation());
+ BundleSpecification[] originalRequired = original.getRequiredBundles();
+ BundleSpecification[] newRequired = new BundleSpecification[originalRequired.length];
+ for (int i = 0; i < newRequired.length; i++)
+ newRequired[i] = createBundleSpecification(originalRequired[i]);
+ bundle.setRequiredBundles(newRequired);
+ ExportPackageDescription[] originalExports = original.getExportPackages();
+ ExportPackageDescription[] newExports = new ExportPackageDescription[originalExports.length];
+ for (int i = 0; i < newExports.length; i++)
+ newExports[i] = createExportPackageDescription(originalExports[i]);
+ bundle.setExportPackages(newExports);
+ ImportPackageSpecification[] originalImports = original.getImportPackages();
+ ImportPackageSpecification[] newImports = new ImportPackageSpecification[originalImports.length];
+ for (int i = 0; i < newImports.length; i++)
+ newImports[i] = createImportPackageSpecification(originalImports[i]);
+ bundle.setImportPackages(newImports);
+ if (original.getHost() != null)
+ bundle.setHost(createHostSpecification(original.getHost()));
+ bundle.setStateBit(BundleDescriptionImpl.SINGLETON, original.isSingleton());
+ bundle.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, original.attachFragments());
+ bundle.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, original.dynamicFragments());
+ bundle.setStateBit(BundleDescriptionImpl.HAS_DYNAMICIMPORT, original.hasDynamicImports());
+ bundle.setPlatformFilter(original.getPlatformFilter());
+ bundle.setExecutionEnvironments(original.getExecutionEnvironments());
+ bundle.setGenericCapabilities(createGenericCapabilities(original.getGenericCapabilities()));
+ bundle.setGenericRequires(createGenericRequires(original.getGenericRequires()));
+ bundle.setNativeCodeSpecification(createNativeCodeSpecification(original.getNativeCodeSpecification()));
+ bundle.setAttributes(original.getAttributes());
+ if (original instanceof BundleDescriptionImpl) {
+ bundle.setDirective(Constants.MANDATORY_DIRECTIVE, ((BundleDescriptionImpl) original).getDirective(Constants.MANDATORY_DIRECTIVE));
+ bundle.setArbitraryDirectives(((BundleDescriptionImpl) original).getArbitraryDirectives());
+ }
+ return bundle;
+ }
+
+ private NativeCodeSpecification createNativeCodeSpecification(NativeCodeSpecification original) {
+ if (original == null)
+ return null;
+ NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl();
+ result.setName(original.getName());
+ result.setOptional(original.isOptional());
+ NativeCodeDescription[] originalDescriptions = original.getPossibleSuppliers();
+ NativeCodeDescriptionImpl[] newDescriptions = new NativeCodeDescriptionImpl[originalDescriptions.length];
+ for (int i = 0; i < originalDescriptions.length; i++) {
+ newDescriptions[i] = new NativeCodeDescriptionImpl();
+ newDescriptions[i].setName(originalDescriptions[i].getName());
+ newDescriptions[i].setNativePaths(originalDescriptions[i].getNativePaths());
+ newDescriptions[i].setProcessors(originalDescriptions[i].getProcessors());
+ newDescriptions[i].setOSNames(originalDescriptions[i].getOSNames());
+ newDescriptions[i].setOSVersions(originalDescriptions[i].getOSVersions());
+ newDescriptions[i].setLanguages(originalDescriptions[i].getLanguages());
+ try {
+ newDescriptions[i].setFilter(originalDescriptions[i].getFilter() == null ? null : originalDescriptions[i].getFilter().toString());
+ } catch (InvalidSyntaxException e) {
+ // this is already tested from the orginal filter
+ }
+ }
+ result.setPossibleSuppliers(newDescriptions);
+ return result;
+ }
+
+ private GenericDescription[] createGenericCapabilities(GenericDescription[] genericCapabilities) {
+ if (genericCapabilities == null || genericCapabilities.length == 0)
+ return null;
+ GenericDescription[] result = new GenericDescription[genericCapabilities.length];
+ for (int i = 0; i < genericCapabilities.length; i++) {
+ GenericDescriptionImpl cap = new GenericDescriptionImpl();
+ cap.setType(genericCapabilities[i].getType());
+ cap.setAttributes(genericCapabilities[i].getAttributes());
+ cap.setDirectives(genericCapabilities[i].getDeclaredDirectives());
+ result[i] = cap;
+ }
+ return result;
+ }
+
+ private GenericSpecification[] createGenericRequires(GenericSpecification[] genericRequires) {
+ if (genericRequires == null || genericRequires.length == 0)
+ return null;
+ GenericSpecification[] result = new GenericSpecification[genericRequires.length];
+ for (int i = 0; i < genericRequires.length; i++) {
+ GenericSpecificationImpl req = new GenericSpecificationImpl();
+ req.setName(genericRequires[i].getName());
+ req.setType(genericRequires[i].getType());
+ req.setResolution(req.getResolution());
+ try {
+ req.setMatchingFilter(genericRequires[i].getMatchingFilter(), false);
+ } catch (InvalidSyntaxException e) {
+ // do nothing; this filter should already have been tested
+ }
+ if (genericRequires[i] instanceof GenericSpecificationImpl) {
+ req.setAttributes(((GenericSpecificationImpl) genericRequires[i]).getAttributes());
+ req.setArbitraryDirectives(((GenericSpecificationImpl) genericRequires[i]).getArbitraryDirectives());
+ }
+ result[i] = req;
+ }
+ return result;
+ }
+
+ public BundleSpecification createBundleSpecification(String requiredSymbolicName, VersionRange requiredVersionRange, boolean export, boolean optional) {
+ BundleSpecificationImpl bundleSpec = new BundleSpecificationImpl();
+ bundleSpec.setName(requiredSymbolicName);
+ bundleSpec.setVersionRange(requiredVersionRange);
+ bundleSpec.setExported(export);
+ bundleSpec.setOptional(optional);
+ return bundleSpec;
+ }
+
+ public BundleSpecification createBundleSpecification(BundleSpecification original) {
+ BundleSpecificationImpl bundleSpec = new BundleSpecificationImpl();
+ bundleSpec.setName(original.getName());
+ bundleSpec.setVersionRange(original.getVersionRange());
+ bundleSpec.setExported(original.isExported());
+ bundleSpec.setOptional(original.isOptional());
+ if (original instanceof BundleSpecificationImpl) {
+ bundleSpec.setAttributes(((BundleSpecificationImpl) original).getAttributes());
+ bundleSpec.setArbitraryDirectives(((BundleSpecificationImpl) original).getArbitraryDirectives());
+ }
+ return bundleSpec;
+ }
+
+ public HostSpecification createHostSpecification(String hostSymbolicName, VersionRange versionRange) {
+ HostSpecificationImpl hostSpec = new HostSpecificationImpl();
+ hostSpec.setName(hostSymbolicName);
+ hostSpec.setVersionRange(versionRange);
+ return hostSpec;
+ }
+
+ public HostSpecification createHostSpecification(HostSpecification original) {
+ HostSpecificationImpl hostSpec = new HostSpecificationImpl();
+ hostSpec.setName(original.getName());
+ hostSpec.setVersionRange(original.getVersionRange());
+ if (original instanceof HostSpecificationImpl) {
+ hostSpec.setAttributes(((HostSpecificationImpl) original).getAttributes());
+ hostSpec.setArbitraryDirectives(((HostSpecificationImpl) original).getArbitraryDirectives());
+ }
+ return hostSpec;
+ }
+
+ public ImportPackageSpecification createImportPackageSpecification(String packageName, VersionRange versionRange, String bundleSymbolicName, VersionRange bundleVersionRange, Map<String, ?> directives, Map<String, ?> attributes, BundleDescription importer) {
+ ImportPackageSpecificationImpl packageSpec = new ImportPackageSpecificationImpl();
+ packageSpec.setName(packageName);
+ packageSpec.setVersionRange(versionRange);
+ packageSpec.setBundleSymbolicName(bundleSymbolicName);
+ packageSpec.setBundleVersionRange(bundleVersionRange);
+ packageSpec.setDirectives(directives);
+ packageSpec.setAttributes(attributes);
+ packageSpec.setBundle(importer);
+ return packageSpec;
+ }
+
+ public ImportPackageSpecification createImportPackageSpecification(ImportPackageSpecification original) {
+ ImportPackageSpecificationImpl packageSpec = new ImportPackageSpecificationImpl();
+ packageSpec.setName(original.getName());
+ packageSpec.setVersionRange(original.getVersionRange());
+ packageSpec.setBundleSymbolicName(original.getBundleSymbolicName());
+ packageSpec.setBundleVersionRange(original.getBundleVersionRange());
+ packageSpec.setDirectives(original.getDirectives());
+ packageSpec.setAttributes(original.getAttributes());
+ if (original instanceof ImportPackageSpecificationImpl) {
+ packageSpec.setArbitraryDirectives(((ImportPackageSpecificationImpl) original).getArbitraryDirectives());
+ }
+ return packageSpec;
+ }
+
+ public ExportPackageDescription createExportPackageDescription(ExportPackageDescription original) {
+ ExportPackageDescriptionImpl exportPackage = new ExportPackageDescriptionImpl();
+ exportPackage.setName(original.getName());
+ exportPackage.setVersion(original.getVersion());
+ exportPackage.setDirectives(original.getDirectives());
+ exportPackage.setAttributes(original.getAttributes());
+ exportPackage.setArbitraryDirectives(((ExportPackageDescriptionImpl) original).getArbitraryDirectives());
+ return exportPackage;
+ }
+
+ public ExportPackageDescription createExportPackageDescription(String packageName, Version version, Map<String, ?> directives, Map<String, ?> attributes, boolean root, BundleDescription exporter) {
+ ExportPackageDescriptionImpl exportPackage = new ExportPackageDescriptionImpl();
+ exportPackage.setName(packageName);
+ exportPackage.setVersion(version);
+ exportPackage.setDirectives(directives);
+ exportPackage.setAttributes(attributes);
+ exportPackage.setExporter(exporter);
+ return exportPackage;
+ }
+
+ /**
+ * @deprecated
+ */
+ public GenericDescription createGenericDescription(String name, String type, Version version, Map<String, ?> attributes) {
+ return createGenericDescription(name, type, version, attributes, null, null);
+ }
+
+ public GenericDescription createGenericDescription(String type, Map<String, ?> attributes, Map<String, String> directives, BundleDescription supplier) {
+ return createGenericDescription(null, type, null, attributes, directives, supplier);
+ }
+
+ private GenericDescription createGenericDescription(String name, String type, Version version, Map<String, ?> attributes, Map<String, String> directives, BundleDescription supplier) {
+ GenericDescriptionImpl result = new GenericDescriptionImpl();
+ result.setType(type);
+ Dictionary<String, Object> attrs = attributes == null ? new Hashtable<String, Object>() : new Hashtable<String, Object>(attributes);
+ if (version != null) {
+ Object versionObj = attrs.get(Constants.VERSION_ATTRIBUTE);
+ if (!(versionObj instanceof Version) && version != null)
+ attrs.put(Constants.VERSION_ATTRIBUTE, version);
+ }
+ if (name != null) {
+ Object nameObj = attrs.get(result.getType());
+ if (!(nameObj instanceof String))
+ attrs.put(result.getType(), name);
+ }
+ result.setAttributes(attrs);
+ result.setDirectives(directives);
+ result.setSupplier(supplier);
+ return result;
+ }
+
+ public GenericSpecification createGenericSpecification(String name, String type, String matchingFilter, boolean optional, boolean multiple) throws InvalidSyntaxException {
+ GenericSpecificationImpl result = new GenericSpecificationImpl();
+ result.setName(name);
+ result.setType(type);
+ result.setMatchingFilter(matchingFilter, true);
+ int resolution = 0;
+ if (optional)
+ resolution |= GenericSpecification.RESOLUTION_OPTIONAL;
+ if (multiple)
+ resolution |= GenericSpecification.RESOLUTION_MULTIPLE;
+ result.setResolution(resolution);
+ return result;
+ }
+
+ public NativeCodeDescription createNativeCodeDescription(String[] nativePaths, String[] processors, String[] osNames, VersionRange[] osVersions, String[] languages, String filter) throws InvalidSyntaxException {
+ NativeCodeDescriptionImpl result = new NativeCodeDescriptionImpl();
+ result.setName(Constants.BUNDLE_NATIVECODE);
+ result.setNativePaths(nativePaths);
+ result.setProcessors(processors);
+ result.setOSNames(osNames);
+ result.setOSVersions(osVersions);
+ result.setLanguages(languages);
+ result.setFilter(filter);
+ return result;
+ }
+
+ public NativeCodeSpecification createNativeCodeSpecification(NativeCodeDescription[] nativeCodeDescriptions, boolean optional) {
+ NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl();
+ result.setName(Constants.BUNDLE_NATIVECODE);
+ result.setOptional(optional);
+ result.setPossibleSuppliers(nativeCodeDescriptions);
+ return result;
+ }
+
+ /**
+ * @deprecated
+ */
+ public State createState() {
+ return internalCreateState();
+ }
+
+ public State createState(boolean createResolver) {
+ State result = internalCreateState();
+ if (createResolver)
+ result.setResolver(new ResolverImpl(false));
+ return result;
+ }
+
+ public State createState(State original) {
+ StateImpl newState = internalCreateState();
+ newState.setTimeStamp(original.getTimeStamp());
+ BundleDescription[] bundles = original.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ BundleDescription newBundle = createBundleDescription(bundles[i]);
+ newState.basicAddBundle(newBundle);
+ DisabledInfo[] infos = original.getDisabledInfos(bundles[i]);
+ for (int j = 0; j < infos.length; j++)
+ newState.addDisabledInfo(new DisabledInfo(infos[j].getPolicyName(), infos[j].getMessage(), newBundle));
+ }
+ newState.setResolved(false);
+ newState.setPlatformProperties(original.getPlatformProperties());
+ return newState;
+ }
+
+ private StateImpl internalCreateState() {
+ StateImpl state = new UserState();
+ state.setFactory(this);
+ return state;
+ }
+
+ /**
+ * @deprecated
+ */
+ public State readState(InputStream stream) throws IOException {
+ return internalReadStateDeprecated(internalCreateState(), new DataInputStream(stream), -1);
+ }
+
+ /**
+ * @deprecated
+ */
+ public State readState(DataInputStream stream) throws IOException {
+ return internalReadStateDeprecated(internalCreateState(), stream, -1);
+ }
+
+ public State readState(File stateDirectory) throws IOException {
+ return internalReadState(internalCreateState(), stateDirectory, -1);
+ }
+
+ private State internalReadStateDeprecated(StateImpl toRestore, DataInputStream stream, long expectedTimestamp) throws IOException {
+ StateReader reader = new StateReader();
+ if (!reader.loadStateDeprecated(toRestore, stream, expectedTimestamp))
+ return null;
+ return toRestore;
+ }
+
+ private State internalReadState(StateImpl toRestore, File stateDirectory, long expectedTimestamp) throws IOException {
+ File stateFile = new File(stateDirectory, StateReader.STATE_FILE);
+ File lazyFile = new File(stateDirectory, StateReader.LAZY_FILE);
+ if (!stateFile.exists() || !lazyFile.exists()) {
+ StorageManager storageManager = new StorageManager(stateDirectory, "none", true); //$NON-NLS-1$
+ try {
+ // if the directory is pointing at the configuration directory then the base files will not exist
+ storageManager.open(true);
+ // try using the storage manager to find the managed state files (bug 143255)
+ File managedState = storageManager.lookup(StateReader.STATE_FILE, false);
+ File managedLazy = storageManager.lookup(StateReader.LAZY_FILE, false);
+ if (managedState != null && managedLazy != null) {
+ stateFile = managedState;
+ lazyFile = managedLazy;
+ }
+ } finally {
+ storageManager.close();
+ }
+ }
+ StateReader reader = new StateReader(stateFile, lazyFile, false);
+ if (!reader.loadState(toRestore, expectedTimestamp))
+ return null;
+ return toRestore;
+ }
+
+ /**
+ * @deprecated
+ */
+ public void writeState(State state, DataOutputStream stream) throws IOException {
+ internalWriteStateDeprecated(state, stream);
+ }
+
+ public void writeState(State state, File stateDirectory) throws IOException {
+ if (stateDirectory == null)
+ throw new IOException();
+ StateWriter writer = new StateWriter();
+ File stateFile = new File(stateDirectory, StateReader.STATE_FILE);
+ File lazyFile = new File(stateDirectory, StateReader.LAZY_FILE);
+ writer.saveState((StateImpl) state, stateFile, lazyFile);
+ }
+
+ /**
+ * @deprecated
+ */
+ public void writeState(State state, OutputStream stream) throws IOException {
+ internalWriteStateDeprecated(state, new DataOutputStream(stream));
+ }
+
+ public void writeState(State state, File stateFile, File lazyFile) throws IOException {
+ StateWriter writer = new StateWriter();
+ writer.saveState((StateImpl) state, stateFile, lazyFile);
+ }
+
+ private void internalWriteStateDeprecated(State state, DataOutputStream stream) throws IOException {
+ if (state.getFactory() != this)
+ throw new IllegalArgumentException();
+ StateWriter writer = new StateWriter();
+ writer.saveStateDeprecated((StateImpl) state, stream);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<BundleSpecification> createBundleSpecifications(String declaration) {
+ try {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, declaration);
+ if (elements == null)
+ return Collections.EMPTY_LIST;
+ List<BundleSpecification> result = new ArrayList<BundleSpecification>(elements.length);
+ for (ManifestElement element : elements)
+ result.add(StateBuilder.createRequiredBundle(element));
+ return result;
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Declaration is invalid: " + declaration).initCause(e); //$NON-NLS-1$
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<HostSpecification> createHostSpecifications(String declaration) {
+ try {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, declaration);
+ if (elements == null)
+ return Collections.EMPTY_LIST;
+ List<HostSpecification> result = new ArrayList<HostSpecification>(elements.length);
+ for (ManifestElement element : elements)
+ result.add(StateBuilder.createHostSpecification(element, null));
+ return result;
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Declaration is invalid: " + declaration).initCause(e); //$NON-NLS-1$
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<ImportPackageSpecification> createImportPackageSpecifications(String declaration) {
+ try {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, declaration);
+ if (elements == null)
+ return Collections.EMPTY_LIST;
+ List<ImportPackageSpecification> result = new ArrayList<ImportPackageSpecification>(elements.length);
+ for (ManifestElement element : elements)
+ StateBuilder.addImportPackages(element, result, 2, false);
+ return result;
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Declaration is invalid: " + declaration).initCause(e); //$NON-NLS-1$
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<GenericDescription> createGenericDescriptions(String declaration) {
+ try {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, declaration);
+ if (elements == null)
+ return Collections.EMPTY_LIST;
+ return StateBuilder.createOSGiCapabilities(elements, new ArrayList<GenericDescription>(elements.length), (Integer) null);
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Declaration is invalid: " + declaration).initCause(e); //$NON-NLS-1$
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<GenericSpecification> createGenericSpecifications(String declaration) {
+ try {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, declaration);
+ if (elements == null)
+ return Collections.EMPTY_LIST;
+ return StateBuilder.createOSGiRequires(elements, new ArrayList<GenericSpecification>(elements.length));
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Declaration is invalid: " + declaration).initCause(e); //$NON-NLS-1$
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<ExportPackageDescription> createExportPackageDescriptions(String declaration) {
+ try {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, declaration);
+ if (elements == null)
+ return Collections.EMPTY_LIST;
+ List<ExportPackageDescription> result = new ArrayList<ExportPackageDescription>(elements.length);
+ for (ManifestElement element : elements)
+ StateBuilder.addExportPackages(element, result, false);
+ return result;
+ } catch (BundleException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Declaration is invalid: " + declaration).initCause(e); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateReader.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateReader.java
new file mode 100644
index 000000000..f121b267a
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateReader.java
@@ -0,0 +1,873 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.util.*;
+import java.util.Map.Entry;
+import org.eclipse.osgi.framework.util.ObjectPool;
+import org.eclipse.osgi.framework.util.SecureAction;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.osgi.framework.*;
+
+/**
+ * This class is internally threadsafe and supports client locking. Clients must <strong>not</strong> hold the monitor for
+ * any {@link StateImpl} or {@link BundleDescriptionImpl} object when calling into the public methods of this class to prevent
+ * possible deadlock.
+ */
+final class StateReader {
+ public static final String STATE_FILE = ".state"; //$NON-NLS-1$
+ public static final String LAZY_FILE = ".lazy"; //$NON-NLS-1$
+ public static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
+ private static final int BUFFER_SIZE_LAZY = 4096;
+ private static final int BUFFER_SIZE_FULLYREAD = 16384;
+ private static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
+
+ // objectTable will be a hashmap of objects. The objects will be things
+ // like BundleDescription, ExportPackageDescription, Version etc.. The integer
+ // index value will be used in the cache to allow cross-references in the
+ // cached state.
+ final Map<Integer, Object> objectTable = Collections.synchronizedMap(new HashMap<Integer, Object>());
+
+ private volatile File stateFile;
+ private volatile File lazyFile;
+
+ private volatile boolean lazyLoad = true;
+ private volatile int numBundles;
+ private volatile boolean accessedFlag = false;
+
+ public static final byte STATE_CACHE_VERSION = 38;
+ public static final byte NULL = 0;
+ public static final byte OBJECT = 1;
+ public static final byte INDEX = 2;
+ public static final byte LONG_STRING = 3;
+
+ public StateReader() //TODO - deprecated
+ {
+ lazyLoad = false;
+ }
+
+ public StateReader(File stateDirectory) {
+ if (!stateDirectory.exists())
+ stateDirectory.mkdirs();
+ this.stateFile = new File(stateDirectory, STATE_FILE);
+ this.lazyFile = new File(stateDirectory, LAZY_FILE);
+ this.lazyLoad = false;
+ }
+
+ public StateReader(File stateFile, File lazyFile, boolean lazyLoad) {
+ this.stateFile = stateFile;
+ this.lazyFile = lazyFile;
+ this.lazyLoad = lazyLoad;
+ }
+
+ private void addToObjectTable(Object object, int index) {
+ objectTable.put(new Integer(index), object);
+ }
+
+ private Object getFromObjectTable(int index) {
+ Object result = objectTable.get(new Integer(index));
+ if (result == null)
+ throw new IllegalStateException("Expected to find an object at table index: " + index); //$NON-NLS-1$
+ return result;
+ }
+
+ private boolean readState(StateImpl state, long expectedTimestamp) throws IOException {
+ DataInputStream in = new DataInputStream(new BufferedInputStream(secureAction.getFileInputStream(stateFile), BUFFER_SIZE_FULLYREAD));
+ DataInputStream lazyIn = null;
+ try {
+ if (in.readByte() != STATE_CACHE_VERSION)
+ return false;
+ byte tag = readTag(in);
+ if (tag != OBJECT)
+ return false;
+ int index = in.readInt();
+ long timestampRead = in.readLong();
+ if (expectedTimestamp >= 0 && timestampRead != expectedTimestamp)
+ return false;
+ addToObjectTable(state, index);
+ // read the platform property keys
+ String[] platformPropKeys = (String[]) readPlatformProp(in);
+ state.addPlatformPropertyKeys(platformPropKeys);
+ int numSets = in.readInt();
+ Dictionary<?, ?>[] platformProps = new Dictionary[numSets];
+ for (int i = 0; i < numSets; i++) {
+ Hashtable<Object, Object> props = new Hashtable<Object, Object>(platformPropKeys.length);
+ int numProps = in.readInt();
+ for (int j = 0; j < numProps; j++) {
+ Object value = readPlatformProp(in);
+ if (value != null && j < platformPropKeys.length)
+ props.put(platformPropKeys[j], value);
+ }
+ platformProps[i] = props;
+ }
+ state.setPlatformProperties(platformProps, false);
+ numBundles = in.readInt();
+ for (int i = 0; i < numBundles; i++) {
+ BundleDescriptionImpl bundle = readBundleDescription(in);
+ state.basicAddBundle(bundle);
+ if (bundle.isResolved())
+ state.addResolvedBundle(bundle);
+ }
+ // read the DisabledInfos
+ int numDisableInfos = in.readInt();
+ for (int i = 0; i < numDisableInfos; i++) {
+ DisabledInfo info = readDisabledInfo(in);
+ state.addDisabledInfo(info);
+ }
+ state.setTimeStamp(timestampRead);
+ state.setResolved(in.readBoolean());
+ if (lazyLoad)
+ return true;
+ //read in from lazy data file; using the fully read buffer size because we are reading the complete file in.
+ lazyIn = new DataInputStream(new BufferedInputStream(secureAction.getFileInputStream(lazyFile), BUFFER_SIZE_FULLYREAD));
+ for (int i = 0; i < numBundles; i++)
+ readBundleDescriptionLazyData(lazyIn, 0);
+ } finally {
+ in.close();
+ if (lazyIn != null)
+ try {
+ lazyIn.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ return true;
+ }
+
+ private boolean readStateDeprecated(StateImpl state, DataInputStream in, long expectedTimestamp) throws IOException {
+ if (in.readByte() != STATE_CACHE_VERSION)
+ return false;
+ byte tag = readTag(in);
+ if (tag != OBJECT)
+ return false;
+ int index = in.readInt();
+ long timestampRead = in.readLong();
+ if (expectedTimestamp >= 0 && timestampRead != expectedTimestamp)
+ return false;
+ addToObjectTable(state, index);
+ // read the platform property keys
+ String[] platformPropKeys = (String[]) readPlatformProp(in);
+ state.addPlatformPropertyKeys(platformPropKeys);
+ int numSets = in.readInt();
+ Dictionary<?, ?>[] platformProps = new Dictionary[numSets];
+ for (int i = 0; i < numSets; i++) {
+ Hashtable<Object, Object> props = new Hashtable<Object, Object>(platformPropKeys.length);
+ int numProps = in.readInt();
+ for (int j = 0; j < numProps; j++) {
+ Object value = readPlatformProp(in);
+ if (value != null && j < platformPropKeys.length)
+ props.put(platformPropKeys[j], value);
+ }
+ platformProps[i] = props;
+ }
+ state.setPlatformProperties(platformProps);
+ numBundles = in.readInt();
+ if (numBundles == 0)
+ return true;
+ for (int i = 0; i < numBundles; i++) {
+ BundleDescriptionImpl bundle = readBundleDescription(in);
+ state.basicAddBundle(bundle);
+ if (bundle.isResolved())
+ state.addResolvedBundle(bundle);
+ }
+ state.setTimeStamp(timestampRead);
+ state.setResolved(in.readBoolean());
+ in.readInt(); // skip past the old offset
+ if (lazyLoad)
+ return true;
+ for (int i = 0; i < numBundles; i++)
+ readBundleDescriptionLazyData(in, 0);
+ return true;
+ }
+
+ private Object readPlatformProp(DataInputStream in) throws IOException {
+ byte type = in.readByte();
+ if (type == NULL)
+ return null;
+ int num = in.readInt();
+ if (num == 1)
+ return readString(in, false);
+ String[] result = new String[num];
+ for (int i = 0; i < result.length; i++)
+ result[i] = readString(in, false);
+ return result;
+ }
+
+ private BundleDescriptionImpl readBundleDescription(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (BundleDescriptionImpl) getFromObjectTable(in.readInt());
+ // first read in non-lazy loaded data
+ BundleDescriptionImpl result = new BundleDescriptionImpl();
+ addToObjectTable(result, in.readInt());
+
+ result.setBundleId(in.readLong());
+ readBaseDescription(result, in);
+ result.setLazyDataOffset(in.readInt());
+ result.setLazyDataSize(in.readInt());
+ result.setStateBit(BundleDescriptionImpl.RESOLVED, in.readBoolean());
+ result.setStateBit(BundleDescriptionImpl.SINGLETON, in.readBoolean());
+ result.setStateBit(BundleDescriptionImpl.HAS_DYNAMICIMPORT, in.readBoolean());
+ result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, in.readBoolean());
+ result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, in.readBoolean());
+ String[] mandatory = readList(in);
+ if (mandatory != null)
+ result.setDirective(Constants.MANDATORY_DIRECTIVE, mandatory);
+ result.setAttributes(readMap(in));
+ result.setArbitraryDirectives(readMap(in));
+ result.setHost(readHostSpec(in));
+
+ // set the bundle dependencies from imports and requires and hosts.
+ int numDeps = in.readInt();
+ if (numDeps > 0) {
+ BundleDescription[] deps = new BundleDescription[numDeps];
+ for (int i = 0; i < numDeps; i++)
+ deps[i] = readBundleDescription(in);
+ result.addDependencies(deps, false); // no need to check dups; we already know there are none when we resolved (bug 152900)
+ }
+ // No need to set the dependencies between fragment and hosts; that was already done in the above loop (bug 152900)
+ // but we do need to set the dependencies between hosts and fragment.
+ HostSpecificationImpl hostSpec = (HostSpecificationImpl) result.getHost();
+ if (hostSpec != null) {
+ BundleDescription[] hosts = hostSpec.getHosts();
+ if (hosts != null) {
+ for (int i = 0; i < hosts.length; i++)
+ ((BundleDescriptionImpl) hosts[i]).addDependency(result, false);
+ }
+ }
+ // the rest is lazy loaded data
+ result.setFullyLoaded(false);
+ return result;
+ }
+
+ private BundleDescriptionImpl readBundleDescriptionLazyData(DataInputStream in, int skip) throws IOException {
+ if (skip > 0)
+ in.skipBytes(skip);
+ int index = in.readInt();
+ BundleDescriptionImpl result = (BundleDescriptionImpl) getFromObjectTable(index);
+ if (result.isFullyLoaded()) {
+ in.skipBytes(result.getLazyDataSize() - 4); // skip to the end subtract 4 for the int read already
+ return result;
+ }
+
+ result.setLocation(readString(in, false));
+ result.setPlatformFilter(readString(in, false));
+
+ int exportCount = in.readInt();
+ if (exportCount > 0) {
+ ExportPackageDescription[] exports = new ExportPackageDescription[exportCount];
+ for (int i = 0; i < exports.length; i++)
+ exports[i] = readExportPackageDesc(in);
+ result.setExportPackages(exports);
+ }
+
+ int importCount = in.readInt();
+ if (importCount > 0) {
+ ImportPackageSpecification[] imports = new ImportPackageSpecification[importCount];
+ for (int i = 0; i < imports.length; i++)
+ imports[i] = readImportPackageSpec(in);
+ result.setImportPackages(imports);
+ }
+
+ int requiredBundleCount = in.readInt();
+ if (requiredBundleCount > 0) {
+ BundleSpecification[] requiredBundles = new BundleSpecification[requiredBundleCount];
+ for (int i = 0; i < requiredBundles.length; i++)
+ requiredBundles[i] = readBundleSpec(in);
+ result.setRequiredBundles(requiredBundles);
+ }
+
+ int selectedCount = in.readInt();
+ if (selectedCount > 0) {
+ ExportPackageDescription[] selected = new ExportPackageDescription[selectedCount];
+ for (int i = 0; i < selected.length; i++)
+ selected[i] = readExportPackageDesc(in);
+ result.setSelectedExports(selected);
+ }
+
+ int substitutedCount = in.readInt();
+ if (substitutedCount > 0) {
+ ExportPackageDescription[] selected = new ExportPackageDescription[substitutedCount];
+ for (int i = 0; i < selected.length; i++)
+ selected[i] = readExportPackageDesc(in);
+ result.setSubstitutedExports(selected);
+ }
+
+ int resolvedCount = in.readInt();
+ if (resolvedCount > 0) {
+ ExportPackageDescription[] resolved = new ExportPackageDescription[resolvedCount];
+ for (int i = 0; i < resolved.length; i++)
+ resolved[i] = readExportPackageDesc(in);
+ result.setResolvedImports(resolved);
+ }
+
+ int resolvedRequiredCount = in.readInt();
+ if (resolvedRequiredCount > 0) {
+ BundleDescription[] resolved = new BundleDescription[resolvedRequiredCount];
+ for (int i = 0; i < resolved.length; i++)
+ resolved[i] = readBundleDescription(in);
+ result.setResolvedRequires(resolved);
+ }
+
+ int eeCount = in.readInt();
+ if (eeCount > 0) {
+ String[] ee = new String[eeCount];
+ for (int i = 0; i < ee.length; i++)
+ ee[i] = readString(in, false);
+ result.setExecutionEnvironments(ee);
+ }
+
+ int dynamicPkgCnt = in.readInt();
+ if (dynamicPkgCnt > 0) {
+ HashMap<String, Long> dynamicStamps = new HashMap<String, Long>(dynamicPkgCnt);
+ for (int i = 0; i < dynamicPkgCnt; i++) {
+ String pkg = readString(in, false);
+ Long stamp = new Long(in.readLong());
+ dynamicStamps.put(pkg, stamp);
+ }
+ result.setDynamicStamps(dynamicStamps);
+ }
+
+ int genericCapCnt = in.readInt();
+ if (genericCapCnt > 0) {
+ GenericDescription[] capabilities = new GenericDescription[genericCapCnt];
+ for (int i = 0; i < capabilities.length; i++)
+ capabilities[i] = readGenericDescription(in);
+ result.setGenericCapabilities(capabilities);
+ }
+
+ int genericReqCnt = in.readInt();
+ if (genericReqCnt > 0) {
+ GenericSpecification[] reqs = new GenericSpecification[genericReqCnt];
+ for (int i = 0; i < reqs.length; i++)
+ reqs[i] = readGenericSpecification(in);
+ result.setGenericRequires(reqs);
+ }
+
+ int selectedGenCapCnt = in.readInt();
+ if (selectedGenCapCnt > 0) {
+ GenericDescription[] capabilities = new GenericDescription[selectedGenCapCnt];
+ for (int i = 0; i < capabilities.length; i++)
+ capabilities[i] = readGenericDescription(in);
+ result.setSelectedCapabilities(capabilities);
+ }
+
+ int resolvedGenCapCnt = in.readInt();
+ if (resolvedGenCapCnt > 0) {
+ GenericDescription[] capabilities = new GenericDescription[resolvedGenCapCnt];
+ for (int i = 0; i < capabilities.length; i++)
+ capabilities[i] = readGenericDescription(in);
+ result.setResolvedCapabilities(capabilities);
+ }
+
+ result.setNativeCodeSpecification(readNativeCode(in));
+
+ @SuppressWarnings("rawtypes")
+ Map raw = readMap(in);
+ result.setStateWires(raw);
+
+ result.setFullyLoaded(true); // set fully loaded before setting the dependencies
+ // No need to add bundle dependencies for hosts, imports or requires;
+ // This is done by readBundleDescription
+ return result;
+ }
+
+ private BundleSpecificationImpl readBundleSpec(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (BundleSpecificationImpl) getFromObjectTable(in.readInt());
+ BundleSpecificationImpl result = new BundleSpecificationImpl();
+ int tableIndex = in.readInt();
+ addToObjectTable(result, tableIndex);
+ readVersionConstraint(result, in);
+ result.setSupplier(readBundleDescription(in));
+ result.setExported(in.readBoolean());
+ result.setOptional(in.readBoolean());
+ result.setAttributes(readMap(in));
+ result.setArbitraryDirectives(readMap(in));
+ return result;
+ }
+
+ private ExportPackageDescriptionImpl readExportPackageDesc(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (ExportPackageDescriptionImpl) getFromObjectTable(in.readInt());
+ ExportPackageDescriptionImpl exportPackageDesc = new ExportPackageDescriptionImpl();
+ int tableIndex = in.readInt();
+ addToObjectTable(exportPackageDesc, tableIndex);
+ readBaseDescription(exportPackageDesc, in);
+ exportPackageDesc.setExporter(readBundleDescription(in));
+ exportPackageDesc.setAttributes(readMap(in));
+ exportPackageDesc.setDirectives(readMap(in));
+ exportPackageDesc.setArbitraryDirectives(readMap(in));
+ exportPackageDesc.setFragmentDeclaration(readExportPackageDesc(in));
+ return exportPackageDesc;
+ }
+
+ private DisabledInfo readDisabledInfo(DataInputStream in) throws IOException {
+ return new DisabledInfo(readString(in, false), readString(in, false), readBundleDescription(in));
+ }
+
+ private Map<String, Object> readMap(DataInputStream in) throws IOException {
+ int count = in.readInt();
+ if (count == 0)
+ return null;
+ HashMap<String, Object> result = new HashMap<String, Object>(count);
+ for (int i = 0; i < count; i++) {
+ String key = readString(in, false);
+ Object value = null;
+ byte type = in.readByte();
+ if (type == 0)
+ value = readString(in, false);
+ else if (type == 1)
+ value = readList(in);
+ else if (type == 2)
+ value = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+ else if (type == 3)
+ value = new Integer(in.readInt());
+ else if (type == 4)
+ value = new Long(in.readLong());
+ else if (type == 5)
+ value = new Double(in.readDouble());
+ else if (type == 6)
+ value = readVersion(in);
+ else if (type == 7) {
+ value = readString(in, false);
+ try {
+ Class<?> uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$
+ Constructor<?> constructor = uriClazz.getConstructor(new Class[] {String.class});
+ value = constructor.newInstance(new Object[] {value});
+ } catch (ClassNotFoundException e) {
+ // oh well cannot support; just use the string
+ } catch (RuntimeException e) { // got some reflection exception
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ } else if (type == 8) {
+ int listType = in.readByte();
+ int size = in.readInt();
+ List<Object> list = new ArrayList<Object>(size);
+ for (int j = 0; j < size; j++) {
+ switch (listType) {
+ case 0 :
+ list.add(readString(in, false));
+ break;
+ case 3 :
+ list.add(new Integer(in.readInt()));
+ break;
+ case 4 :
+ list.add(new Long(in.readLong()));
+ break;
+ case 5 :
+ list.add(new Double(in.readDouble()));
+ break;
+ case 6 :
+ list.add(readVersion(in));
+ break;
+ case 7 :
+ list.add(readStateWire(in));
+ break;
+ default :
+ throw new IOException("Invalid type: " + listType); //$NON-NLS-1$
+ }
+ }
+ value = list;
+ }
+ result.put(key, value);
+ }
+ return result;
+ }
+
+ private Object readStateWire(DataInputStream in) throws IOException {
+ VersionConstraintImpl requirement;
+ BundleDescription requirementHost;
+ BaseDescription capability;
+ BundleDescription capabilityHost;
+
+ byte wireType = in.readByte();
+ switch (wireType) {
+ case 0 :
+ requirement = readImportPackageSpec(in);
+ capability = readExportPackageDesc(in);
+ break;
+ case 1 :
+ requirement = readBundleSpec(in);
+ capability = readBundleDescription(in);
+ break;
+ case 2 :
+ requirement = readHostSpec(in);
+ capability = readBundleDescription(in);
+ break;
+ case 3 :
+ requirement = readGenericSpecification(in);
+ capability = readGenericDescription(in);
+ break;
+ default :
+ throw new IOException("Invalid wire type: " + wireType); //$NON-NLS-1$
+ }
+
+ requirementHost = readBundleDescription(in);
+ capabilityHost = readBundleDescription(in);
+
+ if (requirement.getBundle() == null) {
+ // Need to fix up dynamic imports added by weaving hook (bug 359394)
+ requirement.setBundle(requirementHost);
+ }
+ return new StateWire(requirementHost, requirement, capabilityHost, capability);
+ }
+
+ private String[] readList(DataInputStream in) throws IOException {
+ int count = in.readInt();
+ if (count == 0)
+ return null;
+ String[] result = new String[count];
+ for (int i = 0; i < count; i++)
+ result[i] = readString(in, false);
+ return result;
+ }
+
+ private void readBaseDescription(BaseDescriptionImpl root, DataInputStream in) throws IOException {
+ root.setName(readString(in, false));
+ root.setVersion(readVersion(in));
+ }
+
+ private ImportPackageSpecificationImpl readImportPackageSpec(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (ImportPackageSpecificationImpl) getFromObjectTable(in.readInt());
+ ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl();
+ int tableIndex = in.readInt();
+ addToObjectTable(result, tableIndex);
+ readVersionConstraint(result, in);
+ result.setSupplier(readExportPackageDesc(in));
+ result.setBundleSymbolicName(readString(in, false));
+ result.setBundleVersionRange(readVersionRange(in));
+ result.setAttributes(readMap(in));
+ result.setDirectives(readMap(in));
+ result.setArbitraryDirectives(readMap(in));
+ return result;
+ }
+
+ private HostSpecificationImpl readHostSpec(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (HostSpecificationImpl) getFromObjectTable(in.readInt());
+ HostSpecificationImpl result = new HostSpecificationImpl();
+ int tableIndex = in.readInt();
+ addToObjectTable(result, tableIndex);
+ readVersionConstraint(result, in);
+ int hostCount = in.readInt();
+ if (hostCount > 0) {
+ BundleDescription[] hosts = new BundleDescription[hostCount];
+ for (int i = 0; i < hosts.length; i++)
+ hosts[i] = readBundleDescription(in);
+ result.setHosts(hosts);
+ }
+ result.setAttributes(readMap(in));
+ result.setArbitraryDirectives(readMap(in));
+ return result;
+ }
+
+ private GenericDescription readGenericDescription(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (GenericDescription) getFromObjectTable(in.readInt());
+ int tableIndex = in.readInt();
+ GenericDescriptionImpl result = new GenericDescriptionImpl();
+ addToObjectTable(result, tableIndex);
+ readBaseDescription(result, in);
+ result.setSupplier(readBundleDescription(in));
+ result.setType(readString(in, false));
+ Map<String, Object> mapAttrs = readMap(in);
+ Dictionary<String, Object> attrs = new Hashtable<String, Object>();
+ if (mapAttrs != null) {
+ for (Iterator<String> keys = mapAttrs.keySet().iterator(); keys.hasNext();) {
+ String key = keys.next();
+ attrs.put(key, mapAttrs.get(key));
+ }
+ }
+ result.setAttributes(attrs);
+ Map directives = readMap(in);
+ if (directives != null)
+ result.setDirectives(directives);
+ result.setFragmentDeclaration(readGenericDescription(in));
+ return result;
+ }
+
+ private GenericSpecificationImpl readGenericSpecification(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (GenericSpecificationImpl) getFromObjectTable(in.readInt());
+ GenericSpecificationImpl result = new GenericSpecificationImpl();
+ int tableIndex = in.readInt();
+ addToObjectTable(result, tableIndex);
+ readVersionConstraint(result, in);
+ result.setType(readString(in, false));
+ int num = in.readInt();
+ GenericDescription[] suppliers = num == 0 ? null : new GenericDescription[num];
+ for (int i = 0; i < num; i++)
+ suppliers[i] = readGenericDescription(in);
+ result.setSupplers(suppliers);
+ result.setResolution(in.readInt());
+ try {
+ result.setMatchingFilter(readString(in, false), false);
+ } catch (InvalidSyntaxException e) {
+ // do nothing this filter was tested before
+ }
+ result.setAttributes(readMap(in));
+ result.setArbitraryDirectives(readMap(in));
+ return result;
+ }
+
+ private NativeCodeSpecification readNativeCode(DataInputStream in) throws IOException {
+ if (!in.readBoolean())
+ return null;
+ NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl();
+ result.setOptional(in.readBoolean());
+ int numNativeDesc = in.readInt();
+ NativeCodeDescriptionImpl[] nativeDescs = new NativeCodeDescriptionImpl[numNativeDesc];
+ for (int i = 0; i < numNativeDesc; i++)
+ nativeDescs[i] = readNativeCodeDescription(in);
+ result.setPossibleSuppliers(nativeDescs);
+ int supplierIndex = in.readInt();
+ if (supplierIndex >= 0)
+ result.setSupplier(nativeDescs[supplierIndex]);
+ return result;
+ }
+
+ private NativeCodeDescriptionImpl readNativeCodeDescription(DataInputStream in) throws IOException {
+ NativeCodeDescriptionImpl result = new NativeCodeDescriptionImpl();
+ readBaseDescription(result, in);
+ result.setSupplier(readBundleDescription(in));
+ try {
+ result.setFilter(readString(in, false));
+ } catch (InvalidSyntaxException e) {
+ // do nothing, this filter was tested before
+ }
+ result.setLanguages(readStringArray(in));
+ result.setNativePaths(readStringArray(in));
+ result.setOSNames(readStringArray(in));
+ result.setOSVersions(readVersionRanges(in));
+ result.setProcessors(readStringArray(in));
+ result.setInvalidNativePaths(in.readBoolean());
+ return result;
+ }
+
+ private VersionRange[] readVersionRanges(DataInputStream in) throws IOException {
+ int num = in.readInt();
+ if (num == 0)
+ return null;
+ VersionRange[] result = new VersionRange[num];
+ for (int i = 0; i < num; i++)
+ result[i] = readVersionRange(in);
+ return result;
+ }
+
+ private String[] readStringArray(DataInputStream in) throws IOException {
+ int num = in.readInt();
+ if (num == 0)
+ return null;
+ String[] result = new String[num];
+ for (int i = 0; i < num; i++)
+ result[i] = readString(in, false);
+ return result;
+ }
+
+ // called by readers for VersionConstraintImpl subclasses
+ private void readVersionConstraint(VersionConstraintImpl version, DataInputStream in) throws IOException {
+ version.setName(readString(in, false));
+ version.setVersionRange(readVersionRange(in));
+ }
+
+ private Version readVersion(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return Version.emptyVersion;
+ int majorComponent = in.readInt();
+ int minorComponent = in.readInt();
+ int serviceComponent = in.readInt();
+ String qualifierComponent = readString(in, false);
+ Version result = (Version) ObjectPool.intern(new Version(majorComponent, minorComponent, serviceComponent, qualifierComponent));
+ //Version result = new Version(majorComponent, minorComponent, serviceComponent, qualifierComponent);
+ return result;
+ }
+
+ private VersionRange readVersionRange(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ return new VersionRange(readVersion(in), in.readBoolean(), readVersion(in), in.readBoolean());
+ }
+
+ /**
+ * expectedTimestamp is the expected value for the timestamp. or -1, if
+ * no checking should be performed
+ */
+ public synchronized boolean loadStateDeprecated(StateImpl state, DataInputStream input, long expectedTimestamp) throws IOException {
+ try {
+ return readStateDeprecated(state, input, expectedTimestamp);
+ } finally {
+ input.close();
+ }
+ }
+
+ /**
+ * expectedTimestamp is the expected value for the timestamp. or -1, if
+ * no checking should be performed
+ */
+ public synchronized boolean loadState(StateImpl state, long expectedTimestamp) throws IOException {
+ return readState(state, expectedTimestamp);
+ }
+
+ private String readString(DataInputStream in, boolean intern) throws IOException {
+ byte type = in.readByte();
+ if (type == NULL)
+ return null;
+
+ if (type == LONG_STRING) {
+ int length = in.readInt();
+ byte[] data = new byte[length];
+ in.readFully(data);
+ String string = new String(data, UTF_8);
+
+ if (intern)
+ return string.intern();
+ return (String) ObjectPool.intern(string);
+ }
+
+ if (intern)
+ return in.readUTF().intern();
+ return (String) ObjectPool.intern(in.readUTF());
+ }
+
+ private byte readTag(DataInputStream in) throws IOException {
+ return in.readByte();
+ }
+
+ private DataInputStream openLazyFile() throws IOException {
+ if (lazyFile == null)
+ throw new IOException(); // TODO error message here!
+ return new DataInputStream(new BufferedInputStream(secureAction.getFileInputStream(lazyFile), BUFFER_SIZE_LAZY));
+ }
+
+ boolean isLazyLoaded() {
+ return lazyLoad;
+ }
+
+ boolean getAccessedFlag() {
+ return accessedFlag;
+ }
+
+ void setAccessedFlag(boolean accessedFlag) {
+ this.accessedFlag = accessedFlag;
+ }
+
+ void fullyLoad() {
+ setAccessedFlag(true);
+ DataInputStream in = null;
+ try {
+ in = openLazyFile();
+ for (int i = 0; i < numBundles; i++)
+ readBundleDescriptionLazyData(in, 0);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe.getMessage(), ioe); // TODO need error message here
+ } finally {
+ if (in != null)
+ try {
+ in.close();
+ } catch (IOException e) {
+ // nothing we can do now
+ }
+ }
+ }
+
+ void fullyLoad(BundleDescriptionImpl target) throws IOException {
+ setAccessedFlag(true);
+ DataInputStream in = null;
+ try {
+ in = openLazyFile();
+ // get the set of bundles that must be loaded according to dependencies
+ List<BundleDescriptionImpl> toLoad = new ArrayList<BundleDescriptionImpl>();
+ addDependencies(target, toLoad);
+ int skipBytes[] = getSkipBytes(toLoad);
+ // look for the lazy data of the toLoad list
+ for (int i = 0; i < skipBytes.length; i++)
+ readBundleDescriptionLazyData(in, skipBytes[i]);
+ } finally {
+ if (in != null)
+ in.close();
+ }
+ }
+
+ private void addDependencies(BundleDescriptionImpl target, List<BundleDescriptionImpl> toLoad) {
+ if (toLoad.contains(target) || target.isFullyLoaded())
+ return;
+ Iterator<BundleDescriptionImpl> load = toLoad.iterator();
+ int i = 0;
+ while (load.hasNext()) {
+ // insert the target into the list sorted by lazy data offsets
+ BundleDescriptionImpl bundle = load.next();
+ if (target.getLazyDataOffset() < bundle.getLazyDataOffset())
+ break;
+ i++;
+ }
+ if (i >= toLoad.size())
+ toLoad.add(target);
+ else
+ toLoad.add(i, target);
+ List<BundleDescription> deps = target.getBundleDependencies();
+ for (Iterator<BundleDescription> iter = deps.iterator(); iter.hasNext();)
+ addDependencies((BundleDescriptionImpl) iter.next(), toLoad);
+ }
+
+ private int[] getSkipBytes(List<BundleDescriptionImpl> toLoad) {
+ int[] skipBytes = new int[toLoad.size()];
+ for (int i = 0; i < skipBytes.length; i++) {
+ BundleDescriptionImpl current = toLoad.get(i);
+ if (i == 0) {
+ skipBytes[i] = current.getLazyDataOffset();
+ continue;
+ }
+ BundleDescriptionImpl previous = toLoad.get(i - 1);
+ skipBytes[i] = current.getLazyDataOffset() - previous.getLazyDataOffset() - previous.getLazyDataSize();
+ }
+ return skipBytes;
+ }
+
+ void flushLazyObjectCache() {
+ for (Iterator<Entry<Integer, Object>> entries = objectTable.entrySet().iterator(); entries.hasNext();) {
+ Map.Entry<Integer, Object> entry = entries.next();
+ Object value = entry.getValue();
+ if (value instanceof ExportPackageDescription || value instanceof GenericDescription || value instanceof ImportPackageSpecification || value instanceof BundleSpecification || value instanceof GenericSpecification)
+ entries.remove();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateWriter.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateWriter.java
new file mode 100644
index 000000000..57a10d0eb
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/StateWriter.java
@@ -0,0 +1,711 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.osgi.framework.*;
+
+/**
+ * This class is <strong>not</strong> thread safe. Instances must not be
+ * shared across multiple threads.
+ */
+class StateWriter {
+
+ // objectTable will be a hashmap of objects. The objects will be things
+ // like BundleDescription, ExportPackageDescription, Version etc.. The integer
+ // index value will be used in the cache to allow cross-references in the
+ // cached state.
+ private final Map<Object, Integer> objectTable = new HashMap<Object, Integer>();
+
+ private final List<Object> forcedWrite = new ArrayList<Object>();
+
+ private int addToObjectTable(Object object) {
+ Integer cur = objectTable.get(object);
+ if (cur != null)
+ return cur.intValue();
+ objectTable.put(object, new Integer(objectTable.size()));
+ // return the index of the object just added (i.e. size - 1)
+ return (objectTable.size() - 1);
+ }
+
+ private int getFromObjectTable(Object object) {
+ if (objectTable != null) {
+ Object objectResult = objectTable.get(object);
+ if (objectResult != null) {
+ return ((Integer) objectResult).intValue();
+ }
+ }
+ return -1;
+ }
+
+ private boolean writePrefix(Object object, DataOutputStream out) throws IOException {
+ if (writeIndex(object, out))
+ return true;
+ // add this object to the object table first
+ int index = addToObjectTable(object);
+ out.writeByte(StateReader.OBJECT);
+ out.writeInt(index);
+ return false;
+ }
+
+ private void writeStateDeprecated(StateImpl state, DataOutputStream out) throws IOException {
+ out.write(StateReader.STATE_CACHE_VERSION);
+ if (writePrefix(state, out))
+ return;
+ out.writeLong(state.getTimeStamp());
+ // write the platform property keys
+ String[] platformPropKeys = state.getPlatformPropertyKeys();
+ writePlatformProp(platformPropKeys, out);
+ Dictionary<Object, Object>[] propSet = state.getPlatformProperties();
+ out.writeInt(propSet.length);
+ for (int i = 0; i < propSet.length; i++) {
+ Dictionary<Object, Object> props = propSet[i];
+ out.writeInt(platformPropKeys.length);
+ for (int j = 0; j < platformPropKeys.length; j++)
+ writePlatformProp(props.get(platformPropKeys[j]), out);
+ }
+ BundleDescription[] bundles = state.getBundles();
+ StateHelperImpl.getInstance().sortBundles(bundles);
+ out.writeInt(bundles.length);
+ if (bundles.length == 0)
+ return;
+ for (int i = 0; i < bundles.length; i++)
+ writeBundleDescription(bundles[i], out, false);
+ out.writeBoolean(state.isResolved());
+ // save the lazy data offset
+ out.writeInt(out.size());
+ for (int i = 0; i < bundles.length; i++)
+ writeBundleDescriptionLazyData(bundles[i], out);
+ }
+
+ public void saveState(StateImpl state, File stateFile, File lazyFile) throws IOException {
+ DataOutputStream outLazy = null;
+ DataOutputStream outState = null;
+ FileOutputStream fosLazy = null;
+ FileOutputStream fosState = null;
+ synchronized (state.monitor) {
+ try {
+ BundleDescription[] bundles = state.getBundles();
+ StateHelperImpl.getInstance().sortBundles(bundles);
+ // need to prime the object table with all bundles
+ // this allows us to write only indexes to bundles in the lazy data
+ for (int i = 0; i < bundles.length; i++) {
+ addToObjectTable(bundles[i]);
+ if (bundles[i].getHost() != null)
+ addToObjectTable(bundles[i].getHost());
+ }
+ // first write the lazy data to get the offsets and sizes to the lazy data
+ fosLazy = new FileOutputStream(lazyFile);
+ outLazy = new DataOutputStream(new BufferedOutputStream(fosLazy));
+ for (int i = 0; i < bundles.length; i++)
+ writeBundleDescriptionLazyData(bundles[i], outLazy);
+ // now write the state data
+ fosState = new FileOutputStream(stateFile);
+ outState = new DataOutputStream(new BufferedOutputStream(fosState));
+ outState.write(StateReader.STATE_CACHE_VERSION);
+ if (writePrefix(state, outState))
+ return;
+ outState.writeLong(state.getTimeStamp());
+ // write the platform property keys
+ String[] platformPropKeys = state.getPlatformPropertyKeys();
+ writePlatformProp(platformPropKeys, outState);
+ // write the platform property values
+ Dictionary<Object, Object>[] propSet = state.getPlatformProperties();
+ outState.writeInt(propSet.length);
+ for (int i = 0; i < propSet.length; i++) {
+ Dictionary<Object, Object> props = propSet[i];
+ outState.writeInt(platformPropKeys.length);
+ for (int j = 0; j < platformPropKeys.length; j++)
+ writePlatformProp(props.get(platformPropKeys[j]), outState);
+ }
+ outState.writeInt(bundles.length);
+ for (int i = 0; i < bundles.length; i++)
+ // write out each bundle with the force flag set to make sure
+ // the data is written at least once in the non-lazy state data
+ writeBundleDescription(bundles[i], outState, true);
+ // write the DisabledInfos
+ DisabledInfo[] infos = state.getDisabledInfos();
+ outState.writeInt(infos.length);
+ for (int i = 0; i < infos.length; i++)
+ writeDisabledInfo(infos[i], outState);
+ outState.writeBoolean(state.isResolved());
+ } finally {
+ if (outLazy != null) {
+ try {
+ outLazy.flush();
+ fosLazy.getFD().sync();
+ } catch (IOException e) {
+ // do nothing, we tried
+ }
+ try {
+ outLazy.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ if (outState != null) {
+ try {
+ outState.flush();
+ fosState.getFD().sync();
+ } catch (IOException e) {
+ // do nothing, we tried
+ }
+ try {
+ outState.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+ }
+
+ private void writePlatformProp(Object obj, DataOutputStream out) throws IOException {
+ if (!(obj instanceof String) && !(obj instanceof String[]))
+ out.writeByte(StateReader.NULL);
+ else {
+ out.writeByte(StateReader.OBJECT);
+ if (obj instanceof String) {
+ out.writeInt(1);
+ writeStringOrNull((String) obj, out);
+ } else {
+ String[] props = (String[]) obj;
+ out.writeInt(props.length);
+ for (int i = 0; i < props.length; i++)
+ writeStringOrNull(props[i], out);
+ }
+ }
+ }
+
+ /*
+ * The force flag is used when writing the non-lazy state data. This forces the data to be
+ * written once even if the object exists in the object table.
+ * This is needed because we want to write the lazy data first but we only want
+ * to include indexes to the actual bundles in the lazy data. To do this we
+ * prime the object table with all the bundles first. Then we write the
+ * lazy data. Finally we write the non-lazy data and force a write of the
+ * bundles data once even if the bundle is in the object table.
+ */
+ private void writeBundleDescription(BundleDescription bundle, DataOutputStream out, boolean force) throws IOException {
+ if (force && !forcedWrite.contains(bundle)) {
+ int index = addToObjectTable(bundle);
+ out.writeByte(StateReader.OBJECT);
+ out.writeInt(index);
+ forcedWrite.add(bundle);
+ } else if (writePrefix(bundle, out))
+ return;
+ // first write out non-lazy loaded data
+ out.writeLong(bundle.getBundleId()); // ID must be the first thing
+ writeBaseDescription(bundle, out);
+ out.writeInt(((BundleDescriptionImpl) bundle).getLazyDataOffset());
+ out.writeInt(((BundleDescriptionImpl) bundle).getLazyDataSize());
+ out.writeBoolean(bundle.isResolved());
+ out.writeBoolean(bundle.isSingleton());
+ out.writeBoolean(bundle.hasDynamicImports());
+ out.writeBoolean(bundle.attachFragments());
+ out.writeBoolean(bundle.dynamicFragments());
+ writeList(out, (String[]) ((BundleDescriptionImpl) bundle).getDirective(Constants.MANDATORY_DIRECTIVE));
+ writeMap(out, bundle.getAttributes());
+ writeMap(out, ((BundleDescriptionImpl) bundle).getArbitraryDirectives());
+ writeHostSpec((HostSpecificationImpl) bundle.getHost(), out, force);
+
+ List<BundleDescription> dependencies = ((BundleDescriptionImpl) bundle).getBundleDependencies();
+ out.writeInt(dependencies.size());
+ for (Iterator<BundleDescription> iter = dependencies.iterator(); iter.hasNext();)
+ writeBundleDescription(iter.next(), out, force);
+ // the rest is lazy loaded data
+ }
+
+ private void writeBundleDescriptionLazyData(BundleDescription bundle, DataOutputStream out) throws IOException {
+ int dataStart = out.size(); // save the offset of lazy data start
+ int index = getFromObjectTable(bundle);
+ ((BundleDescriptionImpl) bundle).setLazyDataOffset(out.size());
+ out.writeInt(index);
+
+ writeStringOrNull(bundle.getLocation(), out);
+ writeStringOrNull(bundle.getPlatformFilter(), out);
+
+ ExportPackageDescription[] exports = bundle.getExportPackages();
+ out.writeInt(exports.length);
+ for (int i = 0; i < exports.length; i++)
+ writeExportPackageDesc((ExportPackageDescriptionImpl) exports[i], out);
+
+ ImportPackageSpecification[] imports = bundle.getImportPackages();
+ out.writeInt(imports.length);
+ for (int i = 0; i < imports.length; i++)
+ writeImportPackageSpec((ImportPackageSpecificationImpl) imports[i], out);
+
+ BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
+ out.writeInt(requiredBundles.length);
+ for (int i = 0; i < requiredBundles.length; i++)
+ writeBundleSpec((BundleSpecificationImpl) requiredBundles[i], out);
+
+ ExportPackageDescription[] selectedExports = bundle.getSelectedExports();
+ if (selectedExports == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(selectedExports.length);
+ for (int i = 0; i < selectedExports.length; i++)
+ writeExportPackageDesc((ExportPackageDescriptionImpl) selectedExports[i], out);
+ }
+
+ ExportPackageDescription[] substitutedExports = bundle.getSubstitutedExports();
+ if (substitutedExports == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(substitutedExports.length);
+ for (int i = 0; i < substitutedExports.length; i++)
+ writeExportPackageDesc((ExportPackageDescriptionImpl) substitutedExports[i], out);
+ }
+
+ ExportPackageDescription[] resolvedImports = bundle.getResolvedImports();
+ if (resolvedImports == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(resolvedImports.length);
+ for (int i = 0; i < resolvedImports.length; i++)
+ writeExportPackageDesc((ExportPackageDescriptionImpl) resolvedImports[i], out);
+ }
+
+ BundleDescription[] resolvedRequires = bundle.getResolvedRequires();
+ if (resolvedRequires == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(resolvedRequires.length);
+ for (int i = 0; i < resolvedRequires.length; i++)
+ writeBundleDescription(resolvedRequires[i], out, false);
+ }
+
+ String[] ees = bundle.getExecutionEnvironments();
+ out.writeInt(ees.length);
+ for (int i = 0; i < ees.length; i++)
+ writeStringOrNull(ees[i], out);
+
+ Map<String, Long> dynamicStamps = ((BundleDescriptionImpl) bundle).getDynamicStamps();
+ if (dynamicStamps == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(dynamicStamps.size());
+ for (Iterator<String> pkgs = dynamicStamps.keySet().iterator(); pkgs.hasNext();) {
+ String pkg = pkgs.next();
+ writeStringOrNull(pkg, out);
+ out.writeLong(dynamicStamps.get(pkg).longValue());
+ }
+ }
+
+ GenericDescription[] genericCapabilities = bundle.getGenericCapabilities();
+ if (genericCapabilities == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(genericCapabilities.length);
+ for (int i = 0; i < genericCapabilities.length; i++)
+ writeGenericDescription(genericCapabilities[i], out);
+ }
+
+ GenericSpecification[] genericRequires = bundle.getGenericRequires();
+ if (genericRequires == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(genericRequires.length);
+ for (int i = 0; i < genericRequires.length; i++)
+ writeGenericSpecification((GenericSpecificationImpl) genericRequires[i], out);
+ }
+
+ GenericDescription[] selectedCapabilities = bundle.getSelectedGenericCapabilities();
+ if (selectedCapabilities == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(selectedCapabilities.length);
+ for (int i = 0; i < selectedCapabilities.length; i++)
+ writeGenericDescription(selectedCapabilities[i], out);
+ }
+
+ GenericDescription[] resolvedCapabilities = bundle.getResolvedGenericRequires();
+ if (resolvedCapabilities == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(resolvedCapabilities.length);
+ for (int i = 0; i < resolvedCapabilities.length; i++)
+ writeGenericDescription(resolvedCapabilities[i], out);
+ }
+
+ writeNativeCode(bundle.getNativeCodeSpecification(), out);
+
+ writeMap(out, ((BundleDescriptionImpl) bundle).getWiresInternal());
+ // save the size of the lazy data
+ ((BundleDescriptionImpl) bundle).setLazyDataSize(out.size() - dataStart);
+ }
+
+ private void writeDisabledInfo(DisabledInfo disabledInfo, DataOutputStream out) throws IOException {
+ writeStringOrNull(disabledInfo.getPolicyName(), out);
+ writeStringOrNull(disabledInfo.getMessage(), out);
+ writeBundleDescription(disabledInfo.getBundle(), out, false);
+ }
+
+ private void writeBundleSpec(BundleSpecificationImpl bundle, DataOutputStream out) throws IOException {
+ if (writePrefix(bundle, out))
+ return;
+ writeVersionConstraint(bundle, out);
+ writeBundleDescription((BundleDescription) bundle.getSupplier(), out, false);
+ out.writeBoolean(bundle.isExported());
+ out.writeBoolean(bundle.isOptional());
+ writeMap(out, bundle.getAttributes());
+ writeMap(out, bundle.getArbitraryDirectives());
+ }
+
+ private void writeExportPackageDesc(ExportPackageDescriptionImpl exportPackageDesc, DataOutputStream out) throws IOException {
+ if (writePrefix(exportPackageDesc, out))
+ return;
+ writeBaseDescription(exportPackageDesc, out);
+ writeBundleDescription(exportPackageDesc.getExporter(), out, false);
+ writeMap(out, exportPackageDesc.getAttributes());
+ writeMap(out, exportPackageDesc.getDirectives());
+ writeMap(out, exportPackageDesc.getArbitraryDirectives());
+ writeExportPackageDesc((ExportPackageDescriptionImpl) exportPackageDesc.getFragmentDeclaration(), out);
+ }
+
+ private void writeGenericDescription(GenericDescription description, DataOutputStream out) throws IOException {
+ if (writePrefix(description, out))
+ return;
+ writeBaseDescription(description, out);
+ writeBundleDescription(description.getSupplier(), out, false);
+ writeStringOrNull(description.getType() == GenericDescription.DEFAULT_TYPE ? null : description.getType(), out);
+ Dictionary<String, Object> attrs = description.getAttributes();
+ Map<String, Object> mapAttrs = new HashMap<String, Object>(attrs.size());
+ for (Enumeration<String> keys = attrs.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ mapAttrs.put(key, attrs.get(key));
+ }
+ writeMap(out, mapAttrs);
+ Map<String, String> directives = description.getDeclaredDirectives();
+ writeMap(out, directives);
+ writeGenericDescription((GenericDescription) ((BaseDescriptionImpl) description).getFragmentDeclaration(), out);
+ }
+
+ private void writeGenericSpecification(GenericSpecificationImpl specification, DataOutputStream out) throws IOException {
+ if (writePrefix(specification, out))
+ return;
+ writeVersionConstraint(specification, out);
+ writeStringOrNull(specification.getType() == GenericDescription.DEFAULT_TYPE ? null : specification.getType(), out);
+ GenericDescription[] suppliers = specification.getSuppliers();
+ out.writeInt(suppliers == null ? 0 : suppliers.length);
+ if (suppliers != null)
+ for (int i = 0; i < suppliers.length; i++)
+ writeGenericDescription(suppliers[i], out);
+ out.writeInt(specification.getResolution());
+ writeStringOrNull(specification.getMatchingFilter(), out);
+ writeMap(out, specification.getAttributes());
+ writeMap(out, specification.getArbitraryDirectives());
+ }
+
+ private void writeNativeCode(NativeCodeSpecification nativeCodeSpecification, DataOutputStream out) throws IOException {
+ if (nativeCodeSpecification == null) {
+ out.writeBoolean(false);
+ return;
+ }
+ out.writeBoolean(true);
+ out.writeBoolean(nativeCodeSpecification.isOptional());
+ NativeCodeDescription[] nativeDescs = nativeCodeSpecification.getPossibleSuppliers();
+ int numDescs = nativeDescs == null ? 0 : nativeDescs.length;
+ out.writeInt(numDescs);
+ int supplierIndex = -1;
+ for (int i = 0; i < numDescs; i++) {
+ if (nativeDescs[i] == nativeCodeSpecification.getSupplier())
+ supplierIndex = i;
+ writeNativeCodeDescription(nativeDescs[i], out);
+ }
+ out.writeInt(supplierIndex);
+ }
+
+ private void writeNativeCodeDescription(NativeCodeDescription nativeCodeDescription, DataOutputStream out) throws IOException {
+ writeBaseDescription(nativeCodeDescription, out);
+ writeBundleDescription(nativeCodeDescription.getSupplier(), out, false);
+ Filter filter = nativeCodeDescription.getFilter();
+ writeStringOrNull(filter == null ? null : filter.toString(), out);
+ writeStringArray(nativeCodeDescription.getLanguages(), out);
+ writeStringArray(nativeCodeDescription.getNativePaths(), out);
+ writeStringArray(nativeCodeDescription.getOSNames(), out);
+ writeVersionRanges(nativeCodeDescription.getOSVersions(), out);
+ writeStringArray(nativeCodeDescription.getProcessors(), out);
+ out.writeBoolean(nativeCodeDescription.hasInvalidNativePaths());
+ }
+
+ private void writeVersionRanges(VersionRange[] ranges, DataOutputStream out) throws IOException {
+ out.writeInt(ranges == null ? 0 : ranges.length);
+ if (ranges == null)
+ return;
+ for (int i = 0; i < ranges.length; i++)
+ writeVersionRange(ranges[i], out);
+ }
+
+ private void writeStringArray(String[] strings, DataOutputStream out) throws IOException {
+ out.writeInt(strings == null ? 0 : strings.length);
+ if (strings == null)
+ return;
+ for (int i = 0; i < strings.length; i++)
+ writeStringOrNull(strings[i], out);
+ }
+
+ private void writeMap(DataOutputStream out, Map<String, ?> source) throws IOException {
+ if (source == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(source.size());
+ Iterator<String> iter = source.keySet().iterator();
+ while (iter.hasNext()) {
+ String key = iter.next();
+ Object value = source.get(key);
+ writeStringOrNull(key, out);
+ if (value instanceof String) {
+ out.writeByte(0);
+ writeStringOrNull((String) value, out);
+ } else if (value instanceof String[]) {
+ out.writeByte(1);
+ writeList(out, (String[]) value);
+ } else if (value instanceof Boolean) {
+ out.writeByte(2);
+ out.writeBoolean(((Boolean) value).booleanValue());
+ } else if (value instanceof Integer) {
+ out.writeByte(3);
+ out.writeInt(((Integer) value).intValue());
+ } else if (value instanceof Long) {
+ out.writeByte(4);
+ out.writeLong(((Long) value).longValue());
+ } else if (value instanceof Double) {
+ out.writeByte(5);
+ out.writeDouble(((Double) value).doubleValue());
+ } else if (value instanceof Version) {
+ out.writeByte(6);
+ writeVersion((Version) value, out);
+ } else if ("java.net.URI".equals(value.getClass().getName())) { //$NON-NLS-1$
+ out.writeByte(7);
+ writeStringOrNull(value.toString(), out);
+ } else if (value instanceof List) {
+ writeList(out, (List<?>) value);
+ }
+ }
+ }
+ }
+
+ private void writeList(DataOutputStream out, List<?> list) throws IOException {
+ byte type = getListType(list);
+ if (type == -2)
+ return; // don't understand the list type
+ out.writeByte(8);
+ out.writeByte(type);
+ out.writeInt(list.size());
+ for (Object value : list) {
+ switch (type) {
+ case 0 :
+ writeStringOrNull((String) value, out);
+ break;
+ case 3 :
+ out.writeInt(((Integer) value).intValue());
+ break;
+ case 4 :
+ out.writeLong(((Long) value).longValue());
+ break;
+ case 5 :
+ out.writeDouble(((Double) value).doubleValue());
+ break;
+ case 6 :
+ writeVersion((Version) value, out);
+ break;
+ case 7 :
+ writeStateWire((StateWire) value, out);
+ default :
+ break;
+ }
+ }
+ }
+
+ private void writeStateWire(StateWire wire, DataOutputStream out) throws IOException {
+ VersionConstraint requirement = wire.getDeclaredRequirement();
+ if (requirement instanceof ImportPackageSpecificationImpl) {
+ out.writeByte(0);
+ writeImportPackageSpec((ImportPackageSpecificationImpl) requirement, out);
+ } else if (requirement instanceof BundleSpecificationImpl) {
+ out.writeByte(1);
+ writeBundleSpec((BundleSpecificationImpl) requirement, out);
+ } else if (requirement instanceof HostSpecificationImpl) {
+ out.writeByte(2);
+ writeHostSpec((HostSpecificationImpl) requirement, out, false);
+ } else if (requirement instanceof GenericSpecificationImpl) {
+ out.writeByte(3);
+ writeGenericSpecification((GenericSpecificationImpl) requirement, out);
+ } else
+ throw new IllegalArgumentException("Unknown requiement type: " + requirement.getClass());
+
+ BaseDescription capability = wire.getDeclaredCapability();
+ if (capability instanceof BundleDescription)
+ writeBundleDescription((BundleDescription) capability, out, false);
+ else if (capability instanceof ExportPackageDescriptionImpl)
+ writeExportPackageDesc((ExportPackageDescriptionImpl) capability, out);
+ else if (capability instanceof GenericDescription)
+ writeGenericDescription((GenericDescription) capability, out);
+ else
+ throw new IllegalArgumentException("Unknown capability type: " + requirement.getClass());
+
+ writeBundleDescription(wire.getRequirementHost(), out, false);
+ writeBundleDescription(wire.getCapabilityHost(), out, false);
+ }
+
+ private byte getListType(List<?> list) {
+ if (list.size() == 0)
+ return -1;
+ Object type = list.get(0);
+ if (type instanceof String)
+ return 0;
+ if (type instanceof Integer)
+ return 3;
+ if (type instanceof Long)
+ return 4;
+ if (type instanceof Double)
+ return 5;
+ if (type instanceof Version)
+ return 6;
+ if (type instanceof StateWire)
+ return 7;
+ return -2;
+ }
+
+ private void writeList(DataOutputStream out, String[] list) throws IOException {
+ if (list == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(list.length);
+ for (int i = 0; i < list.length; i++)
+ writeStringOrNull(list[i], out);
+ }
+ }
+
+ private void writeBaseDescription(BaseDescription rootDesc, DataOutputStream out) throws IOException {
+ writeStringOrNull(rootDesc.getName(), out);
+ writeVersion(rootDesc.getVersion(), out);
+ }
+
+ private void writeImportPackageSpec(ImportPackageSpecificationImpl importPackageSpec, DataOutputStream out) throws IOException {
+ if (writePrefix(importPackageSpec, out))
+ return;
+ writeVersionConstraint(importPackageSpec, out);
+ // TODO this is a hack until the state dynamic loading is cleaned up
+ // we should only write the supplier if we are resolved
+ if (importPackageSpec.getBundle().isResolved())
+ writeExportPackageDesc((ExportPackageDescriptionImpl) importPackageSpec.getSupplier(), out);
+ else
+ out.writeByte(StateReader.NULL);
+
+ writeStringOrNull(importPackageSpec.getBundleSymbolicName(), out);
+ writeVersionRange(importPackageSpec.getBundleVersionRange(), out);
+ writeMap(out, importPackageSpec.getAttributes());
+ writeMap(out, importPackageSpec.getDirectives());
+ writeMap(out, importPackageSpec.getArbitraryDirectives());
+ }
+
+ private void writeHostSpec(HostSpecificationImpl host, DataOutputStream out, boolean force) throws IOException {
+ if (host != null && force && !forcedWrite.contains(host)) {
+ int index = addToObjectTable(host);
+ out.writeByte(StateReader.OBJECT);
+ out.writeInt(index);
+ forcedWrite.add(host);
+ } else if (writePrefix(host, out))
+ return;
+ writeVersionConstraint(host, out);
+ BundleDescription[] hosts = host.getHosts();
+ if (hosts == null) {
+ out.writeInt(0);
+ return;
+ }
+ out.writeInt(hosts.length);
+ for (int i = 0; i < hosts.length; i++)
+ writeBundleDescription(hosts[i], out, force);
+ writeMap(out, host.getAttributes());
+ writeMap(out, host.getArbitraryDirectives());
+ }
+
+ // called by writers for VersionConstraintImpl subclasses
+ private void writeVersionConstraint(VersionConstraint constraint, DataOutputStream out) throws IOException {
+ writeStringOrNull(constraint.getName(), out);
+ writeVersionRange(constraint.getVersionRange(), out);
+ }
+
+ private void writeVersion(Version version, DataOutputStream out) throws IOException {
+ if (version == null || version.equals(Version.emptyVersion)) {
+ out.writeByte(StateReader.NULL);
+ return;
+ }
+ out.writeByte(StateReader.OBJECT);
+ out.writeInt(version.getMajor());
+ out.writeInt(version.getMinor());
+ out.writeInt(version.getMicro());
+ writeQualifier(version.getQualifier(), out);
+ }
+
+ private void writeVersionRange(VersionRange versionRange, DataOutputStream out) throws IOException {
+ if (versionRange == null || versionRange.equals(VersionRange.emptyRange)) {
+ out.writeByte(StateReader.NULL);
+ return;
+ }
+ out.writeByte(StateReader.OBJECT);
+ writeVersion(versionRange.getMinimum(), out);
+ out.writeBoolean(versionRange.getIncludeMinimum());
+ writeVersion(versionRange.getMaximum(), out);
+ out.writeBoolean(versionRange.getIncludeMaximum());
+ }
+
+ private boolean writeIndex(Object object, DataOutputStream out) throws IOException {
+ if (object == null) {
+ out.writeByte(StateReader.NULL);
+ return true;
+ }
+ int index = getFromObjectTable(object);
+ if (index == -1)
+ return false;
+ out.writeByte(StateReader.INDEX);
+ out.writeInt(index);
+ return true;
+ }
+
+ public void saveStateDeprecated(StateImpl state, DataOutputStream output) throws IOException {
+ try {
+ writeStateDeprecated(state, output);
+ } finally {
+ output.close();
+ }
+ }
+
+ private void writeStringOrNull(String string, DataOutputStream out) throws IOException {
+ if (string == null)
+ out.writeByte(StateReader.NULL);
+ else {
+ byte[] data = string.getBytes(StateReader.UTF_8);
+
+ if (data.length > 65535) {
+ out.writeByte(StateReader.LONG_STRING);
+ out.writeInt(data.length);
+ out.write(data);
+ } else {
+ out.writeByte(StateReader.OBJECT);
+ out.writeUTF(string);
+ }
+ }
+ }
+
+ private void writeQualifier(String string, DataOutputStream out) throws IOException {
+ if (string != null && string.length() == 0)
+ string = null;
+ writeStringOrNull(string, out);
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/UserState.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/UserState.java
new file mode 100644
index 000000000..905927941
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/UserState.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.BundleException;
+
+/**
+ * This implementation of State does a bookkeeping of all added/removed
+ */
+public class UserState extends StateImpl {
+ // TODO this is not an accurate way to record updates
+ private final Set<String> updated = Collections.synchronizedSet(new HashSet<String>());
+
+ public boolean removeBundle(BundleDescription description) {
+ if (description.getLocation() != null)
+ updated.remove(description.getLocation());
+ if (!super.removeBundle(description))
+ return false;
+ return true;
+ }
+
+ public boolean updateBundle(BundleDescription newDescription) {
+ if (!super.updateBundle(newDescription))
+ return false;
+ updated.add(newDescription.getLocation());
+ return true;
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public StateDelta compare(State baseState) throws BundleException {
+ BundleDescription[] current = this.getBundles();
+ StateDeltaImpl delta = new StateDeltaImpl(this);
+ // process additions and updates
+ for (int i = 0; i < current.length; i++) {
+ BundleDescription existing = baseState.getBundleByLocation(current[i].getLocation());
+ if (existing == null)
+ delta.recordBundleAdded((BundleDescriptionImpl) current[i]);
+ else if (updated.contains(current[i].getLocation()))
+ delta.recordBundleUpdated((BundleDescriptionImpl) current[i]);
+ }
+ // process removals
+ BundleDescription[] existing = baseState.getBundles();
+ for (int i = 0; i < existing.length; i++) {
+ BundleDescription local = getBundleByLocation(existing[i].getLocation());
+ if (local == null)
+ delta.recordBundleRemoved((BundleDescriptionImpl) existing[i]);
+ }
+ return delta;
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
new file mode 100644
index 000000000..73682082f
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.internal.resolver.BaseDescriptionImpl.BaseCapability;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.namespace.AbstractWiringNamespace;
+import org.osgi.framework.wiring.*;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+
+abstract class VersionConstraintImpl implements VersionConstraint {
+
+ protected final Object monitor = new Object();
+
+ private String name;
+ private VersionRange versionRange;
+ private BundleDescription bundle;
+ private BaseDescription supplier;
+ private volatile Object userObject;
+
+ public String getName() {
+ synchronized (this.monitor) {
+ if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(name)) {
+ StateImpl state = (StateImpl) getBundle().getContainingState();
+ return state == null ? EquinoxContainer.NAME : state.getSystemBundle();
+ }
+ return name;
+ }
+ }
+
+ public VersionRange getVersionRange() {
+ synchronized (this.monitor) {
+ if (versionRange == null)
+ return VersionRange.emptyRange;
+ return versionRange;
+ }
+ }
+
+ public BundleDescription getBundle() {
+ synchronized (this.monitor) {
+ return bundle;
+ }
+ }
+
+ public boolean isResolved() {
+ synchronized (this.monitor) {
+ return supplier != null;
+ }
+ }
+
+ public BaseDescription getSupplier() {
+ synchronized (this.monitor) {
+ return supplier;
+ }
+ }
+
+ public boolean isSatisfiedBy(BaseDescription candidate) {
+ synchronized (this.monitor) {
+ return false;
+ }
+ }
+
+ protected void setName(String name) {
+ synchronized (this.monitor) {
+ this.name = name;
+ }
+ }
+
+ protected void setVersionRange(VersionRange versionRange) {
+ synchronized (this.monitor) {
+ this.versionRange = versionRange;
+ }
+ }
+
+ protected void setBundle(BundleDescription bundle) {
+ synchronized (this.monitor) {
+ this.bundle = bundle;
+ }
+ }
+
+ protected void setSupplier(BaseDescription supplier) {
+ synchronized (this.monitor) {
+ this.supplier = supplier;
+ }
+ }
+
+ protected abstract String getInternalNameSpace();
+
+ protected abstract Map<String, String> getInternalDirectives();
+
+ protected abstract Map<String, Object> getInteralAttributes();
+
+ protected abstract boolean hasMandatoryAttributes(String[] mandatory);
+
+ public BundleRequirement getRequirement() {
+ String namespace = getInternalNameSpace();
+ if (namespace == null)
+ return null;
+ return new BundleRequirementImpl(namespace);
+ }
+
+ public Object getUserObject() {
+ return userObject;
+ }
+
+ public void setUserObject(Object userObject) {
+ this.userObject = userObject;
+ }
+
+ class BundleRequirementImpl implements BundleRequirement {
+ private final String namespace;
+
+ public BundleRequirementImpl(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, String> getDirectives() {
+ return Collections.unmodifiableMap(getInternalDirectives());
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> getAttributes() {
+ return Collections.unmodifiableMap(getInteralAttributes());
+ }
+
+ public BundleRevision getRevision() {
+ return getBundle();
+ }
+
+ public boolean matches(BundleCapability capability) {
+ return isSatisfiedBy(((BaseCapability) capability).getBaseDescription());
+ }
+
+ public int hashCode() {
+ return System.identityHashCode(VersionConstraintImpl.this);
+ }
+
+ private VersionConstraintImpl getVersionConstraint() {
+ return VersionConstraintImpl.this;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof BundleRequirementImpl))
+ return false;
+ return ((BundleRequirementImpl) obj).getVersionConstraint() == VersionConstraintImpl.this;
+ }
+
+ public String toString() {
+ return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true);
+ }
+
+ public boolean matches(Capability capability) {
+ if (capability instanceof BundleCapability)
+ return matches((BundleCapability) capability);
+ // now we must do the generic thing
+ if (!namespace.equals(capability.getNamespace()))
+ return false;
+ String filterSpec = getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+ try {
+ if (filterSpec != null && !FrameworkUtil.createFilter(filterSpec).matches(capability.getAttributes()))
+ return false;
+ } catch (InvalidSyntaxException e) {
+ return false;
+ }
+ return hasMandatoryAttributes(ManifestElement.getArrayFromList(capability.getDirectives().get(AbstractWiringNamespace.CAPABILITY_MANDATORY_DIRECTIVE)));
+ }
+
+ public BundleRevision getResource() {
+ return getRevision();
+ }
+ }
+
+ static StringBuffer addFilterAttributes(StringBuffer filter, Map<String, ?> attributes) {
+ for (Map.Entry<String, ?> entry : attributes.entrySet()) {
+ addFilterAttribute(filter, entry.getKey(), entry.getValue());
+ }
+ return filter;
+ }
+
+ static StringBuffer addFilterAttribute(StringBuffer filter, String attr, Object value) {
+ return addFilterAttribute(filter, attr, value, true);
+ }
+
+ static StringBuffer addFilterAttribute(StringBuffer filter, String attr, Object value, boolean escapeWildCard) {
+ if (value instanceof VersionRange) {
+ VersionRange range = (VersionRange) value;
+ filter.append(range.toFilterString(attr));
+ } else {
+ filter.append('(').append(attr).append('=').append(escapeValue(value, escapeWildCard)).append(')');
+ }
+ return filter;
+ }
+
+ private static String escapeValue(Object o, boolean escapeWildCard) {
+ String value = o.toString();
+ boolean escaped = false;
+ int inlen = value.length();
+ int outlen = inlen << 1; /* inlen * 2 */
+
+ char[] output = new char[outlen];
+ value.getChars(0, inlen, output, inlen);
+
+ int cursor = 0;
+ for (int i = inlen; i < outlen; i++) {
+ char c = output[i];
+ switch (c) {
+ case '*' :
+ if (!escapeWildCard)
+ break;
+ case '\\' :
+ case '(' :
+ case ')' :
+ output[cursor] = '\\';
+ cursor++;
+ escaped = true;
+ break;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return escaped ? new String(output, 0, cursor) : value;
+ }
+}

Back to the top