| /******************************************************************************* |
| * Copyright (c) 2004, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.module; |
| |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.framework.wiring.Capability; |
| |
| /* |
| * A companion to BundleDescription from the state used while resolving. |
| */ |
| public class ResolverBundle extends VersionSupplier implements Comparable, BundleRevision { |
| public static final int UNRESOLVED = 0; |
| public static final int RESOLVING = 1; |
| public static final int RESOLVED = 2; |
| |
| private final Long bundleID; |
| private BundleConstraint host; |
| private ResolverImport[] imports; |
| private ResolverExport[] exports; |
| private BundleConstraint[] requires; |
| private GenericCapability[] genericCapabilities; |
| private GenericConstraint[] genericReqiures; |
| // Fragment support |
| private ArrayList<ResolverBundle> fragments; |
| private HashMap<Long, List<ResolverExport>> fragmentExports; |
| private HashMap<Long, List<ResolverImport>> fragmentImports; |
| private HashMap<Long, List<BundleConstraint>> fragmentRequires; |
| private HashMap<Long, List<GenericCapability>> fragmentGenericCapabilities; |
| private HashMap<Long, List<GenericConstraint>> fragmentGenericRequires; |
| // Flag specifying whether this bundle is resolvable |
| private boolean resolvable = true; |
| // Internal resolver state for this bundle |
| private int state = UNRESOLVED; |
| private boolean uninstalled = false; |
| private final ResolverImpl resolver; |
| private boolean newFragmentExports; |
| private boolean newFragmentCapabilities; |
| private ArrayList refs; |
| |
| ResolverBundle(BundleDescription bundle, ResolverImpl resolver) { |
| super(bundle); |
| this.bundleID = new Long(bundle.getBundleId()); |
| this.resolver = resolver; |
| initialize(bundle.isResolved()); |
| } |
| |
| void initialize(boolean useSelectedExports) { |
| if (getBundleDescription().isSingleton()) |
| refs = new ArrayList(); |
| if (getBundleDescription().getHost() != null) { |
| host = new BundleConstraint(this, getBundleDescription().getHost()); |
| exports = new ResolverExport[0]; |
| imports = new ResolverImport[0]; |
| requires = new BundleConstraint[0]; |
| genericReqiures = new GenericConstraint[0]; |
| genericCapabilities = new GenericCapability[0]; |
| return; |
| } |
| |
| ImportPackageSpecification[] actualImports = getBundleDescription().getImportPackages(); |
| // Reorder imports so that optionals are at the end so that we wire statics before optionals |
| ArrayList importList = new ArrayList(actualImports.length); |
| for (int i = actualImports.length - 1; i >= 0; i--) |
| if (ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(actualImports[i].getDirective(Constants.RESOLUTION_DIRECTIVE))) |
| importList.add(new ResolverImport(this, actualImports[i])); |
| else |
| importList.add(0, new ResolverImport(this, actualImports[i])); |
| imports = (ResolverImport[]) importList.toArray(new ResolverImport[importList.size()]); |
| |
| ExportPackageDescription[] actualExports = useSelectedExports ? getBundleDescription().getSelectedExports() : getBundleDescription().getExportPackages(); |
| exports = new ResolverExport[actualExports.length]; |
| for (int i = 0; i < actualExports.length; i++) |
| exports[i] = new ResolverExport(this, actualExports[i]); |
| |
| BundleSpecification[] actualRequires = getBundleDescription().getRequiredBundles(); |
| requires = new BundleConstraint[actualRequires.length]; |
| for (int i = 0; i < requires.length; i++) |
| requires[i] = new BundleConstraint(this, actualRequires[i]); |
| |
| GenericSpecification[] actualGenericRequires = getBundleDescription().getGenericRequires(); |
| genericReqiures = new GenericConstraint[actualGenericRequires.length]; |
| for (int i = 0; i < genericReqiures.length; i++) |
| genericReqiures[i] = new GenericConstraint(this, actualGenericRequires[i]); |
| |
| GenericDescription[] actualCapabilities = useSelectedExports ? getBundleDescription().getSelectedGenericCapabilities() : getBundleDescription().getGenericCapabilities(); |
| genericCapabilities = new GenericCapability[actualCapabilities.length]; |
| for (int i = 0; i < genericCapabilities.length; i++) |
| genericCapabilities[i] = new GenericCapability(this, actualCapabilities[i]); |
| |
| fragments = null; |
| fragmentExports = null; |
| fragmentImports = null; |
| fragmentRequires = null; |
| fragmentGenericCapabilities = null; |
| fragmentGenericRequires = null; |
| } |
| |
| ResolverExport getExport(String name) { |
| ResolverExport[] allExports = getExports(name); |
| return allExports.length == 0 ? null : allExports[0]; |
| } |
| |
| ResolverExport[] getExports(String name) { |
| ArrayList results = new ArrayList(1); // rare to have more than one |
| // it is faster to ask the VersionHashMap for this package name and then compare the exporter to this |
| ResolverExport[] resolverExports = resolver.getResolverExports().get(name); |
| for (ResolverExport export : resolverExports) |
| if (export.getExporter() == this) |
| results.add(export); |
| return (ResolverExport[]) results.toArray(new ResolverExport[results.size()]); |
| } |
| |
| void clearWires() { |
| ResolverImport[] allImports = getImportPackages(); |
| for (int i = 0; i < allImports.length; i++) |
| allImports[i].clearPossibleSuppliers(); |
| |
| if (host != null) |
| host.clearPossibleSuppliers(); |
| |
| BundleConstraint[] allRequires = getRequires(); |
| for (int i = 0; i < allRequires.length; i++) |
| allRequires[i].clearPossibleSuppliers(); |
| |
| GenericConstraint[] allGenericRequires = getGenericRequires(); |
| for (int i = 0; i < allGenericRequires.length; i++) |
| allGenericRequires[i].setMatchingCapability(null); |
| |
| ResolverExport[] allExports = getExportPackages(); |
| for (int i = 0; i < allExports.length; i++) |
| allExports[i].setSubstitute(null); |
| } |
| |
| boolean isResolved() { |
| return getState() == ResolverBundle.RESOLVED; |
| } |
| |
| boolean isFragment() { |
| return host != null; |
| } |
| |
| int getState() { |
| return state; |
| } |
| |
| void setState(int state) { |
| this.state = state; |
| } |
| |
| private <T> List<T> getAll(T[] hostEntries, Map<Long, List<T>> fragmentMap) { |
| List<T> result = new ArrayList<T>(hostEntries.length); |
| for (T entry : hostEntries) |
| result.add(entry); |
| for (ResolverBundle fragment : fragments) { |
| List<T> fragEntries = fragmentMap.get(fragment.bundleID); |
| if (fragEntries != null) |
| result.addAll(fragEntries); |
| } |
| return result; |
| } |
| |
| ResolverImport[] getImportPackages() { |
| if (isFragment() || fragments == null || fragments.size() == 0) |
| return imports; |
| List<ResolverImport> result = getAll(imports, fragmentImports); |
| return result.toArray(new ResolverImport[result.size()]); |
| } |
| |
| ResolverExport[] getExportPackages() { |
| if (isFragment() || fragments == null || fragments.size() == 0) |
| return exports; |
| List<ResolverExport> result = getAll(exports, fragmentExports); |
| return result.toArray(new ResolverExport[result.size()]); |
| } |
| |
| ResolverExport[] getSelectedExports() { |
| return getExports(true); |
| } |
| |
| ResolverExport[] getSubstitutedExports() { |
| return getExports(false); |
| } |
| |
| private ResolverExport[] getExports(boolean selected) { |
| ResolverExport[] results = getExportPackages(); |
| int removedExports = 0; |
| for (int i = 0; i < results.length; i++) |
| if (selected ? results[i].getSubstitute() != null : results[i].getSubstitute() == null) |
| removedExports++; |
| if (removedExports == 0) |
| return results; |
| ResolverExport[] selectedExports = new ResolverExport[results.length - removedExports]; |
| int index = 0; |
| for (int i = 0; i < results.length; i++) { |
| if (selected ? results[i].getSubstitute() != null : results[i].getSubstitute() == null) |
| continue; |
| selectedExports[index] = results[i]; |
| index++; |
| } |
| return selectedExports; |
| } |
| |
| BundleConstraint getHost() { |
| return host; |
| } |
| |
| GenericCapability[] getGenericCapabilities() { |
| if (isFragment() || fragments == null || fragments.size() == 0) |
| return genericCapabilities; |
| List<GenericCapability> result = getAll(genericCapabilities, fragmentGenericCapabilities); |
| return result.toArray(new GenericCapability[result.size()]); |
| } |
| |
| BundleConstraint[] getRequires() { |
| if (isFragment() || fragments == null || fragments.size() == 0) |
| return requires; |
| List<BundleConstraint> result = getAll(requires, fragmentRequires); |
| return result.toArray(new BundleConstraint[result.size()]); |
| } |
| |
| GenericConstraint[] getGenericRequires() { |
| if (isFragment() || fragments == null || fragments.size() == 0) |
| return genericReqiures; |
| List<GenericConstraint> result = getAll(genericReqiures, fragmentGenericRequires); |
| return result.toArray(new GenericConstraint[result.size()]); |
| } |
| |
| BundleConstraint getRequire(String name) { |
| BundleConstraint[] allRequires = getRequires(); |
| for (int i = 0; i < allRequires.length; i++) |
| if (allRequires[i].getVersionConstraint().getName().equals(name)) |
| return allRequires[i]; |
| return null; |
| } |
| |
| public BundleDescription getBundleDescription() { |
| return (BundleDescription) getBaseDescription(); |
| } |
| |
| public ResolverBundle getResolverBundle() { |
| return this; |
| } |
| |
| ResolverImport getImport(String name) { |
| ResolverImport[] allImports = getImportPackages(); |
| for (int i = 0; i < allImports.length; i++) { |
| if (allImports[i].getName().equals(name)) { |
| return allImports[i]; |
| } |
| } |
| return null; |
| } |
| |
| public String toString() { |
| return "[" + getBundleDescription() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private void initFragments() { |
| if (fragments == null) |
| fragments = new ArrayList(1); |
| if (fragmentExports == null) |
| fragmentExports = new HashMap(1); |
| if (fragmentImports == null) |
| fragmentImports = new HashMap(1); |
| if (fragmentRequires == null) |
| fragmentRequires = new HashMap(1); |
| if (fragmentGenericCapabilities == null) |
| fragmentGenericCapabilities = new HashMap(1); |
| if (fragmentGenericRequires == null) |
| fragmentGenericRequires = new HashMap(1); |
| } |
| |
| private boolean isImported(String packageName) { |
| ResolverImport[] allImports = getImportPackages(); |
| for (int i = 0; i < allImports.length; i++) |
| if (packageName.equals(allImports[i].getName())) |
| return true; |
| |
| return false; |
| } |
| |
| private boolean isRequired(String bundleName) { |
| return getRequire(bundleName) != null; |
| } |
| |
| void attachFragment(ResolverBundle fragment, boolean dynamicAttach) { |
| if (isFragment()) |
| return; // cannot attach to fragments; |
| if (!getBundleDescription().attachFragments() || (isResolved() && !getBundleDescription().dynamicFragments())) |
| return; // host is restricting attachment |
| if (fragment.getHost().getNumPossibleSuppliers() > 0 && !((HostSpecification) fragment.getHost().getVersionConstraint()).isMultiHost()) |
| return; // fragment is restricting attachment |
| |
| ImportPackageSpecification[] newImports = fragment.getBundleDescription().getImportPackages(); |
| BundleSpecification[] newRequires = fragment.getBundleDescription().getRequiredBundles(); |
| ExportPackageDescription[] newExports = fragment.getBundleDescription().getExportPackages(); |
| GenericDescription[] newGenericCapabilities = fragment.getBundleDescription().getGenericCapabilities(); |
| GenericSpecification[] newGenericRequires = fragment.getBundleDescription().getGenericRequires(); |
| |
| // if this is not during initialization then check if constraints conflict |
| if (dynamicAttach && constraintsConflict(fragment.getBundleDescription(), newImports, newRequires, newGenericRequires)) |
| return; // do not allow fragments with conflicting constraints |
| if (isResolved() && newExports.length > 0) |
| fragment.setNewFragmentExports(true); |
| if (isResolved() && newGenericCapabilities.length > 0) |
| fragment.setNewFragmentCapabilities(true); |
| |
| initFragments(); |
| // need to make sure there is not already another version of this fragment |
| // already attached to this host |
| for (Iterator iFragments = fragments.iterator(); iFragments.hasNext();) { |
| ResolverBundle existingFragment = (ResolverBundle) iFragments.next(); |
| String bsn = existingFragment.getName(); |
| if (bsn != null && bsn.equals(fragment.getName())) |
| return; |
| } |
| if (fragments.contains(fragment)) |
| return; |
| fragments.add(fragment); |
| fragment.getHost().addPossibleSupplier(this); |
| |
| if (newImports.length > 0) { |
| ArrayList<ResolverImport> hostImports = new ArrayList(newImports.length); |
| for (int i = 0; i < newImports.length; i++) |
| if (!isImported(newImports[i].getName())) |
| hostImports.add(new ResolverImport(this, newImports[i])); |
| fragmentImports.put(fragment.bundleID, hostImports); |
| } |
| |
| if (newRequires.length > 0) { |
| ArrayList<BundleConstraint> hostRequires = new ArrayList(newRequires.length); |
| for (int i = 0; i < newRequires.length; i++) |
| if (!isRequired(newRequires[i].getName())) |
| hostRequires.add(new BundleConstraint(this, newRequires[i])); |
| fragmentRequires.put(fragment.bundleID, hostRequires); |
| } |
| |
| if (newGenericRequires.length > 0) { |
| ArrayList hostGenericRequires = new ArrayList(newGenericRequires.length); |
| for (int i = 0; i < newGenericRequires.length; i++) |
| hostGenericRequires.add(new GenericConstraint(this, newGenericRequires[i])); |
| fragmentGenericRequires.put(fragment.bundleID, hostGenericRequires); |
| } |
| |
| ArrayList<ResolverExport> hostExports = new ArrayList(newExports.length); |
| if (newExports.length > 0 && dynamicAttach) { |
| StateObjectFactory factory = resolver.getState().getFactory(); |
| for (int i = 0; i < newExports.length; i++) { |
| ResolverExport currentExports[] = getExports(newExports[i].getName()); |
| boolean foundEquivalent = false; |
| for (int j = 0; j < currentExports.length && !foundEquivalent; j++) { |
| if (equivalentExports(currentExports[j], newExports[i])) |
| foundEquivalent = true; |
| } |
| if (!foundEquivalent) { |
| ExportPackageDescription hostExport = factory.createExportPackageDescription(newExports[i].getName(), newExports[i].getVersion(), newExports[i].getDirectives(), newExports[i].getAttributes(), true, getBundleDescription()); |
| hostExports.add(new ResolverExport(this, hostExport)); |
| } |
| } |
| fragmentExports.put(fragment.bundleID, hostExports); |
| } |
| |
| List<GenericCapability> hostCapabilities = new ArrayList<GenericCapability>(newGenericCapabilities.length); |
| if (newGenericCapabilities.length > 0 && dynamicAttach) { |
| StateObjectFactory factory = resolver.getState().getFactory(); |
| for (GenericDescription capability : newGenericCapabilities) { |
| Dictionary origAttrs = capability.getAttributes(); |
| Map attrs = new HashMap(); |
| if (origAttrs != null) { |
| for (Enumeration keys = origAttrs.keys(); keys.hasMoreElements();) { |
| Object key = keys.nextElement(); |
| attrs.put(key, origAttrs.get(key)); |
| } |
| } |
| GenericDescription hostCapabililty = factory.createGenericDescription(capability.getType(), attrs, null, getBundleDescription()); |
| hostCapabilities.add(new GenericCapability(this, hostCapabililty)); |
| } |
| fragmentGenericCapabilities.put(fragment.bundleID, hostCapabilities); |
| } |
| if (dynamicAttach) { |
| resolver.getResolverExports().put(hostExports.toArray(new ResolverExport[hostExports.size()])); |
| resolver.addGenerics(hostCapabilities.toArray(new GenericCapability[hostCapabilities.size()])); |
| } |
| } |
| |
| private boolean equivalentExports(ResolverExport existingExport, ExportPackageDescription newDescription) { |
| ExportPackageDescription existingDescription = existingExport.getExportPackageDescription(); |
| if (!existingDescription.getName().equals(newDescription.getName())) |
| return false; |
| if (!existingDescription.getVersion().equals(newDescription.getVersion())) |
| return false; |
| if (!equivalentMaps(existingDescription.getAttributes(), newDescription.getAttributes(), true)) |
| return false; |
| if (!equivalentMaps(existingDescription.getDirectives(), newDescription.getDirectives(), true)) |
| return false; |
| return true; |
| } |
| |
| public static boolean equivalentMaps(Map existingDirectives, Map newDirectives, boolean exactMatch) { |
| if (existingDirectives == null && newDirectives == null) |
| return true; |
| if (existingDirectives == null ? newDirectives != null : newDirectives == null) |
| return false; |
| if (exactMatch && existingDirectives.size() != newDirectives.size()) |
| return false; |
| for (Iterator entries = existingDirectives.entrySet().iterator(); entries.hasNext();) { |
| Entry entry = (Entry) entries.next(); |
| Object newValue = newDirectives.get(entry.getKey()); |
| if (newValue == null || entry.getValue().getClass() != newValue.getClass()) |
| return false; |
| if (newValue instanceof String[]) { |
| if (!Arrays.equals((Object[]) entry.getValue(), (Object[]) newValue)) |
| return false; |
| } else if (!entry.getValue().equals(newValue)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| boolean constraintsConflict(BundleDescription fragment, ImportPackageSpecification[] newImports, BundleSpecification[] newRequires, GenericSpecification[] newGenericRequires) { |
| // this method iterates over all additional constraints from a fragment |
| // if the host is resolved then the fragment is not allowed to add new constraints; |
| // if the host is resolved and it already has a constraint of the same name then ensure the supplier satisfies the fragment's constraint |
| boolean result = false; |
| for (int i = 0; i < newImports.length; i++) { |
| ResolverImport hostImport = getImport(newImports[i].getName()); |
| ResolverExport resolvedExport = (ResolverExport) (hostImport == null ? null : hostImport.getSelectedSupplier()); |
| if ((resolvedExport == null && isResolved()) || (resolvedExport != null && !newImports[i].isSatisfiedBy(resolvedExport.getExportPackageDescription()))) { |
| result = true; |
| resolver.getState().addResolverError(fragment, ResolverError.FRAGMENT_CONFLICT, newImports[i].toString(), newImports[i]); |
| } |
| } |
| for (int i = 0; i < newRequires.length; i++) { |
| BundleConstraint hostRequire = getRequire(newRequires[i].getName()); |
| ResolverBundle resolvedRequire = (ResolverBundle) (hostRequire == null ? null : hostRequire.getSelectedSupplier()); |
| if ((resolvedRequire == null && isResolved()) || (resolvedRequire != null && !newRequires[i].isSatisfiedBy(resolvedRequire.getBundleDescription()))) { |
| result = true; |
| resolver.getState().addResolverError(fragment, ResolverError.FRAGMENT_CONFLICT, newRequires[i].toString(), newRequires[i]); |
| } |
| } |
| // generic constraints cannot conflict; |
| // only check that a fragment does not add generics constraints to an already resolved host |
| if (isResolved() && newGenericRequires != null && newGenericRequires.length > 0) |
| result = true; |
| return result; |
| } |
| |
| private void setNewFragmentExports(boolean newFragmentExports) { |
| this.newFragmentExports = newFragmentExports; |
| } |
| |
| boolean isNewFragmentExports() { |
| return newFragmentExports; |
| } |
| |
| private void setNewFragmentCapabilities(boolean newFragmentCapabilities) { |
| this.newFragmentCapabilities = newFragmentCapabilities; |
| } |
| |
| boolean isNewFragmentCapabilities() { |
| return newFragmentCapabilities; |
| } |
| |
| void detachFragment(ResolverBundle fragment, ResolverConstraint reason) { |
| if (isFragment()) |
| return; |
| initFragments(); |
| |
| // must save off old imports and requires before we remove the fragment; |
| // this will be used to merge the constraints of the same name from the remaining fragments |
| ResolverImport[] oldImports = getImportPackages(); |
| BundleConstraint[] oldRequires = getRequires(); |
| if (!fragments.remove(fragment)) |
| return; |
| |
| fragment.setNewFragmentExports(false); |
| fragment.setNewFragmentCapabilities(false); |
| fragment.getHost().removePossibleSupplier(this); |
| fragmentImports.remove(fragment.bundleID); |
| fragmentRequires.remove(fragment.bundleID); |
| List<ResolverExport> removedExports = fragmentExports.remove(fragment.bundleID); |
| fragmentGenericRequires.remove(fragment.bundleID); |
| List<GenericCapability> removedCapabilities = fragmentGenericCapabilities.remove(fragment.bundleID); |
| if (reason != null) { |
| // the fragment is being detached because one of its imports or requires cannot be resolved; |
| // we need to check the remaining fragment constraints to make sure they do not have |
| // the same unresolved constraint. |
| for (ResolverBundle remainingFrag : fragments) { |
| ArrayList additionalImports = new ArrayList(0); |
| ArrayList additionalRequires = new ArrayList(0); |
| if (hasUnresolvedConstraint(reason, fragment, remainingFrag, oldImports, oldRequires, additionalImports, additionalRequires)) |
| continue; |
| // merge back the additional imports or requires which the detached fragment has in common with the remaining fragment |
| if (additionalImports.size() > 0) { |
| ArrayList remainingImports = (ArrayList) fragmentImports.get(remainingFrag.bundleID); |
| if (remainingImports == null) |
| fragmentImports.put(remainingFrag.bundleID, additionalImports); |
| else |
| remainingImports.addAll(additionalImports); |
| } |
| if (additionalRequires.size() > 0) { |
| ArrayList remainingRequires = (ArrayList) fragmentRequires.get(remainingFrag.bundleID); |
| if (remainingRequires == null) |
| fragmentRequires.put(remainingFrag.bundleID, additionalRequires); |
| else |
| remainingRequires.addAll(additionalRequires); |
| } |
| } |
| } |
| ResolverExport[] results = removedExports == null ? new ResolverExport[0] : removedExports.toArray(new ResolverExport[removedExports.size()]); |
| for (int i = 0; i < results.length; i++) |
| // TODO this is a hack; need to figure out how to indicate that a fragment export is no longer attached |
| results[i].setSubstitute(results[i]); |
| resolver.getResolverExports().remove(results); |
| if (removedCapabilities != null) |
| resolver.removeGenerics(removedCapabilities.toArray(new GenericCapability[removedCapabilities.size()])); |
| } |
| |
| private boolean hasUnresolvedConstraint(ResolverConstraint reason, ResolverBundle detachedFragment, ResolverBundle remainingFragment, ResolverImport[] oldImports, BundleConstraint[] oldRequires, ArrayList additionalImports, ArrayList additionalRequires) { |
| ImportPackageSpecification[] remainingFragImports = remainingFragment.getBundleDescription().getImportPackages(); |
| BundleSpecification[] remainingFragRequires = remainingFragment.getBundleDescription().getRequiredBundles(); |
| VersionConstraint[] constraints; |
| if (reason instanceof ResolverImport) |
| constraints = remainingFragImports; |
| else |
| constraints = remainingFragRequires; |
| for (int i = 0; i < constraints.length; i++) |
| if (reason.getName().equals(constraints[i].getName())) { |
| detachFragment(remainingFragment, null); |
| return true; |
| } |
| for (int i = 0; i < oldImports.length; i++) { |
| if (oldImports[i].getVersionConstraint().getBundle() != detachedFragment.getBundleDescription()) |
| continue; // the constraint is not from the detached fragment |
| for (int j = 0; j < remainingFragImports.length; j++) { |
| if (oldImports[i].getName().equals(remainingFragImports[j].getName())) { |
| // same constraint, must reuse the constraint object but swap out the fragment info |
| additionalImports.add(oldImports[i]); |
| oldImports[i].setVersionConstraint(remainingFragImports[j]); |
| break; |
| } |
| } |
| } |
| for (int i = 0; i < oldRequires.length; i++) { |
| if (oldRequires[i].getVersionConstraint().getBundle() != detachedFragment.getBundleDescription()) |
| continue; // the constraint is not from the detached fragment |
| for (int j = 0; j < remainingFragRequires.length; j++) { |
| if (oldRequires[i].getName().equals(remainingFragRequires[j].getName())) { |
| // same constraint, must reuse the constraint object but swap out the fragment info |
| additionalRequires.add(oldRequires[i]); |
| oldRequires[i].setVersionConstraint(remainingFragRequires[j]); |
| break; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void detachAllFragments() { |
| if (fragments == null) |
| return; |
| ResolverBundle[] allFragments = fragments.toArray(new ResolverBundle[fragments.size()]); |
| for (int i = 0; i < allFragments.length; i++) |
| detachFragment(allFragments[i], null); |
| fragments = null; |
| } |
| |
| boolean isResolvable() { |
| return resolvable; |
| } |
| |
| void setResolvable(boolean resolvable) { |
| this.resolvable = resolvable; |
| } |
| |
| void addExport(ResolverExport re) { |
| ResolverExport[] newExports = new ResolverExport[exports.length + 1]; |
| for (int i = 0; i < exports.length; i++) |
| newExports[i] = exports[i]; |
| newExports[exports.length] = re; |
| exports = newExports; |
| } |
| |
| ResolverImpl getResolver() { |
| return resolver; |
| } |
| |
| void clearRefs() { |
| if (refs != null) |
| refs.clear(); |
| } |
| |
| void addRef(ResolverBundle ref) { |
| if (((BundleDescription) getBaseDescription()).isResolved()) |
| return; // don't care when the bundle is already resolved |
| if (refs != null && !refs.contains(ref)) |
| refs.add(ref); |
| } |
| |
| int getRefs() { |
| return refs == null ? 0 : refs.size(); |
| } |
| |
| ResolverBundle[] getFragments() { |
| return fragments == null ? new ResolverBundle[0] : (ResolverBundle[]) fragments.toArray(new ResolverBundle[fragments.size()]); |
| } |
| |
| /* |
| * This is used to sort bundles by BSN. This is needed to fix bug 174930 |
| * If both BSNs are null then 0 is returned |
| * If this BSN is null the 1 is returned |
| * If the other BSN is null then -1 is returned |
| * otherwise String.compareTo is used |
| */ |
| public int compareTo(Object o) { |
| String bsn = getName(); |
| String otherBsn = ((ResolverBundle) o).getName(); |
| if (bsn == null) |
| return otherBsn == null ? 0 : 1; |
| return otherBsn == null ? -1 : bsn.compareTo(otherBsn); |
| } |
| |
| void setUninstalled() { |
| uninstalled = true; |
| } |
| |
| boolean isUninstalled() { |
| return uninstalled; |
| } |
| |
| public Bundle getBundle() { |
| return getBundleDescription().getBundle(); |
| } |
| |
| public String getSymbolicName() { |
| return getName(); |
| } |
| |
| public List<Capability> getDeclaredCapabilities(String namespace) { |
| // This gives a view of the host and attached fragments. |
| Capability[] capabilities = getGenericCapabilities(); |
| if (namespace == null) |
| return new ArrayList<Capability>(Arrays.asList(capabilities)); |
| List<Capability> result = new ArrayList<Capability>(); |
| for (Capability capability : capabilities) { |
| if (namespace.equals(capability.getNamespace())) |
| result.add(capability); |
| } |
| return result; |
| } |
| |
| public int getTypes() { |
| return getBundleDescription().getTypes(); |
| } |
| |
| public String getNamespace() { |
| return Capability.BUNDLE_CAPABILITY; |
| } |
| |
| public Map<String, String> getDirectives() { |
| return getBundleDescription().getDeclaredDirectives(); |
| } |
| |
| public Map<String, Object> getAttributes() { |
| return getBundleDescription().getDeclaredAttributes(); |
| } |
| |
| public BundleRevision getProviderRevision() { |
| return this; |
| } |
| } |