/******************************************************************************* * 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.internal.actor; import java.io.BufferedInputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Map; import java.util.Properties; import java.util.regex.Pattern; import org.eclipse.buckminster.core.KeyConstants; import org.eclipse.buckminster.core.actor.AbstractActor; import org.eclipse.buckminster.core.actor.IActionContext; import org.eclipse.buckminster.core.actor.MissingPrerequisiteException; import org.eclipse.buckminster.core.cspec.PathGroup; import org.eclipse.buckminster.core.cspec.model.Action; import org.eclipse.buckminster.core.cspec.model.Attribute; import org.eclipse.buckminster.core.cspec.model.CSpec; import org.eclipse.buckminster.core.cspec.model.Prerequisite; import org.eclipse.buckminster.pde.IPDEConstants; import org.eclipse.buckminster.pde.Messages; import org.eclipse.buckminster.pde.internal.model.EditableFeatureModel; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.buckminster.runtime.Trivial; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.internal.p2.core.helpers.FileUtils; import org.eclipse.osgi.util.NLS; import org.eclipse.pde.core.build.IBuildEntry; import org.eclipse.pde.internal.core.ifeature.IFeature; import org.eclipse.pde.internal.core.ifeature.IFeatureInfo; @SuppressWarnings("restriction") public class MergeLicenseFeature extends AbstractActor { public static final String ID = "mergeLicenseFeature"; //$NON-NLS-1$ private static final Pattern propertyFilePattern = Pattern.compile("feature.*\\.properties"); //$NON-NLS-1$ private static FileFilter featurePropertyFiles = new FileFilter() { @Override public boolean accept(File file) { return propertyFilePattern.matcher(file.getName()).matches(); } }; private static void copyBinIncludes(Attribute binIncludes, File attributeBase, IPath outputPath, IActionContext ctx, IProgressMonitor monitor) throws CoreException { // Copy everything specified in binIncludes except the feature.xml // and feature*.properties since they have been dealt with already PathGroup base = new PathGroup(Path.fromOSString(attributeBase.getAbsolutePath()), new IPath[0]); for (PathGroup pg : binIncludes.getPathGroups(ctx, null)) { for (IPath path : pg.getPaths()) { if (path.segmentCount() == 1) { String name = path.segment(0); if (IPDEConstants.FEATURE_MANIFEST.equals(name) || propertyFilePattern.matcher(name).matches()) continue; } base.copyPathTo(path, outputPath, monitor); } } } private static Properties loadFile(File file) throws IOException { InputStream fis = new BufferedInputStream(new FileInputStream(file)); try { Properties props = new Properties(); props.load(fis); return props; } finally { IOUtils.close(fis); } } @Override protected IStatus internalPerform(IActionContext ctx, IProgressMonitor monitor) throws CoreException { Action action = ctx.getAction(); IPath outputPath = AbstractActor.getProductPath(ctx, IPDEConstants.ALIAS_OUTPUT, true); IPath manifestPath = AbstractActor.getProductPath(ctx, IPDEConstants.ALIAS_MANIFEST, true); IPath licenseFeaturePath = null; IPath versionedManifestPath = null; IPath licenseManifestPath = null; CSpec cspec = action.getCSpec(); Attribute binIncludes = null; Attribute licenseFeatureContents = null; for (Prerequisite preq : action.getPrerequisites()) { if (IPDEConstants.ALIAS_LICENSE_FEATURE.equals(preq.getAlias())) { // This prerequisite should appoint the site as a folder // Attribute rt = preq.getReferencedAttribute(cspec, ctx); if (rt == null) continue; licenseFeaturePath = AbstractActor.getSingleAttributePath(ctx, rt, true); continue; } if (IPDEConstants.ALIAS_MANIFEST.equals(preq.getAlias())) { Attribute rt = preq.getReferencedAttribute(cspec, ctx); if (rt == null) continue; versionedManifestPath = AbstractActor.getSingleAttributePath(ctx, rt, true); continue; } if (IPDEConstants.ALIAS_LICENSE_MANIFEST.equals(preq.getAlias())) { Attribute rt = preq.getReferencedAttribute(cspec, ctx); if (rt == null) continue; licenseManifestPath = AbstractActor.getSingleAttributePath(ctx, rt, true); continue; } if (IBuildEntry.BIN_INCLUDES.equals(preq.getAlias())) { binIncludes = preq.getReferencedAttribute(cspec, ctx); continue; } if (IPDEConstants.ALIAS_LICENSE_FEATURE_CONTENTS.equals(preq.getAlias())) { licenseFeatureContents = preq.getReferencedAttribute(cspec, ctx); continue; } } if (licenseFeaturePath == null) throw new MissingPrerequisiteException(action, IPDEConstants.ALIAS_LICENSE_FEATURE); if (versionedManifestPath == null) throw new MissingPrerequisiteException(action, IPDEConstants.ALIAS_MANIFEST); if (!outputPath.hasTrailingSeparator()) throw new IllegalArgumentException(NLS.bind(org.eclipse.buckminster.core.Messages.output_of_action_0_must_be_folder, action.getQualifiedName())); try { File outputDir = outputPath.toFile().getAbsoluteFile(); outputDir.mkdirs(); Map<String, ? extends Object> props = ctx.getProperties(); File licenseFeatureDir; File licenseFeatureFile = licenseFeaturePath.toFile().getAbsoluteFile(); if (licenseFeatureFile.isDirectory()) { licenseFeatureDir = licenseFeatureFile; } else { licenseFeatureDir = new File(new File(props.get(KeyConstants.ACTION_TEMP).toString()), "licenseFeature"); //$NON-NLS-1$ licenseFeatureDir.mkdirs(); FileUtils.unzipFile(licenseFeatureFile, licenseFeatureDir); } File payloadFeatureDir; File payloadFeatureFile = ctx.getComponentLocation().toFile(); if (payloadFeatureFile.isDirectory()) { payloadFeatureDir = payloadFeatureFile; } else { payloadFeatureDir = new File(new File(props.get(KeyConstants.ACTION_TEMP).toString()), "payloadFeature"); //$NON-NLS-1$ payloadFeatureDir.mkdirs(); FileUtils.unzipFile(payloadFeatureFile, payloadFeatureDir); } if (binIncludes != null) copyBinIncludes(binIncludes, payloadFeatureDir, outputPath, ctx, monitor); if (licenseFeatureContents != null) copyBinIncludes(licenseFeatureContents, licenseFeatureDir, outputPath, ctx, monitor); mergeProperties(licenseFeatureDir, payloadFeatureDir, outputDir); EditableFeatureModel payloadFeatureModel = new EditableFeatureModel(versionedManifestPath.append(IPDEConstants.FEATURE_MANIFEST).toFile()); payloadFeatureModel.load(); EditableFeatureModel licenseFeatureModel = new EditableFeatureModel(licenseManifestPath.append(IPDEConstants.FEATURE_MANIFEST).toFile()); licenseFeatureModel.load(); IFeature payloadFeature = payloadFeatureModel.getFeature(); IFeature licenseFeature = licenseFeatureModel.getFeature(); IFeatureInfo info = licenseFeature.getFeatureInfo(IFeature.INFO_LICENSE); if (info != null) payloadFeature.setFeatureInfo(info, IFeature.INFO_LICENSE); info = licenseFeature.getFeatureInfo(IFeature.INFO_COPYRIGHT); if (info != null) payloadFeature.setFeatureInfo(info, IFeature.INFO_COPYRIGHT); if (Trivial.trim(payloadFeature.getProviderName()) == null) payloadFeature.setProviderName(licenseFeature.getProviderName()); payloadFeature.setLicenseFeatureID(null); payloadFeature.setLicenseFeatureVersion(null); File manifestDir = manifestPath.toFile(); manifestDir.mkdirs(); payloadFeatureModel.save(new File(manifestDir, IPDEConstants.FEATURE_MANIFEST)); } catch (IOException e) { throw BuckminsterException.wrap(e); } return Status.OK_STATUS; } private void mergeProperties(File licenseFeatureDir, File payloadFeatureDir, File targetDir) throws CoreException, IOException { // Copy all properties from payload to target File[] payloadPropertyFiles = payloadFeatureDir.listFiles(featurePropertyFiles); for (File payloadPropertyFile : payloadPropertyFiles) org.eclipse.buckminster.core.helpers.FileUtils.copyFile(payloadPropertyFile, targetDir, payloadPropertyFile.getName(), null); // Merge license properties File[] licensePropertyFiles = licenseFeatureDir.listFiles(featurePropertyFiles); for (File licensePropertyFile : licensePropertyFiles) { File featurePropertyFile = new File(targetDir, licensePropertyFile.getName()); if (featurePropertyFile.exists()) { Properties licenseProperties = loadFile(licensePropertyFile); Properties featureProperties = loadFile(featurePropertyFile); Enumeration<?> licenseKeys = licenseProperties.keys(); // Check for conflicting properties while (licenseKeys.hasMoreElements()) { String licenseKey = (String) licenseKeys.nextElement(); if (featureProperties.containsKey(licenseKey)) { throw BuckminsterException.fromMessage(NLS.bind(Messages.error_conflictingProperties, new String[] { licenseKey, licensePropertyFile.getAbsolutePath(), featurePropertyFile.getAbsolutePath() })); } } } // Now append (or create) necessary feature_*.properties files FileWriter featurePropertyWriter = null; FileReader licensePropertyReader = new FileReader(licensePropertyFile); try { featurePropertyWriter = new FileWriter(featurePropertyFile, true); char[] buffer = new char[0x800]; int bytesRead = licensePropertyReader.read(buffer); while (bytesRead > -1) { featurePropertyWriter.write(buffer, 0, bytesRead); bytesRead = licensePropertyReader.read(buffer); } } finally { IOUtils.close(featurePropertyWriter); IOUtils.close(licensePropertyReader); } } } }