Bug 357137 - Changes to equinox resolver to support RFC 112 resolver
diff --git a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
index 5cc0727..142a91e 100644
--- a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
@@ -10,7 +10,8 @@
  org.eclipse.osgi.service.environment;version="1.3",
  org.eclipse.osgi.service.localization;version="1.1",
  org.eclipse.osgi.service.pluginconversion;version="1.0",
- org.eclipse.osgi.service.resolver;version="1.5",
+ org.eclipse.osgi.service.resolver;version="1.6",
+ org.eclipse.osgi.service.resolver.extras;version="1.0"; x-friends:="org.eclipse.equinox.resolver",
  org.eclipse.osgi.service.runnable;version="1.1",
  org.eclipse.osgi.service.security; version="1.0",
  org.eclipse.osgi.service.urlconversion;version="1.0",
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BaseDescription.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BaseDescription.java
index c9b02b7..3c70068 100644
--- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BaseDescription.java
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BaseDescription.java
@@ -71,4 +71,27 @@
 	 * @since 3.7
 	 */
 	public BundleCapability getCapability();
+
+	/**
+	 * Returns the user object associated to this description, or 
+	 * <code>null</code> if none exists.
+	 *  
+	 * @return the user object associated to this description,
+	 * or <code>null</code>
+	 * @since 3.8
+	 */
+	public Object getUserObject();
+
+	/**
+	 * Associates a user-provided object to this description, or
+	 * removes an existing association, if <code>null</code> is provided. The 
+	 * provided object is not interpreted in any ways by this 
+	 * description.
+	 * 
+	 * @param userObject an arbitrary object provided by the user, or 
+	 * <code>null</code>
+	 * @since 3.8
+	 */
+	public void setUserObject(Object userObject);
+
 }
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BundleDescription.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BundleDescription.java
index 38f02be..b7acfcc 100644
--- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BundleDescription.java
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/BundleDescription.java
@@ -223,26 +223,6 @@
 	public BundleDescription[] getDependents();
 
 	/**
-	 * Returns the user object associated to this bundle description, or 
-	 * <code>null</code> if none exists.
-	 *  
-	 * @return the user object associated to this bundle  description,
-	 * or <code>null</code>
-	 */
-	public Object getUserObject();
-
-	/**
-	 * Associates a user-provided object to this bundle description, or
-	 * removes an existing association, if <code>null</code> is provided. The 
-	 * provided object is not interpreted in any ways by this bundle 
-	 * description.
-	 * 
-	 * @param userObject an arbitrary object provided by the user, or 
-	 * <code>null</code>
-	 */
-	public void setUserObject(Object userObject);
-
-	/**
 	 * Returns the platform filter in the form of an LDAP filter
 	 * @return the platfomr filter in the form of an LDAP filter
 	 */
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/StateObjectFactory.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/StateObjectFactory.java
index 3367b0d..c0f6701 100644
--- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/StateObjectFactory.java
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/StateObjectFactory.java
@@ -11,8 +11,7 @@
 package org.eclipse.osgi.service.resolver;
 
 import java.io.*;
-import java.util.Dictionary;
-import java.util.Map;
+import java.util.*;
 import org.eclipse.osgi.internal.resolver.StateObjectFactoryImpl;
 import org.osgi.framework.*;
 
@@ -162,6 +161,27 @@
 	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);
 
 	/**
+	 * Creates a bundle description from the given parameters.
+	 *
+	 * @param id id for the bundle 
+	 * @param symbolicName the symbolic name of the bundle.  This may include directives and/or attributes encoded using the Bundle-SymbolicName header.
+	 * @param version version for the bundle (may be <code>null</code>)
+	 * @param location location for the bundle (may be <code>null</code>)
+	 * @param required version constraints for all required bundles (may be  <code>null</code>)
+	 * @param host version constraint specifying the host for the bundle to be created. Should be <code>null</code> if the bundle is not a fragment
+	 * @param imports version constraints for all packages imported  (may be <code>null</code>)
+	 * @param exports package descriptions of all the exported packages (may be <code>null</code>)
+	 * @param platformFilter the platform filter (may be <code>null</code>)
+	 * @param executionEnvironments the execution environment (may be <code>null</code>)
+	 * @param genericRequires the version constraints for all required capabilities (may be <code>null</code>)
+	 * @param genericCapabilities the specifications of all the capabilities of the bundle (may be <code>null</code>)
+	 * @param nativeCode the native code specification of the bundle (may be <code>null</code>)
+	 * @return the created bundle description
+	 * @since 3.8
+	 */
+	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);
+
+	/**
 	 * Returns a bundle description based on the information in the supplied manifest dictionary.
 	 * The manifest should contain String keys and String values which correspond to 
 	 * proper OSGi manifest headers and values.
@@ -219,6 +239,15 @@
 	public BundleSpecification createBundleSpecification(BundleSpecification original);
 
 	/**
+	 * Creates bundle specifications from the given declaration.  The declaration uses
+	 * the bundle manifest syntax for the Require-Bundle header.
+	 * @param declaration a string declaring bundle specifications
+	 * @return the bundle specifications
+	 * @since 3.8
+	 */
+	public List<BundleSpecification> createBundleSpecifications(String declaration);
+
+	/**
 	 * Creates a host specification from the given parameters.
 	 *  
 	 * @param hostSymbolicName the symbolic name for the host bundle
@@ -229,6 +258,15 @@
 	public HostSpecification createHostSpecification(String hostSymbolicName, VersionRange hostVersionRange);
 
 	/**
+	 * Creates host specifications from the given declaration.  The declaration uses
+	 * the bundle manifest syntax for the Fragment-Host header.
+	 * @param declaration a string declaring host specifications
+	 * @return the host specifications
+	 * @since 3.8
+	 */
+	public List<HostSpecification> createHostSpecifications(String declaration);
+
+	/**
 	 * Creates a host specification that is a copy of the given constraint.
 	 * 
 	 * @param original the constraint to be copied
@@ -258,6 +296,15 @@
 	public ImportPackageSpecification createImportPackageSpecification(ImportPackageSpecification original);
 
 	/**
+	 * Creates an import package specifications from the given declaration.  The declaration uses
+	 * the bundle manifest syntax for the Import-Package header.
+	 * @param declaration a string declaring import package specifications
+	 * @return the import package specifications
+	 * @since 3.8
+	 */
+	public List<ImportPackageSpecification> createImportPackageSpecifications(String declaration);
+
+	/**
 	 * Used by the Resolver to dynamically create ExportPackageDescription objects during the resolution process.
 	 * The Resolver needs to create ExportPackageDescriptions dynamically for a host when a fragment.
 	 * exports a package<p>
@@ -295,6 +342,15 @@
 	public GenericDescription createGenericDescription(String type, Map<String, ?> attributes, Map<String, String> directives, BundleDescription supplier);
 
 	/**
+	 * Creates generic descriptions from the given declaration.  The declaration uses
+	 * the bundle manifest syntax for the Provide-Capability header.
+	 * @param declaration a string declaring generic descriptions
+	 * @return the generic descriptions
+	 * @since 3.8
+	 */
+	public List<GenericDescription> createGenericDescriptions(String declaration);
+
+	/**
 	 * Creates a generic specification from the given parameters
 	 * @param name the name of the generic specification
 	 * @param type the type of the generic specification (may be <code>null</code>)
@@ -307,6 +363,15 @@
 	public GenericSpecification createGenericSpecification(String name, String type, String matchingFilter, boolean optional, boolean multiple) throws InvalidSyntaxException;
 
 	/**
+	 * Creates generic specifications from the given declaration.  The declaration uses
+	 * the bundle manifest syntax for the Require-Capability header.
+	 * @param declaration a string declaring generic specifications
+	 * @return the generic specifications
+	 * @since 3.8
+	 */
+	public List<GenericSpecification> createGenericSpecifications(String declaration);
+
+	/**
 	 * Creates a native code specification from the given parameters
 	 * @param nativeCodeDescriptions the native code descriptors
 	 * @param optional whether the specification is optional
@@ -330,13 +395,22 @@
 	public NativeCodeDescription createNativeCodeDescription(String[] nativePaths, String[] processors, String[] osNames, VersionRange[] osVersions, String[] languages, String filter) throws InvalidSyntaxException;
 
 	/**
-	 * Creates an import package specification that is a copy of the given constraint
+	 * Creates an export package specification that is a copy of the given constraint
 	 * @param original the export package to be copied
 	 * @return the created package
 	 */
 	public ExportPackageDescription createExportPackageDescription(ExportPackageDescription original);
 
 	/**
+	 * Creates export package descriptions from the given declaration.  The declaration uses
+	 * the bundle manifest syntax for the Export-Package header.
+	 * @param declaration a string declaring export package descriptions
+	 * @return the export package descriptions
+	 * @since 3.8
+	 */
+	public List<ExportPackageDescription> createExportPackageDescriptions(String declaration);
+
+	/**
 	 * Persists the given state in the given output stream. Closes the stream.
 	 * 
 	 * @param state the state to be written
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/VersionConstraint.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/VersionConstraint.java
index 3b3be74..2f15515 100644
--- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/VersionConstraint.java
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/VersionConstraint.java
@@ -82,4 +82,26 @@
 	 * @since 3.7
 	 */
 	public BundleRequirement getRequirement();
+
+	/**
+	 * Returns the user object associated to this constraint, or 
+	 * <code>null</code> if none exists.
+	 *  
+	 * @return the user object associated to this constraint,
+	 * or <code>null</code>
+	 * @since 3.8
+	 */
+	public Object getUserObject();
+
+	/**
+	 * Associates a user-provided object to this constraint, or
+	 * removes an existing association, if <code>null</code> is provided. The 
+	 * provided object is not interpreted in any ways by this 
+	 * constrain.
+	 * 
+	 * @param userObject an arbitrary object provided by the user, or 
+	 * <code>null</code>
+	 * @since 3.8
+	 */
+	public void setUserObject(Object userObject);
 }
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/DescriptionReference.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/DescriptionReference.java
new file mode 100644
index 0000000..d7d32cb
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/DescriptionReference.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 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.service.resolver.extras;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+
+/**
+ * A reference to a {@link BaseDescription}.
+ * @since 3.8
+ */
+public interface DescriptionReference {
+	/**
+	 * Returns the {@code BaseDescription} object associated with this
+	 * reference.
+	 * 
+	 * @return The {@code BaseDescription} object associated with this
+	 *         reference.
+	 */
+	public BaseDescription getDescription();
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/Sortable.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/Sortable.java
new file mode 100644
index 0000000..2442c5e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/Sortable.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 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.service.resolver.extras;
+
+import java.util.Comparator;
+
+/**
+ * Represents a collection of elements which can be sorted.
+ * @since 3.8
+ */
+public interface Sortable<E> {
+	/**
+	 * Sorts collection of elements represented by this sortable according
+	 * to the specified comparator.
+	 * @param comparator the comparator used to sort the collection
+	 * of elements represented by this sortable.
+	 */
+	void sort(Comparator<E> comparator);
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/SpecificationReference.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/SpecificationReference.java
new file mode 100644
index 0000000..c63e2c1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/extras/SpecificationReference.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 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.service.resolver.extras;
+
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+
+/**
+ * A reference to a {@link VersionConstraint} specification.
+ * @since 3.8
+ */
+public interface SpecificationReference {
+	/**
+	 * Returns the {@code VersionConstraint} object associated with this
+	 * reference.
+	 * 
+	 * @return The {@code VersionConstraint} object associated with this
+	 *         reference.
+	 */
+	public VersionConstraint getSpecification();
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/ArrayMap.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/ArrayMap.java
index 14512dd..0eb15e7 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/ArrayMap.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/ArrayMap.java
@@ -11,6 +11,7 @@
 package org.eclipse.osgi.internal.baseadaptor;
 
 import java.util.*;
+import org.eclipse.osgi.service.resolver.extras.Sortable;
 
 /**
  * Simple map when dealing with small amounts of entries.
@@ -19,7 +20,7 @@
  * @param <K> The key type
  * @param <V> the value type
  */
-public class ArrayMap<K, V> implements Collection<K> {
+public class ArrayMap<K, V> implements Collection<K>, Sortable<K> {
 	final List<K> keys;
 	final List<V> values;
 
@@ -157,4 +158,17 @@
 	public V getValue(int index) {
 		return values.get(index);
 	}
+
+	public void sort(Comparator<K> comparator) {
+		List<K> sortedKeys = new ArrayList<K>(keys);
+		Collections.sort(sortedKeys, comparator);
+		List<V> sortedValues = new ArrayList<V>(sortedKeys.size());
+		for (K key : sortedKeys) {
+			sortedValues.add(get(key));
+		}
+		clear();
+		for (int i = 0; i < sortedKeys.size(); i++) {
+			put(sortedKeys.get(i), sortedValues.get(i));
+		}
+	}
 }
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
index f750efb..8493bd2 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -11,8 +11,6 @@
  ******************************************************************************/
 package org.eclipse.osgi.internal.module;
 
-import org.osgi.framework.resource.ResourceConstants;
-
 import java.security.AccessController;
 import java.util.*;
 import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
@@ -29,7 +27,9 @@
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.hooks.resolver.ResolverHook;
-import org.osgi.framework.wiring.*;
+import org.osgi.framework.resource.ResourceConstants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
 
 public class ResolverImpl implements Resolver {
 	// Debug fields
@@ -348,23 +348,28 @@
 		// find all available hosts to attach to.
 		boolean foundMatch = false;
 		BundleConstraint hostConstraint = bundle.getHost();
-		List<ResolverBundle> hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
-		List<ResolverBundle> 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));
+		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)));
+			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;
@@ -372,6 +377,7 @@
 		}
 		if (!foundMatch)
 			state.addResolverError(bundle.getBundleDescription(), ResolverError.MISSING_FRAGMENT_HOST, bundle.getHost().getVersionConstraint().toString(), bundle.getHost().getVersionConstraint());
+
 	}
 
 	public synchronized void resolve(BundleDescription[] reRefresh, Dictionary<Object, Object>[] platformProperties) {
@@ -564,7 +570,7 @@
 		if (DEBUG_WIRING)
 			printWirings();
 		// set the resolved status of the bundles in the State
-		stateResolveBundles(bundles);
+		stateResolveBundles(bundleMapping.values().toArray(new ResolverBundle[bundleMapping.size()]));
 	}
 
 	private void selectSingletons(ResolverBundle[] bundles) {
@@ -1340,26 +1346,31 @@
 				ResolverImpl.log("  - already wired"); //$NON-NLS-1$
 			return true; // Already wired (due to grouping dependencies) so just return
 		}
-		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);
-		List<GenericCapability> 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());
+		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)));
+			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) {
@@ -1416,20 +1427,25 @@
 				ResolverImpl.log("  - already wired"); //$NON-NLS-1$
 			return true; // Already wired (due to grouping dependencies) so just return
 		}
-		List<ResolverBundle> bundles = resolverBundles.get(req.getVersionConstraint().getName());
-		List<ResolverBundle> 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());
+		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)));
+			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) {
@@ -1483,20 +1499,25 @@
 		}
 		boolean result = false;
 		ResolverExport[] substitutableExps = imp.getBundle().getExports(imp.getName());
-		List<ResolverExport> exports = resolverExports.get(imp.getName());
-		List<ResolverExport> 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());
+		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)));
+			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)
@@ -1892,6 +1913,9 @@
 		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) {
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java
index 4716318..a24cf9b 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BaseDescriptionImpl.java
@@ -14,6 +14,7 @@
 import java.util.*;
 import java.util.Map.Entry;
 import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.extras.DescriptionReference;
 import org.osgi.framework.Version;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRevision;
@@ -26,6 +27,8 @@
 
 	private volatile Version version;
 
+	private volatile Object userObject;
+
 	public String getName() {
 		return name;
 	}
@@ -104,7 +107,15 @@
 		return new BaseCapability(namespace);
 	}
 
-	class BaseCapability implements BundleCapability {
+	public Object getUserObject() {
+		return userObject;
+	}
+
+	public void setUserObject(Object userObject) {
+		this.userObject = userObject;
+	}
+
+	class BaseCapability implements BundleCapability, DescriptionReference {
 		private final String namespace;
 
 		public BaseCapability(String namespace) {
@@ -160,5 +171,9 @@
 		public BundleRevision getResource() {
 			return getRevision();
 		}
+
+		public BaseDescription getDescription() {
+			return BaseDescriptionImpl.this;
+		}
 	}
 }
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
index 22c38d2..e88293c 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
@@ -53,7 +53,6 @@
 	volatile HostSpecification host; //null if the bundle is not a fragment. volatile to allow unsynchronized checks for null
 	private volatile StateImpl containingState;
 
-	private volatile Object userObject;
 	private volatile int lazyDataOffset = -1;
 	private volatile int lazyDataSize = -1;
 
@@ -581,14 +580,6 @@
 		}
 	}
 
-	public Object getUserObject() {
-		return userObject;
-	}
-
-	public void setUserObject(Object userObject) {
-		this.userObject = userObject;
-	}
-
 	protected void addDependent(BundleDescription dependent) {
 		synchronized (this.monitor) {
 			if (dependents == null)
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
index 114b86c..a934790 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
@@ -31,8 +31,8 @@
 	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};
-	private static final String[] DEFINED_BSN_DIRECTIVES = {Constants.SINGLETON_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.MANDATORY_DIRECTIVE};
-	private static final String[] DEFINED_BSN_MATCHING_ATTRS = {Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.OPTIONAL_ATTRIBUTE, Constants.REPROVIDE_ATTRIBUTE};
+	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, Constants.OPTIONAL_ATTRIBUTE, Constants.REPROVIDE_ATTRIBUTE};
 	private static final String[] DEFINED_REQUIRE_CAPABILITY_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.FILTER_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};
@@ -108,7 +108,7 @@
 		ManifestElement[] provides = ManifestElement.parseHeader(Constants.PROVIDE_PACKAGE, manifest.get(Constants.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, manifestVersion, strict));
+		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));
@@ -194,7 +194,7 @@
 		return result;
 	}
 
-	private static String getPlatformProperty(StateImpl state, String key) {
+	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);
 	}
@@ -228,7 +228,7 @@
 		return result;
 	}
 
-	private static BundleSpecification createRequiredBundle(ManifestElement spec) {
+	static BundleSpecification createRequiredBundle(ManifestElement spec) {
 		BundleSpecificationImpl result = new BundleSpecificationImpl();
 		result.setName(spec.getValue());
 		result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE)));
@@ -306,25 +306,25 @@
 
 	private static String getResolution(String resolution) {
 		String result = ImportPackageSpecification.RESOLUTION_STATIC;
-		if (Constants.RESOLUTION_OPTIONAL.equals(resolution))
-			result = ImportPackageSpecification.RESOLUTION_OPTIONAL;
+		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, int manifestVersion, boolean strict) {
+	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, manifestVersion, strict);
+				addExportPackages(exported[i], allExports, strict);
 		if (provides != null)
 			addProvidePackages(provides, allExports, providedExports);
 		return allExports.toArray(new ExportPackageDescription[allExports.size()]);
 	}
 
-	private static void addExportPackages(ManifestElement exportPackage, List<ExportPackageDescription> allExports, int manifestVersion, boolean strict) {
+	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.
@@ -367,7 +367,7 @@
 		}
 	}
 
-	private static Map<String, String> getDirectives(ManifestElement element, String[] definedDirectives) {
+	static Map<String, String> getDirectives(ManifestElement element, String[] definedDirectives) {
 		Enumeration<String> keys = element.getDirectiveKeys();
 		if (keys == null)
 			return null;
@@ -385,7 +385,7 @@
 		return arbitraryDirectives;
 	}
 
-	private static Map<String, Object> getAttributes(ManifestElement element, String[] definedAttrs) {
+	static Map<String, Object> getAttributes(ManifestElement element, String[] definedAttrs) {
 		Enumeration<String> keys = element.getKeys();
 		Map<String, Object> arbitraryAttrs = null;
 		if (keys == null)
@@ -463,7 +463,7 @@
 		return components;
 	}
 
-	private static HostSpecification createHostSpecification(ManifestElement spec, StateImpl state) {
+	static HostSpecification createHostSpecification(ManifestElement spec, State state) {
 		if (spec == null)
 			return null;
 		HostSpecificationImpl result = new HostSpecificationImpl();
@@ -484,7 +484,7 @@
 		return result == null ? null : result.toArray(new GenericSpecification[result.size()]);
 	}
 
-	private static List<GenericSpecification> createOSGiRequires(ManifestElement[] osgiRequires, List<GenericSpecification> result) throws BundleException {
+	static List<GenericSpecification> createOSGiRequires(ManifestElement[] osgiRequires, List<GenericSpecification> result) throws BundleException {
 		if (osgiRequires == null)
 			return result;
 		if (result == null)
@@ -562,7 +562,7 @@
 		return result == null ? null : result.toArray(new GenericDescription[result.size()]);
 	}
 
-	private static List<GenericDescription> createOSGiCapabilities(ManifestElement[] osgiCapabilities, List<GenericDescription> result, BundleDescription description) throws BundleException {
+	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.
@@ -770,7 +770,7 @@
 		}
 	}
 
-	private static GenericDescription createOsgiIdentityCapability(BundleDescription description) {
+	static GenericDescription createOsgiIdentityCapability(BundleDescription description) {
 		if (description.getSymbolicName() == null)
 			return null;
 		GenericDescriptionImpl result = new GenericDescriptionImpl();
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
index 68f61b2..383b33a 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
@@ -836,7 +836,7 @@
 	private void addSystemExports(List<ExportPackageDescription> exports, ManifestElement[] elements, int index) {
 		if (elements == null)
 			return;
-		ExportPackageDescription[] systemExports = StateBuilder.createExportPackages(elements, null, null, 2, false);
+		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);
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
index df97bf1..264ead8 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
@@ -74,6 +74,52 @@
 		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());
@@ -464,4 +510,88 @@
 		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/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
index a88af77..45359bf 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
@@ -12,16 +12,16 @@
  *******************************************************************************/
 package org.eclipse.osgi.internal.resolver;
 
-import org.osgi.framework.resource.Capability;
-import org.osgi.framework.resource.ResourceConstants;
-
 import java.util.Collections;
 import java.util.Map;
 import org.eclipse.osgi.framework.internal.core.Constants;
 import org.eclipse.osgi.internal.resolver.BaseDescriptionImpl.BaseCapability;
 import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.extras.SpecificationReference;
 import org.eclipse.osgi.util.ManifestElement;
 import org.osgi.framework.*;
+import org.osgi.framework.resource.Capability;
+import org.osgi.framework.resource.ResourceConstants;
 import org.osgi.framework.wiring.*;
 
 abstract class VersionConstraintImpl implements VersionConstraint {
@@ -32,6 +32,7 @@
 	private VersionRange versionRange;
 	private BundleDescription bundle;
 	private BaseDescription supplier;
+	private volatile Object userObject;
 
 	public String getName() {
 		synchronized (this.monitor) {
@@ -114,7 +115,15 @@
 		return new BundleRequirementImpl(namespace);
 	}
 
-	class BundleRequirementImpl implements BundleRequirement {
+	public Object getUserObject() {
+		return userObject;
+	}
+
+	public void setUserObject(Object userObject) {
+		this.userObject = userObject;
+	}
+
+	class BundleRequirementImpl implements BundleRequirement, SpecificationReference {
 		private final String namespace;
 
 		public BundleRequirementImpl(String namespace) {
@@ -182,6 +191,10 @@
 		public BundleRevision getResource() {
 			return getRevision();
 		}
+
+		public VersionConstraint getSpecification() {
+			return VersionConstraintImpl.this;
+		}
 	}
 
 	static StringBuffer addFilterAttributes(StringBuffer filter, Map<String, ?> attributes) {