| /******************************************************************************* |
| * 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; |
| } |
| |
| } |