/******************************************************************************* * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. ******************************************************************************/ package org.eclipse.buckminster.pde.tasks; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.buckminster.core.actor.AbstractActor; import org.eclipse.buckminster.core.actor.IActionContext; import org.eclipse.buckminster.core.helpers.BMProperties; import org.eclipse.buckminster.pde.IPDEConstants; import org.eclipse.buckminster.pde.PDEPlugin; import org.eclipse.buckminster.pde.internal.FeatureModelReader; import org.eclipse.buckminster.pde.internal.model.EditableFeatureModel; import org.eclipse.buckminster.pde.internal.model.ExternalBundleModel; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.pde.core.plugin.IPluginBase; import org.eclipse.pde.internal.build.IBuildPropertiesConstants; import org.eclipse.pde.internal.core.bundle.BundleFragmentModel; import org.eclipse.pde.internal.core.bundle.BundlePluginModel; import org.eclipse.pde.internal.core.feature.FeatureChild; import org.eclipse.pde.internal.core.feature.FeaturePlugin; import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase; import org.eclipse.pde.internal.core.ifeature.IFeature; import org.eclipse.pde.internal.core.ifeature.IFeatureChild; import org.eclipse.pde.internal.core.ifeature.IFeatureInfo; import org.eclipse.pde.internal.core.ifeature.IFeatureModel; import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin; @SuppressWarnings("restriction") public class SourceFeatureCreator implements IPDEConstants, IBuildPropertiesConstants { private static final String FEATURE_SUFFIX = ".feature"; //$NON-NLS-1$ private static final String SOURCE_SUFFIX = ".source"; //$NON-NLS-1$ private static final String DESCRIPTION_FORMAT = "Generated source feature. The original description is:\n\n{0}"; //$NON-NLS-1$ private static final String LABEL_FORMAT = "Source for {0}"; //$NON-NLS-1$ public static String createSourceFeatureId(String originalId) { StringBuilder sourceIdBld = new StringBuilder(); if (originalId.endsWith(FEATURE_SUFFIX)) { sourceIdBld.append(originalId, 0, originalId.length() - FEATURE_SUFFIX.length()); sourceIdBld.append(SOURCE_SUFFIX); sourceIdBld.append(FEATURE_SUFFIX); } else { sourceIdBld.append(originalId); sourceIdBld.append(SOURCE_SUFFIX); } return sourceIdBld.toString(); } private final File inputFile; private final File outputFile; private final List<File> translationFiles; private final List<File> featuresAndBundles; private Map<String, String> translations; private Map<String, String> usedTranslations; public SourceFeatureCreator(File inputFile, List<File> translationFiles, File outputFile, List<File> featuresAndBundles) { this.inputFile = inputFile; this.outputFile = outputFile; this.featuresAndBundles = featuresAndBundles; this.translationFiles = translationFiles; } public void run() throws CoreException, FileNotFoundException { IFeature originalFeature = FeatureModelReader.readEditableFeatureModel(inputFile).getFeature(); EditableFeatureModel featureModel = new EditableFeatureModel(outputFile); featureModel.setDirty(true); IFeature sourceFeature = featureModel.getFeature(); IActionContext ctx = AbstractActor.getActiveContext(); String originalId = originalFeature.getId(); String sourceId = createSourceFeatureId(originalId); sourceFeature.setId(sourceId); sourceFeature.setVersion(originalFeature.getVersion()); String label = originalFeature.getLabel(); Map<String, ? extends Object> props = ctx.getProperties(); String format = (String) props.get(IPDEConstants.PROP_PDE_SOURCE_FEATURE_LABEL_FORMAT); if (format == null) format = LABEL_FORMAT; if (!addLocalizedProperty(ctx, label, format)) label = MessageFormat.format(format, label); sourceFeature.setLabel(label); sourceFeature.setOS(originalFeature.getOS()); sourceFeature.setArch(originalFeature.getArch()); sourceFeature.setWS(originalFeature.getWS()); sourceFeature.setNL(originalFeature.getNL()); String providerName = originalFeature.getProviderName(); addLocalizedProperty(ctx, providerName, null); sourceFeature.setProviderName(providerName); sourceFeature.setURL(originalFeature.getURL()); IFeatureInfo copyright = originalFeature.getFeatureInfo(IFeature.INFO_COPYRIGHT); if (copyright != null) cloneInfo(ctx, sourceFeature, copyright, null); IFeatureInfo license = originalFeature.getFeatureInfo(IFeature.INFO_LICENSE); if (license != null) cloneInfo(ctx, sourceFeature, license, null); IFeatureInfo description = originalFeature.getFeatureInfo(IFeature.INFO_DESCRIPTION); if (description != null) { format = (String) props.get(IPDEConstants.PROP_PDE_SOURCE_FEATURE_DESCRIPTION_FORMAT); if (format == null) format = DESCRIPTION_FORMAT; cloneInfo(ctx, sourceFeature, description, format); } for (File featureOrBundle : featuresAndBundles) { InputStream input = null; try { input = GroupConsolidator.getInput(featureOrBundle, FEATURE_MANIFEST); IFeatureModel model = FeatureModelReader.readFeatureModel(input); IFeature feature = model.getFeature(); FeatureChild fc = new FeatureChild(); fc.setModel(featureModel); fc.loadFrom(model.getFeature()); fc.setArch(feature.getArch()); fc.setOS(feature.getOS()); fc.setWS(feature.getWS()); fc.setNL(feature.getNL()); fc.setLabel(feature.getLabel()); fc.setVersion(feature.getVersion()); sourceFeature.addIncludedFeatures(new IFeatureChild[] { fc }); continue; } catch (FileNotFoundException e) { } finally { IOUtils.close(input); input = null; } try { input = GroupConsolidator.getInput(featureOrBundle, BUNDLE_FILE); ExternalBundleModel model = new ExternalBundleModel(featureOrBundle); model.load(input, true); IBundlePluginModelBase bmodel = model.isFragmentModel() ? new BundleFragmentModel() : new BundlePluginModel(); bmodel.setEnabled(true); bmodel.setBundleModel(model); IPluginBase plugin = bmodel.getPluginBase(); if (plugin.getId() == null) throw BuckminsterException.fromMessage("Unable to extract feature.xml or a valid OSGi bundle manifest from %s", //$NON-NLS-1$ featureOrBundle.getAbsolutePath()); FeaturePlugin fp = new FeaturePlugin(); fp.loadFrom(plugin); fp.setModel(featureModel); fp.setUnpack(false); fp.setVersion(plugin.getVersion()); // Load arch etc. from corresponding original plug-in (if we // find it) // String ver = plugin.getVersion(); String id = plugin.getId(); if (id.endsWith(SOURCE_SUFFIX)) { String origId = id.substring(0, id.length() - SOURCE_SUFFIX.length()); for (IFeaturePlugin originalPlugin : originalFeature.getPlugins()) { if (originalPlugin.getId().equals(origId) && originalPlugin.getVersion().equals(ver)) { fp.setArch(originalPlugin.getArch()); fp.setOS(originalPlugin.getOS()); fp.setWS(originalPlugin.getWS()); fp.setNL(originalPlugin.getNL()); break; } } } sourceFeature.addPlugins(new IFeaturePlugin[] { fp }); continue; } catch (FileNotFoundException e) { } finally { IOUtils.close(input); } } featureModel.save(); saveTranslations(); } private boolean addLocalizedProperty(IActionContext ctx, String key, String format) throws CoreException { if (key == null || !key.startsWith("%")) //$NON-NLS-1$ return false; if (translations == null) { // We need to get hold of the feature.properties from the // original source for (File translationFile : translationFiles) { if ("feature.properties".equals(translationFile.getName())) { //$NON-NLS-1$ InputStream inStream = null; try { inStream = new BufferedInputStream(new FileInputStream(translationFile)); translations = new BMProperties(inStream); } catch (IOException e) { } finally { IOUtils.close(inStream); } break; } } if (translations == null) translations = Collections.emptyMap(); } String mapKey = key.substring(1); String translated = translations.get(mapKey); if (translated == null) { PDEPlugin.getLogger().warning("Unable to find translation for property %s", key); //$NON-NLS-1$ return false; } if (format != null) translated = MessageFormat.format(format, translated); if (usedTranslations == null) usedTranslations = new HashMap<String, String>(); usedTranslations.put(mapKey, translated); return true; } private void cloneInfo(IActionContext ctx, IFeature newOwner, IFeatureInfo original, String descriptionFormat) throws CoreException { IFeatureInfo copy = newOwner.getModel().getFactory().createInfo(original.getIndex()); String description = original.getDescription(); if (!addLocalizedProperty(ctx, description, descriptionFormat) && descriptionFormat != null) description = MessageFormat.format(descriptionFormat, description); copy.setDescription(description); addLocalizedProperty(ctx, original.getLabel(), null); copy.setLabel(original.getLabel()); addLocalizedProperty(ctx, original.getURL(), null); copy.setURL(original.getURL()); newOwner.setFeatureInfo(copy, original.getIndex()); } private void saveTranslations() throws CoreException { if (usedTranslations != null) { OutputStream out = null; try { out = new BufferedOutputStream(new FileOutputStream(new File(outputFile.getParentFile(), "feature.properties"))); //$NON-NLS-1$ BMProperties.store(usedTranslations, out, null); } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { IOUtils.close(out); } } } }