| /******************************************************************************* |
| * Copyright (c) 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.module; |
| |
| import java.util.*; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.osgi.framework.Version; |
| |
| public class ResolverBundle implements VersionSupplier { |
| public static final int UNRESOLVED = 0; |
| public static final int RESOLVING = 1; |
| public static final int RESOLVED = 2; |
| |
| private BundleDescription bundle; |
| private Long bundleID; |
| private BundleConstraint host; |
| private ResolverImport[] imports; |
| private ResolverExport[] exports; |
| private BundleConstraint[] requires; |
| // Fragment support |
| private ArrayList fragments; |
| private HashMap fragmentExports; |
| private HashMap fragmentImports; |
| private HashMap fragmentRequires; |
| // Flag specifying whether this bundle is resolvable |
| private boolean resolvable = true; |
| // Internal resolver state for this bundle |
| private int state = UNRESOLVED; |
| // Store for RESOLVING modules that this module is dependent on (cyclic dependencies) |
| private ArrayList cyclicDependencies = new ArrayList(); |
| |
| private ResolverImpl resolver; |
| |
| ResolverBundle(BundleDescription bundle, ResolverImpl resolver) { |
| this.bundle = bundle; |
| this.bundleID = new Long(bundle.getBundleId()); |
| this.resolver = resolver; |
| initialize(bundle.isResolved()); |
| } |
| |
| void initialize(boolean useSelectedExports) { |
| if (bundle.getHost() != null) { |
| host = new BundleConstraint(this, bundle.getHost()); |
| exports = new ResolverExport[0]; |
| imports = new ResolverImport[0]; |
| requires = new BundleConstraint[0]; |
| return; |
| } |
| |
| ImportPackageSpecification[] actualImports = bundle.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 ((actualImports[i].getResolution() & ImportPackageSpecification.RESOLUTION_OPTIONAL) != 0) { |
| 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; |
| if (useSelectedExports) { |
| actualExports = bundle.getSelectedExports(); |
| } else { |
| actualExports = bundle.getExportPackages(); |
| } |
| exports = new ResolverExport[actualExports.length]; |
| for (int i = 0; i < actualExports.length; i++) { |
| exports[i] = new ResolverExport(this, actualExports[i]); |
| } |
| |
| BundleSpecification[] actualRequires = bundle.getRequiredBundles(); |
| requires = new BundleConstraint[actualRequires.length]; |
| for (int i = 0; i < requires.length; i++) |
| requires[i] = new BundleConstraint(this, actualRequires[i]); |
| |
| fragments = null; |
| fragmentExports = null; |
| fragmentImports = null; |
| fragmentRequires = null; |
| } |
| |
| boolean isExported(ResolverExport exp) { |
| ResolverExport[] exports = getExportPackages(); |
| for (int i = 0; i < exports.length; i++) { |
| if (exp == exports[i]) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| ResolverImport getImport(ResolverExport exp) { |
| ResolverImport[] imports = getImportPackages(); |
| for (int i = 0; i < imports.length; i++) { |
| if (exp.getName().equals(imports[i].getName())) { |
| return imports[i]; |
| } |
| } |
| return null; |
| } |
| |
| ResolverExport getExport(ResolverImport imp) { |
| ResolverExport[] exports = getExportPackages(); |
| for (int i = 0; i < exports.length; i++) { |
| if (imp.getName().equals(exports[i].getName()) && exports[i].getExportPackageDescription().isRoot()) { |
| return exports[i]; |
| } |
| } |
| return null; |
| } |
| |
| /* |
| // Return the ResolverExport object for a given ExportPackageDescription |
| ResolverExport getExport(ExportPackageDescription exp) { |
| ResolverExport[] exports = getExportPackages(); |
| for(int i=0; i<exports.length; i++) { |
| if(exp.getName().equals(exports[i].getName())) { |
| return exports[i]; |
| } |
| } |
| return null; |
| }*/ |
| |
| ResolverExport[] getGroupedExports(String group) { |
| ArrayList result = new ArrayList(); |
| ResolverExport[] exports = getExportPackages(); |
| for (int i = 0; i < exports.length; i++) { |
| if (group.equals(exports[i].getGrouping())) { |
| result.add(exports[i]); |
| } |
| } |
| return (ResolverExport[]) result.toArray(new ResolverExport[result.size()]); |
| } |
| |
| // Iterate thru the imports making sure they are wired |
| boolean isFullyWired() { |
| if (host != null && host.foundMatchingBundles()) |
| return false; |
| |
| ResolverImport[] imports = getImportPackages(); |
| for (int i = 0; i < imports.length; i++) { |
| if (imports[i].getMatchingExport() == null && !imports[i].isOptional() && !imports[i].isDynamic()) { |
| return false; |
| } |
| } |
| |
| BundleConstraint[] requires = getRequires(); |
| for (int i = 0; i < requires.length; i++) |
| if (requires[i].getMatchingBundle() == null && !requires[i].isOptional()) |
| return false; |
| return true; |
| } |
| |
| void clearWires() { |
| ResolverImport[] allImports = getImportPackages(); |
| for (int i = 0; i < allImports.length; i++) { |
| allImports[i].setMatchingExport(null); |
| } |
| |
| if (host != null) |
| host.removeAllMatchingBundles(); |
| BundleConstraint[] allRequires = getRequires(); |
| for (int i = 0; i < allRequires.length; i++) |
| allRequires[i].setMatchingBundle(null); |
| } |
| |
| boolean isResolved() { |
| return getState() == ResolverBundle.RESOLVED; |
| } |
| |
| boolean isFragment() { |
| return host != null; |
| } |
| |
| int getState() { |
| return state; |
| } |
| |
| void setState(int state) { |
| this.state = state; |
| } |
| |
| ResolverImport[] getImportPackages() { |
| if (isFragment()) |
| return new ResolverImport[0]; |
| if (fragments == null || fragments.size() == 0) |
| return imports; |
| ArrayList resultList = new ArrayList(imports.length); |
| for (int i = 0; i < imports.length; i++) |
| resultList.add(imports[i]); |
| for (Iterator iter = fragments.iterator(); iter.hasNext();) { |
| ResolverBundle fragment = (ResolverBundle) iter.next(); |
| ArrayList fragImports = (ArrayList) fragmentImports.get(fragment.bundleID); |
| if (fragImports != null) |
| resultList.addAll(fragImports); |
| } |
| return (ResolverImport[]) resultList.toArray(new ResolverImport[resultList.size()]); |
| } |
| |
| ResolverExport[] getExportPackages() { |
| if (isFragment()) |
| return new ResolverExport[0]; |
| if (fragments == null || fragments.size() == 0) |
| return exports; |
| ArrayList resultList = new ArrayList(exports.length); |
| for (int i = 0; i < exports.length; i++) |
| resultList.add(exports[i]); |
| for (Iterator iter = fragments.iterator(); iter.hasNext();) { |
| ResolverBundle fragment = (ResolverBundle) iter.next(); |
| ArrayList fragExports = (ArrayList) fragmentExports.get(fragment.bundleID); |
| if (fragExports != null) |
| resultList.addAll(fragExports); |
| } |
| return (ResolverExport[]) resultList.toArray(new ResolverExport[resultList.size()]); |
| } |
| |
| ResolverExport[] getSelectedExports() { |
| ResolverExport[] exports = getExportPackages(); |
| ArrayList removedList = null; |
| for (int i = 0; i < exports.length; i++) { |
| ResolverImport imp = getImport(exports[i].getName()); |
| if (imp != null && imp.getMatchingExport() != exports[i] && exports[i].getExportPackageDescription().isRoot()) { |
| if (removedList == null) |
| removedList = new ArrayList(1); |
| removedList.add(exports[i]); |
| } |
| } |
| if (removedList == null) |
| return exports; |
| ResolverExport[] selectedExports = new ResolverExport[exports.length - removedList.size()]; |
| ResolverExport[] removedExports = (ResolverExport[]) removedList.toArray(new ResolverExport[removedList.size()]); |
| int index = 0; |
| for (int i = 0; i < exports.length; i++) { |
| boolean removed = false; |
| for (int j = 0; j < removedExports.length; j++) { |
| if (exports[i] == removedExports[j]) { |
| removed = true; |
| break; |
| } |
| } |
| if (!removed) { |
| selectedExports[index] = exports[i]; |
| index++; |
| } |
| } |
| return selectedExports; |
| } |
| |
| BundleConstraint getHost() { |
| return host; |
| } |
| |
| BundleConstraint[] getRequires() { |
| if (isFragment()) |
| return new BundleConstraint[0]; |
| if (fragments == null || fragments.size() == 0) |
| return requires; |
| ArrayList resultList = new ArrayList(requires.length); |
| for (int i = 0; i < requires.length; i++) |
| resultList.add(requires[i]); |
| for (Iterator iter = fragments.iterator(); iter.hasNext();) { |
| ResolverBundle fragment = (ResolverBundle) iter.next(); |
| ArrayList fragRequires = (ArrayList) fragmentRequires.get(fragment.bundleID); |
| if (fragRequires != null) |
| resultList.addAll(fragRequires); |
| } |
| return (BundleConstraint[]) resultList.toArray(new BundleConstraint[resultList.size()]); |
| } |
| |
| // Returns true if any cyclic dependencies have been recorded |
| boolean isDependentOnCycle() { |
| return cyclicDependencies.size() > 0; |
| } |
| |
| // Record a cyclic dependency (i.e. this module is dependent on the supplied module) |
| void recordCyclicDependency(ResolverBundle dependentOn) { |
| if (!cyclicDependencies.contains(dependentOn)) { |
| cyclicDependencies.add(dependentOn); |
| } |
| } |
| |
| ArrayList getCyclicDependencies() { |
| return cyclicDependencies; |
| } |
| |
| // This method is called by the resolver to tell this modules that a RESOLVING module that it |
| // was dependent on has now RESOLVED. If this is the last/only cyclic dependency then the |
| // resolver will move this module into RESOLVED state |
| boolean cyclicDependencyResolved(ResolverBundle dependentOn) { |
| for (int i = 0; i < cyclicDependencies.size(); i++) { |
| if (dependentOn == cyclicDependencies.get(i)) { |
| cyclicDependencies.remove(i); |
| } |
| } |
| return !isDependentOnCycle(); |
| } |
| |
| // This method is called by the resolver to tell this modules that a RESOLVING module that it |
| // was dependent on is unresolvable. The resolver will move this module into UNRESOLVED state |
| void cyclicDependencyFailed(ResolverBundle dependentOn) { |
| cyclicDependencies = new ArrayList(); |
| detachAllFragments(); |
| ResolverImport[] imports = getImportPackages(); |
| for (int i = 0; i < imports.length; i++) { |
| imports[i].clearUnresolvableWirings(); |
| } |
| } |
| |
| public BundleDescription getBundle() { |
| return bundle; |
| } |
| |
| ResolverImport getImport(String name) { |
| ResolverImport[] imports = getImportPackages(); |
| for (int i = 0; i < imports.length; i++) { |
| if (imports[i].getName().equals(name)) { |
| return imports[i]; |
| } |
| } |
| return null; |
| } |
| |
| public String toString() { |
| return "[" + bundle + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| public Version getVersion() { |
| return bundle.getVersion(); |
| } |
| |
| public String getName() { |
| return bundle.getName(); |
| } |
| |
| 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); |
| } |
| |
| private boolean isImported(String packageName) { |
| for (int i = 0; i < imports.length; i++) |
| if (packageName.equals(imports[i].getName())) |
| return true; |
| |
| return false; |
| } |
| |
| private boolean isExported(String packageName) { |
| for (int i = 0; i < exports.length; i++) |
| if (packageName.equals(exports[i].getName())) |
| return true; |
| |
| return false; |
| } |
| |
| private boolean isRequired(String bundleName) { |
| for (int i = 0; i < requires.length; i++) |
| if (bundleName.equals(requires[i].getVersionConstraint().getName())) |
| return true; |
| |
| return false; |
| } |
| |
| ResolverExport[] attachFragment(ResolverBundle fragment, boolean addExports) { |
| if (isFragment()) |
| return new ResolverExport[0]; // cannot attach to fragments; |
| |
| ImportPackageSpecification[] newImports = fragment.getBundle().getImportPackages(); |
| BundleSpecification[] newRequires = fragment.getBundle().getRequiredBundles(); |
| ExportPackageDescription[] newExports = fragment.getBundle().getExportPackages(); |
| |
| if (newImports.length > 0 || newRequires.length > 0 || newExports.length > 0) { |
| if (isResolved()) |
| return new ResolverExport[0]; |
| } |
| |
| initFragments(); |
| if (fragments.contains(fragment)) |
| return new ResolverExport[0]; |
| fragments.add(fragment); |
| fragment.getHost().addMatchingBundle(this); |
| |
| if (newImports.length > 0) { |
| ArrayList 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 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); |
| } |
| |
| ArrayList hostExports = new ArrayList(newExports.length); |
| if (newExports.length > 0 && addExports) { |
| StateObjectFactory factory = bundle.getContainingState().getFactory(); |
| for (int i = 0; i < newExports.length; i++) { |
| if (!isExported(newExports[i].getName())) { |
| ExportPackageDescription hostExport = factory.createExportPackageDescription(newExports[i].getName(), newExports[i].getVersion(), newExports[i].getGrouping(), newExports[i].getInclude(), newExports[i].getExclude(), newExports[i].getAttributes(), newExports[i].getMandatory(), newExports[i].isRoot(), bundle); |
| hostExports.add(new ResolverExport(this, hostExport)); |
| } |
| } |
| fragmentExports.put(fragment.bundleID, hostExports); |
| } |
| return (ResolverExport[]) hostExports.toArray(new ResolverExport[hostExports.size()]); |
| } |
| |
| ResolverExport[] detachFragment(ResolverBundle fragment) { |
| if (isFragment()) |
| return new ResolverExport[0]; |
| initFragments(); |
| |
| if (!fragments.remove(fragment)) |
| return new ResolverExport[0]; |
| |
| fragment.getHost().removeMatchingBundle(this); |
| fragmentImports.remove(fragment.bundleID); |
| fragmentRequires.remove(fragment.bundleID); |
| ArrayList removedExports = (ArrayList) fragmentExports.remove(fragment.bundleID); |
| |
| return removedExports == null ? new ResolverExport[0] : (ResolverExport[]) removedExports.toArray(new ResolverExport[removedExports.size()]); |
| } |
| |
| void detachAllFragments() { |
| if (fragments == null) |
| return; |
| ResolverBundle[] allFragments = (ResolverBundle[]) fragments.toArray(new ResolverBundle[fragments.size()]); |
| for (int i = 0; i < allFragments.length; i++) |
| detachFragment(allFragments[i]); |
| } |
| |
| boolean isDependentOnUnresolvedFragment(ResolverBundle dependent) { |
| ResolverImport[] imports = dependent.getImportPackages(); |
| for (int i = 0; i < imports.length; i++) { |
| ResolverExport exp = imports[i].getMatchingExport(); |
| if (exp == null || exp.getExporter() != this) |
| continue; |
| |
| if (!isExported(exp)) |
| return true; |
| } |
| return false; |
| } |
| |
| 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; |
| } |
| } |