/*******************************************************************************
* Copyright (c) 2014, 2015 Manumitting Technologies Inc 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:
* Brian de Alwis (MTI) - initial API and implementation
* René Brandstetter - Bug 419749
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654
******************************************************************************/
package org.eclipse.e4.ui.internal.workbench;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IContributor;
import org.eclipse.core.runtime.IExtension;
import org.osgi.framework.Bundle;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
/**
* Sort {@link IExtension}s by their plug-in dependencies such that any extension from bundle A
* should be sorted before any extensions from any bundles that depend on A.
* <p>
* Consider having extensions from bundles A, B, and C, where A depends on B, and B depends on C.
* The result of this sort should be the extensions from C, then from B, and finally from A.
*/
public class ExtensionsSort extends TopologicalSort<IExtension, Bundle> {
@Override
protected Bundle getId(IExtension extension) {
IContributor contributor = extension.getContributor();
return Activator.getDefault().getBundleForName(contributor.getName());
}
/**
* Returns the bundles that the given bundle requires.
*
* @return An unmodifiable {@link Iterable} set of bundles currently required by the geiven
* bundle. An empty {@link Iterable} will be returned if the given {@code Bundle} object
* has become stale or there are no bundles required.
* @throws NullPointerException
* if {@code bundle} is <code>null</code>
*/
@Override
protected Collection<Bundle> getRequirements(Bundle bundle) {
// punt to getDependents()
return null;
}
/**
* Recursively collects all bundles which depend-on/require the given {@link BundleWiring}.
* <p>
* All re-exports will be followed and also be contained in the result.
* </p>
*
* @param dependents
* the result which will contain all the bundles which require the given
* {@link BundleWiring}
* @param providerWiring
* the {@link BundleWiring} for which the requirers should be resolved
* @throws NullPointerException
* if either the requiring or the providerWiring is <code>null</code>
*/
private static void addDependents(Set<Bundle> dependents, BundleWiring providerWiring) {
List<BundleWire> requirerWires = providerWiring
.getProvidedWires(BundleNamespace.BUNDLE_NAMESPACE);
if (requirerWires == null) {
// we don't hold locks while checking the graph, just return if no longer isInUse
return;
}
for (BundleWire requireBundleWire : requirerWires) {
Bundle requirer = requireBundleWire.getRequirer().getBundle();
if (dependents.contains(requirer)) {
continue;
}
dependents.add(requirer);
String reExport = requireBundleWire.getRequirement().getDirectives()
.get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
if (BundleNamespace.VISIBILITY_REEXPORT.equals(reExport)) {
addDependents(dependents, requireBundleWire.getRequirerWiring());
}
}
}
/**
* Returns the bundles that currently require the given bundle.
* <p>
* If {@code bundle} is required and then re-exported by another bundle then all the requiring
* bundles of the re-exporting bundle are included in the returned array as they transitively
* depend on {@code bundle}.
* </p>
*
* @return An unmodifiable {@link Iterable} set of bundles currently requiring {@code bundle}.
* An empty {@link Iterable} will be returned if the {@code bundle} has become stale or
* has no dependents.
* @throws NullPointerException
* if {@code bundle} is <code>null</code>
*/
@Override
protected Collection<Bundle> getDependencies(Bundle bundle) {
BundleWiring providerWiring = bundle.adapt(BundleWiring.class);
if (!providerWiring.isInUse()) {
return Collections.emptySet();
}
Set<Bundle> required = new HashSet<>();
addDependents(required, providerWiring);
return Collections.unmodifiableSet(required);
}
}