diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF index b466a3695f..795bc17d39 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF @@ -33,12 +33,15 @@ Import-Package: org.eclipse.equinox.app;version="[1.0.0,2.0.0)", org.eclipse.equinox.simpleconfigurator.manipulator;version="[2.0.0,3.0.0)", org.eclipse.equinox.spi.p2.publisher, org.eclipse.osgi.framework.util, + org.eclipse.osgi.internal.framework, + org.eclipse.osgi.internal.messages, org.eclipse.osgi.service.datalocation;version="1.3.0", org.eclipse.osgi.service.environment;version="1.1.0", org.eclipse.osgi.service.pluginconversion;version="1.0.0", org.eclipse.osgi.service.resolver;version="1.5.0", org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.3.0", + org.osgi.framework.namespace;version="[1.2.0,2.0.0]", org.osgi.framework.wiring;version="1.2.0", org.osgi.resource;version="1.0.0", org.osgi.service.application;version="1.1.0", diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java index 04c7558e49..0c0197a171 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BundlesAction.java @@ -40,6 +40,7 @@ import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; import org.eclipse.equinox.p2.publisher.*; import org.eclipse.equinox.p2.publisher.actions.*; +import org.eclipse.equinox.p2.publisher.eclipse.statefactory.StateObjectFactoryImpl; import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; @@ -766,7 +767,8 @@ public static Map> getHostLocalizations(File bundleL public static BundleDescription createBundleDescription(Dictionary enhancedManifest, File bundleLocation) { try { - BundleDescription descriptor = StateObjectFactory.defaultFactory.createBundleDescription(null, + ; + BundleDescription descriptor = new StateObjectFactoryImpl().createBundleDescription(null, enhancedManifest, bundleLocation == null ? null : bundleLocation.getAbsolutePath(), 1); // TODO Do // we need // to have a diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BaseDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BaseDescriptionImpl.java new file mode 100644 index 0000000000..90330f9eba --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BaseDescriptionImpl.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.*; +import java.util.Map.Entry; +import org.eclipse.osgi.service.resolver.BaseDescription; +import org.osgi.framework.Version; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRevision; + +public abstract class BaseDescriptionImpl implements BaseDescription { + + protected final Object monitor = new Object(); + + private volatile String name; + + private volatile Version version; + + private volatile Object userObject; + + public String getName() { + return name; + } + + public Version getVersion() { + synchronized (this.monitor) { + if (version == null) + return Version.emptyVersion; + return version; + } + } + + protected void setName(String name) { + this.name = name; + } + + protected void setVersion(Version version) { + this.version = version; + } + + static String toString(Map map, boolean directives) { + if (map.size() == 0) + return ""; //$NON-NLS-1$ + String assignment = directives ? ":=" : "="; //$NON-NLS-1$//$NON-NLS-2$ + Set> set = map.entrySet(); + StringBuilder sb = new StringBuilder(); + for (Entry entry : set) { + sb.append("; "); //$NON-NLS-1$ + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof List) { + @SuppressWarnings("unchecked") + List list = (List) value; + if (list.size() == 0) + continue; + Object component = list.get(0); + String className = component.getClass().getName(); + String type = className.substring(className.lastIndexOf('.') + 1); + sb.append(key).append(':').append("List<").append(type).append(">").append(assignment).append('"'); //$NON-NLS-1$ //$NON-NLS-2$ + for (Object object : list) + sb.append(object).append(','); + sb.setLength(sb.length() - 1); + sb.append('"'); + } else { + String type = ""; //$NON-NLS-1$ + if (!(value instanceof String)) { + String className = value.getClass().getName(); + type = ":" + className.substring(className.lastIndexOf('.') + 1); //$NON-NLS-1$ + } + sb.append(key).append(type).append(assignment).append('"').append(value).append('"'); + } + } + return sb.toString(); + } + + String getInternalNameSpace() { + return null; + } + + public BaseDescription getFragmentDeclaration() { + return null; + } + + public BundleCapability getCapability() { + return getCapability(null); + } + + BundleCapability getCapability(String namespace) { + BaseDescriptionImpl fragmentDeclaration = (BaseDescriptionImpl) getFragmentDeclaration(); + if (fragmentDeclaration != null) + return fragmentDeclaration.getCapability(namespace); + if (namespace == null) + namespace = getInternalNameSpace(); + if (namespace == null) + return null; + return new BaseCapability(namespace); + } + + public Object getUserObject() { + return userObject; + } + + public void setUserObject(Object userObject) { + this.userObject = userObject; + } + + public class BaseCapability implements BundleCapability { + private final String namespace; + + public BaseCapability(String namespace) { + super(); + this.namespace = namespace; + } + + public BundleRevision getRevision() { + return getSupplier(); + } + + public String getNamespace() { + return namespace; + } + + public Map getDirectives() { + return getDeclaredDirectives(); + } + + public Map getAttributes() { + Map attrs = getDeclaredAttributes(); + String internalName = BaseDescriptionImpl.this.getInternalNameSpace(); + if (namespace.equals(internalName)) + return attrs; + // we are doing an alias, must remove internal Name and add alias + attrs = new HashMap<>(attrs); + Object nameValue = attrs.remove(internalName); + if (nameValue != null) + attrs.put(namespace, nameValue); + return Collections.unmodifiableMap(attrs); + } + + @Override + public int hashCode() { + return System.identityHashCode(BaseDescriptionImpl.this); + } + + public BaseDescriptionImpl getBaseDescription() { + return BaseDescriptionImpl.this; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof BaseCapability)) + return false; + return (((BaseCapability) obj).getBaseDescription() == BaseDescriptionImpl.this) && namespace.equals(((BaseCapability) obj).getNamespace()); + } + + @Override + public String toString() { + return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true); + } + + public BundleRevision getResource() { + return getRevision(); + } + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BundleDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BundleDescriptionImpl.java new file mode 100644 index 0000000000..dc6db37b8c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BundleDescriptionImpl.java @@ -0,0 +1,1214 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Danail Nachev - ProSyst - bug 218625 + * Rob Harrop - SpringSource Inc. (bug 247522 and 255520) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.net.URL; +import java.util.*; +import org.eclipse.osgi.framework.util.KeyedElement; +import org.eclipse.osgi.service.resolver.*; +import org.osgi.framework.*; +import org.osgi.framework.wiring.*; +import org.osgi.resource.*; + +public final class BundleDescriptionImpl extends BaseDescriptionImpl implements BundleDescription, KeyedElement { + static final String[] EMPTY_STRING = new String[0]; + static final ImportPackageSpecification[] EMPTY_IMPORTS = new ImportPackageSpecification[0]; + static final BundleSpecification[] EMPTY_BUNDLESPECS = new BundleSpecification[0]; + static final ExportPackageDescription[] EMPTY_EXPORTS = new ExportPackageDescription[0]; + static final BundleDescription[] EMPTY_BUNDLEDESCS = new BundleDescription[0]; + static final GenericSpecification[] EMPTY_GENERICSPECS = new GenericSpecification[0]; + static final GenericDescription[] EMPTY_GENERICDESCS = new GenericDescription[0]; + static final RuntimePermission GET_CLASSLOADER_PERM = new RuntimePermission("getClassLoader"); //$NON-NLS-1$ + + static final int RESOLVED = 0x01; + static final int SINGLETON = 0x02; + static final int REMOVAL_PENDING = 0x04; + static final int FULLY_LOADED = 0x08; + static final int LAZY_LOADED = 0x10; + static final int HAS_DYNAMICIMPORT = 0x20; + static final int ATTACH_FRAGMENTS = 0x40; + static final int DYNAMIC_FRAGMENTS = 0x80; + + // set to fully loaded and allow dynamic fragments by default + private volatile int stateBits = FULLY_LOADED | ATTACH_FRAGMENTS | DYNAMIC_FRAGMENTS; + + private volatile long bundleId = -1; + volatile HostSpecification host; //null if the bundle is not a fragment. volatile to allow unsynchronized checks for null + + private volatile int lazyDataOffset = -1; + private volatile int lazyDataSize = -1; + + private List dependencies; + private List dependents; + private String[] mandatory; + private Map attributes; + private Map arbitraryDirectives; + + private volatile LazyData lazyData; + private volatile int equinox_ee = -1; + + private DescriptionWiring bundleWiring; + + public BundleDescriptionImpl() { + // + } + + public long getBundleId() { + return bundleId; + } + + public String getSymbolicName() { + return getName(); + } + + public BundleDescription getSupplier() { + return this; + } + + public String getLocation() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + return currentData.location; + } + } + + public String getPlatformFilter() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + return currentData.platformFilter; + } + } + + public String[] getExecutionEnvironments() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.executionEnvironments == null) + return EMPTY_STRING; + return currentData.executionEnvironments; + } + } + + public ImportPackageSpecification[] getImportPackages() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.importPackages == null) + return EMPTY_IMPORTS; + return currentData.importPackages; + } + } + + public ImportPackageSpecification[] getAddedDynamicImportPackages() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.addedDynamicImports == null) + return EMPTY_IMPORTS; + return currentData.addedDynamicImports + .toArray(new ImportPackageSpecification[currentData.addedDynamicImports.size()]); + } + } + + public BundleSpecification[] getRequiredBundles() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.requiredBundles == null) + return EMPTY_BUNDLESPECS; + return currentData.requiredBundles; + } + } + + public GenericSpecification[] getGenericRequires() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.genericRequires == null) + return EMPTY_GENERICSPECS; + return currentData.genericRequires; + } + } + + public GenericDescription[] getGenericCapabilities() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.genericCapabilities == null) + return EMPTY_GENERICDESCS; + return currentData.genericCapabilities; + } + } + + public NativeCodeSpecification getNativeCodeSpecification() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + return currentData.nativeCode; + } + } + + public ExportPackageDescription[] getExportPackages() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + return currentData.exportPackages == null ? EMPTY_EXPORTS : currentData.exportPackages; + } + } + + public boolean isResolved() { + return (stateBits & RESOLVED) != 0; + } + + public State getContainingState() { + return null; + } + + public BundleDescription[] getFragments() { + if (host != null) + return EMPTY_BUNDLEDESCS; + throw new IllegalStateException("BundleDescription does not belong to a state."); //$NON-NLS-1$ + } + + public HostSpecification getHost() { + return host; + } + + public boolean isSingleton() { + return (stateBits & SINGLETON) != 0; + } + + public boolean isRemovalPending() { + return (stateBits & REMOVAL_PENDING) != 0; + } + + public boolean hasDynamicImports() { + return (stateBits & HAS_DYNAMICIMPORT) != 0; + } + + public boolean attachFragments() { + return (stateBits & ATTACH_FRAGMENTS) != 0; + } + + public boolean dynamicFragments() { + return (stateBits & DYNAMIC_FRAGMENTS) != 0; + } + + public ExportPackageDescription[] getSelectedExports() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.selectedExports == null) + return EMPTY_EXPORTS; + return currentData.selectedExports; + } + } + + public GenericDescription[] getSelectedGenericCapabilities() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.selectedCapabilities == null) + return EMPTY_GENERICDESCS; + return currentData.selectedCapabilities; + } + } + + public ExportPackageDescription[] getSubstitutedExports() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.substitutedExports == null) + return EMPTY_EXPORTS; + return currentData.substitutedExports; + } + } + + public BundleDescription[] getResolvedRequires() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.resolvedRequires == null) + return EMPTY_BUNDLEDESCS; + return currentData.resolvedRequires; + } + } + + public ExportPackageDescription[] getResolvedImports() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.resolvedImports == null) + return EMPTY_EXPORTS; + return currentData.resolvedImports; + } + } + + public GenericDescription[] getResolvedGenericRequires() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.resolvedCapabilities == null) + return EMPTY_GENERICDESCS; + return currentData.resolvedCapabilities; + } + } + + public Map> getWires() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.stateWires == null) { + currentData.stateWires = new HashMap<>(0); + } + return currentData.stateWires; + } + } + + Map> getWiresInternal() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + return currentData.stateWires; + } + } + + protected void setBundleId(long bundleId) { + this.bundleId = bundleId; + } + + protected void setSymbolicName(String symbolicName) { + setName(symbolicName); + } + + protected void setLocation(String location) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.location = location; + } + } + + protected void setPlatformFilter(String platformFilter) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.platformFilter = platformFilter; + } + } + + protected void setExecutionEnvironments(String[] executionEnvironments) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.executionEnvironments = executionEnvironments == null || executionEnvironments.length > 0 ? executionEnvironments : EMPTY_STRING; + } + } + + protected void setExportPackages(ExportPackageDescription[] exportPackages) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.exportPackages = exportPackages == null || exportPackages.length > 0 ? exportPackages : EMPTY_EXPORTS; + if (exportPackages != null) { + for (ExportPackageDescription exportPackage : exportPackages) { + ((ExportPackageDescriptionImpl) exportPackage).setExporter(this); + } + } + } + } + + protected void setImportPackages(ImportPackageSpecification[] importPackages) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.importPackages = importPackages == null || importPackages.length > 0 ? importPackages : EMPTY_IMPORTS; + if (importPackages != null) { + for (ImportPackageSpecification importPackage : importPackages) { + ((ImportPackageSpecificationImpl) importPackage).setBundle(this); + if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) { + stateBits |= HAS_DYNAMICIMPORT; + } + } + } + } + } + + protected void setRequiredBundles(BundleSpecification[] requiredBundles) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.requiredBundles = requiredBundles == null || requiredBundles.length > 0 ? requiredBundles : EMPTY_BUNDLESPECS; + if (requiredBundles != null) + for (BundleSpecification requiredBundle : requiredBundles) { + ((VersionConstraintImpl) requiredBundle).setBundle(this); + } + } + } + + protected void setGenericCapabilities(GenericDescription[] genericCapabilities) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.genericCapabilities = genericCapabilities == null || genericCapabilities.length > 0 ? genericCapabilities : EMPTY_GENERICDESCS; + if (genericCapabilities != null) + for (GenericDescription genericCapability : genericCapabilities) { + ((GenericDescriptionImpl) genericCapability).setSupplier(this); + } + } + } + + protected void setGenericRequires(GenericSpecification[] genericRequires) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.genericRequires = genericRequires == null || genericRequires.length > 0 ? genericRequires : EMPTY_GENERICSPECS; + if (genericRequires != null) + for (GenericSpecification genericRequire : genericRequires) { + ((VersionConstraintImpl) genericRequire).setBundle(this); + } + } + } + + protected void setNativeCodeSpecification(NativeCodeSpecification nativeCode) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.nativeCode = nativeCode; + if (nativeCode != null) { + ((NativeCodeSpecificationImpl) nativeCode).setBundle(this); + NativeCodeDescription[] suppliers = nativeCode.getPossibleSuppliers(); + if (suppliers != null) + for (NativeCodeDescription supplier : suppliers) { + ((NativeCodeDescriptionImpl) supplier).setSupplier(this); + } + } + } + } + + protected int getStateBits() { + return stateBits; + } + + protected void setStateBit(int stateBit, boolean on) { + synchronized (this.monitor) { + if (on) { + stateBits |= stateBit; + } else { + stateBits &= ~stateBit; + if (stateBit == RESOLVED) { + if (bundleWiring != null) + bundleWiring.invalidate(); + bundleWiring = null; + } + } + } + } + + protected void setContainingState(State value) { + synchronized (this.monitor) { + stateBits &= ~LAZY_LOADED; + } + } + + protected void setHost(HostSpecification host) { + synchronized (this.monitor) { + this.host = host; + if (host != null) { + ((VersionConstraintImpl) host).setBundle(this); + } + } + } + + protected void setLazyLoaded(boolean lazyLoad) { + loadLazyData(); + synchronized (this.monitor) { + if (lazyLoad) + stateBits |= LAZY_LOADED; + else + stateBits &= ~LAZY_LOADED; + } + } + + protected void setSelectedExports(ExportPackageDescription[] selectedExports) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.selectedExports = selectedExports == null || selectedExports.length > 0 ? selectedExports : EMPTY_EXPORTS; + if (selectedExports != null) { + for (ExportPackageDescription selectedExport : selectedExports) { + ((ExportPackageDescriptionImpl) selectedExport).setExporter(this); + } + } + } + } + + protected void setSelectedCapabilities(GenericDescription[] selectedCapabilities) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.selectedCapabilities = selectedCapabilities == null || selectedCapabilities.length > 0 ? selectedCapabilities : EMPTY_GENERICDESCS; + if (selectedCapabilities != null) { + for (GenericDescription capability : selectedCapabilities) { + ((GenericDescriptionImpl) capability).setSupplier(this); + } + } + } + } + + protected void setSubstitutedExports(ExportPackageDescription[] substitutedExports) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.substitutedExports = substitutedExports != null && substitutedExports.length > 0 ? substitutedExports : EMPTY_EXPORTS; + } + } + + protected void setResolvedImports(ExportPackageDescription[] resolvedImports) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.resolvedImports = resolvedImports == null || resolvedImports.length > 0 ? resolvedImports : EMPTY_EXPORTS; + } + } + + protected void setResolvedRequires(BundleDescription[] resolvedRequires) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.resolvedRequires = resolvedRequires == null || resolvedRequires.length > 0 ? resolvedRequires : EMPTY_BUNDLEDESCS; + } + } + + protected void setResolvedCapabilities(GenericDescription[] resolvedCapabilities) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.resolvedCapabilities = resolvedCapabilities == null || resolvedCapabilities.length > 0 ? resolvedCapabilities : EMPTY_GENERICDESCS; + } + } + + protected void setStateWires(Map> stateWires) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.stateWires = stateWires; + } + } + + void clearAddedDynamicImportPackages() { + synchronized (this.monitor) { + checkLazyData(); + lazyData.addedDynamicImports = null; + } + } + + @Override + public String toString() { + if (getSymbolicName() == null) + return "[" + getBundleId() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + return getSymbolicName() + "_" + getVersion(); //$NON-NLS-1$ + } + + public Object getKey() { + return Long.valueOf(bundleId); + } + + public boolean compare(KeyedElement other) { + if (!(other instanceof BundleDescriptionImpl)) + return false; + BundleDescriptionImpl otherBundleDescription = (BundleDescriptionImpl) other; + return bundleId == otherBundleDescription.bundleId; + } + + public int getKeyHashCode() { + return (int) (bundleId ^ (bundleId >>> 32)); + } + + /* TODO Determine if we need more than just Object ID type of hashcode. + public int hashCode() { + if (getSymbolicName() == null) + return (int) (bundleId % Integer.MAX_VALUE); + return (int) ((bundleId * (getSymbolicName().hashCode())) % Integer.MAX_VALUE); + } + */ + + protected void removeDependencies() { + synchronized (this.monitor) { + if (dependencies == null) + return; + for (BundleDescription element : dependencies) { + ((BundleDescriptionImpl) element).removeDependent(this); + } + dependencies = null; + } + } + + protected void addDependencies(BaseDescription[] newDependencies, boolean checkDups) { + synchronized (this.monitor) { + if (newDependencies == null) + return; + if (!checkDups && dependencies == null) + dependencies = new ArrayList<>(newDependencies.length); + for (BaseDescription newDependency : newDependencies) { + addDependency((BaseDescriptionImpl) newDependency, checkDups); + } + } + } + + protected void addDependency(BaseDescriptionImpl dependency, boolean checkDups) { + synchronized (this.monitor) { + BundleDescriptionImpl bundle = (BundleDescriptionImpl) dependency.getSupplier(); + if (bundle == this) + return; + if (dependencies == null) + dependencies = new ArrayList<>(10); + if (!checkDups || !dependencies.contains(bundle)) { + bundle.addDependent(this); + dependencies.add(bundle); + } + } + } + + /* + * Gets all the bundle dependencies as a result of import-package or require-bundle. + * Self and fragment bundles are removed. + */ + List getBundleDependencies() { + synchronized (this.monitor) { + if (dependencies == null) + return new ArrayList<>(0); + ArrayList required = new ArrayList<>(dependencies.size()); + for (BundleDescription dep : dependencies) { + if (dep != this && dep.getHost() == null) + required.add(dep); + } + return required; + } + } + + protected void addDependent(BundleDescription dependent) { + synchronized (this.monitor) { + if (dependents == null) + dependents = new ArrayList<>(10); + // no need to check for duplicates here; this is only called in addDepenency which already checks for dups. + dependents.add(dependent); + } + } + + protected void removeDependent(BundleDescription dependent) { + synchronized (this.monitor) { + if (dependents == null) + return; + dependents.remove(dependent); + } + } + + public BundleDescription[] getDependents() { + synchronized (this.monitor) { + if (dependents == null) + return EMPTY_BUNDLEDESCS; + return dependents.toArray(new BundleDescription[dependents.size()]); + } + } + + boolean hasDependents() { + synchronized (this.monitor) { + return dependents == null ? false : dependents.size() > 0; + } + } + + void setFullyLoaded(boolean fullyLoaded) { + synchronized (this.monitor) { + if (fullyLoaded) { + stateBits |= FULLY_LOADED; + } else { + stateBits &= ~FULLY_LOADED; + } + } + } + + boolean isFullyLoaded() { + return (stateBits & FULLY_LOADED) != 0; + } + + void setLazyDataOffset(int lazyDataOffset) { + this.lazyDataOffset = lazyDataOffset; + } + + int getLazyDataOffset() { + return this.lazyDataOffset; + } + + void setLazyDataSize(int lazyDataSize) { + this.lazyDataSize = lazyDataSize; + } + + int getLazyDataSize() { + return this.lazyDataSize; + } + + // DO NOT call while holding this.monitor + private LazyData loadLazyData() { + // TODO add back if ee min 1.2 adds holdsLock method + // if (Thread.holdsLock(this.monitor)) { + // throw new IllegalStateException("Should not call fullyLoad() holding + // monitor."); //$NON-NLS-1$ + // } + if ((stateBits & LAZY_LOADED) == 0) + return this.lazyData; + + throw new IllegalStateException("No valid reader for the bundle description"); //$NON-NLS-1$ + + } + + void addDynamicResolvedImport(ExportPackageDescriptionImpl result) { + synchronized (this.monitor) { + // mark the dependency + addDependency(result, true); + // add the export to the list of the resolvedImports + checkLazyData(); + if (lazyData.resolvedImports == null) { + lazyData.resolvedImports = new ExportPackageDescription[] {result}; + return; + } + ExportPackageDescription[] newImports = new ExportPackageDescription[lazyData.resolvedImports.length + 1]; + System.arraycopy(lazyData.resolvedImports, 0, newImports, 0, lazyData.resolvedImports.length); + newImports[newImports.length - 1] = result; + lazyData.resolvedImports = newImports; + } + } + + void addDynamicImportPackages(ImportPackageSpecification[] dynamicImport) { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + if (currentData.addedDynamicImports == null) + currentData.addedDynamicImports = new ArrayList<>(); + for (ImportPackageSpecification addImport : dynamicImport) { + if (!ImportPackageSpecification.RESOLUTION_DYNAMIC + .equals(addImport.getDirective(Constants.RESOLUTION_DIRECTIVE))) + throw new IllegalArgumentException("Import must be a dynamic import."); //$NON-NLS-1$ + } + adding: for (ImportPackageSpecification addImport : dynamicImport) { + for (ImportPackageSpecification currentImport : currentData.addedDynamicImports) { + if (equalImports(addImport, currentImport)) + continue adding; + } + ((ImportPackageSpecificationImpl) addImport).setBundle(this); + currentData.addedDynamicImports.add(addImport); + } + } + } + + private boolean equalImports(ImportPackageSpecification addImport, ImportPackageSpecification currentImport) { + if (!isEqual(addImport.getName(), currentImport.getName())) + return false; + if (!isEqual(addImport.getVersionRange(), currentImport.getVersionRange())) + return false; + if (!isEqual(addImport.getBundleSymbolicName(), currentImport.getBundleSymbolicName())) + return false; + if (!isEqual(addImport.getBundleVersionRange(), currentImport.getBundleVersionRange())) + return false; + return isEqual(addImport.getAttributes(), currentImport.getAttributes()); + } + + private boolean isEqual(Object o1, Object o2) { + return (o1 == null) ? o2 == null : o1.equals(o2); + } + + void unload() { + throw new IllegalStateException("BundleDescription does not belong to a reader."); //$NON-NLS-1$ + } + + void setDynamicStamps(Map dynamicStamps) { + synchronized (this.monitor) { + checkLazyData(); + lazyData.dynamicStamps = dynamicStamps; + } + } + + void setDynamicStamp(String requestedPackage, Long timestamp) { + synchronized (this.monitor) { + checkLazyData(); + if (lazyData.dynamicStamps == null) { + if (timestamp == null) + return; + lazyData.dynamicStamps = new HashMap<>(); + } + if (timestamp == null) + lazyData.dynamicStamps.remove(requestedPackage); + else + lazyData.dynamicStamps.put(requestedPackage, timestamp); + } + } + + long getDynamicStamp(String requestedPackage) { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + Long stamp = currentData.dynamicStamps == null ? null + : (Long) currentData.dynamicStamps.get(requestedPackage); + return stamp == null ? 0 : stamp.longValue(); + } + } + + Map getDynamicStamps() { + LazyData currentData = loadLazyData(); + synchronized (this.monitor) { + return currentData.dynamicStamps; + } + } + + public void setEquinoxEE(int equinox_ee) { + this.equinox_ee = equinox_ee; + } + + public int getEquinoxEE() { + return equinox_ee; + } + + private void checkLazyData() { + if (lazyData == null) + lazyData = new LazyData(); + } + + final class LazyData { + String location; + String platformFilter; + + BundleSpecification[] requiredBundles; + ExportPackageDescription[] exportPackages; + ImportPackageSpecification[] importPackages; + GenericDescription[] genericCapabilities; + GenericSpecification[] genericRequires; + NativeCodeSpecification nativeCode; + + ExportPackageDescription[] selectedExports; + GenericDescription[] selectedCapabilities; + BundleDescription[] resolvedRequires; + ExportPackageDescription[] resolvedImports; + GenericDescription[] resolvedCapabilities; + ExportPackageDescription[] substitutedExports; + String[] executionEnvironments; + + Map dynamicStamps; + Map> stateWires; + // Note that this is not persisted in the state cache + List addedDynamicImports; + } + + public Map getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + @SuppressWarnings("unchecked") + void setAttributes(Map attributes) { + synchronized (this.monitor) { + this.attributes = (Map) attributes; + } + } + + Object getDirective(String key) { + synchronized (this.monitor) { + if (Constants.MANDATORY_DIRECTIVE.equals(key)) + return mandatory; + if (Constants.SINGLETON_DIRECTIVE.equals(key)) + return isSingleton() ? Boolean.TRUE : Boolean.FALSE; + if (Constants.FRAGMENT_ATTACHMENT_DIRECTIVE.equals(key)) { + if (!attachFragments()) + return Constants.FRAGMENT_ATTACHMENT_NEVER; + if (dynamicFragments()) + return Constants.FRAGMENT_ATTACHMENT_ALWAYS; + return Constants.FRAGMENT_ATTACHMENT_RESOLVETIME; + } + } + return null; + } + + void setDirective(String key, Object value) { + // only pay attention to mandatory directive for now; others are set with setState method + if (Constants.MANDATORY_DIRECTIVE.equals(key)) + mandatory = (String[]) value; + } + + @SuppressWarnings("unchecked") + void setArbitraryDirectives(Map directives) { + synchronized (this.monitor) { + this.arbitraryDirectives = (Map) directives; + } + } + + Map getArbitraryDirectives() { + synchronized (this.monitor) { + return arbitraryDirectives; + } + } + + public Map getDeclaredDirectives() { + Map result = new HashMap<>(2); + Map arbitrary = getArbitraryDirectives(); + if (arbitrary != null) + result.putAll(arbitrary); + if (!attachFragments()) { + result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_NEVER); + } else { + if (dynamicFragments()) + result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_ALWAYS); + else + result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_RESOLVETIME); + } + if (isSingleton()) + result.put(Constants.SINGLETON_DIRECTIVE, Boolean.TRUE.toString()); + String[] mandatoryDirective = (String[]) getDirective(Constants.MANDATORY_DIRECTIVE); + if (mandatoryDirective != null) + result.put(Constants.MANDATORY_DIRECTIVE, ExportPackageDescriptionImpl.toString(mandatoryDirective)); + return Collections.unmodifiableMap(result); + } + + public Map getDeclaredAttributes() { + Map result = new HashMap<>(1); + synchronized (this.monitor) { + if (attributes != null) + result.putAll(attributes); + } + result.put(BundleRevision.BUNDLE_NAMESPACE, getName()); + result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, getVersion()); + return Collections.unmodifiableMap(result); + } + + public List getDeclaredRequirements(String namespace) { + List result = new ArrayList<>(); + if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) { + BundleSpecification[] requires = getRequiredBundles(); + for (BundleSpecification require : requires) { + result.add(require.getRequirement()); + } + } + if (host != null && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) { + result.add(host.getRequirement()); + } + if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) { + ImportPackageSpecification[] imports = getImportPackages(); + for (ImportPackageSpecification importPkg : imports) + result.add(importPkg.getRequirement()); + } + GenericSpecification[] genericSpecifications = getGenericRequires(); + for (GenericSpecification requirement : genericSpecifications) { + if (namespace == null || namespace.equals(requirement.getType())) + result.add(requirement.getRequirement()); + } + return Collections.unmodifiableList(result); + } + + public List getDeclaredCapabilities(String namespace) { + List result = new ArrayList<>(); + if (host == null) { + if (getSymbolicName() != null) { + if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) { + result.add(BundleDescriptionImpl.this.getCapability()); + } + if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) { + result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE)); + } + } + + } else { + // may need to have a osgi.wiring.fragment capability + } + if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) { + ExportPackageDescription[] exports = getExportPackages(); + for (ExportPackageDescription exportPkg : exports) + result.add(exportPkg.getCapability()); + } + GenericDescription[] genericCapabilities = getGenericCapabilities(); + for (GenericDescription capabilitiy : genericCapabilities) { + if (namespace == null || namespace.equals(capabilitiy.getType())) + result.add(capabilitiy.getCapability()); + } + return Collections.unmodifiableList(result); + } + + public int getTypes() { + return getHost() != null ? BundleRevision.TYPE_FRAGMENT : 0; + } + + public Bundle getBundle() { + Object ref = getUserObject(); + if (ref instanceof BundleReference) + return ((BundleReference) ref).getBundle(); + return null; + } + + @Override + String getInternalNameSpace() { + return BundleRevision.BUNDLE_NAMESPACE; + } + + public BundleWiring getWiring() { + synchronized (this.monitor) { + if (bundleWiring != null || !isResolved()) + return bundleWiring; + return bundleWiring = new DescriptionWiring(); + } + } + + static class BundleWireImpl implements BundleWire { + private final BundleCapability capability; + private final BundleWiring provider; + private final BundleRequirement requirement; + private final BundleWiring requirer; + + public BundleWireImpl(StateWire wire) { + VersionConstraint declaredRequirement = wire.getDeclaredRequirement(); + if (declaredRequirement instanceof HostSpecification) + this.capability = ((BaseDescriptionImpl) wire.getDeclaredCapability()).getCapability(BundleRevision.HOST_NAMESPACE); + else + this.capability = wire.getDeclaredCapability().getCapability(); + this.provider = wire.getCapabilityHost().getWiring(); + this.requirement = declaredRequirement.getRequirement(); + this.requirer = wire.getRequirementHost().getWiring(); + } + + public BundleCapability getCapability() { + return capability; + } + + public BundleRequirement getRequirement() { + return requirement; + } + + public BundleWiring getProviderWiring() { + return provider; + } + + public BundleWiring getRequirerWiring() { + return requirer; + } + + @Override + public int hashCode() { + int hashcode = 31 + capability.hashCode(); + hashcode = hashcode * 31 + requirement.hashCode(); + hashcode = hashcode * 31 + provider.hashCode(); + hashcode = hashcode * 31 + requirer.hashCode(); + return hashcode; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BundleWireImpl)) + return false; + BundleWireImpl other = (BundleWireImpl) obj; + return capability.equals(other.getCapability()) && requirement.equals(other.getRequirement()) && provider.equals(other.getProviderWiring()) && requirer.equals(other.getRequirerWiring()); + } + + @Override + public String toString() { + return getRequirement() + " -> " + getCapability(); //$NON-NLS-1$ + } + + public BundleRevision getProvider() { + return provider.getRevision(); + } + + public BundleRevision getRequirer() { + return requirer.getRevision(); + } + } + + /** + * Coerce the generic type of a list from List + * to List + * @param l List to be coerced. + * @return l coerced to List + */ + @SuppressWarnings("unchecked") + static List asListWire(List l) { + return (List) l; + } + + /** + * Coerce the generic type of a list from List + * to List + * @param l List to be coerced. + * @return l coerced to List + */ + @SuppressWarnings("unchecked") + static List asListCapability(List l) { + return (List) l; + } + + /** + * Coerce the generic type of a list from List + * to List + * @param l List to be coerced. + * @return l coerced to List + */ + @SuppressWarnings("unchecked") + static List asListRequirement(List l) { + return (List) l; + } + + // Note that description wiring are identity equality based + class DescriptionWiring implements BundleWiring { + private volatile boolean valid = true; + + public Bundle getBundle() { + return BundleDescriptionImpl.this.getBundle(); + } + + public boolean isInUse() { + return valid && (isCurrent() || BundleDescriptionImpl.this.hasDependents()); + } + + void invalidate() { + valid = false; + } + + public boolean isCurrent() { + return valid && !BundleDescriptionImpl.this.isRemovalPending(); + } + + public List getCapabilities(String namespace) { + if (!isInUse()) + return null; + List result = new ArrayList<>(); + GenericDescription[] genericCapabilities = getSelectedGenericCapabilities(); + for (GenericDescription capabilitiy : genericCapabilities) { + if (namespace == null || namespace.equals(capabilitiy.getType())) + result.add(capabilitiy.getCapability()); + } + if (host != null) + return result; + if (getSymbolicName() != null) { + if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) { + result.add(BundleDescriptionImpl.this.getCapability()); + } + if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) { + result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE)); + } + } + if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) { + ExportPackageDescription[] exports = getSelectedExports(); + for (ExportPackageDescription exportPkg : exports) + result.add(exportPkg.getCapability()); + } + return result; + } + + public List getResourceCapabilities(String namespace) { + return asListCapability(getCapabilities(namespace)); + } + + public List getRequirements(String namespace) { + List requiredWires = getRequiredWires(namespace); + if (requiredWires == null) + // happens if not in use + return null; + List requirements = new ArrayList<>(requiredWires.size()); + for (BundleWire wire : requiredWires) { + if (!requirements.contains(wire.getRequirement())) + requirements.add(wire.getRequirement()); + } + // get dynamic imports + if (getHost() == null && (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace))) { + // TODO need to handle fragments that add dynamic imports + if (hasDynamicImports()) { + ImportPackageSpecification[] imports = getImportPackages(); + for (ImportPackageSpecification impPackage : imports) { + if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(impPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) { + BundleRequirement req = impPackage.getRequirement(); + if (!requirements.contains(req)) + requirements.add(req); + } + } + } + ImportPackageSpecification[] addedDynamic = getAddedDynamicImportPackages(); + for (ImportPackageSpecification dynamicImport : addedDynamic) { + BundleRequirement req = dynamicImport.getRequirement(); + if (!requirements.contains(req)) + requirements.add(req); + } + } + return requirements; + } + + public List getResourceRequirements(String namespace) { + return asListRequirement(getRequirements(namespace)); + } + + public List getProvidedWires(String namespace) { + if (!isInUse()) + return null; + BundleDescription[] dependentBundles = getDependents(); + List unorderedResult = new ArrayList<>(); + for (BundleDescription dependent : dependentBundles) { + List dependentWires = dependent.getWiring().getRequiredWires(namespace); + if (dependentWires != null) + for (BundleWire bundleWire : dependentWires) { + if (bundleWire.getProviderWiring() == this) + unorderedResult.add(bundleWire); + } + } + List orderedResult = new ArrayList<>(unorderedResult.size()); + List capabilities = getCapabilities(namespace); + for (BundleCapability capability : capabilities) { + for (Iterator wires = unorderedResult.iterator(); wires.hasNext();) { + BundleWire wire = wires.next(); + if (wire.getCapability().equals(capability)) { + wires.remove(); + orderedResult.add(wire); + } + } + } + return orderedResult; + } + + public List getProvidedResourceWires(String namespace) { + return asListWire(getProvidedWires(namespace)); + } + + public List getRequiredWires(String namespace) { + if (!isInUse()) + return null; + List result = Collections.emptyList(); + Map> wireMap = getWires(); + if (namespace == null) { + result = new ArrayList<>(); + for (List wires : wireMap.values()) { + for (StateWire wire : wires) { + result.add(new BundleWireImpl(wire)); + } + } + return result; + } + List wires = wireMap.get(namespace); + if (wires == null) + return result; + result = new ArrayList<>(wires.size()); + for (StateWire wire : wires) { + result.add(new BundleWireImpl(wire)); + } + return result; + } + + public List getRequiredResourceWires(String namespace) { + return asListWire(getRequiredWires(namespace)); + } + + public BundleRevision getRevision() { + return BundleDescriptionImpl.this; + } + + public BundleRevision getResource() { + return getRevision(); + } + + public ClassLoader getClassLoader() { + throw new UnsupportedOperationException(); + } + + public List findEntries(String path, String filePattern, int options) { + return null; + } + + public Collection listResources(String path, String filePattern, int options) { + return null; + } + + @Override + public String toString() { + return BundleDescriptionImpl.this.toString(); + } + } + + public List getCapabilities(String namespace) { + return asListCapability(getDeclaredCapabilities(namespace)); + } + + public List getRequirements(String namespace) { + return asListRequirement(getDeclaredRequirements(namespace)); + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BundleSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BundleSpecificationImpl.java new file mode 100644 index 0000000000..13361b0002 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/BundleSpecificationImpl.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.osgi.service.resolver.BaseDescription; +import org.eclipse.osgi.service.resolver.BundleSpecification; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.osgi.framework.Constants; +import org.osgi.framework.wiring.BundleRevision; + +public class BundleSpecificationImpl extends VersionConstraintImpl implements BundleSpecification { + private boolean exported; + private boolean optional; + private Map attributes; + private Map arbitraryDirectives; + + protected void setExported(boolean exported) { + synchronized (this.monitor) { + this.exported = exported; + } + } + + protected void setOptional(boolean optional) { + synchronized (this.monitor) { + this.optional = optional; + } + } + + public boolean isExported() { + synchronized (this.monitor) { + return exported; + } + } + + public boolean isOptional() { + synchronized (this.monitor) { + return optional; + } + } + + Map getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + @SuppressWarnings("unchecked") + void setAttributes(Map attributes) { + synchronized (this.monitor) { + this.attributes = (Map) attributes; + } + } + + Map getArbitraryDirectives() { + synchronized (this.monitor) { + return arbitraryDirectives; + } + } + + @SuppressWarnings("unchecked") + void setArbitraryDirectives(Map directives) { + synchronized (this.monitor) { + this.arbitraryDirectives = (Map) directives; + } + } + + @Override + public boolean isSatisfiedBy(BaseDescription supplier) { + if (!(supplier instanceof BundleDescriptionImpl)) + return false; + BundleDescriptionImpl candidate = (BundleDescriptionImpl) supplier; + if (candidate.getHost() != null) + return false; + Map requiredAttrs = getAttributes(); + if (requiredAttrs != null) { + Map prividerAttrs = candidate.getAttributes(); + if (prividerAttrs == null) + return false; + for (String key : requiredAttrs.keySet()) { + Object requiredValue = requiredAttrs.get(key); + Object prividedValue = prividerAttrs.get(key); + if (prividedValue == null || !requiredValue.equals(prividedValue)) + return false; + } + } + String[] mandatory = (String[]) candidate.getDirective(Constants.MANDATORY_DIRECTIVE); + if (!hasMandatoryAttributes(mandatory)) + return false; + if (getName() != null && getName().equals(candidate.getSymbolicName()) && (getVersionRange() == null || getVersionRange().isIncluded(candidate.getVersion()))) + return true; + return false; + } + + @Override + protected boolean hasMandatoryAttributes(String[] mandatory) { + if (mandatory != null) { + Map requiredAttrs = getAttributes(); + for (String key : mandatory) { + if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key)) + continue; // has a default value of 0.0.0 + if (requiredAttrs == null || requiredAttrs.get(key) == null) + return false; + } + } + return true; + } + + @Override + public String toString() { + return "Require-Bundle: " + getName() + "; bundle-version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + protected Map getInternalDirectives() { + Map result = new HashMap<>(2); + synchronized (this.monitor) { + if (arbitraryDirectives != null) + result.putAll(arbitraryDirectives); + if (exported) + result.put(Constants.VISIBILITY_DIRECTIVE, Constants.VISIBILITY_REEXPORT); + if (optional) + result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL); + result.put(Constants.FILTER_DIRECTIVE, createFilterDirective()); + return result; + } + } + + private String createFilterDirective() { + StringBuilder filter = new StringBuilder(); + filter.append("(&"); //$NON-NLS-1$ + synchronized (this.monitor) { + addFilterAttribute(filter, BundleRevision.BUNDLE_NAMESPACE, getName()); + VersionRange range = getVersionRange(); + if (range != null && range != VersionRange.emptyRange) + addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, range); + if (attributes != null) + addFilterAttributes(filter, attributes); + } + filter.append(')'); + return filter.toString(); + } + + @Override + protected Map getInteralAttributes() { + return Collections.emptyMap(); + } + + @Override + protected String getInternalNameSpace() { + return BundleRevision.BUNDLE_NAMESPACE; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/EquinoxContainer.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/EquinoxContainer.java new file mode 100644 index 0000000000..971b12d87f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/EquinoxContainer.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2012, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +public class EquinoxContainer { + public static final String NAME = "org.eclipse.osgi"; //$NON-NLS-1$ +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/ExportPackageDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/ExportPackageDescriptionImpl.java new file mode 100644 index 0000000000..d8e4c826f8 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/ExportPackageDescriptionImpl.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ + +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.*; +import org.eclipse.osgi.service.resolver.*; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; +import org.osgi.framework.wiring.BundleRevision; + +class ExportPackageDescriptionImpl extends BaseDescriptionImpl implements ExportPackageDescription { + public static final String EQUINOX_EE = "x-equinox-ee"; //$NON-NLS-1$ + private static final Integer EQUINOX_EE_DEFAULT = Integer.valueOf(-1); + private String[] uses; + private Map attributes; + private Map arbitraryDirectives; + private volatile BundleDescription exporter; + private String exclude; + private String include; + private String[] friends; + private String[] mandatory; + private Boolean internal = Boolean.FALSE; + private int equinox_ee = -1; + private ExportPackageDescription fragmentDeclaration = null; + + public ExportPackageDescriptionImpl() { + super(); + } + + public ExportPackageDescriptionImpl(BundleDescription host, ExportPackageDescription fragmentDeclaration) { + setName(fragmentDeclaration.getName()); + setVersion(fragmentDeclaration.getVersion()); + setDirectives(fragmentDeclaration.getDirectives()); + setArbitraryDirectives(((ExportPackageDescriptionImpl) fragmentDeclaration).getArbitraryDirectives()); + setAttributes(fragmentDeclaration.getAttributes()); + setExporter(host); + this.fragmentDeclaration = fragmentDeclaration; + } + + public Map getDirectives() { + synchronized (this.monitor) { + Map result = new HashMap<>(7); + if (uses != null) + result.put(Constants.USES_DIRECTIVE, uses); + if (exclude != null) + result.put(Constants.EXCLUDE_DIRECTIVE, exclude); + if (include != null) + result.put(Constants.INCLUDE_DIRECTIVE, include); + if (mandatory != null) + result.put(Constants.MANDATORY_DIRECTIVE, mandatory); + if (friends != null) + result.put(StateImpl.FRIENDS_DIRECTIVE, friends); + result.put(StateImpl.INTERNAL_DIRECTIVE, internal); + result.put(EQUINOX_EE, equinox_ee == -1 ? EQUINOX_EE_DEFAULT : Integer.valueOf(equinox_ee)); + return result; + } + } + + public Map getDeclaredDirectives() { + Map result = new HashMap<>(6); + synchronized (this.monitor) { + Map arbitrary = getArbitraryDirectives(); + if (arbitrary != null) + result.putAll(arbitrary); + if (uses != null) + result.put(Constants.USES_DIRECTIVE, toString(uses)); + if (exclude != null) + result.put(Constants.EXCLUDE_DIRECTIVE, exclude); + if (include != null) + result.put(Constants.INCLUDE_DIRECTIVE, include); + if (mandatory != null) + result.put(Constants.MANDATORY_DIRECTIVE, toString(mandatory)); + if (friends != null) + result.put(StateImpl.FRIENDS_DIRECTIVE, toString(friends)); + if (internal != null) + result.put(StateImpl.INTERNAL_DIRECTIVE, internal.toString()); + return Collections.unmodifiableMap(result); + } + } + + public Map getDeclaredAttributes() { + Map result = new HashMap<>(2); + synchronized (this.monitor) { + if (attributes != null) + result.putAll(attributes); + result.put(BundleRevision.PACKAGE_NAMESPACE, getName()); + result.put(Constants.VERSION_ATTRIBUTE, getVersion()); + Version bundleVersion = getSupplier().getVersion(); + if (bundleVersion != null) + result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion); + String symbolicName = getSupplier().getSymbolicName(); + if (symbolicName != null) { + if (symbolicName.equals(EquinoxContainer.NAME)) + result.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Arrays.asList(Constants.SYSTEM_BUNDLE_SYMBOLICNAME, symbolicName)); + else + result.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName); + } + return Collections.unmodifiableMap(result); + } + } + + static String toString(String[] list) { + StringBuilder buffer = new StringBuilder(); + for (String string : list) + buffer.append(string).append(','); + if (buffer.length() > 0) + buffer.setLength(buffer.length() - 1); + return buffer.toString(); + } + + public Object getDirective(String key) { + synchronized (this.monitor) { + if (key.equals(Constants.USES_DIRECTIVE)) + return uses; + if (key.equals(Constants.EXCLUDE_DIRECTIVE)) + return exclude; + if (key.equals(Constants.INCLUDE_DIRECTIVE)) + return include; + if (key.equals(Constants.MANDATORY_DIRECTIVE)) + return mandatory; + if (key.equals(StateImpl.FRIENDS_DIRECTIVE)) + return friends; + if (key.equals(StateImpl.INTERNAL_DIRECTIVE)) + return internal; + if (key.equals(EQUINOX_EE)) + return equinox_ee == -1 ? EQUINOX_EE_DEFAULT : Integer.valueOf(equinox_ee); + return null; + } + } + + public Object setDirective(String key, Object value) { + synchronized (this.monitor) { + if (key.equals(Constants.USES_DIRECTIVE)) + return uses = (String[]) value; + if (key.equals(Constants.EXCLUDE_DIRECTIVE)) + return exclude = (String) value; + if (key.equals(Constants.INCLUDE_DIRECTIVE)) + return include = (String) value; + if (key.equals(Constants.MANDATORY_DIRECTIVE)) + return mandatory = (String[]) value; + if (key.equals(StateImpl.FRIENDS_DIRECTIVE)) + return friends = (String[]) value; + if (key.equals(StateImpl.INTERNAL_DIRECTIVE)) + return internal = (Boolean) value; + if (key.equals(EQUINOX_EE)) { + equinox_ee = ((Integer) value).intValue(); + return value; + } + return null; + } + } + + public void setDirectives(Map directives) { + synchronized (this.monitor) { + if (directives == null) + return; + uses = (String[]) directives.get(Constants.USES_DIRECTIVE); + exclude = (String) directives.get(Constants.EXCLUDE_DIRECTIVE); + include = (String) directives.get(Constants.INCLUDE_DIRECTIVE); + mandatory = (String[]) directives.get(Constants.MANDATORY_DIRECTIVE); + friends = (String[]) directives.get(StateImpl.FRIENDS_DIRECTIVE); + internal = (Boolean) directives.get(StateImpl.INTERNAL_DIRECTIVE); + equinox_ee = ((Integer) directives.get(EQUINOX_EE)).intValue(); + } + } + + @SuppressWarnings("unchecked") + void setArbitraryDirectives(Map directives) { + synchronized (this.monitor) { + this.arbitraryDirectives = (Map) directives; + } + } + + Map getArbitraryDirectives() { + synchronized (this.monitor) { + return arbitraryDirectives; + } + } + + public Map getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + public BundleDescription getSupplier() { + return getExporter(); + } + + public BundleDescription getExporter() { + return exporter; + } + + /** + * @deprecated + */ + @Deprecated + public boolean isRoot() { + return true; + } + + @SuppressWarnings("unchecked") + protected void setAttributes(Map attributes) { + synchronized (this.monitor) { + this.attributes = (Map) attributes; + } + } + + protected void setExporter(BundleDescription exporter) { + this.exporter = exporter; + } + + @Override + public BaseDescription getFragmentDeclaration() { + return fragmentDeclaration; + } + + void setFragmentDeclaration(ExportPackageDescription fragmentDeclaration) { + this.fragmentDeclaration = fragmentDeclaration; + } + + @Override + public String toString() { + return "Export-Package: " + getName() + "; version=\"" + getVersion() + "\""; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + String getInternalNameSpace() { + return BundleRevision.PACKAGE_NAMESPACE; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/GenericDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/GenericDescriptionImpl.java new file mode 100644 index 0000000000..1f48cd30c2 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/GenericDescriptionImpl.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2006, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import org.eclipse.osgi.service.resolver.BaseDescription; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.GenericDescription; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +public class GenericDescriptionImpl extends BaseDescriptionImpl implements GenericDescription { + private Dictionary attributes; + private volatile BundleDescription supplier; + private volatile String type = GenericDescription.DEFAULT_TYPE; + private Map directives; + private GenericDescription fragmentDeclaration; + + public GenericDescriptionImpl() { + super(); + } + + public GenericDescriptionImpl(BundleDescription host, GenericDescription fragmentDeclaration) { + setType(fragmentDeclaration.getType()); + Dictionary origAttrs = fragmentDeclaration.getAttributes(); + if (origAttrs != null) { + Hashtable copyAttrs = new Hashtable<>(); + for (Enumeration keys = origAttrs.keys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + copyAttrs.put(key, origAttrs.get(key)); + } + setAttributes(copyAttrs); + } + Map origDirectives = fragmentDeclaration.getDeclaredDirectives(); + Map copyDirectives = new HashMap<>(origDirectives); + setDirectives(copyDirectives); + setSupplier(host); + this.fragmentDeclaration = fragmentDeclaration; + } + + public Dictionary getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + public BundleDescription getSupplier() { + return supplier; + } + + void setAttributes(Dictionary attributes) { + synchronized (this.monitor) { + this.attributes = attributes; + } + } + + void setDirectives(Map directives) { + synchronized (this.monitor) { + this.directives = directives; + } + } + + void setSupplier(BundleDescription supplier) { + this.supplier = supplier; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Constants.PROVIDE_CAPABILITY).append(": ").append(getType()); //$NON-NLS-1$ + Map attrs = getDeclaredAttributes(); + sb.append(toString(attrs, false)); + return sb.toString(); + } + + /** + * @deprecated + */ + @Override + public String getName() { + synchronized (this.monitor) { + Object name = attributes != null ? attributes.get(getType()) : null; + return name instanceof String ? (String) name : null; + } + } + + public String getType() { + return type; + } + + void setType(String type) { + if (type == null || type.equals(GenericDescription.DEFAULT_TYPE)) + this.type = GenericDescription.DEFAULT_TYPE; + else + this.type = type; + } + + /** + * @deprecated + */ + @Override + public Version getVersion() { + Object version = attributes != null ? attributes.get(Constants.VERSION_ATTRIBUTE) : null; + return version instanceof Version ? (Version) version : super.getVersion(); + } + + public Map getDeclaredDirectives() { + synchronized (this.monitor) { + if (directives == null) + return Collections.emptyMap(); + return Collections.unmodifiableMap(directives); + } + } + + public Map getDeclaredAttributes() { + synchronized (this.monitor) { + Map result = new HashMap<>(5); + if (attributes != null) + for (Enumeration keys = attributes.keys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + Object value = attributes.get(key); + if (value instanceof List) + value = Collections.unmodifiableList((List) value); + result.put(key, value); + } + return Collections.unmodifiableMap(result); + } + } + + @Override + String getInternalNameSpace() { + return getType(); + } + + @Override + public BaseDescription getFragmentDeclaration() { + return fragmentDeclaration; + } + + void setFragmentDeclaration(GenericDescription fragmentDeclaration) { + this.fragmentDeclaration = fragmentDeclaration; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/GenericSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/GenericSpecificationImpl.java new file mode 100644 index 0000000000..674f9b5789 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/GenericSpecificationImpl.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2006, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.osgi.internal.framework.FilterImpl; +import org.eclipse.osgi.service.resolver.BaseDescription; +import org.eclipse.osgi.service.resolver.GenericDescription; +import org.eclipse.osgi.service.resolver.GenericSpecification; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.resource.Namespace; + +public class GenericSpecificationImpl extends VersionConstraintImpl implements GenericSpecification { + private Filter matchingFilter; + private String type = GenericDescription.DEFAULT_TYPE; + private int resolution = 0; + private GenericDescription[] suppliers; + private Map attributes; + private Map arbitraryDirectives; + /* + * Indicates that a generic constraint was from converting the BREE header + */ + public static final int RESOLUTION_FROM_BREE = 0x04; + + public String getMatchingFilter() { + synchronized (this.monitor) { + return matchingFilter == null ? null : matchingFilter.toString(); + } + } + + void setMatchingFilter(String matchingFilter, boolean matchName) throws InvalidSyntaxException { + synchronized (this.monitor) { + String name = getName(); + if (matchName && name != null && !"*".equals(name)) { //$NON-NLS-1$ + String nameFilter = "(" + getType() + "=" + getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + matchingFilter = matchingFilter == null ? nameFilter : "(&" + nameFilter + matchingFilter + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + this.matchingFilter = matchingFilter == null ? null : FilterImpl.newInstance(matchingFilter); + } + } + + void setMatchingFilter(Filter matchingFilter) { + synchronized (this.monitor) { + this.matchingFilter = matchingFilter; + } + } + + @Override + public boolean isSatisfiedBy(BaseDescription supplier) { + if (!(supplier instanceof GenericDescription)) + return false; + GenericDescription candidate = (GenericDescription) supplier; + if (!getType().equals(candidate.getType())) + return false; + // Note that names and versions are only matched by including them in the filter + return matchingFilter == null || matchingFilter.match(candidate.getAttributes()); + } + + @Override + protected boolean hasMandatoryAttributes(String[] mandatory) { + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Constants.REQUIRE_CAPABILITY).append(": ").append(getType()); //$NON-NLS-1$ + if (matchingFilter != null) + sb.append("; filter=\"").append(getMatchingFilter()).append('"'); //$NON-NLS-1$ + return sb.toString(); + } + + public String getType() { + synchronized (this.monitor) { + return type; + } + } + + void setType(String type) { + synchronized (this.monitor) { + if (type == null || type.equals(GenericDescription.DEFAULT_TYPE)) + this.type = GenericDescription.DEFAULT_TYPE; + else + this.type = type; + } + } + + public int getResolution() { + synchronized (this.monitor) { + return resolution; + } + } + + @Override + public boolean isResolved() { + synchronized (this.monitor) { + return suppliers != null && suppliers.length > 0; + } + } + + void setResolution(int resolution) { + synchronized (this.monitor) { + this.resolution = resolution; + } + } + + @Override + public BaseDescription getSupplier() { + synchronized (this.monitor) { + return suppliers == null || suppliers.length == 0 ? null : suppliers[0]; + } + } + + @Override + protected void setSupplier(BaseDescription supplier) { + synchronized (this.monitor) { + if (supplier == null) { + suppliers = null; + return; + } + int len = suppliers == null ? 0 : suppliers.length; + GenericDescription[] temp = new GenericDescription[len + 1]; + if (suppliers != null) + System.arraycopy(suppliers, 0, temp, 0, len); + temp[len] = (GenericDescription) supplier; + suppliers = temp; + } + } + + public GenericDescription[] getSuppliers() { + synchronized (this.monitor) { + return suppliers; + } + } + + void setSupplers(GenericDescription[] suppliers) { + synchronized (this.monitor) { + this.suppliers = suppliers; + } + } + + Map getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + @SuppressWarnings("unchecked") + void setAttributes(Map attributes) { + synchronized (this.monitor) { + this.attributes = (Map) attributes; + } + } + + Map getArbitraryDirectives() { + synchronized (this.monitor) { + return arbitraryDirectives; + } + } + + @SuppressWarnings("unchecked") + void setArbitraryDirectives(Map directives) { + synchronized (this.monitor) { + this.arbitraryDirectives = (Map) directives; + } + } + + @Override + protected Map getInternalDirectives() { + Map result = new HashMap<>(2); + synchronized (this.monitor) { + if (arbitraryDirectives != null) + result.putAll(arbitraryDirectives); + if ((resolution & GenericSpecification.RESOLUTION_OPTIONAL) != 0) + result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL); + if ((resolution & GenericSpecification.RESOLUTION_MULTIPLE) != 0) + result.put(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE, Namespace.CARDINALITY_MULTIPLE); + if (matchingFilter != null) { + result.put(Constants.FILTER_DIRECTIVE, matchingFilter.toString()); + } + } + return result; + } + + @Override + protected Map getInteralAttributes() { + synchronized (this.monitor) { + return attributes == null ? Collections.emptyMap() : new HashMap<>(attributes); + } + } + + @Override + protected String getInternalNameSpace() { + return getType(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/HostSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/HostSpecificationImpl.java new file mode 100644 index 0000000000..482daae9c9 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/HostSpecificationImpl.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ + +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.osgi.service.resolver.BaseDescription; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.HostSpecification; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.osgi.framework.Constants; +import org.osgi.framework.wiring.BundleRevision; + +public class HostSpecificationImpl extends VersionConstraintImpl implements HostSpecification { + + private BundleDescription[] hosts; + private boolean multihost = false; + private Map attributes; + private Map arbitraryDirectives; + + Map getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + @SuppressWarnings("unchecked") + void setAttributes(Map attributes) { + synchronized (this.monitor) { + this.attributes = (Map) attributes; + } + } + + Map getArbitraryDirectives() { + synchronized (this.monitor) { + return arbitraryDirectives; + } + } + + @SuppressWarnings("unchecked") + void setArbitraryDirectives(Map directives) { + synchronized (this.monitor) { + this.arbitraryDirectives = (Map) directives; + } + } + + @Override + public boolean isSatisfiedBy(BaseDescription supplier) { + if (!(supplier instanceof BundleDescriptionImpl)) + return false; + BundleDescriptionImpl candidate = (BundleDescriptionImpl) supplier; + if (candidate.getHost() != null) + return false; + Map requiredAttrs = getAttributes(); + if (requiredAttrs != null) { + Map prividerAttrs = candidate.getAttributes(); + if (prividerAttrs == null) + return false; + for (String key : requiredAttrs.keySet()) { + Object requiredValue = requiredAttrs.get(key); + Object prividedValue = prividerAttrs.get(key); + if (prividedValue == null || !requiredValue.equals(prividedValue)) + return false; + } + } + String[] mandatory = (String[]) candidate.getDirective(Constants.MANDATORY_DIRECTIVE); + if (!hasMandatoryAttributes(mandatory)) + return false; + if (getName() != null && getName().equals(candidate.getSymbolicName()) && (getVersionRange() == null || getVersionRange().isIncluded(candidate.getVersion()))) + return true; + return false; + } + + public BundleDescription[] getHosts() { + synchronized (this.monitor) { + return hosts == null ? BundleDescriptionImpl.EMPTY_BUNDLEDESCS : hosts; + } + } + + @Override + protected boolean hasMandatoryAttributes(String[] mandatory) { + if (mandatory != null) { + Map requiredAttrs = getAttributes(); + for (String key : mandatory) { + if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key)) + continue; // has a default value of 0.0.0 + if (requiredAttrs == null || requiredAttrs.get(key) == null) + return false; + } + } + return true; + } + + @Override + public boolean isResolved() { + synchronized (this.monitor) { + return hosts != null && hosts.length > 0; + } + } + + /* + * The resolve algorithm will call this method to set the hosts. + */ + void setHosts(BundleDescription[] hosts) { + synchronized (this.monitor) { + this.hosts = hosts; + } + } + + @Override + public String toString() { + return "Fragment-Host: " + getName() + "; bundle-version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + public BaseDescription getSupplier() { + synchronized (this.monitor) { + if (hosts == null || hosts.length == 0) + return null; + return hosts[0]; + } + } + + public boolean isMultiHost() { + synchronized (this.monitor) { + return multihost; + } + } + + void setIsMultiHost(boolean multihost) { + synchronized (this.monitor) { + this.multihost = multihost; + } + } + + @Override + protected Map getInternalDirectives() { + Map result = new HashMap<>(2); + synchronized (this.monitor) { + if (arbitraryDirectives != null) + result.putAll(arbitraryDirectives); + result.put(Constants.FILTER_DIRECTIVE, createFilterDirective()); + return result; + } + } + + private String createFilterDirective() { + StringBuilder filter = new StringBuilder(); + filter.append("(&"); //$NON-NLS-1$ + synchronized (this.monitor) { + addFilterAttribute(filter, BundleRevision.HOST_NAMESPACE, getName()); + VersionRange range = getVersionRange(); + if (range != null && range != VersionRange.emptyRange) + addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, range); + if (attributes != null) + addFilterAttributes(filter, attributes); + } + filter.append(')'); + return filter.toString(); + } + + @Override + protected Map getInteralAttributes() { + return Collections.emptyMap(); + } + + @Override + protected String getInternalNameSpace() { + return BundleRevision.HOST_NAMESPACE; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/ImportPackageSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/ImportPackageSpecificationImpl.java new file mode 100644 index 0000000000..ecdd265070 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/ImportPackageSpecificationImpl.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Danail Nachev - ProSyst - bug 218625 + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.*; +import org.eclipse.osgi.service.resolver.*; +import org.osgi.framework.Constants; +import org.osgi.framework.wiring.BundleRevision; + +public class ImportPackageSpecificationImpl extends VersionConstraintImpl implements ImportPackageSpecification { + private String resolution = ImportPackageSpecification.RESOLUTION_STATIC; // the default is static + private String symbolicName; + private VersionRange bundleVersionRange; + private Map attributes; + private Map arbitraryDirectives; + + public Map getDirectives() { + synchronized (this.monitor) { + Map result = new HashMap<>(5); + if (resolution != null) + result.put(Constants.RESOLUTION_DIRECTIVE, resolution); + return result; + } + } + + public Object getDirective(String key) { + synchronized (this.monitor) { + if (key.equals(Constants.RESOLUTION_DIRECTIVE)) + return resolution; + return null; + } + } + + Object setDirective(String key, Object value) { + synchronized (this.monitor) { + if (key.equals(Constants.RESOLUTION_DIRECTIVE)) + return resolution = (String) value; + return null; + } + } + + void setDirectives(Map directives) { + synchronized (this.monitor) { + if (directives == null) + return; + resolution = (String) directives.get(Constants.RESOLUTION_DIRECTIVE); + } + } + + @SuppressWarnings("unchecked") + void setArbitraryDirectives(Map directives) { + synchronized (this.monitor) { + this.arbitraryDirectives = (Map) directives; + } + } + + Map getArbitraryDirectives() { + synchronized (this.monitor) { + return arbitraryDirectives; + } + } + + public String getBundleSymbolicName() { + synchronized (this.monitor) { + if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) { + return EquinoxContainer.NAME; + } + return symbolicName; + } + } + + public VersionRange getBundleVersionRange() { + synchronized (this.monitor) { + if (bundleVersionRange == null) + return VersionRange.emptyRange; + return bundleVersionRange; + } + } + + public Map getAttributes() { + synchronized (this.monitor) { + return attributes; + } + } + + @Override + public boolean isSatisfiedBy(BaseDescription supplier) { + return isSatisfiedBy(supplier, true); + } + + public boolean isSatisfiedBy(BaseDescription supplier, boolean checkEE) { + if (!(supplier instanceof ExportPackageDescription)) + return false; + ExportPackageDescriptionImpl pkgDes = (ExportPackageDescriptionImpl) supplier; + + // If we are in strict mode, check to see if the export specifies friends. + // If it does, are we one of the friends + String[] friends = (String[]) pkgDes.getDirective(StateImpl.FRIENDS_DIRECTIVE); + Boolean internal = (Boolean) pkgDes.getDirective(StateImpl.INTERNAL_DIRECTIVE); + if (internal.booleanValue() || friends != null) { + boolean strict = false; + if (strict) { + if (internal.booleanValue()) + return false; + boolean found = false; + if (friends != null && getBundle().getSymbolicName() != null) + for (String friend : friends) { + if (getBundle().getSymbolicName().equals(friend)) { + found = true; + } + } + if (!found) + return false; + } + } + String exporterSymbolicName = getBundleSymbolicName(); + if (exporterSymbolicName != null) { + BundleDescription exporter = pkgDes.getExporter(); + if (!exporterSymbolicName.equals(exporter.getSymbolicName())) + return false; + if (getBundleVersionRange() != null && !getBundleVersionRange().isIncluded(exporter.getVersion())) + return false; + } + + String name = getName(); + // shortcut '*' + // NOTE: wildcards are supported only in cases where this is a dynamic import + if (!"*".equals(name) && !(name.endsWith(".*") && pkgDes.getName().startsWith(name.substring(0, name.length() - 1))) && !pkgDes.getName().equals(name)) //$NON-NLS-1$ //$NON-NLS-2$ + return false; + if (getVersionRange() != null && !getVersionRange().isIncluded(pkgDes.getVersion())) + return false; + + Map importAttrs = getAttributes(); + if (importAttrs != null) { + Map exportAttrs = pkgDes.getAttributes(); + if (exportAttrs == null) + return false; + for (String importKey : importAttrs.keySet()) { + Object importValue = importAttrs.get(importKey); + Object exportValue = exportAttrs.get(importKey); + if (exportValue == null || !importValue.equals(exportValue)) + return false; + } + } + String[] mandatory = (String[]) pkgDes.getDirective(Constants.MANDATORY_DIRECTIVE); + if (!hasMandatoryAttributes(mandatory)) + return false; + // finally check the ee index + if (!checkEE) + return true; + if (((BundleDescriptionImpl) getBundle()).getEquinoxEE() < 0) + return true; + int eeIndex = ((Integer) pkgDes.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue(); + return eeIndex < 0 || eeIndex == ((BundleDescriptionImpl) getBundle()).getEquinoxEE(); + } + + @Override + protected boolean hasMandatoryAttributes(String[] checkMandatory) { + if (checkMandatory != null) { + Map importAttrs = getAttributes(); + for (String mandatory : checkMandatory) { + if (Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE.equals(mandatory)) { + if (getBundleSymbolicName() == null) + return false; + } else if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(mandatory)) { + if (bundleVersionRange == null) + return false; + } else if (Constants.PACKAGE_SPECIFICATION_VERSION.equals(mandatory) || Constants.VERSION_ATTRIBUTE.equals(mandatory)) { + if (getVersionRange() == null) + return false; + } else { // arbitrary attribute + if (importAttrs == null) + return false; + if (importAttrs.get(mandatory) == null) { + return false; + } + } + } + } + return true; + } + + protected void setBundleSymbolicName(String symbolicName) { + synchronized (this.monitor) { + this.symbolicName = symbolicName; + } + } + + protected void setBundleVersionRange(VersionRange bundleVersionRange) { + synchronized (this.monitor) { + this.bundleVersionRange = bundleVersionRange; + } + } + + @SuppressWarnings("unchecked") + protected void setAttributes(Map attributes) { + synchronized (this.monitor) { + this.attributes = (Map) attributes; + } + } + + @Override + public String toString() { + return "Import-Package: " + getName() + "; version=\"" + getVersionRange() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + protected Map getInternalDirectives() { + synchronized (this.monitor) { + Map result = new HashMap<>(5); + if (arbitraryDirectives != null) + result.putAll(arbitraryDirectives); + if (resolution != null) { + if (ImportPackageSpecification.RESOLUTION_STATIC.equals(resolution)) + result.put(Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_MANDATORY); + else + result.put(Constants.RESOLUTION_DIRECTIVE, resolution); + } + result.put(Constants.FILTER_DIRECTIVE, createFilterDirective()); + return result; + } + } + + private String createFilterDirective() { + StringBuilder filter = new StringBuilder(); + filter.append("(&"); //$NON-NLS-1$ + synchronized (this.monitor) { + addFilterAttribute(filter, BundleRevision.PACKAGE_NAMESPACE, getName(), false); + VersionRange range = getVersionRange(); + if (range != null && range != VersionRange.emptyRange) + addFilterAttribute(filter, Constants.VERSION_ATTRIBUTE, range); + if (symbolicName != null) + addFilterAttribute(filter, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symbolicName); + if (bundleVersionRange != null) + addFilterAttribute(filter, Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersionRange); + if (attributes != null) + addFilterAttributes(filter, attributes); + } + filter.append(')'); + return filter.toString(); + } + + @Override + protected Map getInteralAttributes() { + return Collections.emptyMap(); + } + + @Override + protected String getInternalNameSpace() { + return BundleRevision.PACKAGE_NAMESPACE; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/NativeCodeDescriptionImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/NativeCodeDescriptionImpl.java new file mode 100644 index 0000000000..fb8c467350 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/NativeCodeDescriptionImpl.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2007, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Collections; +import java.util.Dictionary; +import java.util.Map; +import org.eclipse.osgi.internal.framework.FilterImpl; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.NativeCodeDescription; +import org.eclipse.osgi.service.resolver.State; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.Version; + +public class NativeCodeDescriptionImpl extends BaseDescriptionImpl implements NativeCodeDescription { + private static final VersionRange[] EMPTY_VERSIONRANGES = new VersionRange[0]; + + private volatile Filter filter; + private String[] languages; + private String[] nativePaths; + private String[] osNames; + private VersionRange[] osVersions; + private String[] processors; + private BundleDescription supplier; + private volatile boolean invalidNativePaths = false; + + public Filter getFilter() { + return filter; + } + + public String[] getLanguages() { + synchronized (this.monitor) { + if (languages == null) + return BundleDescriptionImpl.EMPTY_STRING; + return languages; + } + } + + public String[] getNativePaths() { + synchronized (this.monitor) { + if (nativePaths == null) + return BundleDescriptionImpl.EMPTY_STRING; + return nativePaths; + } + } + + public String[] getOSNames() { + synchronized (this.monitor) { + if (osNames == null) + return BundleDescriptionImpl.EMPTY_STRING; + return osNames; + } + } + + public VersionRange[] getOSVersions() { + synchronized (this.monitor) { + if (osVersions == null) + return EMPTY_VERSIONRANGES; + return osVersions; + } + } + + public String[] getProcessors() { + synchronized (this.monitor) { + if (processors == null) + return BundleDescriptionImpl.EMPTY_STRING; + return processors; + } + } + + public BundleDescription getSupplier() { + return supplier; + } + + public int compareTo(NativeCodeDescription otherDesc) { + State containingState = getSupplier().getContainingState(); + if (containingState == null) + return 0; + Dictionary[] platformProps = containingState.getPlatformProperties(); + Version osversion; + try { + osversion = Version.parseVersion((String) platformProps[0].get(Constants.FRAMEWORK_OS_VERSION)); + } catch (Exception e) { + osversion = Version.emptyVersion; + } + VersionRange[] thisRanges = getOSVersions(); + VersionRange[] otherRanges = otherDesc.getOSVersions(); + Version thisHighest = getHighestVersionMatch(osversion, thisRanges); + Version otherHighest = getHighestVersionMatch(osversion, otherRanges); + if (thisHighest.compareTo(otherHighest) < 0) + return -1; + return (getLanguages().length == 0 ? 0 : 1) - (otherDesc.getLanguages().length == 0 ? 0 : 1); + } + + public boolean hasInvalidNativePaths() { + return invalidNativePaths; + } + + private Version getHighestVersionMatch(Version version, VersionRange[] ranges) { + Version highest = Version.emptyVersion; + for (VersionRange range : ranges) { + if (range.isIncluded(version) && highest.compareTo(range.getMinimum()) < 0) { + highest = range.getMinimum(); + } + } + return highest; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + String[] paths = getNativePaths(); + for (int i = 0; i < paths.length; i++) { + if (i > 0) { + sb.append("; "); //$NON-NLS-1$ + } + sb.append(paths[i]); + } + + String[] procs = getProcessors(); + for (String proc : procs) { + sb.append("; "); //$NON-NLS-1$ + sb.append(Constants.BUNDLE_NATIVECODE_PROCESSOR); + sb.append('='); + sb.append(proc); + } + + String[] oses = getOSNames(); + for (String os : oses) { + sb.append("; "); //$NON-NLS-1$ + sb.append(Constants.BUNDLE_NATIVECODE_OSNAME); + sb.append('='); + sb.append(os); + } + + VersionRange[] osRanges = getOSVersions(); + for (VersionRange osRange : osRanges) { + sb.append("; "); //$NON-NLS-1$ + sb.append(Constants.BUNDLE_NATIVECODE_OSVERSION); + sb.append("=\""); //$NON-NLS-1$ + sb.append(osRange.toString()); + sb.append('"'); + } + + String[] langs = getLanguages(); + for (String lang : langs) { + sb.append("; "); //$NON-NLS-1$ + sb.append(Constants.BUNDLE_NATIVECODE_LANGUAGE); + sb.append('='); + sb.append(lang); + } + + Filter f = getFilter(); + if (f != null) { + sb.append("; "); //$NON-NLS-1$ + sb.append(Constants.SELECTION_FILTER_ATTRIBUTE); + sb.append("=\""); //$NON-NLS-1$ + sb.append(f.toString()); + sb.append('"'); + } + return (sb.toString()); + } + + void setInvalidNativePaths(boolean invalidNativePaths) { + this.invalidNativePaths = invalidNativePaths; + } + + void setOSNames(String[] osNames) { + synchronized (this.monitor) { + this.osNames = osNames; + } + } + + void setOSVersions(VersionRange[] osVersions) { + synchronized (this.monitor) { + this.osVersions = osVersions; + } + } + + void setFilter(String filter) throws InvalidSyntaxException { + this.filter = filter == null ? null : FilterImpl.newInstance(filter); + } + + void setLanguages(String[] languages) { + synchronized (this.monitor) { + this.languages = languages; + } + } + + void setNativePaths(String[] nativePaths) { + synchronized (this.monitor) { + this.nativePaths = nativePaths; + } + } + + void setProcessors(String[] processors) { + synchronized (this.monitor) { + this.processors = processors; + } + } + + void setSupplier(BundleDescription supplier) { + this.supplier = supplier; + } + + public Map getDeclaredDirectives() { + return Collections.emptyMap(); + } + + public Map getDeclaredAttributes() { + return Collections.emptyMap(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/NativeCodeSpecificationImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/NativeCodeSpecificationImpl.java new file mode 100644 index 0000000000..2043bdcb04 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/NativeCodeSpecificationImpl.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2007, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.*; +import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.osgi.framework.*; + +public class NativeCodeSpecificationImpl extends VersionConstraintImpl implements NativeCodeSpecification { + private static final NativeCodeDescription[] EMPTY_NATIVECODEDESCRIPTIONS = new NativeCodeDescription[0]; + @SuppressWarnings("restriction") + private static org.eclipse.osgi.internal.framework.AliasMapper aliasMapper = new org.eclipse.osgi.internal.framework.AliasMapper(); + private NativeCodeDescription[] possibleSuppliers; + private boolean optional; + + public NativeCodeDescription[] getPossibleSuppliers() { + synchronized (this.monitor) { + if (possibleSuppliers == null) + return EMPTY_NATIVECODEDESCRIPTIONS; + return possibleSuppliers; + } + } + + void setPossibleSuppliers(NativeCodeDescription[] possibleSuppliers) { + synchronized (this.monitor) { + this.possibleSuppliers = possibleSuppliers; + } + } + + public boolean isOptional() { + synchronized (this.monitor) { + return optional; + } + } + + void setOptional(boolean optional) { + synchronized (this.monitor) { + this.optional = optional; + } + } + + @SuppressWarnings("unchecked") + @Override + public boolean isSatisfiedBy(BaseDescription supplier) { + if (!(supplier instanceof NativeCodeDescription)) + return false; + State containingState = getBundle().getContainingState(); + if (containingState == null) + return false; + Dictionary[] platformProps = containingState.getPlatformProperties(); + NativeCodeDescription nativeSupplier = (NativeCodeDescription) supplier; + Filter filter = nativeSupplier.getFilter(); + boolean match = false; + for (int i = 0; i < platformProps.length && !match; i++) { + @SuppressWarnings("rawtypes") + Dictionary props = platformProps[i]; + if (filter != null && !filter.matchCase(props)) + continue; + String[] osNames = nativeSupplier.getOSNames(); + if (osNames.length == 0) + match = true; + else { + Collection platformOSAliases; + Object platformOS = platformProps[i].get(Constants.FRAMEWORK_OS_NAME); + if (platformOS instanceof Collection) { + platformOSAliases = (Collection) platformOS; + } else if (platformOS instanceof String) { + platformOS = aliasMapper.getCanonicalOSName((String) platformOS); + platformOSAliases = aliasMapper.getOSNameAliases((String) platformOS); + } else { + platformOSAliases = platformOS == null ? Collections.emptyList() : Collections.singleton(platformOS); + } + osNamesLoop: for (String osName : osNames) { + String canonicalOSName = aliasMapper.getCanonicalOSName(osName); + for (Object osAlias : platformOSAliases) { + if (osAlias instanceof String) { + match = (((String) osAlias).equalsIgnoreCase(canonicalOSName)); + } else { + match = osAlias.equals(canonicalOSName); + } + if (match) { + break osNamesLoop; + } + } + } + } + if (!match) + continue; + match = false; + + String[] processors = nativeSupplier.getProcessors(); + if (processors.length == 0) + match = true; + else { + Collection platformProcessorAliases; + Object platformProcessor = platformProps[i].get(Constants.FRAMEWORK_PROCESSOR); + if (platformProcessor instanceof Collection) { + platformProcessorAliases = (Collection) platformProcessor; + } else if (platformProcessor instanceof String) { + platformProcessor = aliasMapper.getCanonicalProcessor((String) platformProcessor); + platformProcessorAliases = aliasMapper.getProcessorAliases((String) platformProcessor); + } else { + platformProcessorAliases = platformProcessor == null ? Collections.emptyList() : Collections.singleton(platformProcessor); + } + processorLoop: for (String processor : processors) { + String canonicalProcessor = aliasMapper.getCanonicalProcessor(processor); + for (Object processorAlias : platformProcessorAliases) { + if (processorAlias instanceof String) { + match = ((String) processorAlias).equalsIgnoreCase(canonicalProcessor); + } else { + match = processorAlias.equals(canonicalProcessor); + } + if (match) { + break processorLoop; + } + } + } + } + if (!match) + continue; + match = false; + + String[] languages = nativeSupplier.getLanguages(); + if (languages.length == 0) + match = true; + else { + Object platformLanguage = platformProps[i].get(Constants.FRAMEWORK_LANGUAGE); + if (platformLanguage != null) + for (int j = 0; j < languages.length && !match; j++) { + if ((platformLanguage instanceof String) ? ((String) platformLanguage).equalsIgnoreCase(languages[j]) : platformLanguage.equals(languages[j])) + match = true; + } + } + if (!match) + continue; + match = false; + + VersionRange[] osVersions = nativeSupplier.getOSVersions(); + if (osVersions.length == 0 || platformProps[i].get(Constants.FRAMEWORK_OS_VERSION) == null) + match = true; + else { + Version osversion; + try { + osversion = Version.parseVersion((String) platformProps[i].get(Constants.FRAMEWORK_OS_VERSION)); + } catch (Exception e) { + osversion = Version.emptyVersion; + } + for (int j = 0; j < osVersions.length && !match; j++) { + if (osVersions[j].isIncluded(osversion)) + match = true; + } + } + } + return match; + } + + @Override + protected boolean hasMandatoryAttributes(String[] mandatory) { + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + NativeCodeDescription[] suppliers = getPossibleSuppliers(); + for (int i = 0; i < suppliers.length; i++) { + if (i > 0) + sb.append(", "); //$NON-NLS-1$ + sb.append(suppliers[i].toString()); + } + + return sb.toString(); + } + + @Override + protected Map getInternalDirectives() { + return Collections.emptyMap(); + } + + @Override + protected Map getInteralAttributes() { + return Collections.emptyMap(); + } + + @Override + protected String getInternalNameSpace() { + return null; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateBuilder.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateBuilder.java new file mode 100644 index 0000000000..f40fdc96f9 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateBuilder.java @@ -0,0 +1,923 @@ +/******************************************************************************* + * Copyright (c) 2003, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Danail Nachev - ProSyst - bug 218625 + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.lang.reflect.Constructor; +import java.util.*; +import org.eclipse.osgi.internal.framework.EquinoxContainer; +import org.eclipse.osgi.internal.framework.FilterImpl; +import org.eclipse.osgi.internal.messages.Msg; +import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.*; +import org.osgi.framework.namespace.BundleNamespace; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.resource.Namespace; + +/** + * This class builds bundle description objects from manifests + */ +public class StateBuilder { + private static final String[] DEFINED_EXPORT_PACKAGE_DIRECTIVES = {Constants.USES_DIRECTIVE, Constants.INCLUDE_DIRECTIVE, Constants.EXCLUDE_DIRECTIVE, StateImpl.FRIENDS_DIRECTIVE, StateImpl.INTERNAL_DIRECTIVE, Constants.MANDATORY_DIRECTIVE}; + private static final String[] DEFINED_IMPORT_PACKAGE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE}; + private static final String[] DEFINED_PACKAGE_MATCHING_ATTRS = {Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION, Constants.VERSION_ATTRIBUTE}; + private static final String[] DEFINED_REQUIRE_BUNDLE_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.VISIBILITY_DIRECTIVE}; + private static final String[] DEFINED_FRAGMENT_HOST_DIRECTIVES = {Constants.EXTENSION_DIRECTIVE}; + static final String[] DEFINED_BSN_DIRECTIVES = {Constants.SINGLETON_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.MANDATORY_DIRECTIVE}; + static final String[] DEFINED_BSN_MATCHING_ATTRS = {Constants.BUNDLE_VERSION_ATTRIBUTE, StateImpl.OPTIONAL_ATTRIBUTE, StateImpl.REPROVIDE_ATTRIBUTE}; + private static final String[] DEFINED_REQUIRE_CAPABILITY_DIRECTIVES = {Constants.RESOLUTION_DIRECTIVE, Constants.FILTER_DIRECTIVE, Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE}; + private static final String[] DEFINED_REQUIRE_CAPABILITY_ATTRS = {}; + private static final String[] DEFINED_OSGI_VALIDATE_HEADERS = {Constants.IMPORT_PACKAGE, Constants.DYNAMICIMPORT_PACKAGE, Constants.EXPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.BUNDLE_SYMBOLICNAME, Constants.REQUIRE_BUNDLE}; + static final String GENERIC_REQUIRE = "Eclipse-GenericRequire"; //$NON-NLS-1$ + static final String GENERIC_CAPABILITY = "Eclipse-GenericCapability"; //$NON-NLS-1$ + + private static final String ATTR_TYPE_STRING = "string"; //$NON-NLS-1$ + private static final String ATTR_TYPE_VERSION = "version"; //$NON-NLS-1$ + private static final String ATTR_TYPE_URI = "uri"; //$NON-NLS-1$ + private static final String ATTR_TYPE_LONG = "long"; //$NON-NLS-1$ + private static final String ATTR_TYPE_DOUBLE = "double"; //$NON-NLS-1$ + private static final String ATTR_TYPE_SET = "set"; //$NON-NLS-1$ + private static final String ATTR_TYPE_LIST = "List"; //$NON-NLS-1$ + private static final String OPTIONAL_ATTR = "optional"; //$NON-NLS-1$ + private static final String MULTIPLE_ATTR = "multiple"; //$NON-NLS-1$ + private static final String TRUE = "true"; //$NON-NLS-1$ + + static BundleDescriptionImpl createBundleDescription(State state, Dictionary manifest, + String location) throws BundleException { + BundleDescriptionImpl result = new BundleDescriptionImpl(); + String manifestVersionHeader = manifest.get(Constants.BUNDLE_MANIFESTVERSION); + boolean jreBundle = "true".equals(manifest.get(StateImpl.Eclipse_JREBUNDLE)); //$NON-NLS-1$ + int manifestVersion = 1; + if (manifestVersionHeader != null) + manifestVersion = Integer.parseInt(manifestVersionHeader); + if (manifestVersion >= 2) + validateHeaders(manifest, jreBundle); + + // retrieve the symbolic-name and the singleton status + String symbolicNameHeader = manifest.get(Constants.BUNDLE_SYMBOLICNAME); + if (symbolicNameHeader != null) { + ManifestElement[] symbolicNameElements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicNameHeader); + if (symbolicNameElements.length > 0) { + ManifestElement bsnElement = symbolicNameElements[0]; + result.setSymbolicName(bsnElement.getValue()); + String singleton = bsnElement.getDirective(Constants.SINGLETON_DIRECTIVE); + if (singleton == null) // TODO this is for backward compatibility; need to check manifest version < 2 to allow this after everyone has converted to new syntax + singleton = bsnElement.getAttribute(Constants.SINGLETON_DIRECTIVE); + result.setStateBit(BundleDescriptionImpl.SINGLETON, "true".equals(singleton)); //$NON-NLS-1$ + String fragmentAttachment = bsnElement.getDirective(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE); + if (fragmentAttachment != null) { + if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_RESOLVETIME)) { + result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, true); + result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false); + } else if (fragmentAttachment.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) { + result.setStateBit(BundleDescriptionImpl.ATTACH_FRAGMENTS, false); + result.setStateBit(BundleDescriptionImpl.DYNAMIC_FRAGMENTS, false); + } + } + result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(bsnElement.getDirective(Constants.MANDATORY_DIRECTIVE))); + result.setAttributes(getAttributes(bsnElement, DEFINED_BSN_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(bsnElement, DEFINED_BSN_DIRECTIVES)); + } + } + // retrieve other headers + String version = manifest.get(Constants.BUNDLE_VERSION); + try { + result.setVersion((version != null) ? Version.parseVersion(version) : Version.emptyVersion); + } catch (IllegalArgumentException ex) { + if (manifestVersion >= 2) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_VERSION, version); + throw new BundleException(message + " : " + ex.getMessage(), BundleException.MANIFEST_ERROR, ex); //$NON-NLS-1$ + } + // prior to R4 the Bundle-Version header was not interpreted by the Framework; + // must not fail for old R3 style bundles + } + result.setLocation(location); + result.setPlatformFilter(manifest.get(StateImpl.ECLIPSE_PLATFORMFILTER)); + String[] brees = ManifestElement.getArrayFromList(manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT)); + result.setExecutionEnvironments(brees); + ManifestElement[] host = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, manifest.get(Constants.FRAGMENT_HOST)); + if (host != null) + result.setHost(createHostSpecification(host[0], state)); + ManifestElement[] exports = ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, manifest.get(Constants.EXPORT_PACKAGE)); + ManifestElement[] provides = ManifestElement.parseHeader(StateImpl.PROVIDE_PACKAGE, manifest.get(StateImpl.PROVIDE_PACKAGE)); + boolean strict = false; + List providedExports = new ArrayList<>(provides == null ? 0 : provides.length); + result.setExportPackages(createExportPackages(exports, provides, providedExports, strict)); + ManifestElement[] imports = ManifestElement.parseHeader(Constants.IMPORT_PACKAGE, manifest.get(Constants.IMPORT_PACKAGE)); + ManifestElement[] dynamicImports = ManifestElement.parseHeader(Constants.DYNAMICIMPORT_PACKAGE, manifest.get(Constants.DYNAMICIMPORT_PACKAGE)); + result.setImportPackages(createImportPackages(result.getExportPackages(), providedExports, imports, dynamicImports, manifestVersion)); + ManifestElement[] requires = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, manifest.get(Constants.REQUIRE_BUNDLE)); + result.setRequiredBundles(createRequiredBundles(requires)); + String[][] genericAliases = getGenericAliases(state); + ManifestElement[] genericRequires = getGenericRequires(manifest, genericAliases); + ManifestElement[] osgiRequires = ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, manifest.get(Constants.REQUIRE_CAPABILITY)); + result.setGenericRequires(createGenericRequires(genericRequires, osgiRequires, brees)); + ManifestElement[] genericCapabilities = getGenericCapabilities(manifest, genericAliases); + ManifestElement[] osgiCapabilities = ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, manifest.get(Constants.PROVIDE_CAPABILITY)); + result.setGenericCapabilities(createGenericCapabilities(genericCapabilities, osgiCapabilities, result)); + ManifestElement[] nativeCode = ManifestElement.parseHeader(Constants.BUNDLE_NATIVECODE, manifest.get(Constants.BUNDLE_NATIVECODE)); + result.setNativeCodeSpecification(createNativeCode(nativeCode)); + return result; + } + + private static ManifestElement[] getGenericRequires(Dictionary manifest, String[][] genericAliases) throws BundleException { + ManifestElement[] genericRequires = ManifestElement.parseHeader(GENERIC_REQUIRE, manifest.get(GENERIC_REQUIRE)); + List aliasList = null; + if (genericAliases.length > 0) { + aliasList = new ArrayList<>(genericRequires == null ? 0 : genericRequires.length); + for (String[] genericAlias : genericAliases) { + ManifestElement[] aliasReqs = ManifestElement.parseHeader(genericAlias[1], manifest.get(genericAlias[1])); + if (aliasReqs == null) + continue; + for (ManifestElement aliasReq : aliasReqs) { + StringBuilder strBuf = new StringBuilder(); + strBuf.append(aliasReq.getValue()).append(':').append(genericAlias[2]); + String filter = aliasReq.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(genericAlias[1], strBuf.toString()); + aliasList.add(withType[0]); + } + } + } + if (aliasList == null || aliasList.size() == 0) + return genericRequires; + if (genericRequires != null) + Collections.addAll(aliasList, genericRequires); + return aliasList.toArray(new ManifestElement[aliasList.size()]); + } + + private static ManifestElement[] getGenericCapabilities(Dictionary manifest, String[][] genericAliases) throws BundleException { + ManifestElement[] genericCapabilities = ManifestElement.parseHeader(GENERIC_CAPABILITY, manifest.get(GENERIC_CAPABILITY)); + List aliasList = null; + if (genericAliases.length > 0) { + aliasList = new ArrayList<>(genericCapabilities == null ? 0 : genericCapabilities.length); + for (String[] genericAlias : genericAliases) { + ManifestElement[] aliasCapabilities = ManifestElement.parseHeader(genericAlias[0], manifest.get(genericAlias[0])); + if (aliasCapabilities == null) + continue; + for (ManifestElement aliasCapability : aliasCapabilities) { + StringBuilder strBuf = new StringBuilder(); + strBuf.append(aliasCapability.getValue()).append(':').append(genericAlias[2]); + for (Enumeration keys = aliasCapability.getKeys(); keys != null && keys.hasMoreElements();) { + String key = keys.nextElement(); + strBuf.append("; ").append(key).append("=\"").append(aliasCapability.getAttribute(key)).append("\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + ManifestElement[] withTypes = ManifestElement.parseHeader(genericAlias[0], strBuf.toString()); + aliasList.add(withTypes[0]); + } + } + } + if (aliasList == null || aliasList.size() == 0) + return genericCapabilities; + if (genericCapabilities != null) + Collections.addAll(aliasList, genericCapabilities); + return aliasList.toArray(new ManifestElement[aliasList.size()]); + } + + private static String[][] getGenericAliases(State state) { + String genericAliasesProp = getPlatformProperty(state, "osgi.genericAliases"); //$NON-NLS-1$ + if (genericAliasesProp == null) + return new String[0][0]; + String[] aliases = ManifestElement.getArrayFromList(genericAliasesProp, ","); //$NON-NLS-1$ + String[][] result = new String[aliases.length][]; + for (int i = 0; i < aliases.length; i++) + result[i] = ManifestElement.getArrayFromList(aliases[i], ":"); //$NON-NLS-1$ + return result; + } + + private static String getPlatformProperty(State state, String key) { + Dictionary[] platformProps = state == null ? null : state.getPlatformProperties(); + return platformProps == null || platformProps.length == 0 ? null : (String) platformProps[0].get(key); + } + + private static void validateHeaders(Dictionary manifest, boolean jreBundle) throws BundleException { + for (String definedOSGiValidateHeader : DEFINED_OSGI_VALIDATE_HEADERS) { + String header = manifest.get(definedOSGiValidateHeader); + if (header != null) { + ManifestElement[] elements = ManifestElement.parseHeader(definedOSGiValidateHeader, header); + checkForDuplicateDirectivesAttributes(definedOSGiValidateHeader, elements); + if (definedOSGiValidateHeader == Constants.IMPORT_PACKAGE) { + checkImportExportSyntax(definedOSGiValidateHeader, elements, false, false, jreBundle); + } + if (definedOSGiValidateHeader == Constants.DYNAMICIMPORT_PACKAGE) { + checkImportExportSyntax(definedOSGiValidateHeader, elements, false, true, jreBundle); + } + if (definedOSGiValidateHeader == Constants.EXPORT_PACKAGE) { + checkImportExportSyntax(definedOSGiValidateHeader, elements, true, false, jreBundle); + } + if (definedOSGiValidateHeader == Constants.FRAGMENT_HOST) { + checkExtensionBundle(definedOSGiValidateHeader, elements); + } + } else if (definedOSGiValidateHeader == Constants.BUNDLE_SYMBOLICNAME) { + throw new BundleException(NLS.bind(StateMsg.HEADER_REQUIRED, Constants.BUNDLE_SYMBOLICNAME), BundleException.MANIFEST_ERROR); + } + } + } + + private static BundleSpecification[] createRequiredBundles(ManifestElement[] specs) { + if (specs == null) + return null; + BundleSpecification[] result = new BundleSpecification[specs.length]; + for (int i = 0; i < specs.length; i++) + result[i] = createRequiredBundle(specs[i]); + return result; + } + + static BundleSpecification createRequiredBundle(ManifestElement spec) { + BundleSpecificationImpl result = new BundleSpecificationImpl(); + result.setName(spec.getValue()); + result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE))); + result.setExported(Constants.VISIBILITY_REEXPORT.equals(spec.getDirective(Constants.VISIBILITY_DIRECTIVE)) || "true".equals(spec.getAttribute(StateImpl.REPROVIDE_ATTRIBUTE))); //$NON-NLS-1$ + result.setOptional(Constants.RESOLUTION_OPTIONAL.equals(spec.getDirective(Constants.RESOLUTION_DIRECTIVE)) || "true".equals(spec.getAttribute(StateImpl.OPTIONAL_ATTRIBUTE))); //$NON-NLS-1$ + result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(spec, DEFINED_REQUIRE_BUNDLE_DIRECTIVES)); + return result; + } + + private static ImportPackageSpecification[] createImportPackages(ExportPackageDescription[] exported, List providedExports, ManifestElement[] imported, ManifestElement[] dynamicImported, int manifestVersion) { + List allImports = null; + if (manifestVersion < 2) { + // add implicit imports for each exported package if manifest verions is less than 2. + if (exported.length == 0 && imported == null && dynamicImported == null) + return null; + allImports = new ArrayList<>(exported.length + (imported == null ? 0 : imported.length)); + for (ExportPackageDescription exportDescription : exported) { + if (providedExports.contains(exportDescription.getName())) { + continue; + } + ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl(); + result.setName(exportDescription.getName()); + result.setVersionRange(getVersionRange(exportDescription.getVersion().toString())); + result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_STATIC); + allImports.add(result); + } + } else { + allImports = new ArrayList<>(imported == null ? 0 : imported.length); + } + + // add dynamics first so they will get overriden by static imports if + // the same package is dyanamically imported and statically imported. + if (dynamicImported != null) + for (ManifestElement dynamicImport : dynamicImported) { + addImportPackages(dynamicImport, allImports, manifestVersion, true); + } + if (imported != null) + for (ManifestElement pkgImport : imported) { + addImportPackages(pkgImport, allImports, manifestVersion, false); + } + return allImports.toArray(new ImportPackageSpecification[allImports.size()]); + } + + public static void addImportPackages(ManifestElement importPackage, List allImports, int manifestVersion, boolean dynamic) { + String[] importNames = importPackage.getValueComponents(); + for (String importName : importNames) { + // do not allow for multiple imports of same package of manifest version < 2 + if (manifestVersion < 2) { + Iterator iter = allImports.iterator(); + while (iter.hasNext()) { + if (importName.equals(iter.next().getName())) { + iter.remove(); + } + } + } + ImportPackageSpecificationImpl result = new ImportPackageSpecificationImpl(); + result.setName(importName); + // set common attributes for both dynamic and static imports + String versionString = importPackage.getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString == null) // specification-version aliases to version + versionString = importPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION); + result.setVersionRange(getVersionRange(versionString)); + result.setBundleSymbolicName(importPackage.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)); + result.setBundleVersionRange(getVersionRange(importPackage.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE))); + // only set the matching attributes if manifest version >= 2 + if (manifestVersion >= 2) + result.setAttributes(getAttributes(importPackage, DEFINED_PACKAGE_MATCHING_ATTRS)); + if (dynamic) + result.setDirective(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_DYNAMIC); + else + result.setDirective(Constants.RESOLUTION_DIRECTIVE, getResolution(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))); + result.setArbitraryDirectives(getDirectives(importPackage, DEFINED_IMPORT_PACKAGE_DIRECTIVES)); + allImports.add(result); + } + } + + private static String getResolution(String resolution) { + String result = ImportPackageSpecification.RESOLUTION_STATIC; + if (Constants.RESOLUTION_OPTIONAL.equals(resolution) || ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(resolution)) + result = resolution; + return result; + } + + static ExportPackageDescription[] createExportPackages(ManifestElement[] exported, ManifestElement[] provides, List providedExports, boolean strict) { + int numExports = (exported == null ? 0 : exported.length) + (provides == null ? 0 : provides.length); + if (numExports == 0) + return null; + List allExports = new ArrayList<>(numExports); + if (exported != null) + for (ManifestElement packageExport : exported) { + addExportPackages(packageExport, allExports, strict); + } + if (provides != null) + addProvidePackages(provides, allExports, providedExports); + return allExports.toArray(new ExportPackageDescription[allExports.size()]); + } + + static void addExportPackages(ManifestElement exportPackage, List allExports, boolean strict) { + String[] exportNames = exportPackage.getValueComponents(); + for (String exportName : exportNames) { + // if we are in strict mode and the package is marked as internal, skip it. + if (strict && "true".equals(exportPackage.getDirective(StateImpl.INTERNAL_DIRECTIVE))) //$NON-NLS-1$ + continue; + ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl(); + result.setName(exportName); + String versionString = exportPackage.getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString == null) // specification-version aliases to version + versionString = exportPackage.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION); + if (versionString != null) + result.setVersion(Version.parseVersion(versionString)); + result.setDirective(Constants.USES_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.USES_DIRECTIVE))); + result.setDirective(Constants.INCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.INCLUDE_DIRECTIVE)); + result.setDirective(Constants.EXCLUDE_DIRECTIVE, exportPackage.getDirective(Constants.EXCLUDE_DIRECTIVE)); + result.setDirective(StateImpl.FRIENDS_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(StateImpl.FRIENDS_DIRECTIVE))); + result.setDirective(StateImpl.INTERNAL_DIRECTIVE, Boolean.valueOf(exportPackage.getDirective(StateImpl.INTERNAL_DIRECTIVE))); + result.setDirective(Constants.MANDATORY_DIRECTIVE, ManifestElement.getArrayFromList(exportPackage.getDirective(Constants.MANDATORY_DIRECTIVE))); + result.setAttributes(getAttributes(exportPackage, DEFINED_PACKAGE_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(exportPackage, DEFINED_EXPORT_PACKAGE_DIRECTIVES)); + allExports.add(result); + } + } + + private static void addProvidePackages(ManifestElement[] provides, List allExports, List providedExports) { + ExportPackageDescription[] currentExports = allExports.toArray(new ExportPackageDescription[allExports.size()]); + for (ManifestElement provide : provides) { + boolean duplicate = false; + for (ExportPackageDescription currentExport : currentExports) { + if (provide.getValue().equals(currentExport.getName())) { + duplicate = true; + break; + } + } + if (!duplicate) { + ExportPackageDescriptionImpl result = new ExportPackageDescriptionImpl(); + result.setName(provide.getValue()); + allExports.add(result); + } + providedExports.add(provide.getValue()); + } + } + + static Map getDirectives(ManifestElement element, String[] definedDirectives) { + Enumeration keys = element.getDirectiveKeys(); + if (keys == null) + return null; + Map arbitraryDirectives = null; + keyloop: while (keys.hasMoreElements()) { + String key = keys.nextElement(); + for (String definedDirective : definedDirectives) { + if (definedDirective.equals(key)) + continue keyloop; + } + if (arbitraryDirectives == null) + arbitraryDirectives = new HashMap<>(); + arbitraryDirectives.put(key, element.getDirective(key)); + } + return arbitraryDirectives; + } + + static Map getAttributes(ManifestElement element, String[] definedAttrs) { + Enumeration keys = element.getKeys(); + Map arbitraryAttrs = null; + if (keys == null) + return null; + while (keys.hasMoreElements()) { + boolean definedAttr = false; + String key = keys.nextElement(); + for (String attr : definedAttrs) { + if (attr.equals(key)) { + definedAttr = true; + break; + } + } + String value = element.getAttribute(key); + int colonIndex = key.indexOf(':'); + String type = ATTR_TYPE_STRING; + if (colonIndex > 0) { + type = key.substring(colonIndex + 1).trim(); + key = key.substring(0, colonIndex).trim(); + } + if (!definedAttr) { + if (arbitraryAttrs == null) + arbitraryAttrs = new HashMap<>(); + arbitraryAttrs.put(key, convertValue(type, value)); + } + } + return arbitraryAttrs; + } + + private static Object convertValue(String type, String value) { + + if (ATTR_TYPE_STRING.equalsIgnoreCase(type)) + return value; + + String trimmed = value.trim(); + if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type)) + return Double.valueOf(trimmed); + else if (ATTR_TYPE_LONG.equalsIgnoreCase(type)) + return Long.valueOf(trimmed); + else if (ATTR_TYPE_URI.equalsIgnoreCase(type)) + try { + Class uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$ + Constructor constructor = uriClazz.getConstructor(new Class[] {String.class}); + return constructor.newInstance(new Object[] {trimmed}); + } catch (ClassNotFoundException e) { + // oh well cannot support; just use string + return value; + } catch (RuntimeException e) { // got some reflection exception + throw e; + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + else if (ATTR_TYPE_VERSION.equalsIgnoreCase(type)) + return new Version(trimmed); + else if (ATTR_TYPE_SET.equalsIgnoreCase(type)) + return ManifestElement.getArrayFromList(trimmed, ","); //$NON-NLS-1$ + + // assume list type, anything else will throw an exception + Tokenizer listTokenizer = new Tokenizer(type); + String listType = listTokenizer.getToken("<"); //$NON-NLS-1$ + if (!ATTR_TYPE_LIST.equalsIgnoreCase(listType)) + throw new RuntimeException("Unsupported type: " + type); //$NON-NLS-1$ + char c = listTokenizer.getChar(); + String componentType = ATTR_TYPE_STRING; + if (c == '<') { + componentType = listTokenizer.getToken(">"); //$NON-NLS-1$ + if (listTokenizer.getChar() != '>') + throw new RuntimeException("Invalid type, missing ending '>' : " + type); //$NON-NLS-1$ + } + List tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$ + List components = new ArrayList<>(); + for (String component : tokens) { + components.add(convertValue(componentType, component)); + } + return components; + } + + static HostSpecification createHostSpecification(ManifestElement spec, State state) { + if (spec == null) + return null; + HostSpecificationImpl result = new HostSpecificationImpl(); + result.setName(spec.getValue()); + result.setVersionRange(getVersionRange(spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE))); + String multiple = spec.getDirective("multiple-hosts"); //$NON-NLS-1$ + if (multiple == null) + multiple = getPlatformProperty(state, "osgi.support.multipleHosts"); //$NON-NLS-1$ + result.setIsMultiHost("true".equals(multiple)); //$NON-NLS-1$ + result.setAttributes(getAttributes(spec, DEFINED_BSN_MATCHING_ATTRS)); + result.setArbitraryDirectives(getDirectives(spec, DEFINED_FRAGMENT_HOST_DIRECTIVES)); + return result; + } + + private static GenericSpecification[] createGenericRequires(ManifestElement[] equinoxRequires, ManifestElement[] osgiRequires, String[] brees) throws BundleException { + List result = createEquinoxRequires(equinoxRequires); + result = createOSGiRequires(osgiRequires, result); + result = convertBREEs(brees, result); + return result == null ? null : result.toArray(new GenericSpecification[result.size()]); + } + + static List convertBREEs(String[] brees, List result) throws BundleException { + if (brees == null || brees.length == 0) + return result; + if (result == null) + result = new ArrayList<>(brees.length); + List breeFilters = new ArrayList<>(); + for (String bree : brees) + breeFilters.add(createOSGiEERequirementFilter(bree)); + String filterSpec; + if (breeFilters.size() == 1) { + filterSpec = breeFilters.get(0); + } else { + StringBuilder filterBuf = new StringBuilder("(|"); //$NON-NLS-1$ + for (String breeFilter : breeFilters) { + filterBuf.append(breeFilter); + } + filterSpec = filterBuf.append(")").toString(); //$NON-NLS-1$ + } + GenericSpecificationImpl spec = new GenericSpecificationImpl(); + spec.setResolution(GenericSpecificationImpl.RESOLUTION_FROM_BREE); + spec.setType(StateImpl.OSGI_EE_NAMESPACE); + try { + FilterImpl filter = FilterImpl.newInstance(filterSpec); + spec.setMatchingFilter(filter); + String name = filter.getPrimaryKeyValue(spec.getType()); + if (name != null) + spec.setName(name); + } catch (InvalidSyntaxException e) { + throw new BundleException("Error converting required execution environment.", e); //$NON-NLS-1$ + } + result.add(spec); + return result; + } + + private static String createOSGiEERequirementFilter(String bree) throws BundleException { + String[] nameVersion = getOSGiEENameVersion(bree); + String eeName = nameVersion[0]; + String v = nameVersion[1]; + String filterSpec; + if (v == null) + filterSpec = "(osgi.ee=" + eeName + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + else + filterSpec = "(&(osgi.ee=" + eeName + ")(version=" + v + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + try { + // do a sanity check + FilterImpl.newInstance(filterSpec); + } catch (InvalidSyntaxException e) { + filterSpec = "(osgi.ee=" + bree + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + try { + // do another sanity check + FilterImpl.newInstance(filterSpec); + } catch (InvalidSyntaxException e1) { + throw new BundleException("Error converting required execution environment.", e1); //$NON-NLS-1$ + } + } + return filterSpec; + } + + static String[] getOSGiEENameVersion(String bree) { + String ee1 = null; + String ee2 = null; + String v1 = null; + String v2 = null; + int separator = bree.indexOf('/'); + if (separator <= 0 || separator == bree.length() - 1) { + ee1 = bree; + } else { + ee1 = bree.substring(0, separator); + ee2 = bree.substring(separator + 1); + } + int v1idx = ee1.indexOf('-'); + if (v1idx > 0 && v1idx < ee1.length() - 1) { + // check for > 0 to avoid EEs starting with - + // check for < len - 1 to avoid ending with - + try { + v1 = ee1.substring(v1idx + 1); + // sanity check version format + Version.parseVersion(v1); + ee1 = ee1.substring(0, v1idx); + } catch (IllegalArgumentException e) { + v1 = null; + } + } + + int v2idx = ee2 == null ? -1 : ee2.indexOf('-'); + if (v2idx > 0 && v2idx < ee2.length() - 1) { + // check for > 0 to avoid EEs starting with - + // check for < len - 1 to avoid ending with - + try { + v2 = ee2.substring(v2idx + 1); + Version.parseVersion(v2); + ee2 = ee2.substring(0, v2idx); + } catch (IllegalArgumentException e) { + v2 = null; + } + } + + if (v1 == null) + v1 = v2; + if (v1 != null && v2 != null && !v1.equals(v2)) { + ee1 = bree; + ee2 = null; + v1 = null; + v2 = null; + } + if ("J2SE".equals(ee1)) //$NON-NLS-1$ + ee1 = "JavaSE"; //$NON-NLS-1$ + if ("J2SE".equals(ee2)) //$NON-NLS-1$ + ee2 = "JavaSE"; //$NON-NLS-1$ + + String eeName = ee1 + (ee2 == null ? "" : '/' + ee2); //$NON-NLS-1$ + + return new String[] {eeName, v1}; + } + + static List createOSGiRequires(ManifestElement[] osgiRequires, List result) throws BundleException { + if (osgiRequires == null) + return result; + if (result == null) + result = new ArrayList<>(); + for (ManifestElement element : osgiRequires) { + String[] namespaces = element.getValueComponents(); + for (String namespace : namespaces) { + GenericSpecificationImpl spec = new GenericSpecificationImpl(); + spec.setType(namespace); + String filterSpec = element.getDirective(Constants.FILTER_DIRECTIVE); + if (filterSpec != null) { + try { + FilterImpl filter = FilterImpl.newInstance(filterSpec); + spec.setMatchingFilter(filter); + String name = filter.getPrimaryKeyValue(namespace); + if (name != null) + spec.setName(name); + } catch (InvalidSyntaxException e) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.REQUIRE_CAPABILITY, element.toString()); + throw new BundleException(message + " : filter", BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$ + } + } + String resolutionDirective = element.getDirective(Constants.RESOLUTION_DIRECTIVE); + int resolution = 0; + if (Constants.RESOLUTION_OPTIONAL.equals(resolutionDirective)) + resolution |= GenericSpecification.RESOLUTION_OPTIONAL; + String cardinality = element.getDirective(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE); + if (Namespace.CARDINALITY_MULTIPLE.equals(cardinality)) + resolution |= GenericSpecification.RESOLUTION_MULTIPLE; + spec.setResolution(resolution); + spec.setAttributes(getAttributes(element, DEFINED_REQUIRE_CAPABILITY_ATTRS)); + spec.setArbitraryDirectives(getDirectives(element, DEFINED_REQUIRE_CAPABILITY_DIRECTIVES)); + result.add(spec); + } + } + return result; + } + + private static List createEquinoxRequires(ManifestElement[] equinoxRequires) throws BundleException { + if (equinoxRequires == null) + return null; + ArrayList results = new ArrayList<>(equinoxRequires.length); + for (ManifestElement equinoxRequire : equinoxRequires) { + String[] genericNames = equinoxRequire.getValueComponents(); + for (String genericName : genericNames) { + GenericSpecificationImpl spec = new GenericSpecificationImpl(); + int colonIdx = genericName.indexOf(':'); + if (colonIdx > 0) { + spec.setName(genericName.substring(0, colonIdx)); + spec.setType(genericName.substring(colonIdx + 1)); + } else { + spec.setName(genericName); + } + try { + spec.setMatchingFilter(equinoxRequire.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE), true); + } catch (InvalidSyntaxException e) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, GENERIC_REQUIRE, equinoxRequire.toString()); + throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$ + } + String optional = equinoxRequire.getAttribute(OPTIONAL_ATTR); + String multiple = equinoxRequire.getAttribute(MULTIPLE_ATTR); + int resolution = 0; + if (TRUE.equals(optional)) + resolution |= GenericSpecification.RESOLUTION_OPTIONAL; + if (TRUE.equals(multiple)) + resolution |= GenericSpecification.RESOLUTION_MULTIPLE; + spec.setResolution(resolution); + results.add(spec); + } + } + return results; + } + + private static GenericDescription[] createGenericCapabilities(ManifestElement[] equinoxCapabilities, ManifestElement[] osgiCapabilities, BundleDescription description) throws BundleException { + List result = createEquinoxCapabilities(equinoxCapabilities); + result = createOSGiCapabilities(osgiCapabilities, result, description); + return result == null ? null : result.toArray(new GenericDescription[result.size()]); + } + + static List createOSGiCapabilities(ManifestElement[] osgiCapabilities, List result, BundleDescription description) throws BundleException { + if (result == null) + result = new ArrayList<>(osgiCapabilities == null ? 1 : osgiCapabilities.length + 1); + // Always have an osgi.identity capability if there is a symbolic name. + GenericDescription osgiIdentity = createOsgiIdentityCapability(description); + if (osgiIdentity != null) + // always add the capability to the front + result.add(0, osgiIdentity); + return createOSGiCapabilities(osgiCapabilities, result, (Integer) null); + } + + static List createOSGiCapabilities(ManifestElement[] osgiCapabilities, List result, Integer profileIndex) throws BundleException { + if (osgiCapabilities == null) + return result; + if (result == null) + result = new ArrayList<>(osgiCapabilities.length); + + for (ManifestElement element : osgiCapabilities) { + String[] namespaces = element.getValueComponents(); + for (String namespace : namespaces) { + if (IdentityNamespace.IDENTITY_NAMESPACE.equals(namespace)) + throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$ + + GenericDescriptionImpl desc = new GenericDescriptionImpl(); + desc.setType(namespace); + Map mapAttrs = getAttributes(element, new String[0]); + if (profileIndex != null) + mapAttrs.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex); + Dictionary attrs = mapAttrs == null ? new Hashtable<>() : new Hashtable<>(mapAttrs); + desc.setAttributes(attrs); + Map directives = new HashMap<>(); + Enumeration keys = element.getDirectiveKeys(); + if (keys != null) + for (keys = element.getDirectiveKeys(); keys.hasMoreElements();) { + String key = keys.nextElement(); + directives.put(key, element.getDirective(key)); + } + desc.setDirectives(directives); + result.add(desc); + } + } + return result; + } + + private static List createEquinoxCapabilities(ManifestElement[] equinoxCapabilities) throws BundleException { + if (equinoxCapabilities == null) + return null; + ArrayList results = new ArrayList<>(equinoxCapabilities.length); + for (ManifestElement equinoxCapability : equinoxCapabilities) { + String[] genericNames = equinoxCapability.getValueComponents(); + for (String genericName : genericNames) { + GenericDescriptionImpl desc = new GenericDescriptionImpl(); + String name = genericName; + int colonIdx = genericName.indexOf(':'); + if (colonIdx > 0) { + name = genericName.substring(0, colonIdx); + desc.setType(genericName.substring(colonIdx + 1)); + if (IdentityNamespace.IDENTITY_NAMESPACE.equals(desc.getType())) + throw new BundleException("A bundle is not allowed to define a capability in the " + IdentityNamespace.IDENTITY_NAMESPACE + " name space."); //$NON-NLS-1$ //$NON-NLS-2$ + } + Map mapAttrs = getAttributes(equinoxCapability, new String[] {Constants.VERSION_ATTRIBUTE}); + Dictionary attrs = mapAttrs == null ? new Hashtable<>() : new Hashtable<>(mapAttrs); + attrs.put(desc.getType(), name); + String versionString = equinoxCapability.getAttribute(Constants.VERSION_ATTRIBUTE); + if (versionString != null) + attrs.put(Constants.VERSION_ATTRIBUTE, Version.parseVersion(versionString)); + desc.setAttributes(attrs); + results.add(desc); + } + } + return results; + } + + private static NativeCodeSpecification createNativeCode(ManifestElement[] nativeCode) throws BundleException { + if (nativeCode == null) + return null; + NativeCodeSpecificationImpl result = new NativeCodeSpecificationImpl(); + result.setName(Constants.BUNDLE_NATIVECODE); + int length = nativeCode.length; + if (length > 0 && nativeCode[length - 1].getValue().equals("*")) { //$NON-NLS-1$ + result.setOptional(true); + length--; + } + NativeCodeDescriptionImpl[] suppliers = new NativeCodeDescriptionImpl[length]; + for (int i = 0; i < length; i++) { + suppliers[i] = createNativeCodeDescription(nativeCode[i]); + } + result.setPossibleSuppliers(suppliers); + return result; + } + + private static NativeCodeDescriptionImpl createNativeCodeDescription(ManifestElement manifestElement) throws BundleException { + NativeCodeDescriptionImpl result = new NativeCodeDescriptionImpl(); + result.setName(Constants.BUNDLE_NATIVECODE); + result.setNativePaths(manifestElement.getValueComponents()); + result.setOSNames(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_OSNAME)); + result.setProcessors(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_PROCESSOR)); + result.setOSVersions(createVersionRanges(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_OSVERSION))); + result.setLanguages(manifestElement.getAttributes(Constants.BUNDLE_NATIVECODE_LANGUAGE)); + try { + result.setFilter(manifestElement.getAttribute(Constants.SELECTION_FILTER_ATTRIBUTE)); + } catch (InvalidSyntaxException e) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, Constants.BUNDLE_NATIVECODE, manifestElement.toString()); + throw new BundleException(message + " : " + Constants.SELECTION_FILTER_ATTRIBUTE, BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$ + } + return result; + } + + private static VersionRange[] createVersionRanges(String[] ranges) { + if (ranges == null) + return null; + VersionRange[] result = new VersionRange[ranges.length]; + for (int i = 0; i < result.length; i++) + result[i] = new VersionRange(ranges[i]); + return result; + } + + private static VersionRange getVersionRange(String versionRange) { + if (versionRange == null) + return null; + return new VersionRange(versionRange); + } + + public static void checkImportExportSyntax(String headerKey, ManifestElement[] elements, boolean export, boolean dynamic, boolean jreBundle) throws BundleException { + if (elements == null) + return; + int length = elements.length; + Set packages = new HashSet<>(length); + for (int i = 0; i < length; i++) { + // check for duplicate imports + String[] packageNames = elements[i].getValueComponents(); + for (String packageName : packageNames) { + if (!export && !dynamic && packages.contains(packageName)) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_DUPLICATES, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + // check for java.* + if (export && !jreBundle && packageName.startsWith("java.")) { //$NON-NLS-1$ + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_PACKAGE_JAVA, packageName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + packages.add(packageName); + } + // check for version/specification version mismatch + String version = elements[i].getAttribute(Constants.VERSION_ATTRIBUTE); + if (version != null) { + String specVersion = elements[i].getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION); + if (specVersion != null && !specVersion.equals(version)) + throw new BundleException(NLS.bind(StateMsg.HEADER_VERSION_ERROR, Constants.VERSION_ATTRIBUTE, Constants.PACKAGE_SPECIFICATION_VERSION), BundleException.MANIFEST_ERROR); + } + // check for bundle-symbolic-name and bundle-verion attibures + // (failure) + if (export) { + if (elements[i].getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE) != null) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + if (elements[i].getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) != null) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[i].toString()); + throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_EXPORT_ATTR_ERROR, Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.EXPORT_PACKAGE), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + } + } + + private static void checkForDuplicateDirectivesAttributes(String headerKey, ManifestElement[] elements) throws BundleException { + // check for duplicate directives + for (ManifestElement element : elements) { + Enumeration directiveKeys = element.getDirectiveKeys(); + if (directiveKeys != null) { + while (directiveKeys.hasMoreElements()) { + String key = directiveKeys.nextElement(); + String[] directives = element.getDirectives(key); + if (directives.length > 1) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString()); + throw new BundleException(NLS.bind(message + " : " + StateMsg.HEADER_DIRECTIVE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + } + Enumeration attrKeys = element.getKeys(); + if (attrKeys != null) { + while (attrKeys.hasMoreElements()) { + String key = attrKeys.nextElement(); + String[] attrs = element.getAttributes(key); + if (attrs.length > 1) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, element.toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_ATTRIBUTE_DUPLICATES, key), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + } + } + } + + private static void checkExtensionBundle(String headerKey, ManifestElement[] elements) throws BundleException { + if (elements.length == 0 || elements[0].getDirective(Constants.EXTENSION_DIRECTIVE) == null) + return; + String hostName = elements[0].getValue(); + // XXX: The extension bundle check is done against system.bundle and org.eclipse.osgi + if (!hostName.equals(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) && !hostName.equals(EquinoxContainer.NAME)) { + String message = NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, headerKey, elements[0].toString()); + throw new BundleException(message + " : " + NLS.bind(StateMsg.HEADER_EXTENSION_ERROR, hostName), BundleException.MANIFEST_ERROR); //$NON-NLS-1$ + } + } + + static GenericDescription createOsgiIdentityCapability(BundleDescription description) { + if (description.getSymbolicName() == null) + return null; + GenericDescriptionImpl result = new GenericDescriptionImpl(); + result.setType(IdentityNamespace.IDENTITY_NAMESPACE); + Dictionary attributes = new Hashtable<>(description.getDeclaredAttributes()); + // remove osgi.wiring.bundle and bundle-version attributes + attributes.remove(BundleNamespace.BUNDLE_NAMESPACE); + attributes.remove(Constants.BUNDLE_VERSION_ATTRIBUTE); + attributes.put(IdentityNamespace.IDENTITY_NAMESPACE, description.getSymbolicName()); + attributes.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, description.getHost() == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT); + attributes.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, description.getVersion()); + result.setAttributes(attributes); + Map directives = new HashMap<>(description.getDeclaredDirectives()); + // remove defaults directive values + if (!description.isSingleton()) + directives.remove(Constants.SINGLETON_DIRECTIVE); + if (description.attachFragments() && description.dynamicFragments()) + directives.remove(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE); + result.setDirectives(directives); + return result; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateImpl.java new file mode 100644 index 0000000000..e63cda90d8 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateImpl.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2003, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Danail Nachev - ProSyst - bug 218625 + * Rob Harrop - SpringSource Inc. (bug 247522) + * Karsten Thoms (itemis) - bug 535341 + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import org.osgi.framework.Constants; + +public abstract class StateImpl { + + public static final String ECLIPSE_PLATFORMFILTER = "Eclipse-PlatformFilter"; //$NON-NLS-1$ + public static final String Eclipse_JREBUNDLE = "Eclipse-JREBundle"; //$NON-NLS-1$ + /** + * Manifest Export-Package directive indicating that the exported package should only + * be made available when the resolver is not in strict mode. + */ + public static final String INTERNAL_DIRECTIVE = "x-internal"; //$NON-NLS-1$ + + /** + * Manifest Export-Package directive indicating that the exported package should only + * be made available to friends of the exporting bundle. + */ + public static final String FRIENDS_DIRECTIVE = "x-friends"; //$NON-NLS-1$ + + /** + * Manifest header (named "Provide-Package") + * identifying the packages name + * provided to other bundles which require the bundle. + * + *

+ * NOTE: this is only used for backwards compatibility, bundles manifest using + * syntax version 2 will not recognize this header. + * + *

The attribute value may be retrieved from the + * Dictionary object returned by the Bundle.getHeaders method. + * @deprecated + */ + @Deprecated + public final static String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$ + + /** + * Manifest header attribute (named "reprovide") + * for Require-Bundle + * identifying that any packages that are provided + * by the required bundle must be reprovided by the requiring bundle. + * The default value is false. + *

+ * The attribute value is encoded in the Require-Bundle manifest + * header like: + *

+	 * Require-Bundle: com.acme.module.test; reprovide="true"
+	 * 
+ *

+ * NOTE: this is only used for backwards compatibility, bundles manifest using + * syntax version 2 will not recognize this attribute. + * @deprecated + */ + @Deprecated + public final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$ + + /** + * Manifest header attribute (named "optional") + * for Require-Bundle + * identifying that a required bundle is optional and that + * the requiring bundle can be resolved if there is no + * suitable required bundle. + * The default value is false. + * + *

The attribute value is encoded in the Require-Bundle manifest + * header like: + *

+	 * Require-Bundle: com.acme.module.test; optional="true"
+	 * 
+ *

+ * NOTE: this is only used for backwards compatibility, bundles manifest using + * syntax version 2 will not recognize this attribute. + * @since 1.3 EXPERIMENTAL + * @deprecated + */ + @Deprecated + public final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$ + + public static final String OSGI_RESOLVER_MODE = "osgi.resolverMode"; //$NON-NLS-1$ + public static final String STRICT_MODE = "strict"; //$NON-NLS-1$ + public static final String DEVELOPMENT_MODE = "development"; //$NON-NLS-1$ + + public static final String STATE_SYSTEM_BUNDLE = "osgi.system.bundle"; //$NON-NLS-1$ + + private static final String OSGI_OS = "osgi.os"; //$NON-NLS-1$ + private static final String OSGI_WS = "osgi.ws"; //$NON-NLS-1$ + private static final String OSGI_NL = "osgi.nl"; //$NON-NLS-1$ + private static final String OSGI_ARCH = "osgi.arch"; //$NON-NLS-1$ + public static final String[] PROPS = {OSGI_OS, OSGI_WS, OSGI_NL, OSGI_ARCH, Constants.FRAMEWORK_SYSTEMPACKAGES, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, OSGI_RESOLVER_MODE, Constants.FRAMEWORK_EXECUTIONENVIRONMENT, "osgi.resolveOptional", "osgi.genericAliases", Constants.FRAMEWORK_OS_NAME, Constants.FRAMEWORK_OS_VERSION, Constants.FRAMEWORK_PROCESSOR, Constants.FRAMEWORK_LANGUAGE, STATE_SYSTEM_BUNDLE, Constants.FRAMEWORK_SYSTEMCAPABILITIES, Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA}; //$NON-NLS-1$ //$NON-NLS-2$ + public static final String OSGI_EE_NAMESPACE = "osgi.ee"; //$NON-NLS-1$ + + +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateMessages.properties b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateMessages.properties new file mode 100644 index 0000000000..b1a5c47147 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateMessages.properties @@ -0,0 +1,38 @@ +############################################################################### +# Copyright (c) 2004, 2018 IBM Corporation and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### + +#State/Resolver Messages for EN locale +BUNDLE_NOT_IN_STATE=The bundle is not in the state: {0} +BUNDLE_IN_OTHER_STATE=The bundle belongs to another state: {0} +BUNDLE_PENDING_REMOVE_STATE = The bundle is pending remove in another state: {0} + +HEADER_REQUIRED=The \"{0}\" header must be specified +HEADER_PACKAGE_DUPLICATES=Cannot import a package more than once \"{0}\" +HEADER_PACKAGE_JAVA=Cannot specify java.* packages in Export headers \"{0}\" +HEADER_VERSION_ERROR=The attributes \"{0}\" and \"{1}\" must match +HEADER_EXPORT_ATTR_ERROR=Specifying \"{0}\" in the \"{1}\" header is not permitted +HEADER_DIRECTIVE_DUPLICATES=Duplicate directives are not permitted \"{0}\" +HEADER_ATTRIBUTE_DUPLICATES=Duplicate attributes are not permitted \"{0}\" +HEADER_EXTENSION_ERROR=Extension bundle is not a fragment to the system bundle \"{0}\" + +RES_ERROR_DISABLEDBUNDLE=The bundle is disabled: {0} +RES_ERROR_MISSING_PERMISSION=Missing Permission: {0} +RES_ERROR_MISSING_CONSTRAINT=Missing Constraint: {0} +RES_ERROR_FRAGMENT_CONFLICT=Constraints from the fragment conflict with the host: {0} +RES_ERROR_USES_CONFLICT=Package uses conflict: {0} +RES_ERROR_SINGLETON_CONFLICT=Another singleton version selected: {0} +RES_ERROR_PLATFORM_FILTER=Platform filter did not match: {0} +RES_ERROR_NO_NATIVECODE_MATCH=No match found for native code: {0} +RES_ERROR_NATIVECODE_PATH_INVALID=The native code paths cannot be found: {0} +RES_ERROR_UNKNOWN=Unknown resolution error diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateMsg.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateMsg.java new file mode 100644 index 0000000000..2fd60d42bf --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateMsg.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2004, 2011 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import org.eclipse.osgi.util.NLS; + +public class StateMsg extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.osgi.internal.resolver.StateMessages"; //$NON-NLS-1$ + + public static String BUNDLE_NOT_IN_STATE; + public static String BUNDLE_IN_OTHER_STATE; + public static String BUNDLE_PENDING_REMOVE_STATE; + + public static String HEADER_REQUIRED; + public static String HEADER_PACKAGE_DUPLICATES; + public static String HEADER_PACKAGE_JAVA; + public static String HEADER_VERSION_ERROR; + public static String HEADER_EXPORT_ATTR_ERROR; + public static String HEADER_DIRECTIVE_DUPLICATES; + public static String HEADER_ATTRIBUTE_DUPLICATES; + public static String HEADER_EXTENSION_ERROR; + + public static String RES_ERROR_DISABLEDBUNDLE; + public static String RES_ERROR_MISSING_PERMISSION; + public static String RES_ERROR_MISSING_CONSTRAINT; + public static String RES_ERROR_FRAGMENT_CONFLICT; + public static String RES_ERROR_USES_CONFLICT; + public static String RES_ERROR_SINGLETON_CONFLICT; + public static String RES_ERROR_PLATFORM_FILTER; + public static String RES_ERROR_NO_NATIVECODE_MATCH; + public static String RES_ERROR_NATIVECODE_PATH_INVALID; + public static String RES_ERROR_UNKNOWN; + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, StateMsg.class); + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateObjectFactoryImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateObjectFactoryImpl.java new file mode 100644 index 0000000000..fd652f0746 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/StateObjectFactoryImpl.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Dictionary; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.State; +import org.osgi.framework.BundleException; + +public class StateObjectFactoryImpl { + public BundleDescription createBundleDescription(State xxx, Dictionary manifest, String location, + long xxxx) throws BundleException { + BundleDescriptionImpl result = StateBuilder.createBundleDescription(null, manifest, location); + result.setBundleId(1); + return result; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/Tokenizer.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/Tokenizer.java new file mode 100644 index 0000000000..ccf5467145 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/Tokenizer.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2003, 2016 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simple tokenizer class. Used to parse data. + */ +public class Tokenizer { + protected char value[]; + protected int max; + protected int cursor; + + public Tokenizer(String value) { + this.value = value.toCharArray(); + max = this.value.length; + cursor = 0; + } + + private void skipWhiteSpace() { + char[] val = value; + int cur = cursor; + + for (; cur < max; cur++) { + char c = val[cur]; + if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')) { + continue; + } + break; + } + cursor = cur; + } + + public String getToken(String terminals) { + skipWhiteSpace(); + char[] val = value; + int cur = cursor; + + int begin = cur; + for (; cur < max; cur++) { + char c = val[cur]; + if ((terminals.indexOf(c) != -1)) { + break; + } + } + cursor = cur; + int count = cur - begin; + if (count > 0) { + skipWhiteSpace(); + while (count > 0 && (val[begin + count - 1] == ' ' || val[begin + count - 1] == '\t')) + count--; + return (new String(val, begin, count)); + } + return (null); + } + + public String getEscapedToken(String terminals) { + char[] val = value; + int cur = cursor; + if (cur >= max) + return null; + StringBuilder sb = new StringBuilder(); + char c; + for (; cur < max; cur++) { + c = val[cur]; + // this is an escaped char + if (c == '\\') { + cur++; // skip the escape char + if (cur == max) + break; + c = val[cur]; // include the escaped char + } else if (terminals.indexOf(c) != -1) { + break; + } + sb.append(c); + } + + cursor = cur; + return sb.toString(); + } + + public List getEscapedTokens(String terminals) { + List result = new ArrayList<>(); + for (String token = getEscapedToken(terminals); token != null; token = getEscapedToken(terminals)) { + result.add(token); + getChar(); // consume terminal + } + return result; + } + + public String getString(String terminals, String preserveEscapes) { + skipWhiteSpace(); + char[] val = value; + int cur = cursor; + + if (cur < max) { + if (val[cur] == '\"') /* if a quoted string */ + { + StringBuilder sb = new StringBuilder(); + cur++; /* skip quote */ + char c = '\0'; + for (; cur < max; cur++) { + c = val[cur]; + // this is an escaped char + if (c == '\\') { + cur++; // skip the escape char + if (cur == max) + break; + c = val[cur]; // include the escaped char + if (preserveEscapes != null && preserveEscapes.indexOf(c) != -1) + sb.append('\\'); // must preserve escapes for c + } else if (c == '\"') { + break; + } + sb.append(c); + } + + if (c == '\"') { + cur++; + } // TODO else error; no closing quote? + + cursor = cur; + skipWhiteSpace(); + return sb.toString(); + + } + /* not a quoted string; same as token */ + return getToken(terminals); + } + return (null); + } + + public String getString(String terminals) { + return getString(terminals, null); + } + + public char getChar() { + int cur = cursor; + if (cur < max) { + cursor = cur + 1; + return (value[cur]); + } + return ('\0'); /* end of value */ + } + + public boolean hasMoreTokens() { + if (cursor < max) { + return true; + } + return false; + } +} diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/VersionConstraintImpl.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/VersionConstraintImpl.java new file mode 100644 index 0000000000..c41ab69dda --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/statefactory/VersionConstraintImpl.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2003, 2012 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Danail Nachev - ProSyst - bug 218625 + * Rob Harrop - SpringSource Inc. (bug 247522) + *******************************************************************************/ +package org.eclipse.equinox.p2.publisher.eclipse.statefactory; + +import java.util.Collections; +import java.util.Map; +import org.eclipse.equinox.p2.publisher.eclipse.statefactory.BaseDescriptionImpl.BaseCapability; +import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.eclipse.osgi.util.ManifestElement; +import org.osgi.framework.*; +import org.osgi.framework.namespace.AbstractWiringNamespace; +import org.osgi.framework.wiring.*; +import org.osgi.resource.Capability; +import org.osgi.resource.Namespace; + +abstract class VersionConstraintImpl implements VersionConstraint { + + protected final Object monitor = new Object(); + + private String name; + private VersionRange versionRange; + private BundleDescription bundle; + private BaseDescription supplier; + private volatile Object userObject; + + public String getName() { + synchronized (this.monitor) { + if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(name)) { + return EquinoxContainer.NAME; + } + return name; + } + } + + public VersionRange getVersionRange() { + synchronized (this.monitor) { + if (versionRange == null) + return VersionRange.emptyRange; + return versionRange; + } + } + + public BundleDescription getBundle() { + synchronized (this.monitor) { + return bundle; + } + } + + public boolean isResolved() { + synchronized (this.monitor) { + return supplier != null; + } + } + + public BaseDescription getSupplier() { + synchronized (this.monitor) { + return supplier; + } + } + + public boolean isSatisfiedBy(BaseDescription candidate) { + synchronized (this.monitor) { + return false; + } + } + + protected void setName(String name) { + synchronized (this.monitor) { + this.name = name; + } + } + + protected void setVersionRange(VersionRange versionRange) { + synchronized (this.monitor) { + this.versionRange = versionRange; + } + } + + protected void setBundle(BundleDescription bundle) { + synchronized (this.monitor) { + this.bundle = bundle; + } + } + + protected void setSupplier(BaseDescription supplier) { + synchronized (this.monitor) { + this.supplier = supplier; + } + } + + protected abstract String getInternalNameSpace(); + + protected abstract Map getInternalDirectives(); + + protected abstract Map getInteralAttributes(); + + protected abstract boolean hasMandatoryAttributes(String[] mandatory); + + public BundleRequirement getRequirement() { + String namespace = getInternalNameSpace(); + if (namespace == null) + return null; + return new BundleRequirementImpl(namespace); + } + + public Object getUserObject() { + return userObject; + } + + public void setUserObject(Object userObject) { + this.userObject = userObject; + } + + class BundleRequirementImpl implements BundleRequirement { + private final String namespace; + + public BundleRequirementImpl(String namespace) { + this.namespace = namespace; + } + + public String getNamespace() { + return namespace; + } + + public Map getDirectives() { + return Collections.unmodifiableMap(getInternalDirectives()); + } + + public Map getAttributes() { + return Collections.unmodifiableMap(getInteralAttributes()); + } + + public BundleRevision getRevision() { + return getBundle(); + } + + public boolean matches(BundleCapability capability) { + return isSatisfiedBy(((BaseCapability) capability).getBaseDescription()); + } + + @Override + public int hashCode() { + return System.identityHashCode(VersionConstraintImpl.this); + } + + private VersionConstraintImpl getVersionConstraint() { + return VersionConstraintImpl.this; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof BundleRequirementImpl)) + return false; + return ((BundleRequirementImpl) obj).getVersionConstraint() == VersionConstraintImpl.this; + } + + @Override + public String toString() { + return getNamespace() + BaseDescriptionImpl.toString(getAttributes(), false) + BaseDescriptionImpl.toString(getDirectives(), true); + } + + public boolean matches(Capability capability) { + if (capability instanceof BundleCapability) + return matches((BundleCapability) capability); + // now we must do the generic thing + if (!namespace.equals(capability.getNamespace())) + return false; + String filterSpec = getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE); + try { + if (filterSpec != null && !FrameworkUtil.createFilter(filterSpec).matches(capability.getAttributes())) + return false; + } catch (InvalidSyntaxException e) { + return false; + } + return hasMandatoryAttributes(ManifestElement.getArrayFromList(capability.getDirectives().get(AbstractWiringNamespace.CAPABILITY_MANDATORY_DIRECTIVE))); + } + + public BundleRevision getResource() { + return getRevision(); + } + } + + static StringBuilder addFilterAttributes(StringBuilder filter, Map attributes) { + for (Map.Entry entry : attributes.entrySet()) { + addFilterAttribute(filter, entry.getKey(), entry.getValue()); + } + return filter; + } + + static StringBuilder addFilterAttribute(StringBuilder filter, String attr, Object value) { + return addFilterAttribute(filter, attr, value, true); + } + + static StringBuilder addFilterAttribute(StringBuilder filter, String attr, Object value, boolean escapeWildCard) { + if (value instanceof VersionRange) { + VersionRange range = (VersionRange) value; + filter.append(range.toFilterString(attr)); + } else { + filter.append('(').append(attr).append('=').append(escapeValue(value, escapeWildCard)).append(')'); + } + return filter; + } + + private static String escapeValue(Object o, boolean escapeWildCard) { + String value = o.toString(); + boolean escaped = false; + int inlen = value.length(); + int outlen = inlen << 1; /* inlen * 2 */ + + char[] output = new char[outlen]; + value.getChars(0, inlen, output, inlen); + + int cursor = 0; + for (int i = inlen; i < outlen; i++) { + char c = output[i]; + switch (c) { + case '*' : + if (!escapeWildCard) + break; + case '\\' : + case '(' : + case ')' : + output[cursor] = '\\'; + cursor++; + escaped = true; + break; + } + + output[cursor] = c; + cursor++; + } + + return escaped ? new String(output, 0, cursor) : value; + } +}