blob: 046b9636b580b49c299417a0ca213a073b3a9b0c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2013 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
* Simon Scholz <simon.scholz@vogella.com> - Bug 444808
*******************************************************************************/
package org.eclipse.pde.internal.core.plugin;
import java.util.ArrayList;
import java.util.Locale;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.runtime.CoreException;
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.VersionRange;
import org.eclipse.pde.core.IModelChangedEvent;
import org.eclipse.pde.core.plugin.IMatchRules;
import org.eclipse.pde.core.plugin.IPluginBase;
import org.eclipse.pde.core.plugin.IPluginImport;
import org.eclipse.pde.core.plugin.IPluginLibrary;
import org.eclipse.pde.internal.core.ICoreConstants;
import org.eclipse.pde.internal.core.PDECoreMessages;
import org.eclipse.pde.internal.core.PDEState;
import org.eclipse.pde.internal.core.bundle.BundlePluginBase;
import org.osgi.framework.Version;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public abstract class PluginBase extends AbstractExtensions implements IPluginBase {
private static final long serialVersionUID = 1L;
private static final Version maxVersion = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
private ArrayList<IPluginLibrary> fLibraries = new ArrayList<>();
private ArrayList<IPluginImport> fImports = new ArrayList<>();
private String fProviderName;
private String fId;
private String fVersion;
private boolean fHasBundleStructure;
private String fBundleSourceEntry;
private boolean fExportsExternalAnnotations;
public PluginBase(boolean readOnly) {
super(readOnly);
}
@Override
public void add(IPluginLibrary library) throws CoreException {
ensureModelEditable();
fLibraries.add(library);
((PluginLibrary) library).setInTheModel(true);
((PluginLibrary) library).setParent(this);
fireStructureChanged(library, IModelChangedEvent.INSERT);
}
@Override
public void add(IPluginImport iimport) throws CoreException {
ensureModelEditable();
((PluginImport) iimport).setInTheModel(true);
((PluginImport) iimport).setParent(this);
fImports.add(iimport);
fireStructureChanged(iimport, IModelChangedEvent.INSERT);
}
public void add(IPluginImport[] iimports) throws CoreException {
ensureModelEditable();
for (IPluginImport iimport : iimports) {
((PluginImport) iimport).setInTheModel(true);
((PluginImport) iimport).setParent(this);
fImports.add(iimport);
}
fireStructureChanged(iimports, IModelChangedEvent.INSERT);
}
@Override
public IPluginLibrary[] getLibraries() {
// Returns an empty array if no libraries are specified in the manifest of the plug-in.
// If no libraries are specified, the root of the bundle '.' is the default library location
return fLibraries.toArray(new IPluginLibrary[fLibraries.size()]);
}
@Override
public IPluginImport[] getImports() {
return fImports.toArray(new IPluginImport[fImports.size()]);
}
@Override
public IPluginBase getPluginBase() {
return this;
}
@Override
public String getProviderName() {
return fProviderName;
}
@Override
public String getVersion() {
return fVersion;
}
@Override
public String getId() {
return fId;
}
void load(BundleDescription bundleDesc, PDEState state) {
fId = bundleDesc.getSymbolicName();
fVersion = bundleDesc.getVersion().toString();
fName = state.getPluginName(bundleDesc.getBundleId());
fProviderName = state.getProviderName(bundleDesc.getBundleId());
fHasBundleStructure = state.hasBundleStructure(bundleDesc.getBundleId());
fBundleSourceEntry = state.getBundleSourceEntry(bundleDesc.getBundleId());
fExportsExternalAnnotations = state.exportsExternalAnnotations(bundleDesc.getBundleId());
loadRuntime(bundleDesc, state);
loadImports(bundleDesc);
}
@Override
public void restoreProperty(String name, Object oldValue, Object newValue) throws CoreException {
if (name.equals(P_ID)) {
setId(newValue != null ? newValue.toString() : null);
return;
}
if (name.equals(P_VERSION)) {
setVersion(newValue != null ? newValue.toString() : null);
return;
}
if (name.equals(P_PROVIDER)) {
setProviderName(newValue != null ? newValue.toString() : null);
return;
}
if (name.equals(P_LIBRARY_ORDER)) {
swap((IPluginLibrary) oldValue, (IPluginLibrary) newValue);
return;
}
super.restoreProperty(name, oldValue, newValue);
}
void load(Node node, String schemaVersion) {
if (node == null) {
return;
}
fSchemaVersion = schemaVersion;
fId = getNodeAttribute(node, "id"); //$NON-NLS-1$
fName = getNodeAttribute(node, "name"); //$NON-NLS-1$
fProviderName = getNodeAttribute(node, "provider-name"); //$NON-NLS-1$
fVersion = getNodeAttribute(node, "version"); //$NON-NLS-1$
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
processChild(child);
}
}
}
void loadRuntime(BundleDescription description, PDEState state) {
String[] libraryNames = state.getLibraryNames(description.getBundleId());
for (String libraryName : libraryNames) {
PluginLibrary library = new PluginLibrary();
library.setModel(getModel());
library.setInTheModel(true);
library.setParent(this);
library.load(libraryName);
fLibraries.add(library);
}
}
void loadRuntime(Node node) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().toLowerCase(Locale.ENGLISH).equals("library")) { //$NON-NLS-1$
PluginLibrary library = new PluginLibrary();
library.setModel(getModel());
library.setInTheModel(true);
library.setParent(this);
fLibraries.add(library);
library.load(child);
}
}
}
void loadImports(BundleDescription description) {
BundleSpecification[] required = description.getRequiredBundles();
for (BundleSpecification spec : required) {
PluginImport importElement = new PluginImport();
importElement.setModel(getModel());
importElement.setInTheModel(true);
importElement.setParent(this);
fImports.add(importElement);
importElement.load(spec);
}
BundleDescription[] imported = getImportedBundles(description);
for (BundleDescription element : imported) {
PluginImport importElement = new PluginImport();
importElement.setModel(getModel());
importElement.setInTheModel(true);
importElement.setParent(this);
fImports.add(importElement);
importElement.load(element);
}
}
/**
* Returns the bundles that export packages imported by the given bundle
* via the Import-Package header. Provided as a static utility method so
* it can be reused in {@link BundlePluginBase}
*
* @param root the given bundle
*
* @return an array of bundles that export packages being imported by the given bundle
*/
public static BundleDescription[] getImportedBundles(BundleDescription root) {
if (root == null) {
return new BundleDescription[0];
}
ExportPackageDescription[] packages = root.getResolvedImports();
ArrayList<BundleDescription> resolvedImports = new ArrayList<>(packages.length);
for (int i = 0; i < packages.length; i++) {
if (!root.getLocation().equals(packages[i].getExporter().getLocation()) && !resolvedImports.contains(packages[i].getExporter())) {
resolvedImports.add(packages[i].getExporter());
}
}
return resolvedImports.toArray(new BundleDescription[resolvedImports.size()]);
}
void loadImports(Node node) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().toLowerCase(Locale.ENGLISH).equals("import")) { //$NON-NLS-1$
PluginImport importElement = new PluginImport();
importElement.setModel(getModel());
importElement.setInTheModel(true);
importElement.setParent(this);
fImports.add(importElement);
importElement.load(child);
}
}
}
@Override
protected void processChild(Node child) {
String name = child.getNodeName().toLowerCase(Locale.ENGLISH);
if (name.equals("runtime")) { //$NON-NLS-1$
loadRuntime(child);
} else if (name.equals("requires")) { //$NON-NLS-1$
loadImports(child);
// check to see if this model is a workspace model. If so, don't load extensions/extension points through Node.
// Instead, the extensions/extension points will be control by the extension registry.
// One instance of where we want to load an external model's extensions/extension points from a Node is the convertSchemaToHTML ANT task.
} else if (getModel().getUnderlyingResource() == null) {
super.processChild(child);
}
}
@Override
public void remove(IPluginLibrary library) throws CoreException {
ensureModelEditable();
fLibraries.remove(library);
((PluginLibrary) library).setInTheModel(false);
fireStructureChanged(library, IModelChangedEvent.REMOVE);
}
@Override
public void remove(IPluginImport iimport) throws CoreException {
ensureModelEditable();
fImports.remove(iimport);
((PluginImport) iimport).setInTheModel(false);
fireStructureChanged(iimport, IModelChangedEvent.REMOVE);
}
public void remove(IPluginImport[] iimports) throws CoreException {
ensureModelEditable();
for (IPluginImport iimport : iimports) {
fImports.remove(iimport);
((PluginImport) iimport).setInTheModel(false);
}
fireStructureChanged(iimports, IModelChangedEvent.REMOVE);
}
@Override
public void reset() {
fLibraries = new ArrayList<>();
fImports = new ArrayList<>();
fProviderName = null;
fSchemaVersion = null;
fVersion = ""; //$NON-NLS-1$
fName = ""; //$NON-NLS-1$
fId = ""; //$NON-NLS-1$
if (getModel() != null && getModel().getUnderlyingResource() != null) {
fId = getModel().getUnderlyingResource().getProject().getName();
fName = fId;
fVersion = ICoreConstants.DEFAULT_VERSION;
}
super.reset();
}
@Override
public void setProviderName(String providerName) throws CoreException {
ensureModelEditable();
String oldValue = fProviderName;
fProviderName = providerName;
firePropertyChanged(P_PROVIDER, oldValue, fProviderName);
}
@Override
public void setVersion(String newVersion) throws CoreException {
ensureModelEditable();
String oldValue = fVersion;
fVersion = newVersion;
firePropertyChanged(P_VERSION, oldValue, fVersion);
}
@Override
public void setId(String newId) throws CoreException {
ensureModelEditable();
String oldValue = fId;
fId = newId;
firePropertyChanged(P_ID, oldValue, fId);
}
public void internalSetVersion(String newVersion) {
fVersion = newVersion;
}
@Override
public void swap(IPluginLibrary l1, IPluginLibrary l2) throws CoreException {
ensureModelEditable();
int index1 = fLibraries.indexOf(l1);
int index2 = fLibraries.indexOf(l2);
if (index1 == -1 || index2 == -1) {
throwCoreException(PDECoreMessages.PluginBase_librariesNotFoundException);
}
fLibraries.set(index2, l1);
fLibraries.set(index1, l2);
firePropertyChanged(this, P_LIBRARY_ORDER, l1, l2);
}
@Override
public void swap(IPluginImport import1, IPluginImport import2) throws CoreException {
ensureModelEditable();
int index1 = fImports.indexOf(import1);
int index2 = fImports.indexOf(import2);
if (index1 == -1 || index2 == -1) {
throwCoreException(PDECoreMessages.PluginBase_importsNotFoundException);
}
fImports.set(index2, import1);
fImports.set(index1, import2);
firePropertyChanged(this, P_IMPORT_ORDER, import1, import2);
}
@Override
public boolean isValid() {
return hasRequiredAttributes();
}
@Override
protected boolean hasRequiredAttributes() {
if (fName == null) {
return false;
}
if (fId == null) {
return false;
}
if (fVersion == null) {
return false;
}
// validate libraries
for (int i = 0; i < fLibraries.size(); i++) {
IPluginLibrary library = fLibraries.get(i);
if (!library.isValid()) {
return false;
}
}
// validate imports
for (int i = 0; i < fImports.size(); i++) {
IPluginImport iimport = fImports.get(i);
if (!iimport.isValid()) {
return false;
}
}
return super.hasRequiredAttributes();
}
protected SAXParser getSaxParser() throws ParserConfigurationException, SAXException, FactoryConfigurationError {
return SAXParserFactory.newInstance().newSAXParser();
}
public static int getMatchRule(VersionRange versionRange) {
if (versionRange == null || versionRange.getMinimum() == null) {
return IMatchRules.NONE;
}
Version minimum = versionRange.getLeft();
Version maximum = versionRange.getRight() == null ? maxVersion : versionRange.getRight();
if (maximum.compareTo(maxVersion) >= 0) {
return IMatchRules.GREATER_OR_EQUAL;
} else if (minimum.equals(maximum)) {
return IMatchRules.PERFECT;
} else if (!versionRange.isIncluded(minimum) || versionRange.isIncluded(maximum)) {
return IMatchRules.NONE; // no real match rule for this
} else if (minimum.getMajor() == maximum.getMajor() - 1) {
return IMatchRules.COMPATIBLE;
} else if (minimum.getMajor() != maximum.getMajor()) {
return IMatchRules.NONE; // no real match rule for this
} else if (minimum.getMinor() == maximum.getMinor() - 1) {
return IMatchRules.EQUIVALENT;
} else if (minimum.getMinor() != maximum.getMinor()) {
return IMatchRules.NONE; // no real match rule for this
} else if (minimum.getMicro() == maximum.getMicro() - 1) {
return IMatchRules.PERFECT; // this is as close as we got
}
return IMatchRules.NONE; // no real match rule for this
}
public boolean hasBundleStructure() {
return fHasBundleStructure;
}
/**
* @return The bundle source entry from the manifest for this plugin or <code>null</code> if no entry exists.
*/
public String getBundleSourceEntry() {
return fBundleSourceEntry;
}
public boolean exportsExternalAnnotations() {
return fExportsExternalAnnotations;
}
}