| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| * Marc-Andre Laperle (Ericsson) - Fix NPE (Bug 419759) |
| *******************************************************************************/ |
| package org.eclipse.pde.internal.build.tasks; |
| |
| import java.io.*; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Enumeration; |
| import java.util.Properties; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.types.FileSet; |
| import org.apache.tools.ant.types.PatternSet.NameEntry; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.pde.build.Constants; |
| |
| /** |
| * Internal task. |
| * This task aims at replacing the generic ids used into a feature.xml by another value, and also replace the feature version number if necessary. |
| * @since 3.0 |
| */ |
| public class LicenseReplaceTask extends Task { |
| // Path of the file where we are replacing the values |
| private String filePath; |
| |
| // Path to license text |
| private String licensePath; |
| |
| private class Feature { |
| private static final String FEATURE_START_TAG = "<feature";//$NON-NLS-1$ |
| private static final String LICENSE_START_TAG = "<license"; //$NON-NLS-1$; |
| private static final String LICENSE_END_TAG = "</license>"; //$NON-NLS-1$; |
| private static final String URL_ATTR = "url";//$NON-NLS-1$ |
| private static final String DOUBLE_QUOTE = "\""; //$NON-NLS-1$ |
| private static final String COMMENT_START_TAG = "<!--"; //$NON-NLS-1$ |
| private static final String COMMENT_END_TAG = "-->"; //$NON-NLS-1$ |
| |
| private final String featureFilePath; |
| private String urlText; |
| private String license; |
| private StringBuffer buffer; |
| private int startLicenseText = -1; |
| private int endLicenseText = -1; |
| private int startURLText = -1; |
| private int endURLText = -1; |
| private int startURLWord = -1; |
| private int endURLWord = -1; |
| private int insertionPoint = -1; |
| private boolean contentChanged; |
| |
| public String getUrl() { |
| if (contentChanged) { |
| throw new IllegalStateException(TaskMessages.error_noCallAfterReplace); |
| } |
| return urlText; |
| } |
| |
| public String getLicenseText() { |
| if (contentChanged) { |
| throw new IllegalStateException(TaskMessages.error_noCallAfterReplace); |
| } |
| return license; |
| } |
| |
| public void replace(String licenseURL, String licenseText) { |
| if (contentChanged) { |
| throw new IllegalStateException(TaskMessages.error_noCallAfterReplace); |
| } |
| |
| if (startLicenseText > 0 && endLicenseText > startLicenseText) { |
| // Replace license text |
| if (licenseText != null) { |
| buffer.replace(startLicenseText, endLicenseText, licenseText); |
| contentChanged = true; |
| } |
| } else if (insertionPoint > -1) { |
| //insert new license after <feature> |
| StringBuffer newLicense = new StringBuffer(); |
| newLicense.append('\n'); |
| newLicense.append(LICENSE_START_TAG + " " + URL_ATTR + "="); //$NON-NLS-1$//$NON-NLS-2$ |
| if (licenseURL != null) |
| newLicense.append(licenseURL); |
| newLicense.append(" >"); //$NON-NLS-1$ |
| if (licenseText != null) |
| newLicense.append(licenseText); |
| newLicense.append(LICENSE_END_TAG); |
| |
| buffer.insert(insertionPoint, newLicense.toString()); |
| contentChanged = true; |
| return; |
| } else { |
| return; |
| } |
| |
| if (startURLText == endURLText) { |
| // Replace empty payload URL |
| if (licenseURL == null) { |
| // with empty license URL |
| // No-op |
| } else { |
| // with non-empty license URL |
| buffer.replace(startURLText, endURLText + 1, " url=" + licenseURL + ">"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } else { |
| // Replace non-empty payload URL |
| if (licenseURL == null) { |
| // with empty license URL |
| // No-op |
| } else { |
| // with non-empty license URL |
| buffer.replace(startURLText, endURLText + 1, licenseURL); |
| } |
| } |
| |
| int start = buffer.indexOf("license-feature="); //$NON-NLS-1$ |
| if (start != -1) { |
| int end = buffer.indexOf("\"", start); //$NON-NLS-1$ |
| if (end < buffer.length()) { |
| end = buffer.indexOf("\"", end + 1); //$NON-NLS-1$ |
| if (end != -1) { |
| buffer.replace(start, end + 1, ""); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| start = buffer.indexOf("license-feature-version="); //$NON-NLS-1$ |
| if (start != -1) { |
| int end = buffer.indexOf("\"", start); //$NON-NLS-1$ |
| if (end < buffer.length()) { |
| end = buffer.indexOf("\"", end + 1); //$NON-NLS-1$ |
| if (end != -1) { |
| buffer.replace(start, end + 1, ""); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| |
| public void write() { |
| if (!contentChanged) |
| return; |
| |
| try (OutputStreamWriter w = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(featureFilePath)), StandardCharsets.UTF_8)) { |
| w.write(buffer.toString()); |
| } catch (FileNotFoundException e) { |
| // ignore |
| } catch (IOException e) { |
| throw new BuildException(e); |
| } |
| } |
| |
| Feature(String featureFilePath) { |
| super(); |
| this.featureFilePath = featureFilePath + '/' + Constants.FEATURE_FILENAME_DESCRIPTOR; |
| parse(); |
| } |
| |
| private void parse() { |
| buffer = null; |
| try { |
| buffer = readFile(new File(featureFilePath)); |
| } catch (IOException e) { |
| throw new BuildException(e); |
| } |
| |
| int startFeature = scanNoComment(buffer, 0, FEATURE_START_TAG, true); |
| if (startFeature == -1) |
| return; |
| |
| int endFeature = scan(buffer, startFeature, ">"); //$NON-NLS-1$ |
| insertionPoint = endFeature + 1; |
| |
| int startLicense = scanNoComment(buffer, 0, LICENSE_START_TAG, false); |
| if (startLicense == -1) |
| return; |
| |
| int endLicense = scan(buffer, startLicense, ">"); //$NON-NLS-1$ |
| |
| boolean urlFound = false; |
| while (!urlFound) { |
| startURLWord = scan(buffer, startLicense, URL_ATTR); |
| if (startURLWord == -1 || startURLWord > endLicense) { |
| startURLText = startLicense + LICENSE_START_TAG.length(); |
| endURLText = startURLText; |
| } else { |
| |
| if (!Character.isWhitespace(buffer.charAt(startURLWord - 1))) { |
| startLicense = startURLWord + URL_ATTR.length(); |
| continue; |
| } |
| |
| //Verify that the word url found is the actual attribute |
| endURLWord = startURLWord + URL_ATTR.length(); |
| while (Character.isWhitespace(buffer.charAt(endURLWord)) && endURLWord < endLicense) { |
| endURLWord++; |
| } |
| if (endURLWord > endLicense) { //id has not been found |
| System.err.println("Could not find the tag 'id' in the license header, file: " + featureFilePath); //$NON-NLS-1$ |
| return; |
| } |
| |
| if (buffer.charAt(endURLWord) != '=') { |
| startLicense = endURLWord; |
| continue; |
| } |
| startURLText = scan(buffer, startURLWord + 1, DOUBLE_QUOTE); |
| endURLText = scan(buffer, startURLText + 1, DOUBLE_QUOTE); |
| urlText = (buffer.substring(startURLText, endURLText + 1)); |
| } |
| urlFound = true; |
| } |
| startLicenseText = scan(buffer, endURLText, ">") + 1; //$NON-NLS-1$ |
| endLicenseText = scan(buffer, startLicenseText, LICENSE_END_TAG, true) - 1; |
| license = buffer.substring(startLicenseText, endLicenseText); |
| } |
| |
| private StringBuffer readFile(File targetName) throws IOException { |
| InputStreamReader reader = new InputStreamReader(new BufferedInputStream(new FileInputStream(targetName)), StandardCharsets.UTF_8); |
| StringBuffer result = new StringBuffer(); |
| char[] buf = new char[4096]; |
| int count; |
| try { |
| count = reader.read(buf, 0, buf.length); |
| while (count != -1) { |
| result.append(buf, 0, count); |
| count = reader.read(buf, 0, buf.length); |
| } |
| } finally { |
| try { |
| reader.close(); |
| } catch (IOException e) { |
| // ignore exceptions here |
| } |
| } |
| return result; |
| } |
| |
| private int scan(StringBuffer buf, int start, String targetName) { |
| return scan(buf, start, new String[] {targetName}, false); |
| } |
| |
| private int scan(StringBuffer buf, int start, String targetName, boolean wholeWord) { |
| return scan(buf, start, new String[] {targetName}, wholeWord); |
| } |
| |
| private int scan(StringBuffer buf, int start, String[] targets, boolean wholeWord) { |
| for (int i = start; i < buf.length(); i++) { |
| for (int j = 0; j < targets.length; j++) { |
| if (i < buf.length() - targets[j].length()) { |
| String candidate = targets[j]; |
| String match = buf.substring(i, i + candidate.length()); |
| if (candidate.equalsIgnoreCase(match)) { |
| if (!wholeWord || Character.isWhitespace(buf.charAt(i + candidate.length()))) |
| return i; |
| } |
| } |
| } |
| } |
| return -1; |
| } |
| |
| private int scanNoComment(StringBuffer bug, int start, String thisTarget, boolean wholeWord) { |
| int startComment = scan(buffer, start, COMMENT_START_TAG); |
| int endComment = startComment > -1 ? scan(buffer, startComment, COMMENT_END_TAG) : -1; |
| int startTarget = scan(buffer, start, thisTarget, wholeWord); |
| |
| while (startComment != -1 && startTarget > startComment && startTarget < endComment) { |
| startTarget = scan(buffer, endComment, thisTarget, wholeWord); |
| startComment = scan(buffer, endComment, COMMENT_START_TAG); |
| endComment = startComment > -1 ? scan(buffer, startComment, COMMENT_END_TAG) : -1; |
| } |
| return startTarget; |
| } |
| } |
| |
| /** |
| * The directory containing the feature |
| * @param path |
| */ |
| public void setFeatureFilePath(String path) { |
| filePath = path; |
| } |
| |
| /** |
| * The directory containing the license feature |
| * @param path |
| */ |
| public void setLicenseFilePath(String path) { |
| licensePath = path; |
| } |
| |
| @Override |
| public void execute() { |
| Feature payloadFeature = new Feature(filePath); |
| Feature licenseFeature = new Feature(licensePath); |
| |
| // Replace license information in target feature.xml |
| payloadFeature.replace(licenseFeature.getUrl(), licenseFeature.getLicenseText()); |
| payloadFeature.write(); |
| |
| // Append license feature_*.properties files to target feature_*.properties files |
| FileSet fileSet = new FileSet(); |
| fileSet.setProject(getProject()); |
| fileSet.setDir(new File(licensePath)); |
| NameEntry fileInclude = fileSet.createInclude(); |
| fileInclude.setName("feature*.properties"); //$NON-NLS-1$ |
| |
| String[] propertyFiles = fileSet.getDirectoryScanner().getIncludedFiles(); |
| |
| for (int i = 0; i < propertyFiles.length; i++) { |
| String propertyFile = propertyFiles[i]; |
| |
| File featurePropertyFile = new File(filePath, propertyFile); |
| File licensePropertyFile = new File(licensePath, propertyFile); |
| FileInputStream fis = null; |
| if (featurePropertyFile.exists()) { |
| try { |
| fis = new FileInputStream(licensePropertyFile); |
| Properties licenseProperties = new Properties(); |
| licenseProperties.load(fis); |
| fis.close(); |
| |
| fis = new FileInputStream(featurePropertyFile); |
| Properties featureProperties = new Properties(); |
| featureProperties.load(fis); |
| fis.close(); |
| |
| Enumeration<Object> licenseKeys = licenseProperties.keys(); |
| while (licenseKeys.hasMoreElements()) { |
| String licenseKey = (String) licenseKeys.nextElement(); |
| if (featureProperties.containsKey(licenseKey)) { |
| throw new BuildException(NLS.bind(TaskMessages.error_conflictingProperties, new String[] {licenseKey, licensePropertyFile.getAbsolutePath(), featurePropertyFile.getAbsolutePath()})); |
| } |
| } |
| } catch (FileNotFoundException e) { |
| // DO Nothing |
| } catch (IOException e) { |
| throw new BuildException(e); |
| } |
| } |
| |
| // Now append (or create) necessary feature_*.properties files |
| |
| try (FileWriter featurePropertyWriter = new FileWriter(featurePropertyFile, true); FileReader licensePropertyReader = new FileReader(licensePropertyFile)) { |
| char[] buffer = new char[1024]; |
| int bytesRead = licensePropertyReader.read(buffer); |
| while (bytesRead > -1) { |
| featurePropertyWriter.write(buffer, 0, bytesRead); |
| bytesRead = licensePropertyReader.read(buffer); |
| } |
| } catch (IOException e) { |
| throw new BuildException(e); |
| } |
| } |
| } |
| } |