blob: 74090571a1af30a073e654471cb961408ace5ccc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.egf.core.platform.internal.pde;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.osgi.service.resolver.BaseDescription;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.BundleSpecification;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.eclipse.osgi.service.resolver.HostSpecification;
import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
import org.eclipse.osgi.service.resolver.StateHelper;
import org.eclipse.pde.core.build.IBuild;
import org.eclipse.pde.core.build.IBuildEntry;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
import org.eclipse.pde.internal.core.ClasspathUtilCore;
import org.eclipse.pde.internal.core.ICoreConstants;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.PDECoreMessages;
import org.eclipse.pde.internal.core.TargetPlatformHelper;
import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
public class PlatformRequiredPluginsClasspathContainer extends PlatformClasspathContainer implements IClasspathContainer {
private IPluginModelBase fModel;
private IBuild fBuild;
private static boolean DEBUG = false;
private IClasspathEntry[] fEntries = null;
static {
DEBUG = PDECore.getDefault().isDebugging() && "true".equals(Platform.getDebugOption("org.eclipse.pde.core/classpath")); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Constructor for RequiredPluginsClasspathContainer.
*/
public PlatformRequiredPluginsClasspathContainer(IPluginModelBase model) {
this(model, null);
}
public PlatformRequiredPluginsClasspathContainer(IPluginModelBase model, IBuild build) {
fModel = model;
fBuild = build;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.IClasspathContainer#getKind()
*/
public int getKind() {
return K_APPLICATION;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.IClasspathContainer#getPath()
*/
public IPath getPath() {
return PDECore.REQUIRED_PLUGINS_CONTAINER_PATH;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.IClasspathContainer#getDescription()
*/
public String getDescription() {
return PDECoreMessages.RequiredPluginsClasspathContainer_description;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.IClasspathContainer#getClasspathEntries()
*/
public IClasspathEntry[] getClasspathEntries() {
if (fModel == null) {
if (DEBUG) {
System.out.println("********Returned an empty container"); //$NON-NLS-1$
System.out.println();
}
return new IClasspathEntry[0];
}
if (fEntries == null) {
fEntries = computePluginEntries();
}
if (DEBUG) {
System.out.println("Dependencies for plugin '" + fModel.getPluginBase().getId() + "':"); //$NON-NLS-1$ //$NON-NLS-2$
for (int i = 0; i < fEntries.length; i++) {
System.out.println(fEntries[i]);
}
System.out.println();
}
return fEntries;
}
private IClasspathEntry[] computePluginEntries() {
List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
try {
BundleDescription desc = fModel.getBundleDescription();
if (desc == null) {
return new IClasspathEntry[0];
}
Map<BundleDescription, List<Rule>> map = retrieveVisiblePackagesFromState(desc);
Set<BundleDescription> added = new HashSet<BundleDescription>();
// to avoid cycles, e.g. when a bundle imports a package it exports
added.add(desc);
HostSpecification host = desc.getHost();
if (host != null) {
addHostPlugin(host, added, map, entries);
} else if ("true".equals(System.getProperty("pde.allowCycles"))) { //$NON-NLS-1$ //$NON-NLS-2$
BundleDescription[] fragments = desc.getFragments();
for (int i = 0; i < fragments.length; i++) {
if (fragments[i].isResolved()) {
addPlugin(fragments[i], false, map, entries);
}
}
}
// add dependencies
BundleSpecification[] required = desc.getRequiredBundles();
for (int i = 0; i < required.length; i++) {
addDependency((BundleDescription) required[i].getSupplier(), added, map, entries);
}
if (fBuild == null && fModel.getUnderlyingResource() != null && fModel.getUnderlyingResource().getProject() != null) {
fBuild = ClasspathUtilCore.getBuild(fModel);
}
if (fBuild != null) {
addSecondaryDependencies(desc, added, entries);
}
// add Import-Package
// sort by symbolicName_version to get a consistent order
Map<String, BundleDescription> sortedMap = new TreeMap<String, BundleDescription>();
Iterator<BundleDescription> iter = map.keySet().iterator();
while (iter.hasNext()) {
BundleDescription bundle = iter.next();
sortedMap.put(bundle.toString(), bundle);
}
iter = sortedMap.values().iterator();
while (iter.hasNext()) {
BundleDescription bundle = iter.next();
IPluginModelBase model = PluginRegistry.findModel(bundle);
if (model != null && model.isEnabled()) {
addDependencyViaImportPackage(model.getBundleDescription(), added, map, entries);
}
}
if (fBuild != null) {
addExtraClasspathEntries(added, entries);
}
} catch (CoreException e) {
// Just ignore
}
return entries.toArray(new IClasspathEntry[entries.size()]);
}
private Map<BundleDescription, List<Rule>> retrieveVisiblePackagesFromState(BundleDescription desc) {
Map<BundleDescription, List<Rule>> visiblePackages = new HashMap<BundleDescription, List<Rule>>();
StateHelper helper = Platform.getPlatformAdmin().getStateHelper();
addVisiblePackagesFromState(helper, desc, visiblePackages);
if (desc.getHost() != null) {
addVisiblePackagesFromState(helper, (BundleDescription) desc.getHost().getSupplier(), visiblePackages);
}
return visiblePackages;
}
private void addVisiblePackagesFromState(StateHelper helper, BundleDescription desc, Map<BundleDescription, List<Rule>> visiblePackages) {
if (desc == null) {
return;
}
ExportPackageDescription[] exports = helper.getVisiblePackages(desc);
for (int i = 0; i < exports.length; i++) {
BundleDescription exporter = exports[i].getExporter();
if (exporter == null) {
continue;
}
List<Rule> list = visiblePackages.get(exporter);
if (list == null) {
list = new ArrayList<Rule>();
visiblePackages.put(exporter, list);
}
Rule rule = getRule(helper, desc, exports[i]);
if (list.contains(rule) == false) {
list.add(rule);
}
}
}
private Rule getRule(StateHelper helper, BundleDescription desc, ExportPackageDescription export) {
Rule rule = new Rule();
rule.discouraged = helper.getAccessCode(desc, export) == StateHelper.ACCESS_DISCOURAGED;
String name = export.getName();
rule.path = (name.equals(".")) ? new Path("*") : new Path(name.replaceAll("\\.", "/") + "/*"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
return rule;
}
protected void addDependencyViaImportPackage(BundleDescription desc, Set<BundleDescription> added, Map<BundleDescription, List<Rule>> map, List<IClasspathEntry> entries) throws CoreException {
if (desc == null || added.add(desc) == false) {
return;
}
addPlugin(desc, true, map, entries);
if (hasExtensibleAPI(desc) && desc.getContainingState() != null) {
BundleDescription[] fragments = desc.getFragments();
for (int i = 0; i < fragments.length; i++) {
if (fragments[i].isResolved()) {
addDependencyViaImportPackage(fragments[i], added, map, entries);
}
}
}
}
private void addDependency(BundleDescription desc, Set<BundleDescription> added, Map<BundleDescription, List<Rule>> map, List<IClasspathEntry> entries) throws CoreException {
addDependency(desc, added, map, entries, true);
}
private void addDependency(BundleDescription desc, Set<BundleDescription> added, Map<BundleDescription, List<Rule>> map, List<IClasspathEntry> entries, boolean useInclusion) throws CoreException {
if (desc == null || added.add(desc) == false) {
return;
}
BundleDescription[] fragments = hasExtensibleAPI(desc) ? desc.getFragments() : new BundleDescription[0];
// add fragment patches before host
for (int i = 0; i < fragments.length; i++) {
if (fragments[i].isResolved() && ClasspathUtilCore.isPatchFragment(fragments[i])) {
addDependency(fragments[i], added, map, entries, useInclusion);
}
}
addPlugin(desc, useInclusion, map, entries);
// add fragments that are not patches after the host
for (int i = 0; i < fragments.length; i++) {
if (fragments[i].isResolved() && ClasspathUtilCore.isPatchFragment(fragments[i]) == false) {
addDependency(fragments[i], added, map, entries, useInclusion);
}
}
BundleSpecification[] required = desc.getRequiredBundles();
for (int i = 0; i < required.length; i++) {
if (required[i].isExported()) {
addDependency((BundleDescription) required[i].getSupplier(), added, map, entries, useInclusion);
}
}
}
private boolean addPlugin(BundleDescription desc, boolean useInclusions, Map<BundleDescription, List<Rule>> map, List<IClasspathEntry> entries) throws CoreException {
IPluginModelBase model = PluginRegistry.findModel(desc);
if (model == null || model.isEnabled() == false) {
return false;
}
IResource resource = model.getUnderlyingResource();
Rule[] rules = useInclusions ? getInclusions(map, model) : null;
if (resource != null) {
addProjectEntry(resource.getProject(), rules, entries);
} else {
addExternalPlugin(model, rules, entries);
// Check whether or not we face a directory
IPath location = new Path(model.getInstallLocation());
if (location.toFile() == null || location.toFile().isDirectory()) {
// Add a IClasspathEntry to this directory
if (location.toFile() != null && location.toFile().exists()) {
addLibraryEntry(location, null, rules, getClasspathAttributes(model), entries);
}
}
}
return true;
}
private Rule[] getInclusions(Map<BundleDescription, List<Rule>> map, IPluginModelBase model) {
BundleDescription desc = model.getBundleDescription();
if (desc == null || "false".equals(System.getProperty("pde.restriction")) //$NON-NLS-1$ //$NON-NLS-2$
|| !(fModel instanceof IBundlePluginModelBase) || TargetPlatformHelper.getTargetVersion() < 3.1) {
return null;
}
Rule[] rules;
if (desc.getHost() != null) {
rules = getInclusions(map, (BundleDescription) desc.getHost().getSupplier());
} else {
rules = getInclusions(map, desc);
}
return (rules.length == 0 && !ClasspathUtilCore.hasBundleStructure(model)) ? null : rules;
}
private Rule[] getInclusions(Map<BundleDescription, List<Rule>> map, BundleDescription desc) {
List<Rule> list = map.get(desc);
return list != null ? list.toArray(new Rule[list.size()]) : new Rule[0];
}
private void addHostPlugin(HostSpecification hostSpec, Set<BundleDescription> added, Map<BundleDescription, List<Rule>> map, List<IClasspathEntry> entries) throws CoreException {
BaseDescription desc = hostSpec.getSupplier();
if (desc instanceof BundleDescription) {
BundleDescription host = (BundleDescription) desc;
// add host plug-in
if (added.add(host) && addPlugin(host, false, map, entries)) {
BundleSpecification[] required = host.getRequiredBundles();
for (int i = 0; i < required.length; i++) {
addDependency((BundleDescription) required[i].getSupplier(), added, map, entries);
}
// add Import-Package
ImportPackageSpecification[] imports = host.getImportPackages();
for (int i = 0; i < imports.length; i++) {
BaseDescription supplier = imports[i].getSupplier();
if (supplier instanceof ExportPackageDescription) {
addDependencyViaImportPackage(((ExportPackageDescription) supplier).getExporter(), added, map, entries);
}
}
}
}
}
private boolean hasExtensibleAPI(BundleDescription desc) {
IPluginModelBase model = PluginRegistry.findModel(desc);
return model != null ? ClasspathUtilCore.hasExtensibleAPI(model) : false;
}
protected void addExtraClasspathEntries(Set<BundleDescription> added, List<IClasspathEntry> entries) {
IBuildEntry[] buildEntries = fBuild.getBuildEntries();
for (int i = 0; i < buildEntries.length; i++) {
String name = buildEntries[i].getName();
if (name.equals(IBuildPropertiesConstants.PROPERTY_JAR_EXTRA_CLASSPATH) || name.startsWith(IBuildPropertiesConstants.PROPERTY_EXTRAPATH_PREFIX)) {
addExtraClasspathEntries(added, entries, buildEntries[i].getTokens());
}
}
}
protected void addExtraClasspathEntries(Set<BundleDescription> added, List<IClasspathEntry> entries, String[] tokens) {
for (int i = 0; i < tokens.length; i++) {
IPath path = Path.fromPortableString(tokens[i]);
if (path.isAbsolute() == false) {
File file = new File(fModel.getInstallLocation(), path.toString());
if (file.exists()) {
IFile resource = PDECore.getWorkspace().getRoot().getFileForLocation(new Path(file.getAbsolutePath()));
if (resource != null && resource.getProject().equals(fModel.getUnderlyingResource().getProject())) {
addExtraLibrary(resource.getFullPath(), null, entries);
continue;
}
}
if (path.segmentCount() >= 3 && "..".equals(path.segment(0))) { //$NON-NLS-1$
path = path.removeFirstSegments(1);
path = Path.fromPortableString("platform:/plugin/").append(path); //$NON-NLS-1$
} else {
continue;
}
}
if (path.toPortableString().startsWith("platform:") == false) { //$NON-NLS-1$
addExtraLibrary(path, null, entries);
} else {
int count = path.getDevice() == null ? 4 : 3;
if (path.segmentCount() >= count) {
String pluginID = path.segment(count - 2);
if (added.contains(pluginID)) {
continue;
}
IPluginModelBase model = PluginRegistry.findModel(pluginID);
if (model != null && model.isEnabled()) {
path = path.setDevice(null);
path = path.removeFirstSegments(count - 1);
if (model.getUnderlyingResource() == null) {
File file = new File(model.getInstallLocation(), path.toOSString());
if (file.exists()) {
addExtraLibrary(new Path(file.getAbsolutePath()), model, entries);
}
} else {
IProject project = model.getUnderlyingResource().getProject();
IFile file = project.getFile(path);
if (file.exists()) {
addExtraLibrary(file.getFullPath(), model, entries);
}
}
}
}
}
}
}
private void addSecondaryDependencies(BundleDescription desc, Set<BundleDescription> added, List<IClasspathEntry> entries) {
try {
IBuildEntry entry = fBuild.getEntry(IBuildEntry.SECONDARY_DEPENDENCIES);
if (entry != null) {
String[] tokens = entry.getTokens();
for (int i = 0; i < tokens.length; i++) {
String pluginId = tokens[i];
// Get PluginModelBase first to resolve system.bundle entry if it exists
IPluginModelBase model = PluginRegistry.findModel(pluginId);
if (model != null) {
BundleDescription bundleDesc = model.getBundleDescription();
if (added.contains(bundleDesc)) {
continue;
}
Map<BundleDescription, List<Rule>> rules = new HashMap<BundleDescription, List<Rule>>();
findExportedPackages(bundleDesc, desc, rules);
addDependency(bundleDesc, added, rules, entries, true);
}
}
}
} catch (CoreException e) {
return;
}
}
protected final void findExportedPackages(BundleDescription desc, BundleDescription projectDesc, Map<BundleDescription, List<Rule>> map) {
if (desc != null) {
Stack<BundleDescription> stack = new Stack<BundleDescription>();
stack.add(desc);
while (stack.isEmpty() == false) {
BundleDescription bdesc = stack.pop();
ExportPackageDescription[] expkgs = bdesc.getExportPackages();
List<Rule> rules = new ArrayList<Rule>();
for (int i = 0; i < expkgs.length; i++) {
Rule rule = new Rule();
rule.discouraged = restrictPackage(projectDesc, expkgs[i]);
rule.path = new Path(expkgs[i].getName().replaceAll("\\.", "/") + "/*"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
rules.add(rule);
}
map.put(bdesc, rules);
// Look at re-exported Require-Bundles for any other exported packages
BundleSpecification[] requiredBundles = bdesc.getRequiredBundles();
for (int i = 0; i < requiredBundles.length; i++) {
if (requiredBundles[i].isExported()) {
BaseDescription bd = requiredBundles[i].getSupplier();
if (bd != null && bd instanceof BundleDescription) {
stack.add((BundleDescription) bd);
}
}
}
}
}
}
private boolean restrictPackage(BundleDescription desc, ExportPackageDescription pkg) {
String[] friends = (String[]) pkg.getDirective(ICoreConstants.FRIENDS_DIRECTIVE);
if (friends != null) {
String symbolicName = desc.getSymbolicName();
for (int i = 0; i < friends.length; i++) {
if (symbolicName.equals(friends[i]))
return false;
}
return true;
}
return (((Boolean) pkg.getDirective(ICoreConstants.INTERNAL_DIRECTIVE)).booleanValue());
}
private void addExtraLibrary(IPath path, IPluginModelBase model, List<IClasspathEntry> entries) {
IPath srcPath = null;
if (model != null) {
IPath shortPath = path.removeFirstSegments(path.matchingFirstSegments(new Path(model.getInstallLocation())));
srcPath = ClasspathUtilCore.getSourceAnnotation(model, shortPath.toString());
} else {
String filename = ClasspathUtilCore.getSourceZipName(path.lastSegment());
IPath candidate = path.removeLastSegments(1).append(filename);
if (PDECore.getWorkspace().getRoot().getFile(candidate).exists()) {
srcPath = candidate;
}
}
IClasspathEntry clsEntry = JavaCore.newLibraryEntry(path, srcPath, null);
if (entries.contains(clsEntry) == false) {
entries.add(clsEntry);
}
}
}