Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2013-06-19 09:12:02 -0400
committerThomas Watson2013-07-12 14:59:59 -0400
commit9eb78bc46db677c14ed8cbb5581f05008fbcb2e6 (patch)
tree74a01abb39343c18a985aa0eab71c614f0f97745 /bundles/org.eclipse.osgi.compatibility.plugins/src
parentf06f147312372e02aac14920a567604c548a946b (diff)
downloadrt.equinox.framework-9eb78bc46db677c14ed8cbb5581f05008fbcb2e6.tar.gz
rt.equinox.framework-9eb78bc46db677c14ed8cbb5581f05008fbcb2e6.tar.xz
rt.equinox.framework-9eb78bc46db677c14ed8cbb5581f05008fbcb2e6.zip
Add compatibility fragment to support 2.0 style plugins
Diffstat (limited to 'bundles/org.eclipse.osgi.compatibility.plugins/src')
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IModel.java92
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IPluginInfo.java52
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterHook.java122
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterImpl.java788
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.java37
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.properties25
-rw-r--r--bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginParser.java708
7 files changed, 1824 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IModel.java b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IModel.java
new file mode 100644
index 000000000..d53ed6882
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IModel.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.compatibility.plugins;
+
+/**
+ * Internal class.
+ */
+public interface IModel {
+ public static final int INDENT = 2;
+ public static final int RADIX = 36;
+
+ public static final String TRUE = "true"; //$NON-NLS-1$
+ public static final String FALSE = "false"; //$NON-NLS-1$
+
+ public static final String REGISTRY = "plugin-registry"; //$NON-NLS-1$
+ public static final String REGISTRY_PATH = "path"; //$NON-NLS-1$
+
+ public static final String FRAGMENT = "fragment"; //$NON-NLS-1$
+ public static final String FRAGMENT_ID = "id"; //$NON-NLS-1$
+ public static final String FRAGMENT_NAME = "name"; //$NON-NLS-1$
+ public static final String FRAGMENT_PROVIDER = "provider-name"; //$NON-NLS-1$
+ public static final String FRAGMENT_VERSION = "version"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_ID = "plugin-id"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_VERSION = "plugin-version"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH = "match"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_PERFECT = "perfect"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_EQUIVALENT = "equivalent"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_COMPATIBLE = "compatible"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_GREATER_OR_EQUAL = "greaterOrEqual"; //$NON-NLS-1$
+
+ public static final String PLUGIN = "plugin"; //$NON-NLS-1$
+ public static final String PLUGIN_ID = "id"; //$NON-NLS-1$
+ public static final String PLUGIN_NAME = "name"; //$NON-NLS-1$
+ public static final String PLUGIN_VENDOR = "vendor-name"; //$NON-NLS-1$
+ public static final String PLUGIN_PROVIDER = "provider-name"; //$NON-NLS-1$
+ public static final String PLUGIN_VERSION = "version"; //$NON-NLS-1$
+ public static final String PLUGIN_CLASS = "class"; //$NON-NLS-1$
+
+ public static final String PLUGIN_REQUIRES = "requires"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_PLATFORM = "platform-version"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_PLUGIN = "plugin"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_PLUGIN_VERSION = "version"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_OPTIONAL = "optional"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_IMPORT = "import"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_EXPORT = "export"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH = "match"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_EXACT = "exact"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_PERFECT = "perfect"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_EQUIVALENT = "equivalent"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_COMPATIBLE = "compatible"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL = "greaterOrEqual"; //$NON-NLS-1$
+
+ public static final String PLUGIN_KEY_VERSION_SEPARATOR = "_"; //$NON-NLS-1$
+
+ public static final String RUNTIME = "runtime"; //$NON-NLS-1$
+
+ public static final String LIBRARY = "library"; //$NON-NLS-1$
+ public static final String LIBRARY_NAME = "name"; //$NON-NLS-1$
+ public static final String LIBRARY_SOURCE = "source"; //$NON-NLS-1$
+ public static final String LIBRARY_TYPE = "type"; //$NON-NLS-1$
+ public static final String LIBRARY_EXPORT = "export"; //$NON-NLS-1$
+ public static final String LIBRARY_EXPORT_MASK = "name"; //$NON-NLS-1$
+ public static final String LIBRARY_PACKAGES = "packages"; //$NON-NLS-1$
+ public static final String LIBRARY_PACKAGES_PREFIXES = "prefixes"; //$NON-NLS-1$
+
+ public static final String EXTENSION_POINT = "extension-point"; //$NON-NLS-1$
+ public static final String EXTENSION_POINT_NAME = "name"; //$NON-NLS-1$
+ public static final String EXTENSION_POINT_ID = "id"; //$NON-NLS-1$
+ public static final String EXTENSION_POINT_SCHEMA = "schema"; //$NON-NLS-1$
+
+ public static final String EXTENSION = "extension"; //$NON-NLS-1$
+ public static final String EXTENSION_NAME = "name"; //$NON-NLS-1$
+ public static final String EXTENSION_ID = "id"; //$NON-NLS-1$
+ public static final String EXTENSION_TARGET = "point"; //$NON-NLS-1$
+
+ public static final String ELEMENT = "element"; //$NON-NLS-1$
+ public static final String ELEMENT_NAME = "name"; //$NON-NLS-1$
+ public static final String ELEMENT_VALUE = "value"; //$NON-NLS-1$
+
+ public static final String PROPERTY = "property"; //$NON-NLS-1$
+ public static final String PROPERTY_NAME = "name"; //$NON-NLS-1$
+ public static final String PROPERTY_VALUE = "value"; //$NON-NLS-1$
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IPluginInfo.java b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IPluginInfo.java
new file mode 100644
index 000000000..214c95d2d
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/IPluginInfo.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.compatibility.plugins;
+
+import java.util.*;
+
+/**
+ * Interface used as an entry to the IPluginConverter
+ *
+ * <p>Internal class.</p>
+ */
+public interface IPluginInfo {
+ public Map<String, List<String>> getLibraries();
+
+ public String[] getLibrariesName();
+
+ public ArrayList<PluginParser.Prerequisite> getRequires();
+
+ public String getMasterId();
+
+ public String getMasterVersion();
+
+ public String getMasterMatch();
+
+ public String getPluginClass();
+
+ public String getUniqueId();
+
+ public String getVersion();
+
+ public boolean isFragment();
+
+ public Set<String> getPackageFilters();
+
+ public String getPluginName();
+
+ public String getProviderName();
+
+ public boolean isSingleton();
+
+ public boolean hasExtensionExtensionPoints();
+
+ String validateForm();
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterHook.java b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterHook.java
new file mode 100644
index 000000000..392155ee3
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterHook.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.compatibility.plugins;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.internal.hookregistry.*;
+import org.eclipse.osgi.service.pluginconversion.PluginConversionException;
+import org.eclipse.osgi.service.pluginconversion.PluginConverter;
+import org.eclipse.osgi.storage.BundleInfo.Generation;
+import org.eclipse.osgi.storage.bundlefile.*;
+import org.osgi.framework.*;
+
+public class PluginConverterHook implements HookConfigurator {
+ @Override
+ public void addHooks(final HookRegistry hookRegistry) {
+ PluginConverterImpl tempConverter;
+ try {
+ tempConverter = new PluginConverterImpl(hookRegistry);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ final PluginConverterImpl converter = tempConverter;
+ hookRegistry.addBundleFileWrapperFactoryHook(new BundleFileWrapperFactoryHook() {
+
+ @Override
+ public BundleFile wrapBundleFile(final BundleFile bundleFile, Generation generation, boolean base) {
+ if (!base) {
+ return null;
+ }
+ return new BundleFile(bundleFile.getBaseFile()) {
+
+ @Override
+ public void open() throws IOException {
+ bundleFile.open();
+ }
+
+ @Override
+ public File getFile(String path, boolean nativeCode) {
+ return bundleFile.getFile(path, nativeCode);
+ }
+
+ @Override
+ public Enumeration<String> getEntryPaths(String path) {
+ return bundleFile.getEntryPaths(path);
+ }
+
+ @Override
+ public BundleEntry getEntry(String path) {
+ BundleEntry entry = bundleFile.getEntry(path);
+ if (!PluginConverterImpl.OSGI_BUNDLE_MANIFEST.equals(path)) {
+ return entry;
+ }
+ Headers<String, String> headers = null;
+ if (entry != null) {
+ try {
+ headers = Headers.parseManifest(entry.getInputStream());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ if (headers.containsKey(Constants.BUNDLE_MANIFESTVERSION)) {
+ return entry;
+ }
+ if (headers.containsKey(Constants.BUNDLE_SYMBOLICNAME)) {
+ return entry;
+ }
+ }
+ try {
+ File manifest = converter.convertManifest(bundleFile.getBaseFile(), null, true, null, true, null, false);
+ if (manifest == null) {
+ return entry;
+ }
+ return new FileBundleEntry(manifest, PluginConverterImpl.OSGI_BUNDLE_MANIFEST);
+ } catch (PluginConversionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean containsDir(String dir) {
+ return bundleFile.containsDir(dir);
+ }
+
+ @Override
+ public void close() throws IOException {
+ bundleFile.close();
+ }
+ };
+ }
+ });
+
+ hookRegistry.addActivatorHookFactory(new ActivatorHookFactory() {
+
+ @Override
+ public BundleActivator createActivator() {
+ return new BundleActivator() {
+ ServiceRegistration<PluginConverter> reg;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ reg = context.registerService(PluginConverter.class, converter, null);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ reg.unregister();
+ }
+ };
+ }
+ });
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterImpl.java b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterImpl.java
new file mode 100644
index 000000000..9ec8f8d01
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterImpl.java
@@ -0,0 +1,788 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.compatibility.plugins;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.internal.hookregistry.HookRegistry;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.osgi.service.pluginconversion.PluginConversionException;
+import org.eclipse.osgi.service.pluginconversion.PluginConverter;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+/**
+ * Internal class.
+ */
+public class PluginConverterImpl implements PluginConverter {
+ public static boolean DEBUG = false;
+ /** bundle manifest type unknown */
+ static public final byte MANIFEST_TYPE_UNKNOWN = 0x00;
+ /** bundle manifest type bundle (META-INF/MANIFEST.MF) */
+ static public final byte MANIFEST_TYPE_BUNDLE = 0x01;
+ /** bundle manifest type plugin (plugin.xml) */
+ static public final byte MANIFEST_TYPE_PLUGIN = 0x02;
+ /** bundle manifest type fragment (fragment.xml) */
+ static public final byte MANIFEST_TYPE_FRAGMENT = 0x04;
+ /** bundle manifest type jared bundle */
+ static public final byte MANIFEST_TYPE_JAR = 0x08;
+ private static final String SEMICOLON = "; "; //$NON-NLS-1$
+ private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
+ private static final String LIST_SEPARATOR = ",\n "; //$NON-NLS-1$
+ private static final String LINE_SEPARATOR = "\n "; //$NON-NLS-1$
+ private static final String DOT = "."; //$NON-NLS-1$
+ private static int MAXLINE = 511;
+ private final EquinoxConfiguration configuration;
+ private final File cacheLocation;
+ private BufferedWriter out;
+ private IPluginInfo pluginInfo;
+ private File pluginManifestLocation;
+ private ZipFile pluginZip;
+ private Dictionary<String, String> generatedManifest;
+ private byte manifestType;
+ private Version target;
+ private Dictionary<String, String> devProperties;
+ static final Version TARGET31 = new Version(3, 1, 0);
+ static final Version TARGET32 = new Version(3, 2, 0);
+ private static final String MANIFEST_VERSION = "Manifest-Version"; //$NON-NLS-1$
+ private static final String PLUGIN_PROPERTIES_FILENAME = "plugin"; //$NON-NLS-1$
+ @SuppressWarnings("deprecation")
+ private static final String[] ARCH_LIST = {org.eclipse.osgi.service.environment.Constants.ARCH_PA_RISC, org.eclipse.osgi.service.environment.Constants.ARCH_PPC, org.eclipse.osgi.service.environment.Constants.ARCH_SPARC, org.eclipse.osgi.service.environment.Constants.ARCH_X86, org.eclipse.osgi.service.environment.Constants.ARCH_AMD64, org.eclipse.osgi.service.environment.Constants.ARCH_IA64};
+ static public final String FRAGMENT_MANIFEST = "fragment.xml"; //$NON-NLS-1$
+ static public final String GENERATED_FROM = "Generated-from"; //$NON-NLS-1$
+ static public final String MANIFEST_TYPE_ATTRIBUTE = "type"; //$NON-NLS-1$
+ private static final String[] OS_LIST = {org.eclipse.osgi.service.environment.Constants.OS_AIX, org.eclipse.osgi.service.environment.Constants.OS_HPUX, org.eclipse.osgi.service.environment.Constants.OS_LINUX, org.eclipse.osgi.service.environment.Constants.OS_MACOSX, org.eclipse.osgi.service.environment.Constants.OS_QNX, org.eclipse.osgi.service.environment.Constants.OS_SOLARIS, org.eclipse.osgi.service.environment.Constants.OS_WIN32};
+ protected static final String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$
+ protected static final String PI_BOOT = "org.eclipse.core.boot"; //$NON-NLS-1$
+ protected static final String PI_RUNTIME_COMPATIBILITY = "org.eclipse.core.runtime.compatibility"; //$NON-NLS-1$
+ static public final String PLUGIN_MANIFEST = "plugin.xml"; //$NON-NLS-1$
+ private static final String COMPATIBILITY_ACTIVATOR = "org.eclipse.core.internal.compatibility.PluginActivator"; //$NON-NLS-1$
+ private static final String[] WS_LIST = {org.eclipse.osgi.service.environment.Constants.WS_CARBON, org.eclipse.osgi.service.environment.Constants.WS_GTK, org.eclipse.osgi.service.environment.Constants.WS_MOTIF, org.eclipse.osgi.service.environment.Constants.WS_PHOTON, org.eclipse.osgi.service.environment.Constants.WS_WIN32};
+ private static final String IGNORE_DOT = "@ignoredot@"; //$NON-NLS-1$
+
+ public static final String PLUGIN_CLASS = "Plugin-Class"; //$NON-NLS-1$
+ public static final String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$
+ public final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$
+ public final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$
+ public static final String ECLIPSE_LAZYSTART = "Eclipse-LazyStart"; //$NON-NLS-1$
+ public static final String ECLIPSE_LAZYSTART_EXCEPTIONS = "exceptions"; //$NON-NLS-1$
+
+ /**
+ * Manifest header used to specify the auto start properties of a bundle
+ * @deprecated use {@link #ECLIPSE_LAZYSTART}
+ */
+ public static final String ECLIPSE_AUTOSTART = "Eclipse-AutoStart"; //$NON-NLS-1$
+ public static final String OSGI_BUNDLE_MANIFEST = "META-INF/MANIFEST.MF"; //$NON-NLS-1$
+
+ public PluginConverterImpl(HookRegistry hookRegistry) throws IOException {
+ this.configuration = hookRegistry.getConfiguration();
+ Location configLocation = hookRegistry.getContainer().getLocations().getConfigurationLocation();
+ URL cacheURL = configLocation.getDataArea("org.eclipse.osgi.compatibility.plugin"); //$NON-NLS-1$
+ this.cacheLocation = new File(cacheURL.getPath());
+ }
+
+ private void init() {
+ // need to make sure these fields are cleared out for each conversion.
+ out = null;
+ pluginInfo = null;
+ pluginManifestLocation = null;
+ pluginZip = null;
+ generatedManifest = new Hashtable<String, String>(10);
+ manifestType = MANIFEST_TYPE_UNKNOWN;
+ target = null;
+ devProperties = null;
+ }
+
+ private boolean fillPluginInfo(File pluginBaseLocation) throws PluginConversionException {
+ pluginManifestLocation = pluginBaseLocation;
+ if (pluginManifestLocation == null)
+ throw new IllegalArgumentException();
+ InputStream pluginFile = null;
+ try {
+ try {
+ pluginFile = findPluginManifest(pluginBaseLocation);
+ } catch (IOException e) {
+ throw new PluginConversionException(NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_FILENOTFOUND, pluginBaseLocation.getAbsolutePath()), e);
+ }
+ if (pluginFile == null)
+ return false;
+ pluginInfo = parsePluginInfo(pluginFile);
+ } finally {
+ if (pluginZip != null)
+ try {
+ pluginZip.close();
+ pluginZip = null;
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ String validation = pluginInfo.validateForm();
+ if (validation != null)
+ throw new PluginConversionException(validation);
+ return true;
+ }
+
+ private Set<String> filterExport(Set<String> exportToFilter, Collection<String> filter) {
+ if (filter == null || filter.contains("*")) //$NON-NLS-1$
+ return exportToFilter;
+ Set<String> filteredExport = new HashSet<String>(exportToFilter.size());
+ for (String anExport : exportToFilter) {
+ for (String aFilter : filter) {
+ int dotStar = aFilter.indexOf(".*"); //$NON-NLS-1$
+ if (dotStar != -1)
+ aFilter = aFilter.substring(0, dotStar);
+ if (anExport.equals(aFilter)) {
+ filteredExport.add(anExport);
+ break;
+ }
+ }
+ }
+ return filteredExport;
+ }
+
+ private List<String> findOSJars(File pluginRoot, String path, boolean filter) {
+ path = path.substring(4);
+ List<String> found = new ArrayList<String>(0);
+ for (int i = 0; i < OS_LIST.length; i++) {
+ //look for os/osname/path
+ String searchedPath = "os/" + OS_LIST[i] + "/" + path; //$NON-NLS-1$ //$NON-NLS-2$
+ if (new File(pluginRoot, searchedPath).exists())
+ found.add(searchedPath + (filter ? ";(os=" + WS_LIST[i] + ")" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ //look for os/osname/archname/path
+ for (int j = 0; j < ARCH_LIST.length; j++) {
+ searchedPath = "os/" + OS_LIST[i] + "/" + ARCH_LIST[j] + "/" + path; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (new File(pluginRoot, searchedPath).exists()) {
+ found.add(searchedPath + (filter ? ";(& (os=" + WS_LIST[i] + ") (arch=" + ARCH_LIST[j] + ")" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+ }
+ }
+ return found;
+ }
+
+ private InputStream findPluginManifest(File baseLocation) throws IOException {
+ //Here, we can not use the bundlefile because it may explode the jar and returns a location from which we will not be able to derive the jars location
+ if (pluginZip != null)
+ try {
+ pluginZip.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ pluginZip = null;
+ if (!baseLocation.isDirectory()) {
+ manifestType |= MANIFEST_TYPE_JAR;
+ pluginZip = new ZipFile(baseLocation);
+ }
+
+ if (pluginZip != null) {
+ ZipEntry manifestEntry = pluginZip.getEntry(PLUGIN_MANIFEST);
+ if (manifestEntry != null) {
+ manifestType |= MANIFEST_TYPE_PLUGIN;
+ return pluginZip.getInputStream(manifestEntry);
+ }
+ } else {
+ File manifestFile = new File(baseLocation, PLUGIN_MANIFEST);
+ if (manifestFile.exists()) {
+ manifestType |= MANIFEST_TYPE_PLUGIN;
+ return new FileInputStream(manifestFile);
+ }
+ }
+
+ if (pluginZip != null) {
+ ZipEntry manifestEntry = pluginZip.getEntry(FRAGMENT_MANIFEST);
+ if (manifestEntry != null) {
+ manifestType |= MANIFEST_TYPE_PLUGIN;
+ return pluginZip.getInputStream(manifestEntry);
+ }
+ } else {
+ File manifestFile = new File(baseLocation, FRAGMENT_MANIFEST);
+ if (manifestFile.exists()) {
+ manifestType |= MANIFEST_TYPE_FRAGMENT;
+ return new FileInputStream(manifestFile);
+ }
+ }
+
+ return null;
+ }
+
+ private List<String> findWSJars(File pluginRoot, String path, boolean filter) {
+ path = path.substring(4);
+ List<String> found = new ArrayList<String>(0);
+ for (int i = 0; i < WS_LIST.length; i++) {
+ String searchedPath = "ws/" + WS_LIST[i] + path; //$NON-NLS-1$
+ if (new File(pluginRoot, searchedPath).exists()) {
+ found.add(searchedPath + (filter ? ";(ws=" + WS_LIST[i] + ")" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ return found;
+ }
+
+ protected void fillManifest(boolean compatibilityManifest, boolean analyseJars) {
+ generateManifestVersion();
+ generateHeaders();
+ generateClasspath();
+ generateActivator();
+ generatePluginClass();
+ if (analyseJars)
+ generateProvidePackage();
+ generateRequireBundle();
+ generateLocalizationEntry();
+ generateEclipseHeaders();
+ if (compatibilityManifest) {
+ generateTimestamp();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void writeManifest(File generationLocation, Dictionary<String, String> manifestToWrite, boolean compatibilityManifest) throws PluginConversionException {
+ long start = System.currentTimeMillis();
+ try {
+ File parentFile = new File(generationLocation.getParent());
+ parentFile.mkdirs();
+ generationLocation.createNewFile();
+ if (!generationLocation.isFile()) {
+ String message = NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_ERROR_CREATING_BUNDLE_MANIFEST, this.pluginInfo.getUniqueId(), generationLocation);
+ throw new PluginConversionException(message);
+ }
+ // replaces any eventual existing file
+ manifestToWrite = new Hashtable<String, String>((Hashtable) manifestToWrite);
+ // MANIFEST.MF files must be written using UTF-8
+ out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(generationLocation), UTF_8));
+ writeEntry(MANIFEST_VERSION, manifestToWrite.remove(MANIFEST_VERSION));
+ writeEntry(GENERATED_FROM, manifestToWrite.remove(GENERATED_FROM)); //Need to do this first uptoDate check expect the generated-from tag to be in the first line
+ // always attempt to write the Bundle-ManifestVersion header if it exists (bug 109863)
+ writeEntry(Constants.BUNDLE_MANIFESTVERSION, manifestToWrite.remove(Constants.BUNDLE_MANIFESTVERSION));
+ writeEntry(Constants.BUNDLE_NAME, manifestToWrite.remove(Constants.BUNDLE_NAME));
+ writeEntry(Constants.BUNDLE_SYMBOLICNAME, manifestToWrite.remove(Constants.BUNDLE_SYMBOLICNAME));
+ writeEntry(Constants.BUNDLE_VERSION, manifestToWrite.remove(Constants.BUNDLE_VERSION));
+ writeEntry(Constants.BUNDLE_CLASSPATH, manifestToWrite.remove(Constants.BUNDLE_CLASSPATH));
+ writeEntry(Constants.BUNDLE_ACTIVATOR, manifestToWrite.remove(Constants.BUNDLE_ACTIVATOR));
+ writeEntry(Constants.BUNDLE_VENDOR, manifestToWrite.remove(Constants.BUNDLE_VENDOR));
+ writeEntry(Constants.FRAGMENT_HOST, manifestToWrite.remove(Constants.FRAGMENT_HOST));
+ writeEntry(Constants.BUNDLE_LOCALIZATION, manifestToWrite.remove(Constants.BUNDLE_LOCALIZATION));
+ // always attempt to write the Export-Package header if it exists (bug 109863)
+ writeEntry(Constants.EXPORT_PACKAGE, manifestToWrite.remove(Constants.EXPORT_PACKAGE));
+ // always attempt to write the Provide-Package header if it exists (bug 109863)
+ writeEntry(PluginConverterImpl.PROVIDE_PACKAGE, manifestToWrite.remove(PluginConverterImpl.PROVIDE_PACKAGE));
+ writeEntry(Constants.REQUIRE_BUNDLE, manifestToWrite.remove(Constants.REQUIRE_BUNDLE));
+ Enumeration<String> keys = manifestToWrite.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ writeEntry(key, manifestToWrite.get(key));
+ }
+ out.flush();
+ } catch (IOException e) {
+ String message = NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_ERROR_CREATING_BUNDLE_MANIFEST, this.pluginInfo.getUniqueId(), generationLocation);
+ throw new PluginConversionException(message, e);
+ } finally {
+ if (out != null)
+ try {
+ out.close();
+ } catch (IOException e) {
+ // only report problems writing to/flushing the file
+ }
+ }
+ if (DEBUG)
+ System.out.println("Time to write out converted manifest to: " + generationLocation + ": " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ private void generateLocalizationEntry() {
+ generatedManifest.put(Constants.BUNDLE_LOCALIZATION, PLUGIN_PROPERTIES_FILENAME);
+ }
+
+ private void generateManifestVersion() {
+ generatedManifest.put(MANIFEST_VERSION, "1.0"); //$NON-NLS-1$
+ }
+
+ private boolean requireRuntimeCompatibility() {
+ ArrayList<PluginParser.Prerequisite> requireList = pluginInfo.getRequires();
+ for (Iterator<PluginParser.Prerequisite> iter = requireList.iterator(); iter.hasNext();) {
+ if (iter.next().getName().equalsIgnoreCase(PI_RUNTIME_COMPATIBILITY))
+ return true;
+ }
+ return false;
+ }
+
+ private void generateActivator() {
+ if (!pluginInfo.isFragment())
+ if (!requireRuntimeCompatibility()) {
+ String pluginClass = pluginInfo.getPluginClass();
+ if (pluginClass != null && !pluginClass.trim().equals("")) //$NON-NLS-1$
+ generatedManifest.put(Constants.BUNDLE_ACTIVATOR, pluginClass);
+ } else {
+ generatedManifest.put(Constants.BUNDLE_ACTIVATOR, COMPATIBILITY_ACTIVATOR);
+ }
+ }
+
+ private void generateClasspath() {
+ String[] classpath = pluginInfo.getLibrariesName();
+ if (classpath.length != 0)
+ generatedManifest.put(Constants.BUNDLE_CLASSPATH, getStringFromArray(classpath, LIST_SEPARATOR));
+ }
+
+ private void generateHeaders() {
+ if (TARGET31.compareTo(target) <= 0)
+ generatedManifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); //$NON-NLS-1$
+ generatedManifest.put(Constants.BUNDLE_NAME, pluginInfo.getPluginName());
+ generatedManifest.put(Constants.BUNDLE_VERSION, pluginInfo.getVersion());
+ generatedManifest.put(Constants.BUNDLE_SYMBOLICNAME, getSymbolicNameEntry());
+ String provider = pluginInfo.getProviderName();
+ if (provider != null)
+ generatedManifest.put(Constants.BUNDLE_VENDOR, provider);
+ if (pluginInfo.isFragment()) {
+ StringBuffer hostBundle = new StringBuffer();
+ hostBundle.append(pluginInfo.getMasterId());
+ String versionRange = getVersionRange(pluginInfo.getMasterVersion(), pluginInfo.getMasterMatch()); // TODO need to get match rule here!
+ if (versionRange != null)
+ hostBundle.append(versionRange);
+ generatedManifest.put(Constants.FRAGMENT_HOST, hostBundle.toString());
+ }
+ }
+
+ /*
+ * Generates an entry in the form:
+ * <symbolic-name>[; singleton=true]
+ */
+ private String getSymbolicNameEntry() {
+ // false is the default, so don't bother adding anything
+ if (!pluginInfo.isSingleton())
+ return pluginInfo.getUniqueId();
+ StringBuffer result = new StringBuffer(pluginInfo.getUniqueId());
+ result.append(SEMICOLON);
+ result.append(Constants.SINGLETON_DIRECTIVE);
+ String assignment = TARGET31.compareTo(target) <= 0 ? ":=" : "="; //$NON-NLS-1$ //$NON-NLS-2$
+ result.append(assignment).append("true"); //$NON-NLS-1$
+ return result.toString();
+ }
+
+ private void generatePluginClass() {
+ if (requireRuntimeCompatibility()) {
+ String pluginClass = pluginInfo.getPluginClass();
+ if (pluginClass != null)
+ generatedManifest.put(PluginConverterImpl.PLUGIN_CLASS, pluginClass);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void generateProvidePackage() {
+ Set<String> exports = getExports();
+ if (exports != null && exports.size() != 0) {
+ generatedManifest.put(TARGET31.compareTo(target) <= 0 ? Constants.EXPORT_PACKAGE : PluginConverterImpl.PROVIDE_PACKAGE, getStringFromCollection(exports, LIST_SEPARATOR));
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void generateRequireBundle() {
+ ArrayList<PluginParser.Prerequisite> requiredBundles = pluginInfo.getRequires();
+ if (requiredBundles.size() == 0)
+ return;
+ StringBuffer bundleRequire = new StringBuffer();
+ for (Iterator<PluginParser.Prerequisite> iter = requiredBundles.iterator(); iter.hasNext();) {
+ PluginParser.Prerequisite element = iter.next();
+ StringBuffer modImport = new StringBuffer(element.getName());
+ String versionRange = getVersionRange(element.getVersion(), element.getMatch());
+ if (versionRange != null)
+ modImport.append(versionRange);
+ if (element.isExported()) {
+ if (TARGET31.compareTo(target) <= 0)
+ modImport.append(';').append(Constants.VISIBILITY_DIRECTIVE).append(":=").append(Constants.VISIBILITY_REEXPORT);//$NON-NLS-1$
+ else
+ modImport.append(';').append(PluginConverterImpl.REPROVIDE_ATTRIBUTE).append("=true");//$NON-NLS-1$
+ }
+ if (element.isOptional()) {
+ if (TARGET31.compareTo(target) <= 0)
+ modImport.append(';').append(Constants.RESOLUTION_DIRECTIVE).append(":=").append(Constants.RESOLUTION_OPTIONAL);//$NON-NLS-1$
+ else
+ modImport.append(';').append(PluginConverterImpl.OPTIONAL_ATTRIBUTE).append("=true");//$NON-NLS-1$
+ }
+ bundleRequire.append(modImport.toString());
+ if (iter.hasNext())
+ bundleRequire.append(LIST_SEPARATOR);
+ }
+ generatedManifest.put(Constants.REQUIRE_BUNDLE, bundleRequire.toString());
+ }
+
+ private void generateTimestamp() {
+ // so it is easy to tell which ones are generated
+ generatedManifest.put(GENERATED_FROM, Long.toString(getTimeStamp(pluginManifestLocation, manifestType)) + ";" + MANIFEST_TYPE_ATTRIBUTE + "=" + manifestType); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @SuppressWarnings("deprecation")
+ private void generateEclipseHeaders() {
+ if (pluginInfo.isFragment())
+ return;
+
+ String pluginClass = pluginInfo.getPluginClass();
+ if (pluginInfo.hasExtensionExtensionPoints() || (pluginClass != null && !pluginClass.trim().equals(""))) //$NON-NLS-1$
+ generatedManifest.put(TARGET32.compareTo(target) <= 0 ? PluginConverterImpl.ECLIPSE_LAZYSTART : PluginConverterImpl.ECLIPSE_AUTOSTART, "true"); //$NON-NLS-1$
+ }
+
+ private Set<String> getExports() {
+ Map<String, List<String>> libs = pluginInfo.getLibraries();
+ if (libs == null)
+ return null;
+
+ //If we are in dev mode, then add the binary folders on the list libs with the export clause set to be the cumulation of the export clause of the real libs
+ if (devProperties != null || configuration.inDevelopmentMode()) {
+ String[] devClassPath = configuration.getDevClassPath(pluginInfo.getUniqueId(), devProperties);
+ // collect export clauses
+ List<String> allExportClauses = new ArrayList<String>(libs.size());
+ Set<Map.Entry<String, List<String>>> libEntries = libs.entrySet();
+ for (Iterator<Map.Entry<String, List<String>>> iter = libEntries.iterator(); iter.hasNext();) {
+ Map.Entry<String, List<String>> element = iter.next();
+ allExportClauses.addAll(element.getValue());
+ }
+ if (devClassPath != null) {
+ // bug 88498
+ // if there is a devClassPath defined for this plugin and the @ignoredot@ flag is true
+ // then we will ignore the '.' library specified in the plugin.xml
+ String[] ignoreDotProp = configuration.getDevClassPath(IGNORE_DOT, devProperties);
+ if (devClassPath.length > 0 && ignoreDotProp != null && ignoreDotProp.length > 0 && "true".equals(ignoreDotProp[0])) //$NON-NLS-1$
+ libs.remove(DOT);
+ for (int i = 0; i < devClassPath.length; i++)
+ libs.put(devClassPath[i], allExportClauses);
+ }
+ }
+
+ Set<String> result = new TreeSet<String>();
+ Set<Map.Entry<String, List<String>>> libEntries = libs.entrySet();
+ for (Iterator<Map.Entry<String, List<String>>> iter = libEntries.iterator(); iter.hasNext();) {
+ Map.Entry<String, List<String>> element = iter.next();
+ List<String> filter = element.getValue();
+ if (filter.size() == 0) //If the library is not exported, then ignore it
+ continue;
+ String libEntryText = element.getKey().trim();
+ File libraryLocation;
+ if (libEntryText.equals(DOT))
+ libraryLocation = pluginManifestLocation;
+ else {
+ // in development time, libEntries may contain absolute locations (linked folders)
+ File libEntryAsPath = new File(libEntryText);
+ libraryLocation = libEntryAsPath.isAbsolute() ? libEntryAsPath : new File(pluginManifestLocation, libEntryText);
+ }
+ Set<String> exports = null;
+ if (libraryLocation.exists()) {
+ if (libraryLocation.isFile())
+ exports = filterExport(getExportsFromJAR(libraryLocation), filter); //TODO Need to handle $xx$ variables
+ else if (libraryLocation.isDirectory())
+ exports = filterExport(getExportsFromDir(libraryLocation), filter);
+ } else {
+ List<String> expandedLibs = getLibrariesExpandingVariables(element.getKey(), false);
+ exports = new HashSet<String>();
+ for (Iterator<String> iterator = expandedLibs.iterator(); iterator.hasNext();) {
+ String libName = iterator.next();
+ File libFile = new File(pluginManifestLocation, libName);
+ if (libFile.isFile()) {
+ exports.addAll(filterExport(getExportsFromJAR(libFile), filter));
+ }
+ }
+ }
+ if (exports != null)
+ result.addAll(exports);
+ }
+ return result;
+ }
+
+ private Set<String> getExportsFromDir(File location) {
+ return getExportsFromDir(location, ""); //$NON-NLS-1$
+ }
+
+ private Set<String> getExportsFromDir(File location, String packageName) {
+ String prefix = (packageName.length() > 0) ? (packageName + '.') : ""; //$NON-NLS-1$
+ String[] files = location.list();
+ Set<String> exportedPaths = new HashSet<String>();
+ boolean containsFile = false;
+ if (files != null)
+ for (int i = 0; i < files.length; i++) {
+ if (!isValidPackageName(files[i]))
+ continue;
+ File pkgFile = new File(location, files[i]);
+ if (pkgFile.isDirectory())
+ exportedPaths.addAll(getExportsFromDir(pkgFile, prefix + files[i]));
+ else
+ containsFile = true;
+ }
+ if (containsFile)
+ // Allow the default package to be provided. If the default package
+ // contains a File then use "." as the package name to provide for default.
+ if (packageName.length() > 0)
+ exportedPaths.add(packageName);
+ else
+ exportedPaths.add(DOT);
+ return exportedPaths;
+ }
+
+ private Set<String> getExportsFromJAR(File jarFile) {
+ Set<String> names = new HashSet<String>();
+ ZipFile file = null;
+ try {
+ file = new ZipFile(jarFile);
+ } catch (IOException e) {
+ String message = NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_PLUGIN_LIBRARY_IGNORED, jarFile, pluginInfo.getUniqueId());
+ configuration.getHookRegistry().getContainer().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, message, null);
+ return names;
+ }
+ //Run through the entries
+ for (Enumeration<? extends ZipEntry> entriesEnum = file.entries(); entriesEnum.hasMoreElements();) {
+ ZipEntry entry = entriesEnum.nextElement();
+ String name = entry.getName();
+ if (!isValidPackageName(name))
+ continue;
+ int lastSlash = name.lastIndexOf("/"); //$NON-NLS-1$
+ //Ignore folders that do not contain files
+ if (lastSlash != -1) {
+ if (lastSlash != name.length() - 1 && name.lastIndexOf(' ') == -1)
+ names.add(name.substring(0, lastSlash).replace('/', '.'));
+ } else {
+ // Allow the default package to be provided. If the default package
+ // contains a File then use "." as the package name to provide for default.
+ names.add(DOT);
+ }
+ }
+ try {
+ file.close();
+ } catch (IOException e) {
+ // Nothing to do
+ }
+ return names;
+ }
+
+ private List<String> getLibrariesExpandingVariables(String libraryPath, boolean filter) {
+ String var = hasPrefix(libraryPath);
+ if (var == null) {
+ List<String> returnValue = new ArrayList<String>(1);
+ returnValue.add(libraryPath);
+ return returnValue;
+ }
+ if (var.equals("ws")) { //$NON-NLS-1$
+ return findWSJars(pluginManifestLocation, libraryPath, filter);
+ }
+ if (var.equals("os")) { //$NON-NLS-1$
+ return findOSJars(pluginManifestLocation, libraryPath, filter);
+ }
+ return new ArrayList<String>(0);
+ }
+
+ //return a String representing the string found between the $s
+ private String hasPrefix(String libPath) {
+ if (libPath.startsWith("$ws$")) //$NON-NLS-1$
+ return "ws"; //$NON-NLS-1$
+ if (libPath.startsWith("$os$")) //$NON-NLS-1$
+ return "os"; //$NON-NLS-1$
+ if (libPath.startsWith("$nl$")) //$NON-NLS-1$
+ return "nl"; //$NON-NLS-1$
+ return null;
+ }
+
+ private boolean isValidPackageName(String name) {
+ if (name.indexOf(' ') > 0 || name.equalsIgnoreCase("META-INF") || name.startsWith("META-INF/")) //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ return true;
+ }
+
+ /**
+ * Parses the plugin manifest to find out: - the plug-in unique identifier -
+ * the plug-in version - runtime/libraries entries - the plug-in class -
+ * the master plugin (for a fragment)
+ */
+ private IPluginInfo parsePluginInfo(InputStream pluginLocation) throws PluginConversionException {
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(pluginLocation);
+ return new PluginParser(configuration, target).parsePlugin(input);
+ } catch (Exception e) {
+ String message = NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_ERROR_PARSING_PLUGIN_MANIFEST, pluginManifestLocation);
+ throw new PluginConversionException(message, e);
+ } finally {
+ if (input != null)
+ try {
+ input.close();
+ } catch (IOException e) {
+ //ignore exception
+ }
+ }
+ }
+
+ public static boolean upToDate(File generationLocation, File pluginLocation, byte manifestType) {
+ if (!generationLocation.isFile())
+ return false;
+ String secondLine = null;
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(generationLocation)));
+ reader.readLine();
+ secondLine = reader.readLine();
+ } catch (IOException e) {
+ // not a big deal - we could not read an existing manifest
+ return false;
+ } finally {
+ if (reader != null)
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ String tag = GENERATED_FROM + ": "; //$NON-NLS-1$
+ if (secondLine == null || !secondLine.startsWith(tag))
+ return false;
+
+ secondLine = secondLine.substring(tag.length());
+ ManifestElement generatedFrom;
+ try {
+ generatedFrom = ManifestElement.parseHeader(PluginConverterImpl.GENERATED_FROM, secondLine)[0];
+ } catch (BundleException be) {
+ return false;
+ }
+ String timestampStr = generatedFrom.getValue();
+ try {
+ return Long.parseLong(timestampStr.trim()) == getTimeStamp(pluginLocation, manifestType);
+ } catch (NumberFormatException nfe) {
+ // not a big deal - just a bogus existing manifest that will be ignored
+ }
+ return false;
+ }
+
+ public static long getTimeStamp(File pluginLocation, byte manifestType) {
+ if ((manifestType & MANIFEST_TYPE_JAR) != 0)
+ return pluginLocation.lastModified();
+ else if ((manifestType & MANIFEST_TYPE_PLUGIN) != 0)
+ return new File(pluginLocation, PLUGIN_MANIFEST).lastModified();
+ else if ((manifestType & MANIFEST_TYPE_FRAGMENT) != 0)
+ return new File(pluginLocation, FRAGMENT_MANIFEST).lastModified();
+ else if ((manifestType & MANIFEST_TYPE_BUNDLE) != 0)
+ return new File(pluginLocation, PluginConverterImpl.OSGI_BUNDLE_MANIFEST).lastModified();
+ return -1;
+ }
+
+ private void writeEntry(String key, String value) throws IOException {
+ if (value != null && value.length() > 0) {
+ out.write(splitOnComma(key + ": " + value)); //$NON-NLS-1$
+ out.write('\n');
+ }
+ }
+
+ private String splitOnComma(String value) {
+ if (value.length() < MAXLINE || value.indexOf(LINE_SEPARATOR) >= 0)
+ return value; // assume the line is already split
+ String[] values = ManifestElement.getArrayFromList(value);
+ if (values == null || values.length == 0)
+ return value;
+ StringBuffer sb = new StringBuffer(value.length() + ((values.length - 1) * LIST_SEPARATOR.length()));
+ for (int i = 0; i < values.length - 1; i++)
+ sb.append(values[i]).append(LIST_SEPARATOR);
+ sb.append(values[values.length - 1]);
+ return sb.toString();
+ }
+
+ private String getStringFromArray(String[] values, String separator) {
+ if (values == null)
+ return ""; //$NON-NLS-1$
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < values.length; i++) {
+ if (i > 0)
+ result.append(separator);
+ result.append(values[i]);
+ }
+ return result.toString();
+ }
+
+ private String getStringFromCollection(Collection<String> collection, String separator) {
+ StringBuffer result = new StringBuffer();
+ boolean first = true;
+ for (Iterator<String> i = collection.iterator(); i.hasNext();) {
+ if (first)
+ first = false;
+ else
+ result.append(separator);
+ result.append(i.next());
+ }
+ return result.toString();
+ }
+
+ private synchronized Dictionary<String, String> convertManifest(File pluginBaseLocation, boolean compatibility, String targetVersion, boolean analyseJars, Dictionary<String, String> devProps, boolean failOnNoManifest) throws PluginConversionException {
+ long start = System.currentTimeMillis();
+ if (DEBUG)
+ System.out.println("Convert " + pluginBaseLocation); //$NON-NLS-1$
+ init();
+ this.target = targetVersion == null ? TARGET32 : new Version(targetVersion);
+ this.devProperties = devProps;
+ if (!fillPluginInfo(pluginBaseLocation)) {
+ if (failOnNoManifest) {
+ throw new PluginConversionException(NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_FILENOTFOUND, pluginBaseLocation.getAbsolutePath()));
+ }
+ return null;
+ }
+ fillManifest(compatibility, analyseJars);
+ if (DEBUG)
+ System.out.println("Time to convert manifest for: " + pluginBaseLocation + ": " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return generatedManifest;
+ }
+
+ public Dictionary<String, String> convertManifest(File pluginBaseLocation, boolean compatibility, String targetVersion, boolean analyseJars, Dictionary<String, String> devProps) throws PluginConversionException {
+ return convertManifest(pluginBaseLocation, compatibility, targetVersion, analyseJars, devProps, true);
+ }
+
+ synchronized File convertManifest(File pluginBaseLocation, File bundleManifestLocation, boolean compatibilityManifest, String targetVersion, boolean analyseJars, Dictionary<String, String> devProps, boolean failOnNoManifest) throws PluginConversionException {
+ Dictionary<String, String> converted = convertManifest(pluginBaseLocation, compatibilityManifest, targetVersion, analyseJars, devProps, failOnNoManifest);
+ if (converted == null) {
+ return null;
+ }
+ if (bundleManifestLocation == null) {
+ bundleManifestLocation = new File(cacheLocation, pluginInfo.getUniqueId() + '_' + pluginInfo.getVersion() + ".MF"); //$NON-NLS-1$
+ }
+ if (upToDate(bundleManifestLocation, pluginManifestLocation, manifestType))
+ return bundleManifestLocation;
+ writeManifest(bundleManifestLocation, generatedManifest, compatibilityManifest);
+ return bundleManifestLocation;
+ }
+
+ public File convertManifest(File pluginBaseLocation, File bundleManifestLocation, boolean compatibilityManifest, String targetVersion, boolean analyseJars, Dictionary<String, String> devProps) throws PluginConversionException {
+ return convertManifest(pluginBaseLocation, bundleManifestLocation, compatibilityManifest, targetVersion, analyseJars, devProps, true);
+ }
+
+ private String getVersionRange(String reqVersion, String matchRule) {
+ if (reqVersion == null)
+ return null;
+
+ Version minVersion = Version.parseVersion(reqVersion);
+ String versionRange;
+ if (matchRule != null) {
+ if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_PERFECT)) {
+ versionRange = new VersionRange(minVersion, true, minVersion, true).toString();
+ } else if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_EQUIVALENT)) {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor(), minVersion.getMinor() + 1, 0, ""), false).toString(); //$NON-NLS-1$
+ } else if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_COMPATIBLE)) {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor() + 1, 0, 0, ""), false).toString(); //$NON-NLS-1$
+ } else if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL)) {
+ // just return the reqVersion here without any version range
+ versionRange = reqVersion;
+ } else {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor() + 1, 0, 0, ""), false).toString(); //$NON-NLS-1$
+ }
+ } else {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor() + 1, 0, 0, ""), false).toString(); //$NON-NLS-1$
+ }
+
+ StringBuffer result = new StringBuffer();
+ result.append(';').append(Constants.BUNDLE_VERSION_ATTRIBUTE).append('=');
+ result.append('\"').append(versionRange).append('\"');
+ return result.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.java b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.java
new file mode 100644
index 000000000..8bec72eec
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.compatibility.plugins;
+
+import org.eclipse.osgi.util.NLS;
+
+public class PluginConverterMsg extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.osgi.compatibility.plugins.PluginConverterMsg"; //$NON-NLS-1$
+
+ public static String ECLIPSE_CONVERTER_ERROR_CONVERTING;
+
+ public static String ECLIPSE_CONVERTER_FILENOTFOUND;
+ public static String ECLIPSE_CONVERTER_ERROR_CREATING_BUNDLE_MANIFEST;
+ public static String ECLIPSE_CONVERTER_PLUGIN_LIBRARY_IGNORED;
+
+ public static String ECLIPSE_CONVERTER_ERROR_PARSING_PLUGIN_MANIFEST;
+ public static String ECLIPSE_CONVERTER_MISSING_ATTRIBUTE;
+ public static String parse_error;
+ public static String parse_errorNameLineColumn;
+
+ public static String ECLIPSE_CONVERTER_NO_SAX_FACTORY;
+ public static String ECLIPSE_CONVERTER_PARSE_UNKNOWNTOP_ELEMENT;
+
+ static {
+ // initialize resource bundles
+ NLS.initializeMessages(BUNDLE_NAME, PluginConverterMsg.class);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.properties b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.properties
new file mode 100644
index 000000000..4ad20856b
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginConverterMsg.properties
@@ -0,0 +1,25 @@
+###############################################################################
+# Copyright (c) 2004, 2013 IBM Corporation 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:
+# IBM Corporation - initial API and implementation
+###############################################################################
+
+#External Messages for EN locale
+
+#Conversion messages
+ECLIPSE_CONVERTER_ERROR_CONVERTING=Error converting plugin at {0}.
+ECLIPSE_CONVERTER_ERROR_CREATING_BUNDLE_MANIFEST=Error creating bundle manifest file for {0} at {1}.
+ECLIPSE_CONVERTER_ERROR_PARSING_PLUGIN_MANIFEST=Error parsing plugin manifest file {0} at {1}.
+ECLIPSE_CONVERTER_MISSING_ATTRIBUTE=Error parsing {0} manifest. Missing attribute \"{1}\" in element \"{2}\".
+ECLIPSE_CONVERTER_PLUGIN_LIBRARY_IGNORED=Plugin library {0} ignored when creating manifest for {1}.
+ECLIPSE_CONVERTER_NO_SAX_FACTORY=No SAX factory parser has been found.
+ECLIPSE_CONVERTER_PARSE_UNKNOWNTOP_ELEMENT = Unknown element \"{0}\", found at the top level, ignored.
+ECLIPSE_CONVERTER_FILENOTFOUND = Could not find a META-INF/MANIFEST.MF, plugin.xml or a fragment.xml in {0}.
+parse_error=Error parsing manifest: {0}
+parse_errorNameLineColumn=Error parsing manifest at \"{0}\" line \"{1}\" column \"{2}\": {3}
+
diff --git a/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginParser.java b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginParser.java
new file mode 100644
index 000000000..e6cb4d3a5
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.plugins/src/org/eclipse/osgi/compatibility/plugins/PluginParser.java
@@ -0,0 +1,708 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2013 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.compatibility.plugins;
+
+import java.io.InputStream;
+import java.util.*;
+import javax.xml.parsers.SAXParserFactory;
+import org.eclipse.osgi.container.Module;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.util.tracker.ServiceTracker;
+import org.xml.sax.*;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Internal class.
+ */
+public class PluginParser extends DefaultHandler implements IModel {
+ private static ServiceTracker<SAXParserFactory, SAXParserFactory> xmlTracker = null;
+
+ private final EquinoxConfiguration configuration;
+ private PluginInfo manifestInfo = new PluginInfo();
+ Version target; // The targeted platform for the given manifest
+ static final Version TARGET21 = new Version(2, 1, 0);
+
+ public class PluginInfo implements IPluginInfo {
+ String schemaVersion;
+ String pluginId;
+ String version;
+ String vendor;
+
+ // an ordered list of library path names.
+ List<String> libraryPaths;
+ // TODO Should get rid of the libraries map and just have a
+ // list of library export statements instead. Library paths must
+ // preserve order.
+ Map<String, List<String>> libraries; //represent the libraries and their export statement
+ ArrayList<PluginParser.Prerequisite> requires;
+ private boolean requiresExpanded = false; //indicates if the requires have been processed.
+ boolean compatibilityFound = false; //set to true is the requirement list contain compatilibity
+ String pluginClass;
+ String masterPluginId;
+ String masterVersion;
+ String masterMatch;
+ private Set<String> filters;
+ String pluginName;
+ boolean singleton;
+ boolean fragment;
+ private final static String TARGET21_STRING = "2.1"; //$NON-NLS-1$
+ boolean hasExtensionExtensionPoints = false;
+
+ public boolean isFragment() {
+ return fragment;
+ }
+
+ public String toString() {
+ return "plugin-id: " + pluginId + " version: " + version + " libraries: " + libraries + " class:" + pluginClass + " master: " + masterPluginId + " master-version: " + masterVersion + " requires: " + requires + " singleton: " + singleton; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
+ }
+
+ public Map<String, List<String>> getLibraries() {
+ if (libraries == null)
+ return new HashMap<String, List<String>>(0);
+ return libraries;
+ }
+
+ public ArrayList<Prerequisite> getRequires() {
+ if (!TARGET21.equals(target) && schemaVersion == null && !requiresExpanded) {
+ requiresExpanded = true;
+ if (requires == null) {
+ requires = new ArrayList<Prerequisite>(1);
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, TARGET21_STRING, false, false, IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL));
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null));
+ } else {
+ //Add elements on the requirement list of ui and help.
+ for (int i = 0; i < requires.size(); i++) {
+ Prerequisite analyzed = requires.get(i);
+ if ("org.eclipse.ui".equals(analyzed.getName())) { //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.workbench.texteditor", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.jface.text", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.editors", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.views", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.ide", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ } else if ("org.eclipse.help".equals(analyzed.getName())) { //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.help.base", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ } else if (PluginConverterImpl.PI_RUNTIME.equals(analyzed.getName()) && !compatibilityFound) {
+ requires.add(i + 1, new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, analyzed.isExported(), null));
+ }
+ }
+ if (!requires.contains(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null))) {
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null));
+ }
+ //Remove any prereq on runtime and add a prereq on runtime 2.1
+ //This is used to recognize the version for which the given plugin was initially targeted.
+ Prerequisite runtimePrereq = new Prerequisite(PluginConverterImpl.PI_RUNTIME, null, false, false, null);
+ requires.remove(runtimePrereq);
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, TARGET21_STRING, false, false, IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL));
+ }
+ }
+ if (requires == null)
+ return requires = new ArrayList<Prerequisite>(0);
+
+ return requires;
+ }
+
+ public String getMasterId() {
+ return masterPluginId;
+ }
+
+ public String getMasterVersion() {
+ return masterVersion;
+ }
+
+ public String getMasterMatch() {
+ return masterMatch;
+ }
+
+ public String getPluginClass() {
+ return pluginClass;
+ }
+
+ public String getUniqueId() {
+ return pluginId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public Set<String> getPackageFilters() {
+ return filters;
+ }
+
+ public String[] getLibrariesName() {
+ if (libraryPaths == null)
+ return new String[0];
+ return libraryPaths.toArray(new String[libraryPaths.size()]);
+ }
+
+ public String getPluginName() {
+ return pluginName;
+ }
+
+ public String getProviderName() {
+ return vendor;
+ }
+
+ public boolean isSingleton() {
+ return singleton;
+ }
+
+ public boolean hasExtensionExtensionPoints() {
+ return hasExtensionExtensionPoints;
+ }
+
+ public String getRoot() {
+ return isFragment() ? FRAGMENT : PLUGIN;
+ }
+
+ /*
+ * Provides some basic form of validation. Since plugin/fragment is the only mandatory
+ * attribute, it is the only one we cara about here.
+ */
+ public String validateForm() {
+ if (this.pluginId == null)
+ return NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), PLUGIN_ID, getRoot()});
+ if (this.pluginName == null)
+ return NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), PLUGIN_NAME, getRoot()});
+ if (this.version == null)
+ return NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), PLUGIN_VERSION, getRoot()});
+ if (isFragment() && this.masterPluginId == null)
+ return NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), FRAGMENT_PLUGIN_ID, getRoot()});
+ if (isFragment() && this.masterVersion == null)
+ return NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), FRAGMENT_PLUGIN_VERSION, getRoot()});
+ return null;
+ }
+ }
+
+ // Current State Information
+ Stack<Integer> stateStack = new Stack<Integer>();
+
+ // Current object stack (used to hold the current object we are populating in this plugin info
+ Stack<Object> objectStack = new Stack<Object>();
+ Locator locator = null;
+
+ // Valid States
+ private static final int IGNORED_ELEMENT_STATE = 0;
+ private static final int INITIAL_STATE = 1;
+ private static final int PLUGIN_STATE = 2;
+ private static final int PLUGIN_RUNTIME_STATE = 3;
+ private static final int PLUGIN_REQUIRES_STATE = 4;
+ private static final int PLUGIN_EXTENSION_POINT_STATE = 5;
+ private static final int PLUGIN_EXTENSION_STATE = 6;
+ private static final int RUNTIME_LIBRARY_STATE = 7;
+ private static final int LIBRARY_EXPORT_STATE = 8;
+ private static final int PLUGIN_REQUIRES_IMPORT_STATE = 9;
+ private static final int FRAGMENT_STATE = 11;
+
+ public PluginParser(EquinoxConfiguration configuration, Version target) {
+ super();
+ this.configuration = configuration;
+ this.target = target;
+ }
+
+ /**
+ * Receive a Locator object for document events.
+ *
+ * <p>
+ * By default, do nothing. Application writers may override this method in
+ * a subclass if they wish to store the locator for use with other document
+ * events.
+ * </p>
+ *
+ * @param locator A locator for all SAX document events.
+ * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+ * @see org.xml.sax.Locator
+ */
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ public void endDocument() {
+ // nothing
+ }
+
+ public void endElement(String uri, String elementName, String qName) {
+ switch (stateStack.peek().intValue()) {
+ case IGNORED_ELEMENT_STATE :
+ stateStack.pop();
+ break;
+ case INITIAL_STATE :
+ // shouldn't get here
+ // internalError(Policy.bind("parse.internalStack", elementName)); //$NON-NLS-1$
+ break;
+ case PLUGIN_STATE :
+ case FRAGMENT_STATE :
+ break;
+ case PLUGIN_RUNTIME_STATE :
+ if (elementName.equals(RUNTIME)) {
+ stateStack.pop();
+ }
+ break;
+ case PLUGIN_REQUIRES_STATE :
+ if (elementName.equals(PLUGIN_REQUIRES)) {
+ stateStack.pop();
+ objectStack.pop();
+ }
+ break;
+ case PLUGIN_EXTENSION_POINT_STATE :
+ if (elementName.equals(EXTENSION_POINT)) {
+ stateStack.pop();
+ }
+ break;
+ case PLUGIN_EXTENSION_STATE :
+ if (elementName.equals(EXTENSION)) {
+ stateStack.pop();
+ }
+ break;
+ case RUNTIME_LIBRARY_STATE :
+ if (elementName.equals(LIBRARY)) {
+ String curLibrary = (String) objectStack.pop();
+ if (!curLibrary.trim().equals("")) { //$NON-NLS-1$
+ @SuppressWarnings("unchecked")
+ List<String> exports = (List<String>) objectStack.pop();
+ if (manifestInfo.libraries == null) {
+ manifestInfo.libraries = new HashMap<String, List<String>>(3);
+ manifestInfo.libraryPaths = new ArrayList<String>(3);
+ }
+ manifestInfo.libraries.put(curLibrary, exports);
+ manifestInfo.libraryPaths.add(curLibrary.replace('\\', '/'));
+ }
+ stateStack.pop();
+ }
+ break;
+ case LIBRARY_EXPORT_STATE :
+ if (elementName.equals(LIBRARY_EXPORT)) {
+ stateStack.pop();
+ }
+ break;
+ case PLUGIN_REQUIRES_IMPORT_STATE :
+ if (elementName.equals(PLUGIN_REQUIRES_IMPORT)) {
+ stateStack.pop();
+ }
+ break;
+ }
+ }
+
+ public void error(SAXParseException ex) {
+ logStatus(ex);
+ }
+
+ public void fatalError(SAXParseException ex) throws SAXException {
+ logStatus(ex);
+ throw ex;
+ }
+
+ public void handleExtensionPointState(String elementName, Attributes attributes) {
+ // nothing to do for extension-points' children
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ manifestInfo.hasExtensionExtensionPoints = true;
+ }
+
+ public void handleExtensionState(String elementName, Attributes attributes) {
+ // nothing to do for extensions' children
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ manifestInfo.hasExtensionExtensionPoints = true;
+ }
+
+ public void handleInitialState(String elementName, Attributes attributes) {
+ if (elementName.equals(PLUGIN)) {
+ stateStack.push(new Integer(PLUGIN_STATE));
+ parsePluginAttributes(attributes);
+ } else if (elementName.equals(FRAGMENT)) {
+ manifestInfo.fragment = true;
+ stateStack.push(new Integer(FRAGMENT_STATE));
+ parseFragmentAttributes(attributes);
+ } else {
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+ }
+
+ public void handleLibraryExportState(String elementName, Attributes attributes) {
+ // All elements ignored.
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ }
+
+ public void handleLibraryState(String elementName, Attributes attributes) {
+ if (elementName.equals(LIBRARY_EXPORT)) {
+ // Change State
+ stateStack.push(new Integer(LIBRARY_EXPORT_STATE));
+ // The top element on the stack much be a library element
+ String currentLib = (String) objectStack.peek();
+ if (attributes == null)
+ return;
+ String maskValue = attributes.getValue("", LIBRARY_EXPORT_MASK); //$NON-NLS-1$
+ // pop off the library - already in currentLib
+ objectStack.pop();
+ @SuppressWarnings("unchecked")
+ List<String> exportMask = (List<String>) objectStack.peek();
+ // push library back on
+ objectStack.push(currentLib);
+ //Split the export upfront
+ if (maskValue != null) {
+ StringTokenizer tok = new StringTokenizer(maskValue, ","); //$NON-NLS-1$
+ while (tok.hasMoreTokens()) {
+ String value = tok.nextToken();
+ if (!exportMask.contains(maskValue))
+ exportMask.add(value.trim());
+ }
+ }
+ return;
+ }
+ if (elementName.equals(LIBRARY_PACKAGES)) {
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ return;
+ }
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ return;
+ }
+
+ public void handlePluginState(String elementName, Attributes attributes) {
+ if (elementName.equals(RUNTIME)) {
+ // We should only have one Runtime element in a plugin or fragment
+ Object whatIsIt = objectStack.peek();
+ if ((whatIsIt instanceof PluginInfo) && ((PluginInfo) objectStack.peek()).libraries != null) {
+ // This is at least the 2nd Runtime element we have hit. Ignore it.
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ return;
+ }
+ stateStack.push(new Integer(PLUGIN_RUNTIME_STATE));
+ // Push a new vector to hold all the library entries objectStack.push(new Vector());
+ return;
+ }
+ if (elementName.equals(PLUGIN_REQUIRES)) {
+ stateStack.push(new Integer(PLUGIN_REQUIRES_STATE));
+ // Push a new vector to hold all the prerequisites
+ objectStack.push(new ArrayList<String>());
+ parseRequiresAttributes(attributes);
+ return;
+ }
+ if (elementName.equals(EXTENSION_POINT)) {
+ // mark the plugin as singleton and ignore all elements under extension (if there are any)
+ manifestInfo.singleton = true;
+ stateStack.push(new Integer(PLUGIN_EXTENSION_POINT_STATE));
+ return;
+ }
+ if (elementName.equals(EXTENSION)) {
+ // mark the plugin as singleton and ignore all elements under extension (if there are any)
+ manifestInfo.singleton = true;
+ stateStack.push(new Integer(PLUGIN_EXTENSION_STATE));
+ return;
+ }
+ // If we get to this point, the element name is one we don't currently accept.
+ // Set the state to indicate that this element will be ignored
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+
+ public void handleRequiresImportState(String elementName, Attributes attributes) {
+ // All elements ignored.
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ }
+
+ public void handleRequiresState(String elementName, Attributes attributes) {
+ if (elementName.equals(PLUGIN_REQUIRES_IMPORT)) {
+ parsePluginRequiresImport(attributes);
+ return;
+ }
+ // If we get to this point, the element name is one we don't currently accept.
+ // Set the state to indicate that this element will be ignored
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+
+ public void handleRuntimeState(String elementName, Attributes attributes) {
+ if (elementName.equals(LIBRARY)) {
+ // Change State
+ stateStack.push(new Integer(RUNTIME_LIBRARY_STATE));
+ // Process library attributes
+ parseLibraryAttributes(attributes);
+ return;
+ }
+ // If we get to this point, the element name is one we don't currently accept.
+ // Set the state to indicate that this element will be ignored
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+
+ private void logStatus(SAXParseException ex) {
+ String name = ex.getSystemId();
+ if (name == null)
+ name = ""; //$NON-NLS-1$
+ else
+ name = name.substring(1 + name.lastIndexOf("/")); //$NON-NLS-1$
+ String msg;
+ if (name.equals("")) //$NON-NLS-1$
+ msg = NLS.bind(PluginConverterMsg.parse_error, ex.getMessage());
+ else
+ msg = NLS.bind(PluginConverterMsg.parse_errorNameLineColumn, new String[] {name, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber()), ex.getMessage()});
+
+ configuration.getHookRegistry().getContainer().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, msg, null);
+ }
+
+ synchronized public PluginInfo parsePlugin(InputStream in) throws Exception {
+ Module systemModule = configuration.getHookRegistry().getContainer().getStorage().getModuleContainer().getModule(0);
+ Bundle systemBundle = systemModule.getBundle();
+ BundleContext systemContext = systemBundle.getBundleContext();
+ SAXParserFactory factory = acquireXMLParsing(systemContext);
+ if (factory == null) {
+ configuration.getHookRegistry().getContainer().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, PluginConverterMsg.ECLIPSE_CONVERTER_NO_SAX_FACTORY, null);
+ return null;
+ }
+
+ factory.setNamespaceAware(true);
+ factory.setNamespaceAware(true);
+ try {
+ factory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
+ } catch (SAXException se) {
+ // ignore; we can still operate without string-interning
+ }
+ factory.setValidating(false);
+ factory.newSAXParser().parse(in, this);
+ return manifestInfo;
+ }
+
+ public static SAXParserFactory acquireXMLParsing(BundleContext context) {
+ if (xmlTracker == null) {
+ xmlTracker = new ServiceTracker<SAXParserFactory, SAXParserFactory>(context, "javax.xml.parsers.SAXParserFactory", null); //$NON-NLS-1$
+ xmlTracker.open();
+ }
+ SAXParserFactory result = xmlTracker.getService();
+ if (result != null)
+ return result;
+ // backup to using jaxp to create a new instance
+ return SAXParserFactory.newInstance();
+ }
+
+ public static void releaseXMLParsing() {
+ if (xmlTracker != null)
+ xmlTracker.close();
+ }
+
+ public void parseFragmentAttributes(Attributes attributes) {
+ // process attributes
+ objectStack.push(manifestInfo);
+ int len = attributes.getLength();
+ for (int i = 0; i < len; i++) {
+ String attrName = attributes.getLocalName(i);
+ String attrValue = attributes.getValue(i).trim();
+ if (attrName.equals(FRAGMENT_ID))
+ manifestInfo.pluginId = attrValue;
+ else if (attrName.equals(FRAGMENT_NAME))
+ manifestInfo.pluginName = attrValue;
+ else if (attrName.equals(FRAGMENT_VERSION))
+ manifestInfo.version = attrValue;
+ else if (attrName.equals(FRAGMENT_PROVIDER))
+ manifestInfo.vendor = attrValue;
+ else if (attrName.equals(FRAGMENT_PLUGIN_ID))
+ manifestInfo.masterPluginId = attrValue;
+ else if (attrName.equals(FRAGMENT_PLUGIN_VERSION))
+ manifestInfo.masterVersion = attrValue;
+ else if (attrName.equals(FRAGMENT_PLUGIN_MATCH))
+ manifestInfo.masterMatch = attrValue;
+ }
+ }
+
+ public void parseLibraryAttributes(Attributes attributes) {
+ // Push a vector to hold the export mask
+ objectStack.push(new ArrayList<String>());
+ String current = attributes.getValue("", LIBRARY_NAME); //$NON-NLS-1$
+ objectStack.push(current);
+ }
+
+ public void parsePluginAttributes(Attributes attributes) {
+ // process attributes
+ objectStack.push(manifestInfo);
+ int len = attributes.getLength();
+ for (int i = 0; i < len; i++) {
+ String attrName = attributes.getLocalName(i);
+ String attrValue = attributes.getValue(i).trim();
+ if (attrName.equals(PLUGIN_ID))
+ manifestInfo.pluginId = attrValue;
+ else if (attrName.equals(PLUGIN_NAME))
+ manifestInfo.pluginName = attrValue;
+ else if (attrName.equals(PLUGIN_VERSION))
+ manifestInfo.version = attrValue;
+ else if (attrName.equals(PLUGIN_VENDOR) || (attrName.equals(PLUGIN_PROVIDER)))
+ manifestInfo.vendor = attrValue;
+ else if (attrName.equals(PLUGIN_CLASS))
+ manifestInfo.pluginClass = attrValue;
+ }
+ }
+
+ public class Prerequisite {
+ String name;
+ String version;
+ boolean optional;
+ boolean export;
+ String match;
+
+ public boolean isExported() {
+ return export;
+ }
+
+ public String getMatch() {
+ return match;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public Prerequisite(String preqName, String prereqVersion, boolean isOtional, boolean isExported, String prereqMatch) {
+ name = preqName;
+ version = prereqVersion;
+ optional = isOtional;
+ export = isExported;
+ match = prereqMatch;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public boolean equals(Object prereq) {
+ if (!(prereq instanceof Prerequisite))
+ return false;
+ return name.equals(((Prerequisite) prereq).name);
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
+ public void parsePluginRequiresImport(Attributes attributes) {
+ if (manifestInfo.requires == null) {
+ manifestInfo.requires = new ArrayList<Prerequisite>();
+ // to avoid cycles
+ // if (!manifestInfo.pluginId.equals(PluginConverterImpl.PI_RUNTIME)) //$NON-NLS-1$
+ // manifestInfo.requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, null, false, false, null)); //$NON-NLS-1$
+ }
+ // process attributes
+ String plugin = attributes.getValue("", PLUGIN_REQUIRES_PLUGIN); //$NON-NLS-1$
+ if (plugin == null)
+ return;
+ if (plugin.equals(PluginConverterImpl.PI_BOOT))
+ return;
+ if (plugin.equals(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY))
+ manifestInfo.compatibilityFound = true;
+ String version = attributes.getValue("", PLUGIN_REQUIRES_PLUGIN_VERSION); //$NON-NLS-1$
+ String optional = attributes.getValue("", PLUGIN_REQUIRES_OPTIONAL); //$NON-NLS-1$
+ String export = attributes.getValue("", PLUGIN_REQUIRES_EXPORT); //$NON-NLS-1$
+ String match = attributes.getValue("", PLUGIN_REQUIRES_MATCH); //$NON-NLS-1$
+ manifestInfo.requires.add(new Prerequisite(plugin, version, "true".equalsIgnoreCase(optional) ? true : false, "true".equalsIgnoreCase(export) ? true : false, match)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void parseRequiresAttributes(Attributes attributes) {
+ //Nothing to do.
+ }
+
+ static String replace(String s, String from, String to) {
+ String str = s;
+ int fromLen = from.length();
+ int toLen = to.length();
+ int ix = str.indexOf(from);
+ while (ix != -1) {
+ str = str.substring(0, ix) + to + str.substring(ix + fromLen);
+ ix = str.indexOf(from, ix + toLen);
+ }
+ return str;
+ }
+
+ public void startDocument() {
+ stateStack.push(new Integer(INITIAL_STATE));
+ }
+
+ public void startElement(String uri, String elementName, String qName, Attributes attributes) {
+ switch (stateStack.peek().intValue()) {
+ case INITIAL_STATE :
+ handleInitialState(elementName, attributes);
+ break;
+ case FRAGMENT_STATE :
+ case PLUGIN_STATE :
+ handlePluginState(elementName, attributes);
+ break;
+ case PLUGIN_RUNTIME_STATE :
+ handleRuntimeState(elementName, attributes);
+ break;
+ case PLUGIN_REQUIRES_STATE :
+ handleRequiresState(elementName, attributes);
+ break;
+ case PLUGIN_EXTENSION_POINT_STATE :
+ handleExtensionPointState(elementName, attributes);
+ break;
+ case PLUGIN_EXTENSION_STATE :
+ handleExtensionState(elementName, attributes);
+ break;
+ case RUNTIME_LIBRARY_STATE :
+ handleLibraryState(elementName, attributes);
+ break;
+ case LIBRARY_EXPORT_STATE :
+ handleLibraryExportState(elementName, attributes);
+ break;
+ case PLUGIN_REQUIRES_IMPORT_STATE :
+ handleRequiresImportState(elementName, attributes);
+ break;
+ default :
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ }
+ }
+
+ public void warning(SAXParseException ex) {
+ logStatus(ex);
+ }
+
+ private void internalError(String elementName) {
+ String message = NLS.bind(PluginConverterMsg.ECLIPSE_CONVERTER_PARSE_UNKNOWNTOP_ELEMENT, elementName);
+ configuration.getHookRegistry().getContainer().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, (manifestInfo.pluginId == null ? message : "Plug-in : " + manifestInfo.pluginId + ", " + message), null); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ /**
+ * @throws SAXException
+ */
+ public void processingInstruction(String instructionTarget, String data) throws SAXException {
+ // Since 3.0, a processing instruction of the form <?eclipse version="3.0"?> at
+ // the start of the manifest file is used to indicate the plug-in manifest
+ // schema version in effect. Pre-3.0 (i.e., 2.1) plug-in manifest files do not
+ // have one of these, and this is how we can distinguish the manifest of a
+ // pre-3.0 plug-in from a post-3.0 one (for compatibility tranformations).
+ if (instructionTarget.equalsIgnoreCase("eclipse")) { //$NON-NLS-1$
+ // just the presence of this processing instruction indicates that this
+ // plug-in is at least 3.0
+ manifestInfo.schemaVersion = "3.0"; //$NON-NLS-1$
+ StringTokenizer tokenizer = new StringTokenizer(data, "=\""); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ if (token.equalsIgnoreCase("version")) { //$NON-NLS-1$
+ if (!tokenizer.hasMoreTokens()) {
+ break;
+ }
+ manifestInfo.schemaVersion = tokenizer.nextToken();
+ break;
+ }
+ }
+ }
+ }
+}

Back to the top