diff options
author | Thomas Watson | 2006-03-17 14:47:21 +0000 |
---|---|---|
committer | Thomas Watson | 2006-03-17 14:47:21 +0000 |
commit | ae17b137d9a857e925149f706498edcd1b63ba5b (patch) | |
tree | d214047411e84adba252dd3b56e9de4cac1ab670 | |
parent | 16a7c7d7c3bb7bee0f0a017c162dd00e74087a00 (diff) | |
download | rt.equinox.framework-ae17b137d9a857e925149f706498edcd1b63ba5b.tar.gz rt.equinox.framework-ae17b137d9a857e925149f706498edcd1b63ba5b.tar.xz rt.equinox.framework-ae17b137d9a857e925149f706498edcd1b63ba5b.zip |
Bug 123707 consider resolver support for matching arbitrary properties
27 files changed, 1544 insertions, 178 deletions
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java index f936cf2e8..f77b54999 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/AllTests.java @@ -31,6 +31,7 @@ public class AllTests extends TestCase { suite.addTest(VersionRangeTests.suite()); suite.addTest(R4ResolverTest.suite()); suite.addTest(XFriendsInternalResolverTest.suite()); + suite.addTest(GenericCapabilityTest.suite()); return suite; } } diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/GenericCapabilityTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/GenericCapabilityTest.java new file mode 100644 index 000000000..e6eef306a --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/GenericCapabilityTest.java @@ -0,0 +1,310 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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.tests.services.resolver; + +import java.util.Dictionary; +import java.util.Hashtable; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.eclipse.osgi.service.resolver.*; +import org.osgi.framework.*; + +public class GenericCapabilityTest extends AbstractStateTest { + private static final String GENERIC_REQUIRE = "Eclipse-GenericRequire"; //$NON-NLS-1$ + private static final String GENERIC_CAPABILITY = "Eclipse-GenericCapability"; //$NON-NLS-1$ + public static Test suite() { + return new TestSuite(GenericCapabilityTest.class); + } + + public GenericCapabilityTest(String name) { + super(name); + } + + public void testGenericsBasics() throws BundleException { + State state = buildEmptyState(); + Hashtable manifest = new Hashtable(); + long bundleID = 0; + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapablity"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer capabililty = new StringBuffer(); + capabililty.append("foo; version=\"1.3.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("bar:bartype; version=\"1.4.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("test.types:testtype;"); + capabililty.append(" aVersion:version=\"2.0.0\";"); + capabililty.append(" aLong:long=\"10000000000\";"); + capabililty.append(" aDouble:double=\"1.000109\";"); + capabililty.append(" aUri:uri=\"file:/test\";"); + capabililty.append(" aSet:set=\"a,b,c,d\";"); + capabililty.append(" aString:string=\"someString\""); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + BundleDescription genCap = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest = new Hashtable(); + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericRequire"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer required = new StringBuffer(); + required.append("foo; selection-filter=\"(version>=1.3.0)\","); + required.append("bar:bartype; selection-filter=\"(attr1=value1)\","); + required.append("test.types:testtype; selection-filter=\"(&(aVersion>=2.0.0)(aLong>=5555)(aDouble>=1.00)(aUri=file:/test)(aSet=c)(aString=someString))\""); + manifest.put(GENERIC_REQUIRE, required.toString()); + BundleDescription genReq = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + state.addBundle(genCap); + state.addBundle(genReq); + + state.resolve(); + assertTrue("1.0", genCap.isResolved()); + assertTrue("1.1", genReq.isResolved()); + GenericSpecification[] genSpecs = genReq.getGenericRequires(); + assertTrue("2.0", genSpecs.length == 3); + assertTrue("2.1", genSpecs[0].isResolved()); + assertEquals("2.1.1", genSpecs[0].getSupplier(), genCap.getGenericCapabilities()[0]); + assertTrue("2.2", genSpecs[1].isResolved()); + assertEquals("2.2.1", genSpecs[1].getSupplier(), genCap.getGenericCapabilities()[1]); + assertTrue("2.3", genSpecs[2].isResolved()); + assertEquals("2.3.1", genSpecs[2].getSupplier(), genCap.getGenericCapabilities()[2]); + } + + public void testGenericsFrags() throws BundleException { + State state = buildEmptyState(); + Hashtable manifest = new Hashtable(); + long bundleID = 0; + + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapablity"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer capabililty = new StringBuffer(); + capabililty.append("foo; version=\"1.3.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("bar:bartype; version=\"1.4.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("test.types:testtype;"); + capabililty.append(" aVersion:version=\"2.0.0\";"); + capabililty.append(" aLong:long=\"10000000000\";"); + capabililty.append(" aDouble:double=\"1.000109\";"); + capabililty.append(" aUri:uri=\"file:/test\";"); + capabililty.append(" aSet:set=\"a,b,c,d\";"); + capabililty.append(" aString:string=\"someString\""); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + BundleDescription genCap = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapability.frag1"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + manifest.put(Constants.FRAGMENT_HOST, "genericCapablity;bundle-version=\"[1.0.0,2.0.0)\""); + capabililty = new StringBuffer(); + capabililty.append("fragmentStuff"); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + BundleDescription genCapFrag = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest = new Hashtable(); + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericRequire"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer required = new StringBuffer(); + required.append("foo; selection-filter=\"(version>=1.3.0)\","); + required.append("bar:bartype; selection-filter=\"(attr1=value1)\","); + required.append("test.types:testtype; selection-filter=\"(&(aVersion>=2.0.0)(aLong>=5555)(aDouble>=1.00)(aUri=file:/test)(aSet=c)(aString=someString))\","); + required.append("fragmentStuff"); + manifest.put(GENERIC_REQUIRE, required.toString()); + BundleDescription genReq = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + state.addBundle(genCap); + state.addBundle(genCapFrag); + state.addBundle(genReq); + + state.resolve(); + assertTrue("1.0", genCap.isResolved()); + assertTrue("1.1", genReq.isResolved()); + assertTrue("1.2", genCapFrag.isResolved()); + GenericSpecification[] genSpecs = genReq.getGenericRequires(); + assertTrue("2.0", genSpecs.length == 4); + assertTrue("2.1", genSpecs[0].isResolved()); + assertEquals("2.1.1", genSpecs[0].getSupplier(), genCap.getGenericCapabilities()[0]); + assertTrue("2.2", genSpecs[1].isResolved()); + assertEquals("2.2.1", genSpecs[1].getSupplier(), genCap.getGenericCapabilities()[1]); + assertTrue("2.3", genSpecs[2].isResolved()); + assertEquals("2.3.1", genSpecs[2].getSupplier(), genCap.getGenericCapabilities()[2]); + assertTrue("2.4", genSpecs[3].isResolved()); + assertEquals("2.4.1", genSpecs[3].getSupplier(), genCapFrag.getGenericCapabilities()[0]); + } + + public void testGenericsAliases() throws BundleException { + State state = buildEmptyState(); + Dictionary[] allPlatProps = state.getPlatformProperties(); + Dictionary platProps = (Dictionary) ((Hashtable)allPlatProps[0]).clone(); + platProps.put("osgi.genericAliases", "Export-Service:Import-Service:service,TJW-Export:TJW-Import:tjw"); + state.setPlatformProperties(platProps); + + Hashtable manifest = new Hashtable(); + long bundleID = 0; + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapablity"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + manifest.put(Constants.EXPORT_SERVICE, "org.osgi.service.log.LogService; version=1.2"); + manifest.put("TJW-Export", "my.great.stuff; aLong:long=5150; aDouble:double=3.14; aVersion:version=1.2.0"); + BundleDescription genCap = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest = new Hashtable(); + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericRequire"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + manifest.put(Constants.IMPORT_SERVICE, "org.osgi.service.log.LogService; selection-filter=(version>=1.0.0)"); + manifest.put("TJW-Import", "my.great.stuff; selection-filter=(&(aLong<=10000)(aLong>=5000))"); + BundleDescription genReq = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + state.addBundle(genCap); + state.addBundle(genReq); + + state.resolve(); + assertTrue("1.0", genCap.isResolved()); + assertTrue("1.1", genReq.isResolved()); + GenericSpecification[] genSpecs = genReq.getGenericRequires(); + assertTrue("2.0", genSpecs.length == 2); + assertTrue("2.1", genSpecs[0].isResolved()); + assertEquals("2.1.1", genSpecs[0].getSupplier(), genCap.getGenericCapabilities()[0]); + assertTrue("2.2", genSpecs[1].isResolved()); + assertEquals("2.2.1", genSpecs[1].getSupplier(), genCap.getGenericCapabilities()[1]); + } + + public void testGenericsOptionalMultiple() throws BundleException { + State state = buildEmptyState(); + Hashtable manifest = new Hashtable(); + long bundleID = 0; + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapablity"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer capabililty = new StringBuffer(); + capabililty.append("foo; version=\"1.3.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("bar:bartype; version=\"1.4.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("test.types:testtype;"); + capabililty.append(" aVersion:version=\"2.0.0\";"); + capabililty.append(" aLong:long=\"10000000000\";"); + capabililty.append(" aDouble:double=\"1.000109\";"); + capabililty.append(" aUri:uri=\"file:/test\";"); + capabililty.append(" aSet:set=\"a,b,c,d\";"); + capabililty.append(" aString:string=\"someString\","); + capabililty.append("test.real.optional:thisisoptional,"); + capabililty.append("test.real.multiple:thisismultiple; version=1.0,"); + capabililty.append("test.real.multiple:thisismultiple; version=2.0"); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + BundleDescription genCap = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest = new Hashtable(); + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericRequire"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer required = new StringBuffer(); + required.append("foo; selection-filter=\"(version>=1.3.0)\","); + required.append("bar:bartype; selection-filter=\"(attr1=value1)\","); + required.append("test.types:testtype; selection-filter=\"(&(aVersion>=2.0.0)(aLong>=5555)(aDouble>=1.00)(aUri=file:/test)(aSet=c)(aString=someString))\","); + required.append("test.optional:thisisoptional; optional=true,"); + required.append("test.real.optional:thisisoptional; optional=true,"); + required.append("test.real.multiple:thisismultiple; multiple=true"); + manifest.put(GENERIC_REQUIRE, required.toString()); + BundleDescription genReq = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + state.addBundle(genCap); + state.addBundle(genReq); + + state.resolve(); + assertTrue("1.0", genCap.isResolved()); + assertTrue("1.1", genReq.isResolved()); + GenericSpecification[] genSpecs = genReq.getGenericRequires(); + assertTrue("2.0", genSpecs.length == 6); + assertTrue("2.1", genSpecs[0].isResolved()); + assertEquals("2.1.1", genSpecs[0].getSupplier(), genCap.getGenericCapabilities()[0]); + assertTrue("2.2", genSpecs[1].isResolved()); + assertEquals("2.2.1", genSpecs[1].getSupplier(), genCap.getGenericCapabilities()[1]); + assertTrue("2.3", genSpecs[2].isResolved()); + assertEquals("2.3.1", genSpecs[2].getSupplier(), genCap.getGenericCapabilities()[2]); + assertFalse("2.4", genSpecs[3].isResolved()); + assertTrue("2.5", genSpecs[4].isResolved()); + assertEquals("2.5.1", genSpecs[4].getSupplier(), genCap.getGenericCapabilities()[3]); + assertTrue("2.6", genSpecs[5].isResolved()); + GenericDescription[] suppliers = genSpecs[5].getSuppliers(); + assertTrue("2.6.1", suppliers != null && suppliers.length == 2); + assertEquals("2.6.2", suppliers[0], genCap.getGenericCapabilities()[5]); + assertEquals("2.6.3", suppliers[1], genCap.getGenericCapabilities()[4]); + } + + public void testGenericsCycles() throws BundleException { + State state = buildEmptyState(); + Hashtable manifest = new Hashtable(); + long bundleID = 0; + + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapablity"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + StringBuffer capabililty = new StringBuffer(); + capabililty.append("foo; version=\"1.3.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("bar:bartype; version=\"1.4.1\"; attr1=\"value1\"; attr2=\"value2\","); + capabililty.append("test.types:testtype;"); + capabililty.append(" aVersion:version=\"2.0.0\";"); + capabililty.append(" aLong:long=\"10000000000\";"); + capabililty.append(" aDouble:double=\"1.000109\";"); + capabililty.append(" aUri:uri=\"file:/test\";"); + capabililty.append(" aSet:set=\"a,b,c,d\";"); + capabililty.append(" aString:string=\"someString\""); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + StringBuffer required = new StringBuffer(); + required.append("foo:cycle; selection-filter=\"(version>=1.3.0)\""); + manifest.put(GENERIC_REQUIRE, required.toString()); + BundleDescription genCap = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericCapability.frag1"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + manifest.put(Constants.FRAGMENT_HOST, "genericCapablity;bundle-version=\"[1.0.0,2.0.0)\""); + capabililty = new StringBuffer(); + capabililty.append("fragmentStuff"); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + BundleDescription genCapFrag = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + manifest = new Hashtable(); + manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + manifest.put(Constants.BUNDLE_SYMBOLICNAME, "genericRequire"); + manifest.put(Constants.BUNDLE_VERSION, "1.0.0"); + capabililty = new StringBuffer(); + capabililty.append("foo:cycle; version:version=\"2.0\""); + manifest.put(GENERIC_CAPABILITY, capabililty.toString()); + required = new StringBuffer(); + required.append("foo; selection-filter=\"(version>=1.3.0)\","); + required.append("bar:bartype; selection-filter=\"(attr1=value1)\","); + required.append("test.types:testtype; selection-filter=\"(&(aVersion>=2.0.0)(aLong>=5555)(aDouble>=1.00)(aUri=file:/test)(aSet=c)(aString=someString))\","); + required.append("fragmentStuff"); + manifest.put(GENERIC_REQUIRE, required.toString()); + BundleDescription genReq = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++); + + state.addBundle(genCap); + state.addBundle(genCapFrag); + state.addBundle(genReq); + + state.resolve(); + assertTrue("1.0", genCap.isResolved()); + assertTrue("1.1", genReq.isResolved()); + assertTrue("1.2", genCapFrag.isResolved()); + GenericSpecification[] genSpecs = genReq.getGenericRequires(); + assertTrue("2.0", genSpecs.length == 4); + assertTrue("2.1", genSpecs[0].isResolved()); + assertEquals("2.1.1", genSpecs[0].getSupplier(), genCap.getGenericCapabilities()[0]); + assertTrue("2.2", genSpecs[1].isResolved()); + assertEquals("2.2.1", genSpecs[1].getSupplier(), genCap.getGenericCapabilities()[1]); + assertTrue("2.3", genSpecs[2].isResolved()); + assertEquals("2.3.1", genSpecs[2].getSupplier(), genCap.getGenericCapabilities()[2]); + assertTrue("2.4", genSpecs[3].isResolved()); + assertEquals("2.4.1", genSpecs[3].getSupplier(), genCapFrag.getGenericCapabilities()[0]); + genSpecs = genCap.getGenericRequires(); + assertTrue("3.0", genSpecs.length == 1); + assertTrue("3.1", genSpecs[0].isResolved()); + assertEquals("3.1.1", genSpecs[0].getSupplier(), genReq.getGenericCapabilities()[0]); + } +} diff --git a/bundles/org.eclipse.osgi/.options b/bundles/org.eclipse.osgi/.options index 8bbbdde11..1bd2359d1 100644 --- a/bundles/org.eclipse.osgi/.options +++ b/bundles/org.eclipse.osgi/.options @@ -63,6 +63,7 @@ org.eclipse.osgi/resolver/imports = false org.eclipse.osgi/resolver/requires = false org.eclipse.osgi/resolver/grouping = false org.eclipse.osgi/resolver/cycles = false +org.eclipse.osgi/resolver/generics = false #### Profile settings org.eclipse.osgi/profile/startup = false 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 1ae732049..32f60a0c4 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. + * Copyright (c) 2004, 2006 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 @@ -26,9 +26,17 @@ public interface BaseDescription { * @return the name */ public String getName(); + /** * Returns the version. * @return the version */ public Version getVersion(); + + /** + * Returns the bundle which supplies this base description + * @return the bundle which supplies this base description + * @since 3.2 + */ + public BundleDescription getSupplier(); } 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 be37cd5c0..344406736 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -17,7 +17,7 @@ package org.eclipse.osgi.service.resolver; * </p> * @since 3.1 */ -public interface BundleDescription extends BaseDescription{ +public interface BundleDescription extends BaseDescription { /** * Gets the Bundle-SymbolicName of this BundleDescription. @@ -58,6 +58,20 @@ public interface BundleDescription extends BaseDescription{ public ImportPackageSpecification[] getImportPackages(); /** + * Returns an array of generic specifications constraints required by this bundle. + * @return an array of generic specifications + * @since 3.2 + */ + public GenericSpecification[] getGenericRequires(); + + /** + * Returns an array of generic descriptions for the capabilities of this bundle. + * @return an array of generic descriptions + * @since 3.2 + */ + public GenericDescription[] getGenericCapabilities(); + + /** * Returns true if this bundle has one or more dynamically imported packages. * @return true if this bundle has one or more dynamically imported packages. */ diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/GenericDescription.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/GenericDescription.java new file mode 100644 index 000000000..39e5de970 --- /dev/null +++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/GenericDescription.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2006 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; + +import java.util.Dictionary; + +/** + * A description of a generic capability. + * @since 3.2 + */ +public interface GenericDescription extends BaseDescription { + /** + * The default type of generic capability. + */ + public static String DEFAULT_TYPE = "generic"; //$NON-NLS-1$ + + /** + * Returns the arbitrary attributes for this description + * @return the arbitrary attributes for this description + */ + public Dictionary getAttributes(); + + /** + * Returns the type of generic description capability + * @return the type of generic description capability + */ + public String getType(); +} diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/GenericSpecification.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/GenericSpecification.java new file mode 100644 index 000000000..4b0a12d5b --- /dev/null +++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/GenericSpecification.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2006 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; + +/** + * A specification which depends on a generic capability + * @since 3.2 + */ +public interface GenericSpecification extends VersionConstraint { + /** + * The optional resolution type + * @see #getResolution() + */ + public static final int RESOLUTION_OPTIONAL = 0x01; + /** + * The multiple resolution type + * @see #getResolution() + */ + public static final int RESOLUTION_MULTIPLE = 0x02; + + /** + * Returns a matching filter used to match with a suppliers attributes + * @return a matching filter used to match with a suppliers attributes + */ + public String getMatchingFilter(); + + /** + * Returns the type of generic specification + * @return the type of generic specification + */ + public String getType(); + + /** + * Returns the resolution type of the required capability. The returned + * value is a bit mask that may have the optional bit {@link #RESOLUTION_OPTIONAL} + * and/or the multiple bit {@link #RESOLUTION_MULTIPLE} set. + * + * @return the resolution type of the required capability + */ + public int getResolution(); + + /** + * Returns the suppliers of the capability. If the the resolution is multiple then + * more than one supplier may be returned + * @return the suppliers of the capability + */ + public GenericDescription[] getSuppliers(); +} diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/ResolverError.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/ResolverError.java index 0e1b4c5d4..f325615c8 100644 --- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/ResolverError.java +++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/ResolverError.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -116,6 +116,12 @@ public interface ResolverError { public static final int MISSING_EXECUTION_ENVIRONMENT = 0x4000; /** + * Error type constant (bit mask) indicating that a bundle could not be + * resolved because the required generic capability could not be resolved. + */ + public static final int MISSING_GENERIC_CAPABILITY = 0x8000; + + /** * Returns the bundle which this ResolverError is for * @return the bundle which this ResolverError is for */ 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 94e505adc..51ec03d88 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -14,8 +14,7 @@ import java.io.*; import java.util.Dictionary; import java.util.Map; import org.eclipse.osgi.internal.resolver.StateObjectFactoryImpl; -import org.osgi.framework.BundleException; -import org.osgi.framework.Version; +import org.osgi.framework.*; /** * A factory for states and their component objects. @@ -32,7 +31,7 @@ public interface StateObjectFactory { * of a running Equinox framework. */ public static final StateObjectFactory defaultFactory = new StateObjectFactoryImpl(); - + /** * Creates an empty state. The returned state does not have an * attached resolver. @@ -84,6 +83,34 @@ public interface StateObjectFactory { public BundleDescription createBundleDescription(long id, String symbolicName, Version version, String location, BundleSpecification[] required, HostSpecification host, ImportPackageSpecification[] imports, ExportPackageDescription[] exports, String[] providedPackages, boolean singleton); /** + * Creates a bundle description from the given parameters. + * + * @param id id for the bundle + * @param symbolicName symbolic name for the bundle (may be + * <code>null</code>) + * @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 providedPackages the list of provided packages (may be <code>null</code>) + * @param singleton whether the bundle created should be a singleton + * @param attachFragments whether the bundle allows fragments to attach + * @param dynamicFragments whether the bundle allows fragments to dynamically attach + * @param platformFilter the platform filter (may be <code>null</code>) + * @param executionEnvironment 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>) + * @return the created bundle description + */ + 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); + + /** * 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. @@ -164,9 +191,9 @@ public interface StateObjectFactory { * @param versionRange the package versionRange (may be <code>null</code>). * @param bundleSymbolicName the Bundle-SymbolicName of the bundle that must export the package (may be <code>null</code>) * @param bundleVersionRange the bundle versionRange (may be <code>null</code>). - * @param directives the directives for this package + * @param directives the directives for this package (may be <code>null</code>) * @param attributes the arbitrary attributes for the package import (may be <code>null</code>) - * @param importer the importing bundle + * @param importer the importing bundle (may be <code>null</code>) * @return the created package specification */ public ImportPackageSpecification createImportPackageSpecification(String packageName, VersionRange versionRange, String bundleSymbolicName, VersionRange bundleVersionRange, Map directives, Map attributes, BundleDescription importer); @@ -183,17 +210,39 @@ public interface StateObjectFactory { * The Resolver needs to create ExportPackageDescriptions dynamally for a host when a fragment. * exports a package<p> * - * @param packageName - * @param version - * @param directives - * @param attributes - * @param root - * @param exporter + * @param packageName the package name + * @param version the version of the package (may be <code>null</code>) + * @param directives the directives for the package (may be <code>null</code>) + * @param attributes the attributes for the package (may be <code>null</code>) + * @param root whether the package is a root package + * @param exporter the exporter of the package (may be <code>null</code>) * @return the created package */ public ExportPackageDescription createExportPackageDescription(String packageName, Version version, Map directives, Map attributes, boolean root, BundleDescription exporter); /** + * Creates a generic description from the given parameters + * @param name the name of the generic description + * @param type the type of the generic description (may be <code>null</code>) + * @param version the version of the generic description (may be <code>null</code>) + * @param attributes the attributes for the generic description (may be <code>null</code>) + * @return the created generic description + */ + public GenericDescription createGenericDescription(String name, String type, Version version, Map attributes); + + /** + * 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>) + * @param matchingFilter the matching filter (may be <code>null</code>) + * @param optional whether the specification is optional + * @param multiple whether the specification allows for multiple suppliers + * @return the created generic specification + * @throws InvalidSyntaxException if the matching filter is invalid + */ + public GenericSpecification createGenericSpecification(String name, String type, String matchingFilter, boolean optional, boolean multiple) throws InvalidSyntaxException; + + /** * Creates an import package specification that is a copy of the given constraint * @param original the export package to be copied * @return the created package diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/ManifestElement.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/ManifestElement.java index 90a58b436..48bd05567 100644 --- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/ManifestElement.java +++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/util/ManifestElement.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -322,60 +322,70 @@ public class ManifestElement { * @throws BundleException if the header value is invalid */ public static ManifestElement[] parseHeader(String header, String value) throws BundleException { - if (value == null) { + if (value == null) return (null); - } - Vector headerElements = new Vector(10, 10); + ArrayList headerElements = new ArrayList(10); Tokenizer tokenizer = new Tokenizer(value); parseloop: while (true) { String next = tokenizer.getToken(";,"); //$NON-NLS-1$ - if (next == null) { + if (next == null) throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } ArrayList headerValues = new ArrayList(); StringBuffer headerValue = new StringBuffer(next); headerValues.add(next); - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.print("paserHeader: " + next); //$NON-NLS-1$ - } + boolean directive = false; char c = tokenizer.getChar(); // Header values may be a list of ';' separated values. Just append them all into one value until the first '=' or ',' while (c == ';') { next = tokenizer.getToken(";,=:"); //$NON-NLS-1$ - if (next == null) { + if (next == null) throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } c = tokenizer.getChar(); + while (c == ':') { // may not really be a := + c = tokenizer.getChar(); + if (c != '=') { + String restOfNext = tokenizer.getToken(";,=:"); //$NON-NLS-1$ + if (restOfNext == null) + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + next += ":" + c + restOfNext; //$NON-NLS-1$ + c = tokenizer.getChar(); + } else + directive = true; + } if (c == ';' || c == '\0') /* more */{ headerValues.add(next); headerValue.append(";").append(next); //$NON-NLS-1$ - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.print(";" + next); //$NON-NLS-1$ - } } } // found the header value create a manifestElement for it. ManifestElement manifestElement = new ManifestElement(); manifestElement.value = headerValue.toString(); manifestElement.valueComponents = (String[]) headerValues.toArray(new String[headerValues.size()]); - boolean directive = false; - if (c == ':') { - c = tokenizer.getChar(); - if (c != '=') - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - directive = true; - } - // now add any attributes for the manifestElement. - while (c == '=') { + + // now add any attributes/directives for the manifestElement. + while (c == '=' || c == ':') { + while (c == ':') { // may not really be a := + c = tokenizer.getChar(); + if (c != '=') { + String restOfNext = tokenizer.getToken("=:"); //$NON-NLS-1$ + if (restOfNext == null) + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + next += ":" + c + restOfNext; //$NON-NLS-1$ + c = tokenizer.getChar(); + } else + directive = true; + } String val = tokenizer.getString(";,"); //$NON-NLS-1$ - if (val == null) { + if (val == null) throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.print(";" + next + "=" + val); //$NON-NLS-1$ //$NON-NLS-2$ - } try { if (directive) manifestElement.addDirective(next, val); @@ -383,41 +393,30 @@ public class ManifestElement { manifestElement.addAttribute(next, val); directive = false; } catch (Exception e) { - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); } c = tokenizer.getChar(); if (c == ';') /* more */{ next = tokenizer.getToken("=:"); //$NON-NLS-1$ - if (next == null) { - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } + if (next == null) + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); c = tokenizer.getChar(); - if (c == ':') { - c = tokenizer.getChar(); - if (c != '=') - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - directive = true; - } } } - headerElements.addElement(manifestElement); - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + headerElements.add(manifestElement); + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.println(""); //$NON-NLS-1$ - } - if (c == ',') /* another manifest element */{ + if (c == ',') /* another manifest element */ continue parseloop; - } - if (c == '\0') /* end of value */{ + if (c == '\0') /* end of value */ break parseloop; - } - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); } int size = headerElements.size(); - if (size == 0) { + if (size == 0) return (null); - } - ManifestElement[] result = new ManifestElement[size]; - headerElements.copyInto(result); + + ManifestElement[] result = (ManifestElement[]) headerElements.toArray(new ManifestElement[size]); return (result); } diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/GenericCapability.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/GenericCapability.java new file mode 100644 index 000000000..daaf76af1 --- /dev/null +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/GenericCapability.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006 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; + +public class GenericCapability extends VersionSupplier { + ResolverBundle resolverBundle; + + GenericCapability(ResolverBundle resolverBundle, BaseDescription base) { + super(base); + this.resolverBundle = resolverBundle; + } + + public BundleDescription getBundle() { + return getBaseDescription().getSupplier(); + } + + public boolean isFromFragment() { + return resolverBundle.isFragment(); + } + + public ResolverBundle getResolverBundle() { + return resolverBundle; + } +} diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/GenericConstraint.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/GenericConstraint.java new file mode 100644 index 000000000..d6533cd06 --- /dev/null +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/GenericConstraint.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2006 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.ArrayList; +import org.eclipse.osgi.service.resolver.VersionConstraint; + +public class GenericConstraint extends ResolverConstraint { + private ArrayList matchingCapability; + + GenericConstraint(ResolverBundle bundle, VersionConstraint constraint) { + super(bundle, constraint); + } + + boolean isOptional() { + return false; + } + + boolean isSatisfiedBy(VersionSupplier vs) { + return getVersionConstraint().isSatisfiedBy(vs.getBaseDescription()); + } + + public void setMatchingCapability(GenericCapability capability) { + if (capability == null) { + matchingCapability = null; + return; + } + if (matchingCapability == null) + matchingCapability = new ArrayList(1); + matchingCapability.add(capability); + } + + public GenericCapability[] getMatchingCapabilities() { + return matchingCapability == null || matchingCapability.size() == 0 ? null : (GenericCapability[]) matchingCapability.toArray(new GenericCapability[matchingCapability.size()]); + } + + void removeMatchingCapability(GenericCapability capability) { + if (matchingCapability != null) + matchingCapability.remove(capability); + } +} diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java index 529b6121c..9f3594871 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. + * Copyright (c) 2004, 2006 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 @@ -27,11 +27,14 @@ public class ResolverBundle extends VersionSupplier { private ResolverImport[] imports; private ResolverExport[] exports; private BundleConstraint[] requires; + private GenericCapability[] capabilities; + private GenericConstraint[] genericReqiures; // Fragment support private ArrayList fragments; private HashMap fragmentExports; private HashMap fragmentImports; private HashMap fragmentRequires; + private HashMap fragmentGenericRequires; // Flag specifying whether this bundle is resolvable private boolean resolvable = true; // Internal resolver state for this bundle @@ -51,11 +54,17 @@ public class ResolverBundle extends VersionSupplier { void initialize(boolean useSelectedExports) { if (getBundle().isSingleton()) refs = new ArrayList(); + // always add generic capabilities + GenericDescription[] actualCapabilities = getBundle().getGenericCapabilities(); + capabilities = new GenericCapability[actualCapabilities.length]; + for (int i = 0; i < capabilities.length; i++) + capabilities[i] = new GenericCapability(this, actualCapabilities[i]); if (getBundle().getHost() != null) { host = new BundleConstraint(this, getBundle().getHost()); exports = new ResolverExport[0]; imports = new ResolverImport[0]; requires = new BundleConstraint[0]; + genericReqiures = new GenericConstraint[0]; return; } @@ -79,10 +88,16 @@ public class ResolverBundle extends VersionSupplier { for (int i = 0; i < requires.length; i++) requires[i] = new BundleConstraint(this, actualRequires[i]); + GenericSpecification[] actualGenericRequires = getBundle().getGenericRequires(); + genericReqiures = new GenericConstraint[actualGenericRequires.length]; + for (int i = 0; i < genericReqiures.length; i++) + genericReqiures[i] = new GenericConstraint(this, actualGenericRequires[i]); + fragments = null; fragmentExports = null; fragmentImports = null; fragmentRequires = null; + fragmentGenericRequires = null; } ResolverExport getExport(String name) { @@ -114,6 +129,10 @@ public class ResolverBundle extends VersionSupplier { BundleConstraint[] allRequires = getRequires(); for (int i = 0; i < allRequires.length; i++) allRequires[i].setMatchingBundle(null); + + GenericConstraint[] allGenericRequires = getGenericRequires(); + for (int i = 0; i < allGenericRequires.length; i++) + allGenericRequires[i].setMatchingCapability(null); } boolean isResolved() { @@ -189,6 +208,10 @@ public class ResolverBundle extends VersionSupplier { return host; } + GenericCapability[] getGenericCapabilities() { + return capabilities; + } + BundleConstraint[] getRequires() { if (isFragment()) return new BundleConstraint[0]; @@ -206,6 +229,21 @@ public class ResolverBundle extends VersionSupplier { return (BundleConstraint[]) resultList.toArray(new BundleConstraint[resultList.size()]); } + GenericConstraint[] getGenericRequires() { + if (isFragment() || fragments == null || fragments.size() == 0) + return genericReqiures; + ArrayList resultList = new ArrayList(genericReqiures.length); + for (int i = 0; i < genericReqiures.length; i++) + resultList.add(genericReqiures[i]); + for (Iterator iter = fragments.iterator(); iter.hasNext();) { + ResolverBundle fragment = (ResolverBundle) iter.next(); + ArrayList fragGenericRegs = (ArrayList) fragmentGenericRequires.get(fragment.bundleID); + if (fragGenericRegs != null) + resultList.addAll(fragGenericRegs); + } + return (GenericConstraint[]) resultList.toArray(new GenericConstraint[resultList.size()]); + } + BundleConstraint getRequire(String name) { BundleConstraint[] allRequires = getRequires(); for (int i = 0; i < allRequires.length; i++) @@ -241,6 +279,8 @@ public class ResolverBundle extends VersionSupplier { fragmentImports = new HashMap(1); if (fragmentRequires == null) fragmentRequires = new HashMap(1); + if (fragmentGenericRequires == null) + fragmentGenericRequires = new HashMap(1); } private boolean isImported(String packageName) { @@ -271,9 +311,10 @@ public class ResolverBundle extends VersionSupplier { ImportPackageSpecification[] newImports = fragment.getBundle().getImportPackages(); BundleSpecification[] newRequires = fragment.getBundle().getRequiredBundles(); ExportPackageDescription[] newExports = fragment.getBundle().getExportPackages(); + GenericSpecification[] newGenericRequires = fragment.getBundle().getGenericRequires(); // if this is not during initialization then check if constraints conflict - if (dynamicAttach && constraintsConflict(fragment.getBundle(), newImports, newRequires)) + if (dynamicAttach && constraintsConflict(fragment.getBundle(), newImports, newRequires, newGenericRequires)) return new ResolverExport[0]; // do not allow fragments with conflicting constraints if (isResolved() && newExports.length > 0) fragment.setNewFragmentExports(true); @@ -300,6 +341,13 @@ public class ResolverBundle extends VersionSupplier { fragmentRequires.put(fragment.bundleID, hostRequires); } + if (newGenericRequires.length > 0) { + ArrayList hostGenericRequires = new ArrayList(newGenericRequires.length); + for (int i = 0; i < newGenericRequires.length; i++) + hostGenericRequires.add(new GenericConstraint(this, newGenericRequires[i])); + fragmentGenericRequires.put(fragment.bundleID, hostGenericRequires); + } + ArrayList hostExports = new ArrayList(newExports.length); if (newExports.length > 0 && dynamicAttach) { StateObjectFactory factory = resolver.getState().getFactory(); @@ -314,7 +362,7 @@ public class ResolverBundle extends VersionSupplier { return (ResolverExport[]) hostExports.toArray(new ResolverExport[hostExports.size()]); } - private boolean constraintsConflict(BundleDescription fragment, ImportPackageSpecification[] newImports, BundleSpecification[] newRequires) { + private boolean constraintsConflict(BundleDescription fragment, ImportPackageSpecification[] newImports, BundleSpecification[] newRequires, GenericSpecification[] newGenericRequires) { for (int i = 0; i < newImports.length; i++) { ResolverImport importPkg = getImport(newImports[i].getName()); if ((importPkg == null && isResolved()) || (importPkg != null && !isIncluded(newImports[i].getVersionRange(), importPkg.getVersionConstraint().getVersionRange()))) { @@ -329,7 +377,7 @@ public class ResolverBundle extends VersionSupplier { return true; // do not allow additional constraints when host is already resolved } } - return false; + return !isResolved() ? false : newGenericRequires != null && newGenericRequires.length > 0; } // checks that the inner VersionRange is included in the outer VersionRange @@ -361,6 +409,7 @@ public class ResolverBundle extends VersionSupplier { ArrayList fragImports = (ArrayList) fragmentImports.remove(fragment.bundleID); ArrayList fragRequires = (ArrayList) fragmentRequires.remove(fragment.bundleID); ArrayList removedExports = (ArrayList) fragmentExports.remove(fragment.bundleID); + fragmentGenericRequires.remove(fragment.bundleID); if (reason != null) { ResolverBundle[] remainingFrags = (ResolverBundle[]) fragments.toArray(new ResolverBundle[fragments.size()]); for (int i = 0; i < remainingFrags.length; i++) { @@ -431,10 +480,12 @@ public class ResolverBundle extends VersionSupplier { if (refs != null) refs.clear(); } + void addRef(ResolverBundle ref) { if (refs != null && !refs.contains(ref)) refs.add(ref); } + int getRefs() { return refs == null ? 0 : refs.size(); } 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 883fefb24..9f31a29d7 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. + * Copyright (c) 2004, 2006 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 @@ -26,12 +26,14 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver 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_GROUPING = RESOLVER + "/grouping"; //$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_GROUPING = false; public static boolean DEBUG_CYCLES = false; @@ -50,6 +52,8 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver private VersionHashMap resolverExports = null; // Repository for bundles private VersionHashMap resolverBundles = null; + // Repository for generics + private VersionHashMap resolverGenerics = null; // List of unresolved bundles private ArrayList unresolvedBundles = null; // Keys are BundleDescriptions, values are ResolverBundles @@ -69,6 +73,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver private void initialize() { resolverExports = new VersionHashMap(this); resolverBundles = new VersionHashMap(this); + resolverGenerics = new VersionHashMap(this); unresolvedBundles = new ArrayList(); bundleMapping = new HashMap(); BundleDescription[] bundles = state.getBundles(); @@ -110,6 +115,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver if (!pending || bundleDesc.isResolved()) { resolverExports.put(bundle.getExportPackages()); resolverBundles.put(bundle.getName(), bundle); + resolverGenerics.put(bundle.getGenericCapabilities()); } if (bundleDesc.isResolved()) { bundle.setState(ResolverBundle.RESOLVED); @@ -146,6 +152,29 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver 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, ArrayList visited) { + if (constraint.getMatchingCapabilities() != null) + return; + GenericDescription[] suppliers = ((GenericSpecification) constraint.getVersionConstraint()).getSuppliers(); + if (suppliers == null) + return; + Object[] matches = resolverGenerics.get(constraint.getName()); + for (int i = 0; i < matches.length; i++) { + GenericCapability match = (GenericCapability) matches[i]; + for (int j = 0; j < suppliers.length; j++) + if (match.getBaseDescription() == suppliers[j]) + constraint.setMatchingCapability(match); + } + GenericCapability[] matchingCapabilities = constraint.getMatchingCapabilities(); + if (matchingCapabilities != null) + for (int i = 0; i < matchingCapabilities.length; i++) + rewireBundle(matchingCapabilities[i].getResolverBundle(), visited); } private void rewireRequire(BundleConstraint req, ArrayList visited) { @@ -238,7 +267,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver state.addResolverError(bundle, ResolverError.MISSING_EXECUTION_ENVIRONMENT, bundleEE.toString(), null); return false; } - + // check the platform filter String platformFilter = bundle.getPlatformFilter(); if (platformFilter == null) @@ -313,11 +342,12 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver // reorder exports and bundles after unresolving the bundles resolverExports.reorder(); resolverBundles.reorder(); + resolverGenerics.reorder(); // always get the latest EEs getCurrentEEs(platformProperties); // keep a list of rejected singltons ArrayList rejectedSingletons = new ArrayList(); - boolean resolveOptional = platformProperties.length == 0 ? false : "true".equals(platformProperties[0].get("osgi.resolveOptional")); //$NON-NLS-1$//$NON-NLS-2$ + 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(); @@ -379,7 +409,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver } private void getCurrentEEs(Dictionary[] platformProperties) { - CURRENT_EES = new String[platformProperties.length][]; + 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$ @@ -533,6 +563,23 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver boolean failed = false; if (!failed) { + GenericConstraint[] genericRequires = bundle.getGenericRequires(); + for (int i = 0; i < genericRequires.length; i++) { + 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()) { + resolverExports.remove(bundle.detachFragment((ResolverBundle) bundleMapping.get(genericRequires[i].getVersionConstraint().getBundle()), null)); + continue; + } + failed = true; + break; + } + } + } + + 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++) { @@ -590,6 +637,58 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver return bundle.getState() != ResolverBundle.UNRESOLVED; } + private boolean resolveGenericReq(GenericConstraint constraint, ArrayList cycle) { + if (DEBUG_REQUIRES) + ResolverImpl.log("Trying to resolve: " + constraint.getBundle() + ", " + constraint.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$ + GenericCapability[] matchingCapabilities = constraint.getMatchingCapabilities(); + if (matchingCapabilities != null) { + // Check for unrecorded cyclic dependency + for (int i = 0; i < matchingCapabilities.length; i++) + if (matchingCapabilities[i].getResolverBundle().getState() == ResolverBundle.RESOLVING) + if (!cycle.contains(constraint.getBundle())) + cycle.add(constraint.getBundle()); + if (DEBUG_REQUIRES) + ResolverImpl.log(" - already wired"); //$NON-NLS-1$ + return true; // Already wired (due to grouping dependencies) so just return + } + Object[] capabilities = resolverGenerics.get(constraint.getVersionConstraint().getName()); + boolean result = false; + for (int i = 0; i < capabilities.length; i++) { + GenericCapability capability = (GenericCapability) capabilities[i]; + if (DEBUG_GENERICS) + ResolverImpl.log("CHECKING GENERICS: " + capability.getBaseDescription()); //$NON-NLS-1$ + // Check if capability matches + if (constraint.isSatisfiedBy(capability)) { + capability.getResolverBundle().addRef(constraint.getBundle()); + if (result && (((GenericSpecification) constraint.getVersionConstraint()).getResolution() & GenericSpecification.RESOLUTION_MULTIPLE) == 0) + continue; // found a match already and this is not a multiple constraint + constraint.setMatchingCapability(capability); // Wire to the capability + if (constraint.getBundle() == capability.getResolverBundle()) { + result = true; // Wired to ourselves + continue; + } + ResolverBundle[] capabilityHosts = capability.isFromFragment() ? capability.getResolverBundle().getHost().getMatchingBundles() : new ResolverBundle[] {capability.getResolverBundle()}; + boolean foundResolvedMatch = false; + for (int j = 0; capabilityHosts != null && j < capabilityHosts.length; j++) + if (capabilityHosts[j].getState() == ResolverBundle.RESOLVED || resolveBundle(capabilityHosts[j], cycle)) { + foundResolvedMatch |= !capability.isFromFragment() ? true : capability.getResolverBundle().getHost().getMatchingBundles() != null; + // Check cyclic dependencies + if (capabilityHosts[j].getState() == ResolverBundle.RESOLVING) + if (!cycle.contains(capabilityHosts[j])) + cycle.add(capabilityHosts[j]); + } + if (!foundResolvedMatch) { + constraint.removeMatchingCapability(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 : (((GenericSpecification) constraint.getVersionConstraint()).getResolution() & GenericSpecification.RESOLUTION_OPTIONAL) != 0; + } + // Resolve the supplied import. Returns true if the import can be resolved, false otherwise private boolean resolveRequire(BundleConstraint req, ArrayList cycle) { if (DEBUG_REQUIRES) @@ -875,6 +974,15 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver BaseDescription supplier = bundle == null ? null : bundle.getBundle(); state.resolveConstraint(requires[i].getVersionConstraint(), supplier); } + GenericConstraint[] genericRequires = rb.getGenericRequires(); + for (int i = 0; i < genericRequires.length; i++) { + GenericCapability[] 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) { @@ -1021,6 +1129,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver unresolvedBundles.add(rb); resolverExports.put(rb.getExportPackages()); resolverBundles.put(rb.getName(), rb); + resolverGenerics.put(rb.getGenericCapabilities()); } } @@ -1041,6 +1150,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver if (!pending || !bundle.isResolved()) { resolverExports.remove(rb.getExportPackages()); resolverBundles.remove(rb); + resolverGenerics.remove(rb.getGenericCapabilities()); } unresolvedBundles.remove(rb); } @@ -1056,6 +1166,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver state.removeBundleComplete((BundleDescription) removedBundles[i]); resolverExports.remove(re.getExportPackages()); resolverBundles.remove(re); + resolverGenerics.remove(re.getGenericCapabilities()); bundleMapping.remove(removedBundles[i]); groupingChecker.removeAllExportConstraints(re); // the bundle is removed @@ -1083,6 +1194,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver public void flush() { resolverExports = null; resolverBundles = null; + resolverGenerics = null; unresolvedBundles = null; bundleMapping = null; Object[] removed = removalPending.getAllValues(); @@ -1110,6 +1222,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver 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_GROUPING = options.getBooleanOption(OPTION_GROUPING, false); DEBUG_CYCLES = options.getBooleanOption(OPTION_CYCLES, false); } 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 56a30da4f..b509586bd 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. + * Copyright (c) 2004, 2006 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 @@ -13,7 +13,7 @@ package org.eclipse.osgi.internal.resolver; import org.eclipse.osgi.service.resolver.BaseDescription; import org.osgi.framework.Version; -public class BaseDescriptionImpl implements BaseDescription { +public abstract class BaseDescriptionImpl implements BaseDescription { private String name; private Version version; 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 b8c75671c..e846afbc4 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -22,6 +22,8 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle 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 int RESOLVED = 0x01; static final int SINGLETON = 0x02; @@ -62,6 +64,10 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle return getName(); } + public BundleDescription getSupplier() { + return this; + } + public String getLocation() { fullyLoad(); return lazyData.location; @@ -93,6 +99,20 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle return lazyData.requiredBundles; } + public GenericSpecification[] getGenericRequires() { + fullyLoad(); + if (lazyData.genericRequires == null) + return EMPTY_GENERICSPECS; + return lazyData.genericRequires; + } + + public GenericDescription[] getGenericCapabilities() { + fullyLoad(); + if (lazyData.genericCapabilities == null) + return EMPTY_GENERICDESCS; + return lazyData.genericCapabilities; + } + public ExportPackageDescription[] getExportPackages() { fullyLoad(); return lazyData.exportPackages == null ? EMPTY_EXPORTS : lazyData.exportPackages; @@ -215,6 +235,22 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle } } + protected void setGenericCapabilities(GenericDescription[] genericCapabilities) { + 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) { + checkLazyData(); + lazyData.genericRequires = genericRequires; + if (genericRequires != null) + for (int i = 0; i < genericRequires.length; i++) + ((VersionConstraintImpl) genericRequires[i]).setBundle(this); + } + protected int getStateBits() { return stateBits; } @@ -323,11 +359,7 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle } protected synchronized void addDependency(BaseDescriptionImpl dependency) { - BundleDescriptionImpl bundle; - if (dependency instanceof ExportPackageDescription) - bundle = (BundleDescriptionImpl) ((ExportPackageDescription) dependency).getExporter(); - else - bundle = (BundleDescriptionImpl) dependency; + BundleDescriptionImpl bundle = (BundleDescriptionImpl) dependency.getSupplier(); if (bundle == this) return; if (dependencies == null) @@ -412,7 +444,7 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle private void fullyLoad() { if ((stateBits & LAZY_LOADED) == 0) return; - if (isFullyLoaded()){ + if (isFullyLoaded()) { containingState.getReader().setAccessedFlag(true); // set reader accessed flag return; } @@ -501,6 +533,8 @@ public class BundleDescriptionImpl extends BaseDescriptionImpl implements Bundle BundleSpecification[] requiredBundles; ExportPackageDescription[] exportPackages; ImportPackageSpecification[] importPackages; + GenericDescription[] genericCapabilities; + GenericSpecification[] genericRequires; ExportPackageDescription[] selectedExports; BundleDescription[] resolvedRequires; diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java index 1de78413d..76c6ee821 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ExportPackageDescriptionImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. + * Copyright (c) 2004, 2006 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 @@ -32,7 +32,7 @@ public class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements private int tableIndex; public Map getDirectives() { - Map result = new HashMap (5); + Map result = new HashMap(5); if (uses != null) result.put(Constants.USES_DIRECTIVE, uses); if (exclude != null) @@ -47,7 +47,7 @@ public class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements result.put(EQUINOX_EE, equinox_ee == -1 ? EQUINOX_EE_DEFAULT : new Integer(equinox_ee)); return result; } - + public Object getDirective(String key) { if (key.equals(Constants.USES_DIRECTIVE)) return uses; @@ -68,7 +68,7 @@ public class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements public Object setDirective(String key, Object value) { if (key.equals(Constants.USES_DIRECTIVE)) - return uses = (String[])value; + return uses = (String[]) value; if (key.equals(Constants.EXCLUDE_DIRECTIVE)) return exclude = (String) value; if (key.equals(Constants.INCLUDE_DIRECTIVE)) @@ -89,19 +89,23 @@ public class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements public void setDirectives(Map directives) { 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(Constants.FRIENDS_DIRECTIVE); - internal = (Boolean)directives.get(Constants.INTERNAL_DIRECTIVE); - equinox_ee = ((Integer)directives.get(EQUINOX_EE)).intValue(); + 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(Constants.FRIENDS_DIRECTIVE); + internal = (Boolean) directives.get(Constants.INTERNAL_DIRECTIVE); + equinox_ee = ((Integer) directives.get(EQUINOX_EE)).intValue(); } - + public Map getAttributes() { return attributes; } + public BundleDescription getSupplier() { + return getExporter(); + } + public BundleDescription getExporter() { return exporter; } @@ -123,7 +127,7 @@ public class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements } public String toString() { - return "Export-Package: " + getName() + "; version=\"" + getVersion() + "\""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + return "Export-Package: " + getName() + "; version=\"" + getVersion() + "\""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ } int getTableIndex() { diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java new file mode 100644 index 000000000..09276606c --- /dev/null +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/GenericDescriptionImpl.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006 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.util.Dictionary; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.GenericDescription; +import org.osgi.framework.Constants; + +public class GenericDescriptionImpl extends BaseDescriptionImpl implements GenericDescription { + private Dictionary attributes; + private BundleDescription supplier; + private String type = GenericDescription.DEFAULT_TYPE; + + public Dictionary getAttributes() { + return attributes; + } + + public BundleDescription getSupplier() { + return supplier; + } + + void setAttributes(Dictionary attributes) { + this.attributes = attributes; + // always add/replace the version attribute with the actual Version object + attributes.put(Constants.VERSION_ATTRIBUTE, getVersion()); + } + + void setSupplier(BundleDescription supplier) { + this.supplier = supplier; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(StateBuilder.GENERIC_CAPABILITY).append(": ").append(getName()); //$NON-NLS-1$ + if (getType() != GenericDescription.DEFAULT_TYPE) + sb.append(':').append(getType()); + return sb.toString(); + } + + 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; + } +} diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java new file mode 100644 index 000000000..8f6d44bb4 --- /dev/null +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/GenericSpecificationImpl.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2006 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.service.resolver.*; +import org.osgi.framework.*; + +public class GenericSpecificationImpl extends VersionConstraintImpl implements GenericSpecification { + private Filter matchingFilter; + private String type = GenericDescription.DEFAULT_TYPE; + private int resolution = 0; + private GenericDescription[] suppliers; + + public String getMatchingFilter() { + return matchingFilter == null ? null : matchingFilter.toString(); + } + + void setMatchingFilter(String matchingFilter) throws InvalidSyntaxException { + this.matchingFilter = matchingFilter == null ? null : FrameworkUtil.createFilter(matchingFilter); + } + + public boolean isSatisfiedBy(BaseDescription supplier) { + if (!(supplier instanceof GenericDescription)) + return false; + GenericDescription candidate = (GenericDescription) supplier; + if (getName() == null || !getName().equals(candidate.getName())) + return false; + if (getType() == null || !getType().equals(candidate.getType())) + return false; + // Note that versions are only matched by including them in the filter + return matchingFilter == null || matchingFilter.match(candidate.getAttributes()); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(StateBuilder.GENERIC_REQUIRE).append(": ").append(getName()); //$NON-NLS-1$ + if (getType() != GenericDescription.DEFAULT_TYPE) + sb.append(':').append(getType()); + if (matchingFilter != null) + sb.append("; ").append(getMatchingFilter()); //$NON-NLS-1$ + return sb.toString(); + } + + 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; + } + + public int getResolution() { + return resolution; + } + + public boolean isResolved() { + return suppliers != null && suppliers.length > 0; + } + + void setResolution(int resolution) { + this.resolution = resolution; + } + + public BaseDescription getSupplier() { + return suppliers == null || suppliers.length == 0 ? null : suppliers[0]; + } + + protected void setSupplier(BaseDescription supplier) { + 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() { + return suppliers; + } + + void setSupplers(GenericDescription[] suppliers) { + this.suppliers = suppliers; + } +} diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java index 6c8fa56dc..69b6dadf2 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverErrorImpl.java @@ -54,6 +54,7 @@ public class ResolverErrorImpl implements ResolverError { 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()); 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 d9490b1f6..ae19cf7ac 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -10,21 +10,32 @@ *******************************************************************************/ package org.eclipse.osgi.internal.resolver; +import java.lang.reflect.Constructor; import java.util.*; import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.service.resolver.*; import org.eclipse.osgi.util.ManifestElement; import org.eclipse.osgi.util.NLS; -import org.osgi.framework.BundleException; -import org.osgi.framework.Version; +import org.osgi.framework.*; /** * This class builds bundle description objects from manifests */ class StateBuilder { - static String[] DEFINED_MATCHING_ATTRS = {Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION, Constants.VERSION_ATTRIBUTE}; + static final String[] DEFINED_MATCHING_ATTRS = {Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION, Constants.VERSION_ATTRIBUTE}; + static final String[] DEFINED_OSGI_VALIDATE_HEADERS = {Constants.IMPORT_PACKAGE, Constants.DYNAMICIMPORT_PACKAGE, Constants.EXPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.BUNDLE_SYMBOLICNAME, Constants.REEXPORT_PACKAGE, Constants.REQUIRE_BUNDLE}; + static final String GENERIC_REQUIRE = "Eclipse-GenericRequire"; //$NON-NLS-1$ + static final String GENERIC_CAPABILITY = "Eclipse-GenericCapability"; //$NON-NLS-1$ - static String[] DEFINED_OSGI_VALIDATE_HEADERS = {Constants.IMPORT_PACKAGE, Constants.DYNAMICIMPORT_PACKAGE, Constants.EXPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.BUNDLE_SYMBOLICNAME, Constants.REEXPORT_PACKAGE, Constants.REQUIRE_BUNDLE}; + 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 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 manifest, String location) throws BundleException { BundleDescriptionImpl result = new BundleDescriptionImpl(); @@ -85,6 +96,80 @@ class StateBuilder { result.setImportPackages(createImportPackages(result.getExportPackages(), providedExports, imports, dynamicImports, manifestVersion)); ManifestElement[] requires = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, (String) manifest.get(Constants.REQUIRE_BUNDLE)); result.setRequiredBundles(createRequiredBundles(requires)); + String[][] genericAliases = getGenericAliases(state); + ManifestElement[] genericRequires = getGenericRequires(manifest, genericAliases); + result.setGenericRequires(createGenericRequires(genericRequires)); + ManifestElement[] genericCapabilities = getGenericCapabilities(manifest, genericAliases); + result.setGenericCapabilities(createGenericCapabilities(genericCapabilities)); + return result; + } + + private static ManifestElement[] getGenericRequires(Dictionary manifest, String[][] genericAliases) throws BundleException { + ManifestElement[] genericRequires = ManifestElement.parseHeader(GENERIC_REQUIRE, (String) manifest.get(GENERIC_REQUIRE)); + ArrayList aliasList = null; + if (genericAliases.length > 0) { + aliasList = new ArrayList(genericRequires == null ? 0 : genericRequires.length); + for (int i = 0; i < genericAliases.length; i++) { + ManifestElement[] aliasReqs = ManifestElement.parseHeader(genericAliases[i][1], (String) 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 (ManifestElement[]) aliasList.toArray(new ManifestElement[aliasList.size()]); + } + + private static ManifestElement[] getGenericCapabilities(Dictionary manifest, String[][] genericAliases) throws BundleException { + ManifestElement[] genericCapabilities = ManifestElement.parseHeader(GENERIC_CAPABILITY, (String) manifest.get(GENERIC_CAPABILITY)); + ArrayList aliasList = null; + if (genericAliases.length > 0) { + aliasList = new ArrayList(genericCapabilities == null ? 0 : genericCapabilities.length); + for (int i = 0; i < genericAliases.length; i++) { + ManifestElement[] aliasCapabilities = ManifestElement.parseHeader(genericAliases[i][0], (String) 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 keys = aliasCapabilities[j].getKeys(); keys != null && keys.hasMoreElements();) { + String key = (String) 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 (ManifestElement[]) aliasList.toArray(new ManifestElement[aliasList.size()]); + } + + private static String[][] getGenericAliases(StateImpl state) { + Dictionary[] platformProps = state == null ? null : state.getPlatformProperties(); + String genericAliasesProp = platformProps == null || platformProps.length == 0 ? null : (String) platformProps[0].get("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; } @@ -271,10 +356,41 @@ class StateBuilder { break; } } + String value = exportPackage.getAttribute(key); + int colonIndex = key.indexOf(':'); + String type = ATTR_TYPE_STRING; + if (colonIndex > 0) { + type = key.substring(colonIndex + 1); + key = key.substring(0, colonIndex); + } if (!definedAttr) { if (arbitraryAttrs == null) arbitraryAttrs = new HashMap(); - arbitraryAttrs.put(key, exportPackage.getAttribute(key)); + Object putValue = value; + if (ATTR_TYPE_STRING.equals(type)) + putValue = value; + else if (ATTR_TYPE_DOUBLE.equals(type)) + putValue = new Double(value); + else if (ATTR_TYPE_LONG.equals(type)) + putValue = new Long(value); + else if (ATTR_TYPE_URI.equals(type)) + try { + Class uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$ + Constructor constructor = uriClazz.getConstructor(new Class[] {String.class}); + putValue = constructor.newInstance(new Object[] {value}); + } catch (ClassNotFoundException e) { + // oh well cannot support; just use string + putValue = value; + } catch (Exception e) { // got some reflection exception + if (e instanceof RuntimeException) + throw (RuntimeException) e; + throw new RuntimeException(e.getMessage()); + } + else if (ATTR_TYPE_VERSION.equals(type)) + putValue = new Version(value); + else if (ATTR_TYPE_SET.equals(type)) + putValue = ManifestElement.getArrayFromList(value, ","); //$NON-NLS-1$ + arbitraryAttrs.put(key, putValue); } } return arbitraryAttrs; @@ -290,6 +406,74 @@ class StateBuilder { return result; } + private static GenericSpecification[] createGenericRequires(ManifestElement[] genericRequires) throws BundleException { + if (genericRequires == null) + return null; + ArrayList results = new ArrayList(genericRequires.length); + for (int i = 0; i < genericRequires.length; i++) { + String[] genericNames = genericRequires[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(genericRequires[i].getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE)); + } catch (InvalidSyntaxException e) { + throw new BundleException(Constants.SELECTION_FILTER_ATTRIBUTE, e); + } + String optional = genericRequires[i].getAttribute(OPTIONAL_ATTR); + String multiple = genericRequires[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 (GenericSpecification[]) results.toArray(new GenericSpecification[results.size()]); + } + + private static GenericDescription[] createGenericCapabilities(ManifestElement[] genericCapabilities) { + if (genericCapabilities == null) + return null; + ArrayList results = new ArrayList(genericCapabilities.length); + for (int i = 0; i < genericCapabilities.length; i++) { + String[] genericNames = genericCapabilities[i].getValueComponents(); + for (int j = 0; j < genericNames.length; j++) { + GenericDescriptionImpl desc = new GenericDescriptionImpl(); + int colonIdx = genericNames[j].indexOf(':'); + if (colonIdx > 0) { + desc.setName(genericNames[j].substring(0, colonIdx)); + desc.setType(genericNames[j].substring(colonIdx + 1)); + } else + desc.setName(genericNames[j]); + String versionString = genericCapabilities[i].getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString != null) + desc.setVersion(Version.parseVersion(versionString)); + Map mapAttrs = getAttributes(genericCapabilities[i], new String[] {Constants.VERSION_ATTRIBUTE}); + Object version = mapAttrs == null ? null : mapAttrs.remove(Constants.VERSION_ATTRIBUTE); + if (version instanceof Version) // this is just incase someone uses version:version as a key + desc.setVersion((Version) version); + Dictionary attrs = new Hashtable(); + if (mapAttrs != null) { + for (Iterator keys = mapAttrs.keySet().iterator(); keys.hasNext();) { + Object key = keys.next(); + attrs.put(key, mapAttrs.get(key)); + } + } + desc.setAttributes(attrs); + results.add(desc); + } + } + return (GenericDescription[]) results.toArray(new GenericDescription[results.size()]); + } + private static VersionRange getVersionRange(String versionRange) { if (versionRange == null) return null; @@ -351,7 +535,6 @@ class StateBuilder { throw new BundleException(NLS.bind(StateMsg.HEADER_REEXPORT_USES, Constants.USES_DIRECTIVE, Constants.REEXPORT_PACKAGE)); } - private static void checkExtensionBundle(ManifestElement[] elements) throws BundleException { if (elements.length == 0 || elements[0].getDirective(Constants.EXTENSION_DIRECTIVE) == null) return; diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java index 3b7b3413b..228245b1f 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateHelperImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. + * Copyright (c) 2004, 2006 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 @@ -85,8 +85,29 @@ public class StateHelperImpl implements StateHelper { return result; } + private Map getGenericsMap(State state, boolean resolved) { + Map result = new HashMap(11); + 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]; + Set genericSet = (Set) result.get(description.getName()); + if (genericSet == null) { + genericSet = new HashSet(1); + result.put(description.getName(), genericSet); + } + genericSet.add(description); + } + } + return result; + } + private VersionConstraint[] getUnsatisfiedLeaves(State state, BundleDescription[] bundles) { Map packages = getExportedPackageMap(state); + Map generics = getGenericsMap(state, false); HashSet result = new HashSet(11); for (int i = 0; i < bundles.length; i++) { BundleDescription description = bundles[i]; @@ -103,6 +124,11 @@ public class StateHelperImpl implements StateHelper { if (exports != null) for (Iterator iter = exports.iterator(); iter.hasNext() && !satisfied;) satisfied |= constraint.isSatisfiedBy((ExportPackageDescription) iter.next()); + } else if (constraint instanceof GenericSpecification) { + Set genericSet = (Set) generics.get(constraint.getName()); + if (genericSet != null) + for (Iterator iter = genericSet.iterator(); iter.hasNext() && !satisfied;) + satisfied |= constraint.isSatisfiedBy((GenericDescription) iter.next()); } if (!satisfied) result.add(constraint); @@ -140,6 +166,10 @@ public class StateHelperImpl implements StateHelper { for (int i = 0; i < packages.length; i++) if (!packages[i].isResolved() && !isResolvable(packages[i])) unsatisfied.add(packages[i]); + GenericSpecification[] generics = bundle.getGenericRequires(); + for (int i = 0; i < generics.length; i++) + if (!generics[i].isResolved() && !isResolvable(generics[i])) + unsatisfied.add(generics[i]); return (VersionConstraint[]) unsatisfied.toArray(new VersionConstraint[unsatisfied.size()]); } @@ -154,6 +184,17 @@ public class StateHelperImpl implements StateHelper { return false; } + private boolean isResolvable(GenericSpecification constraint) { + Map genericCapabilities = getGenericsMap(constraint.getBundle().getContainingState(), true); + Set genericSet = (Set) genericCapabilities.get(constraint.getName()); + if (genericSet == null) + return false; + for (Iterator iter = genericSet.iterator(); iter.hasNext();) + if (constraint.isSatisfiedBy((GenericDescription) iter.next())) + return true; + return false; + } + /** * @see StateHelper */ 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 c4c2186ba..6841f4d97 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -24,7 +24,7 @@ import org.osgi.framework.BundleException; import org.osgi.framework.Version; public abstract class StateImpl implements State { - public static final String[] PROPS = {"osgi.os", "osgi.ws", "osgi.nl", "osgi.arch", Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES, Constants.OSGI_RESOLVER_MODE, Constants.FRAMEWORK_EXECUTIONENVIRONMENT, "osgi.resolveOptional"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + public static final String[] PROPS = {"osgi.os", "osgi.ws", "osgi.nl", "osgi.arch", Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES, Constants.OSGI_RESOLVER_MODE, Constants.FRAMEWORK_EXECUTIONENVIRONMENT, "osgi.resolveOptional", "osgi.genericAliases"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ transient private Resolver resolver; transient private StateDeltaImpl changes; transient private boolean resolving = false; @@ -266,6 +266,15 @@ public abstract class StateImpl implements State { bundle.addDependencies(hosts); bundle.addDependencies(resolvedRequires); bundle.addDependencies(resolvedImports); + // add dependecies for generics + GenericSpecification[] genericRequires = bundle.getGenericRequires(); + if (genericRequires.length > 0) { + ArrayList genericSuppliers = new ArrayList(genericRequires.length); + for (int i = 0; i < genericRequires.length; i++) + if (genericRequires[i].getSupplier() != null) + genericSuppliers.add(genericRequires[i].getSupplier()); + bundle.addDependencies((BaseDescription[]) genericSuppliers.toArray(new BaseDescription[genericSuppliers.size()])); + } } private void unresolveConstraints(BundleDescriptionImpl bundle) { @@ -508,7 +517,7 @@ public abstract class StateImpl implements State { ExportPackageDescription[] exports = systemBundle.getExportPackages(); ArrayList newExports = new ArrayList(exports.length); for (int i = 0; i < exports.length; i++) - if (((Integer)exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() < 0) + if (((Integer) exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() < 0) newExports.add(exports[i]); addSystemExports(newExports); systemBundle.setExportPackages((ExportPackageDescription[]) newExports.toArray(new ExportPackageDescription[newExports.size()])); @@ -525,7 +534,7 @@ public abstract class StateImpl implements State { ExportPackageDescription[] systemExports = StateBuilder.createExportPackages(elements, null, null, null, 2, false); Integer profInx = new Integer(i); for (int j = 0; j < systemExports.length; j++) { - ((ExportPackageDescriptionImpl)systemExports[j]).setDirective(ExportPackageDescriptionImpl.EQUINOX_EE, profInx); + ((ExportPackageDescriptionImpl) systemExports[j]).setDirective(ExportPackageDescriptionImpl.EQUINOX_EE, profInx); exports.add(systemExports[j]); } } catch (BundleException e) { @@ -626,7 +635,6 @@ public abstract class StateImpl implements State { } } - public ExportPackageDescription[] getSystemPackages() { ArrayList result = new ArrayList(); BundleDescription[] systemBundles = getBundles(Constants.getInternalSymbolicName()); @@ -634,7 +642,7 @@ public abstract class StateImpl implements State { 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) + if (((Integer) exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() >= 0) result.add(exports[i]); } return (ExportPackageDescription[]) result.toArray(new ExportPackageDescription[result.size()]); 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 a161a073d..ab71b23c0 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -11,12 +11,11 @@ package org.eclipse.osgi.internal.resolver; import java.io.*; -import java.util.Dictionary; -import java.util.Map; +import java.util.*; +import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.internal.module.ResolverImpl; import org.eclipse.osgi.service.resolver.*; -import org.osgi.framework.BundleException; -import org.osgi.framework.Version; +import org.osgi.framework.*; public class StateObjectFactoryImpl implements StateObjectFactory { @@ -25,12 +24,16 @@ public class StateObjectFactoryImpl implements StateObjectFactory { } public BundleDescription createBundleDescription(State state, Dictionary manifest, String location, long id) throws BundleException { - BundleDescriptionImpl result = (BundleDescriptionImpl) StateBuilder.createBundleDescription((StateImpl)state, manifest, location); + BundleDescriptionImpl result = (BundleDescriptionImpl) StateBuilder.createBundleDescription((StateImpl) state, manifest, location); result.setBundleId(id); return result; } 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); + } + + 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) { BundleDescriptionImpl bundle = new BundleDescriptionImpl(); bundle.setBundleId(id); bundle.setSymbolicName(symbolicName); @@ -41,6 +44,11 @@ public class StateObjectFactoryImpl implements StateObjectFactory { 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.setGenericRequires(genericRequires); + bundle.setGenericCapabilities(genericCapabilities); return bundle; } @@ -73,9 +81,42 @@ public class StateObjectFactoryImpl implements StateObjectFactory { 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())); return bundle; } + 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.setName(genericCapabilities[i].getName()); + cap.setVersion(genericCapabilities[i].getVersion()); + cap.setAttributes(genericCapabilities[i].getAttributes()); + 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()); + try { + req.setMatchingFilter(genericRequires[i].getMatchingFilter()); + } catch (InvalidSyntaxException e) { + // do nothing; this filter should aready have been tested + } + result[i] = req; + } + return result; + } + public BundleSpecification createBundleSpecification(String requiredSymbolicName, VersionRange requiredVersionRange, boolean export, boolean optional) { BundleSpecificationImpl bundleSpec = new BundleSpecificationImpl(); bundleSpec.setName(requiredSymbolicName); @@ -146,6 +187,38 @@ public class StateObjectFactoryImpl implements StateObjectFactory { return exportPackage; } + public GenericDescription createGenericDescription(String name, String type, Version version, Map attributes) { + GenericDescriptionImpl result = new GenericDescriptionImpl(); + result.setName(name); + result.setType(type); + result.setVersion(version); + Object versionObj = attributes == null ? null : attributes.remove(Constants.VERSION_ATTRIBUTE); + if (versionObj instanceof Version) // this is just incase someone uses version:version as a key + result.setVersion((Version) versionObj); + Dictionary attrs = new Hashtable(); + if (attributes != null) + for (Iterator keys = attributes.keySet().iterator(); keys.hasNext();) { + Object key = keys.next(); + attrs.put(key, attributes.get(key)); + } + result.setAttributes(attrs); + 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); + int resolution = 0; + if (optional) + resolution |= GenericSpecification.RESOLUTION_OPTIONAL; + if (multiple) + resolution |= GenericSpecification.RESOLUTION_MULTIPLE; + result.setResolution(resolution); + return result; + } + public SystemState createSystemState() { SystemState state = new SystemState(); state.setFactory(this); diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java index 5d339429e..17bb6ff7e 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -12,9 +12,11 @@ package org.eclipse.osgi.internal.resolver; import java.io.*; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.util.*; import org.eclipse.osgi.framework.util.SecureAction; import org.eclipse.osgi.service.resolver.*; +import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.Version; class StateReader { @@ -35,7 +37,7 @@ class StateReader { private int numBundles; private boolean accessedFlag = false; - public static final byte STATE_CACHE_VERSION = 24; + public static final byte STATE_CACHE_VERSION = 25; public static final byte NULL = 0; public static final byte OBJECT = 1; public static final byte INDEX = 2; @@ -303,6 +305,22 @@ class StateReader { 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); + } + 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 @@ -345,13 +363,33 @@ class StateReader { Object value = null; byte type = in.readByte(); if (type == 0) - value = readString(in, false); + 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 (Exception e) { // got some reflection exception + if (e instanceof RuntimeException) + throw (RuntimeException) e; + throw new RuntimeException(e.getMessage()); + } + } result.put(key, value); } return result; @@ -399,6 +437,47 @@ class StateReader { 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.setType(readString(in, false)); + Map mapAttrs = readMap(in); + Dictionary attrs = new Hashtable(); + if (mapAttrs != null) { + for (Iterator keys = mapAttrs.keySet().iterator(); keys.hasNext();) { + Object key = keys.next(); + attrs.put(key, mapAttrs.get(key)); + } + } + result.setAttributes(attrs); + return result; + } + + private GenericSpecification readGenericSpecification(DataInputStream in) throws IOException { + GenericSpecificationImpl result = new GenericSpecificationImpl(); + 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)); + } catch (InvalidSyntaxException e) { + // do nothing this filter was tested before + } + return result; + } + // called by readers for VersionConstraintImpl subclasses private void readVersionConstraint(VersionConstraintImpl version, DataInputStream in) throws IOException { version.setName(readString(in, false)); diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java index 527f9d6bd..8cd2bad9c 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -13,6 +13,7 @@ package org.eclipse.osgi.internal.resolver; import java.io.*; import java.util.*; import org.eclipse.osgi.service.resolver.*; +import org.osgi.framework.Constants; import org.osgi.framework.Version; class StateWriter { @@ -60,7 +61,7 @@ class StateWriter { out.writeLong(state.getTimeStamp()); Dictionary[] propSet = state.getPlatformProperties(); out.writeInt(propSet.length); - for (int i = 0; i < propSet.length; i++){ + for (int i = 0; i < propSet.length; i++) { Dictionary props = propSet[i]; out.writeInt(StateImpl.PROPS.length); for (int j = 0; j < StateImpl.PROPS.length; j++) @@ -106,7 +107,7 @@ class StateWriter { outState.writeLong(state.getTimeStamp()); Dictionary[] propSet = state.getPlatformProperties(); outState.writeInt(propSet.length); - for (int i = 0; i < propSet.length; i++){ + for (int i = 0; i < propSet.length; i++) { Dictionary props = propSet[i]; outState.writeInt(StateImpl.PROPS.length); for (int j = 0; j < StateImpl.PROPS.length; j++) @@ -129,11 +130,11 @@ class StateWriter { } catch (IOException e) { // do nothing, we tried } - try { - outLazy.close(); - } catch (IOException e) { - // do nothing - } + try { + outLazy.close(); + } catch (IOException e) { + // do nothing + } if (outState != null) try { outState.flush(); @@ -141,11 +142,11 @@ class StateWriter { } catch (IOException e) { // do nothing, we tried } - try { - outState.close(); - } catch (IOException e) { - // do nothing - } + try { + outState.close(); + } catch (IOException e) { + // do nothing + } } } @@ -258,7 +259,7 @@ class StateWriter { for (int i = 0; i < ees.length; i++) writeStringOrNull(ees[i], out); - HashMap dynamicStamps = ((BundleDescriptionImpl)bundle).getDynamicStamps(); + HashMap dynamicStamps = ((BundleDescriptionImpl) bundle).getDynamicStamps(); if (dynamicStamps == null) out.writeInt(0); else { @@ -266,10 +267,28 @@ class StateWriter { for (Iterator pkgs = dynamicStamps.keySet().iterator(); pkgs.hasNext();) { String pkg = (String) pkgs.next(); writeStringOrNull(pkg, out); - out.writeLong(((Long)dynamicStamps.get(pkg)).longValue()); + out.writeLong(((Long) 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(genericRequires[i], out); + } + // save the size of the lazy data ((BundleDescriptionImpl) bundle).setLazyDataSize(out.size() - dataStart); } @@ -290,6 +309,33 @@ class StateWriter { writeMap(out, exportPackageDesc.getDirectives()); } + private void writeGenericDescription(GenericDescription description, DataOutputStream out) throws IOException { + if (writePrefix(description, out)) + return; + writeBaseDescription(description, out); + writeStringOrNull(description.getType() == GenericDescription.DEFAULT_TYPE ? null : description.getType(), out); + Dictionary attrs = description.getAttributes(); + Map mapAttrs = new HashMap(attrs.size()); + for (Enumeration keys = attrs.keys(); keys.hasMoreElements();) { + Object key = keys.nextElement(); + if (!Constants.VERSION_ATTRIBUTE.equals(key)) + mapAttrs.put(key, attrs.get(key)); + } + writeMap(out, mapAttrs); + } + + private void writeGenericSpecification(GenericSpecification specification, DataOutputStream out) throws IOException { + 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); + } + private void writeMap(DataOutputStream out, Map source) throws IOException { if (source == null) { out.writeInt(0); @@ -312,6 +358,18 @@ class StateWriter { } 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); } } } diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java index 90a58b436..48bd05567 100644 --- a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java +++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2005 IBM Corporation and others. + * Copyright (c) 2003, 2006 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 @@ -322,60 +322,70 @@ public class ManifestElement { * @throws BundleException if the header value is invalid */ public static ManifestElement[] parseHeader(String header, String value) throws BundleException { - if (value == null) { + if (value == null) return (null); - } - Vector headerElements = new Vector(10, 10); + ArrayList headerElements = new ArrayList(10); Tokenizer tokenizer = new Tokenizer(value); parseloop: while (true) { String next = tokenizer.getToken(";,"); //$NON-NLS-1$ - if (next == null) { + if (next == null) throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } ArrayList headerValues = new ArrayList(); StringBuffer headerValue = new StringBuffer(next); headerValues.add(next); - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.print("paserHeader: " + next); //$NON-NLS-1$ - } + boolean directive = false; char c = tokenizer.getChar(); // Header values may be a list of ';' separated values. Just append them all into one value until the first '=' or ',' while (c == ';') { next = tokenizer.getToken(";,=:"); //$NON-NLS-1$ - if (next == null) { + if (next == null) throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } c = tokenizer.getChar(); + while (c == ':') { // may not really be a := + c = tokenizer.getChar(); + if (c != '=') { + String restOfNext = tokenizer.getToken(";,=:"); //$NON-NLS-1$ + if (restOfNext == null) + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + next += ":" + c + restOfNext; //$NON-NLS-1$ + c = tokenizer.getChar(); + } else + directive = true; + } if (c == ';' || c == '\0') /* more */{ headerValues.add(next); headerValue.append(";").append(next); //$NON-NLS-1$ - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.print(";" + next); //$NON-NLS-1$ - } } } // found the header value create a manifestElement for it. ManifestElement manifestElement = new ManifestElement(); manifestElement.value = headerValue.toString(); manifestElement.valueComponents = (String[]) headerValues.toArray(new String[headerValues.size()]); - boolean directive = false; - if (c == ':') { - c = tokenizer.getChar(); - if (c != '=') - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - directive = true; - } - // now add any attributes for the manifestElement. - while (c == '=') { + + // now add any attributes/directives for the manifestElement. + while (c == '=' || c == ':') { + while (c == ':') { // may not really be a := + c = tokenizer.getChar(); + if (c != '=') { + String restOfNext = tokenizer.getToken("=:"); //$NON-NLS-1$ + if (restOfNext == null) + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + next += ":" + c + restOfNext; //$NON-NLS-1$ + c = tokenizer.getChar(); + } else + directive = true; + } String val = tokenizer.getString(";,"); //$NON-NLS-1$ - if (val == null) { + if (val == null) throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.print(";" + next + "=" + val); //$NON-NLS-1$ //$NON-NLS-2$ - } try { if (directive) manifestElement.addDirective(next, val); @@ -383,41 +393,30 @@ public class ManifestElement { manifestElement.addAttribute(next, val); directive = false; } catch (Exception e) { - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); } c = tokenizer.getChar(); if (c == ';') /* more */{ next = tokenizer.getToken("=:"); //$NON-NLS-1$ - if (next == null) { - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - } + if (next == null) + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); c = tokenizer.getChar(); - if (c == ':') { - c = tokenizer.getChar(); - if (c != '=') - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); - directive = true; - } } } - headerElements.addElement(manifestElement); - if (Debug.DEBUG && Debug.DEBUG_MANIFEST) { + headerElements.add(manifestElement); + if (Debug.DEBUG && Debug.DEBUG_MANIFEST) Debug.println(""); //$NON-NLS-1$ - } - if (c == ',') /* another manifest element */{ + if (c == ',') /* another manifest element */ continue parseloop; - } - if (c == '\0') /* end of value */{ + if (c == '\0') /* end of value */ break parseloop; - } - throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); + throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value)); } int size = headerElements.size(); - if (size == 0) { + if (size == 0) return (null); - } - ManifestElement[] result = new ManifestElement[size]; - headerElements.copyInto(result); + + ManifestElement[] result = (ManifestElement[]) headerElements.toArray(new ManifestElement[size]); return (result); } |