blob: 6190447b8c45fa467344c6413bbdb42401281f1b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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.equinox.internal.p2.publisher;
import java.io.*;
import java.util.*;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.osgi.service.pluginconversion.PluginConversionException;
import org.eclipse.osgi.service.pluginconversion.PluginConverter;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
public class BundleDescriptionFactory {
public static final String DIR = "dir"; //$NON-NLS-1$
public static final String JAR = "jar"; //$NON-NLS-1$
private static final String FEATURE_FILENAME_DESCRIPTOR = "feature.xml"; //$NON-NLS-1$
private static final String PLUGIN_FILENAME_DESCRIPTOR = "plugin.xml"; //$NON-NLS-1$
private static final String FRAGMENT_FILENAME_DESCRIPTOR = "fragment.xml"; //$NON-NLS-1$
public static String BUNDLE_SHAPE = "Eclipse-BundleShape"; //$NON-NLS-1$
// static final String DEFAULT_BUNDLE_LOCALIZATION = "plugin"; //$NON-NLS-1$
// static final String PROPERTIES_FILE_EXTENSION = ".properties"; //$NON-NLS-1$
// static final String MANIFEST_LOCALIZATIONS = "eclipse.p2.manifest.localizations"; //$NON-NLS-1$
//
// static final Locale DEFAULT_LOCALE = new Locale("df", "LT"); //$NON-NLS-1$//$NON-NLS-2$
// static final Locale PSEUDO_LOCALE = new Locale("zz", "ZZ"); //$NON-NLS-1$//$NON-NLS-2$
StateObjectFactory factory;
State state;
public static BundleDescriptionFactory getBundleDescriptionFactory(BundleContext context) {
PlatformAdmin platformAdmin = (PlatformAdmin) ServiceHelper.getService(context, PlatformAdmin.class.getName());
if (platformAdmin == null)
throw new IllegalStateException("PlatformAdmin not registered."); //$NON-NLS-1$
return new BundleDescriptionFactory(platformAdmin.getFactory(), null);
}
public BundleDescriptionFactory(StateObjectFactory factory, State state) {
this.factory = factory;
this.state = state;
//TODO find a state and a factory when not provided
}
private static PluginConverter acquirePluginConverter() {
return (PluginConverter) ServiceHelper.getService(Activator.getContext(), PluginConverter.class.getName());
}
private static Dictionary convertPluginManifest(File bundleLocation, boolean logConversionException) {
PluginConverter converter;
try {
converter = acquirePluginConverter();
if (converter == null) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Unable to aquire PluginConverter service during generation for: " + bundleLocation));
return null;
}
return converter.convertManifest(bundleLocation, false, null, true, null);
} catch (PluginConversionException convertException) {
// only log the exception if we had a plugin.xml or fragment.xml and we failed conversion
if (bundleLocation.getName().equals(FEATURE_FILENAME_DESCRIPTOR))
return null;
if (!new File(bundleLocation, PLUGIN_FILENAME_DESCRIPTOR).exists() && !new File(bundleLocation, FRAGMENT_FILENAME_DESCRIPTOR).exists())
return null;
if (logConversionException) {
IStatus status = new Status(IStatus.WARNING, Activator.ID, 0, NLS.bind(Messages.exception_errorConverting, bundleLocation.getAbsolutePath()), convertException);
LogHelper.log(status);
}
return null;
}
}
public BundleDescription getBundleDescription(Dictionary enhancedManifest, File bundleLocation) {
try {
BundleDescription descriptor = factory.createBundleDescription(state, enhancedManifest, bundleLocation != null ? bundleLocation.getAbsolutePath() : null, 1); //TODO Do we need to have a real bundle id
descriptor.setUserObject(enhancedManifest);
return descriptor;
} catch (BundleException e) {
String message = NLS.bind(Messages.exception_stateAddition, bundleLocation == null ? null : bundleLocation.getAbsoluteFile());
IStatus status = new Status(IStatus.WARNING, Activator.ID, message, e);
LogHelper.log(status);
return null;
}
}
public BundleDescription getBundleDescription(File bundleLocation) {
Dictionary manifest = loadManifest(bundleLocation);
if (manifest == null)
return null;
return getBundleDescription(manifest, bundleLocation);
}
public BundleDescription getBundleDescription(InputStream manifestStream, File bundleLocation) {
Hashtable entries = new Hashtable();
try {
ManifestElement.parseBundleManifest(manifestStream, entries);
return getBundleDescription(entries, bundleLocation);
} catch (IOException e) {
String message = "An error occurred while reading the bundle description.";
IStatus status = new Status(IStatus.ERROR, Activator.ID, message, e);
LogHelper.log(status);
} catch (BundleException e) {
String message = "An error occurred while reading the bundle description.";
IStatus status = new Status(IStatus.ERROR, Activator.ID, message, e);
LogHelper.log(status);
}
return null;
}
public static Dictionary loadManifest(File bundleLocation) {
InputStream manifestStream = null;
ZipFile jarFile = null;
try {
if ("jar".equalsIgnoreCase(new Path(bundleLocation.getName()).getFileExtension()) && bundleLocation.isFile()) { //$NON-NLS-1$
jarFile = new ZipFile(bundleLocation, ZipFile.OPEN_READ);
ZipEntry manifestEntry = jarFile.getEntry(JarFile.MANIFEST_NAME);
if (manifestEntry != null) {
manifestStream = jarFile.getInputStream(manifestEntry);
}
} else {
File manifestFile = new File(bundleLocation, JarFile.MANIFEST_NAME);
if (manifestFile.exists())
manifestStream = new BufferedInputStream(new FileInputStream(manifestFile));
}
} catch (IOException e) {
//ignore but log
LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "An error occurred while loading the bundle manifest.", e));
}
Dictionary manifest = null;
if (manifestStream != null) {
try {
Map manifestMap = ManifestElement.parseBundleManifest(manifestStream, null);
// TODO temporary hack. We are reading a Map but everyone wants a Dictionary so convert.
// real answer is to have people expect a Map but that is a wider change.
manifest = new Hashtable(manifestMap);
} catch (IOException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "An error occurred while loading the bundle manifest.", e));
return null;
} catch (BundleException e) {
LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "An error occurred while loading the bundle manifest.", e));
return null;
} finally {
try {
if (jarFile != null)
jarFile.close();
} catch (IOException e2) {
//Ignore
}
}
} else {
manifest = convertPluginManifest(bundleLocation, true);
}
if (manifest == null)
return null;
//Deal with the pre-3.0 plug-in shape who have a default jar manifest.mf
if (manifest.get(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME) == null)
manifest = convertPluginManifest(bundleLocation, true);
if (manifest == null)
return null;
// if the bundle itself does not define its shape, infer the shape from the current form
if (manifest.get(BUNDLE_SHAPE) == null)
manifest.put(BUNDLE_SHAPE, bundleLocation.isDirectory() ? DIR : JAR);
getManifestLocalizations(manifest, bundleLocation);
// localizeManifest(manifest, bundleLocation);
return manifest;
}
// private Properties loadProperties(File bundleLocation, String localizationFile) throws IOException {
// Properties result = new Properties();
// InputStream propertyStream = null;
// try {
// try {
// if (bundleLocation.isDirectory())
// propertyStream = new FileInputStream(new File(bundleLocation, localizationFile));
// else {
// URLConnection connection = new URL("jar:" + bundleLocation.toURL().toExternalForm() + "!/" + localizationFile).openConnection(); //$NON-NLS-1$ //$NON-NLS-2$
// connection.setUseCaches(false);
// propertyStream = connection.getInputStream();
// }
// } catch (FileNotFoundException e) {
// // if there is no messages file then just return;
// return result;
// }
// result.load(propertyStream);
// } finally {
// if (propertyStream != null)
// propertyStream.close();
// }
// return result;
// }
// Collect the manifest localizations from the bundle directory
// and store them in the manifest.
private static void getManifestLocalizations(Dictionary manifest, File bundleLocation) {
// Map localizations;
// Locale defaultLocale = null; // = Locale.ENGLISH; // TODO: get this from GeneratorInfo
// String bundleLocalization = (String) manifest.get(Constants.BUNDLE_LOCALIZATION);
// if (bundleLocalization == null || bundleLocalization.trim().length() == 0)
// bundleLocalization = DEFAULT_BUNDLE_LOCALIZATION;
//
// if ("jar".equalsIgnoreCase(new Path(bundleLocation.getName()).getFileExtension()) && //$NON-NLS-1$
// bundleLocation.isFile()) {
// localizations = getJarManifestLocalization(bundleLocation, bundleLocalization, manifest, defaultLocale);
// } else {
// localizations = getDirManifestLocalization(bundleLocation, bundleLocalization, manifest, defaultLocale);
// }
//
// if (localizations.size() > 0) {
// manifest.put(MANIFEST_LOCALIZATIONS, localizations);
// }
}
// private Map getJarManifestLocalization(File bundleLocation, String bundleLocalization, Dictionary manifest, Locale defaultLocale) {
// ZipFile jarFile = null;
// Map localizations = new HashMap(4);
// try {
// jarFile = new ZipFile(bundleLocation, ZipFile.OPEN_READ);
// for (Enumeration entries = jarFile.entries(); entries.hasMoreElements();) {
// ZipEntry nextEntry = (ZipEntry) entries.nextElement();
// String nextName = nextEntry.getName();
// String localeString = getLocaleString(nextName, bundleLocalization);
//
// if (!nextEntry.isDirectory() && localeString != null) {
// Locale nextLocale = getLocale(localeString);
// InputStream stream = null;
// try {
// stream = jarFile.getInputStream(nextEntry);
// Properties properties = new Properties();
// properties.load(stream);
// Properties localizedStrings = getLocalizedProperties(manifest, properties);
// if (localizedStrings.size() > 0) {
// localizations.put(nextLocale, localizedStrings);
// if (DEFAULT_LOCALE.equals(nextLocale) && defaultLocale != null) {
// localizations.put(nextLocale, localizedStrings);
// }
// }
// } finally {
// if (stream != null)
// stream.close();
// }
// }
// }
// } catch (IOException ioe) {
// ioe.printStackTrace();
// } finally {
// if (jarFile != null) {
// try {
// jarFile.close();
// } catch (IOException ioe) {
// // do nothing
// }
// }
// }
//
// return localizations;
// }
//
// private Map getDirManifestLocalization(File bundleLocation, String bundleLocalization, Dictionary manifest, Locale defaultLocale) {
// File localizationPath = new File(bundleLocation, bundleLocalization);
// File localizationDir = localizationPath.getParentFile();
// String localizationFile = localizationPath.getName();
// String[] localizationFiles = localizationDir.list(new LocalizationFileFilter(localizationFile));
//
// HashMap localizations = null;
//
// if (localizationFiles != null) {
// localizations = new HashMap(localizationFiles.length);
// for (int i = 0; i < localizationFiles.length; i++) {
// String nextFile = localizationFiles[i];
// Locale nextLocale = getLocale(getLocaleString(nextFile, localizationFile));
//
// try {
// Properties properties = loadProperties(bundleLocation, nextFile);
// Properties localizedStrings = getLocalizedProperties(manifest, properties);
// if (localizedStrings.size() > 0) {
// localizations.put(nextLocale, localizedStrings);
// if (DEFAULT_LOCALE.equals(nextLocale) && defaultLocale != null) {
// localizations.put(nextLocale, localizedStrings);
// }
// }
// } catch (IOException ioe) {
// ioe.printStackTrace();
// }
// }
// }
//
// return localizations;
// }
// private class LocalizationFileFilter implements FilenameFilter {
//
// String filenamePrefix;
//
// public LocalizationFileFilter(String filenamePrefix) {
// this.filenamePrefix = filenamePrefix;
// }
//
// /* (non-Javadoc)
// * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
// */
// public boolean accept(File directory, String filename) {
// return (getLocaleString(filename, filenamePrefix) != null ? true : false);
// }
// }
// static public String getLocaleString(String filename, String filenamePrefix) {
// String localeString = null;
// if (filename.startsWith(filenamePrefix) && filename.endsWith(PROPERTIES_FILE_EXTENSION)) {
// if (filename.length() > filenamePrefix.length() + PROPERTIES_FILE_EXTENSION.length()) {
// localeString = filename.substring(filenamePrefix.length() + 1, filename.length() - PROPERTIES_FILE_EXTENSION.length());
// } else {
// localeString = ""; //$NON-NLS-1$
// }
// }
// return localeString;
// }
// static private Locale getLocale(String localeString) {
// Locale locale = DEFAULT_LOCALE;
// if (localeString.length() == 5 && localeString.indexOf('_') == 2) {
// locale = new Locale(localeString.substring(0, 2), localeString.substring(3, 5));
// } else if (localeString.length() == 2) {
// locale = new Locale(localeString.substring(0, 2));
// }
// return locale;
// }
//
// static private Properties getLocalizedProperties(Dictionary manifest, Properties properties) {
// // Walk over the manifest and find all %xxx with the string value
// // in the properties file and copy them to the localized properties.
// Properties localizedProperties = new Properties();
// for (Enumeration e = manifest.keys(); e.hasMoreElements();) {
// String key = (String) e.nextElement();
// Object value = manifest.get(key);
// if (value instanceof String) {
// String stringValue = (String) value;
// if (stringValue.startsWith("%")) { //$NON-NLS-1$
// String newValue = properties.getProperty(stringValue.substring(1));
// if (newValue != null)
// localizedProperties.put(key, newValue);
// }
// }
// }
// return localizedProperties;
// }
}