001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM - Initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.build.tasks;
011:
012: import java.io.*;
013: import java.util.*;
014: import org.apache.tools.ant.BuildException;
015: import org.apache.tools.ant.Task;
016:
017: /**
018: * Internal task.
019: * 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.
020: * @since 3.0
021: */
022: public class IdReplaceTask extends Task {
023: private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
024: private static final String FEATURE_START_TAG = "<feature";//$NON-NLS-1$
025: private static final String ID = "id";//$NON-NLS-1$
026: private static final String VERSION = "version";//$NON-NLS-1$
027: private static final String COMMA = ","; //$NON-NLS-1$
028: private static final String BACKSLASH = "\""; //$NON-NLS-1$
029: private static final String EMPTY = ""; //$NON-NLS-1$
030: private static final String PLUGIN_START_TAG = "<plugin"; //$NON-NLS-1$
031: private static final String INCLUDES_START_TAG = "<includes"; //$NON-NLS-1$
032: private static final String COMMENT_START_TAG = "<!--"; //$NON-NLS-1$
033: private static final String COMMENT_END_TAG = "-->"; //$NON-NLS-1$
034:
035: //Path of the file where we are replacing the values
036: private String featureFilePath;
037: //Map of the plugin ids and version (key) and their version number (value)
038: // the key is id:version and the value is the actual version of the element
039: // the keys are such that a regular lookup will always return the appropriate value if available
040: private Map pluginIds = new HashMap(10);
041: //Map of the feature ids and version (key) and their version number (value)
042: // the key is id:version and the value is the actual version of the element
043: // the keys are such that a regular lookup will always return the appropriate value if available
044: private Map featureIds = new HashMap(4);
045: //The new version number for this feature
046: private String selfVersion;
047:
048: private boolean contentChanged = false;
049:
050: private final static String GENERIC_VERSION_NUMBER = "0.0.0"; //$NON-NLS-1$
051: private final static String QUALIFIER = "qualifier"; //$NON-NLS-1$
052:
053: /**
054: * The location of a feature.xml file
055: * @param path
056: */
057: public void setFeatureFilePath(String path) {
058: featureFilePath = path;
059: }
060:
061: /**
062: * The value with which the current version of the feature will be replaced.
063: * @param version
064: */
065: public void setSelfVersion(String version) {
066: selfVersion = version;
067: }
068:
069: /**
070: * Set the values to use when replacing a generic value used in a plugin reference.
071: * Note all the pluginIds that have a generic number into the feature.xml must be
072: * listed in <param>values</param>.
073: * @param values a comma separated list alternating pluginId and versionNumber.
074: * For example: org.eclipse.pde.build,2.1.0,org.eclipse.core.resources,1.2.0
075: */
076: public void setPluginIds(String values) {
077: pluginIds = new HashMap(10);
078: for (StringTokenizer tokens = new StringTokenizer(values, COMMA); tokens
079: .hasMoreTokens();) {
080: String token = tokens.nextToken().trim();
081: String id = EMPTY;
082: if (!token.equals(EMPTY))
083: id = token;
084:
085: String version = EMPTY;
086: token = tokens.nextToken().trim();
087: if (!token.equals(EMPTY))
088: version = token;
089: pluginIds.put(id, version);
090: }
091: }
092:
093: /**
094: * Set the values to use when replacing a generic value used in a feature reference
095: * Note that all the featureIds that have a generic number into the feature.xml must
096: * be liste in <param>values</param>.
097: * @param values
098: */
099: public void setFeatureIds(String values) {
100: featureIds = new HashMap(10);
101: for (StringTokenizer tokens = new StringTokenizer(values, COMMA); tokens
102: .hasMoreTokens();) {
103: String token = tokens.nextToken().trim();
104: String id = EMPTY;
105: if (!token.equals(EMPTY))
106: id = token;
107:
108: String version = EMPTY;
109: token = tokens.nextToken().trim();
110: if (!token.equals(EMPTY))
111: version = token;
112: featureIds.put(id, version);
113: }
114: }
115:
116: public void execute() {
117: StringBuffer buffer = null;
118: try {
119: buffer = readFile(new File(featureFilePath));
120: } catch (IOException e) {
121: throw new BuildException(e);
122: }
123:
124: //Skip feature declaration because it contains the word "plugin"
125: int startComment = scan(buffer, 0, COMMENT_START_TAG);
126: int endComment = startComment > -1 ? scan(buffer, startComment,
127: COMMENT_END_TAG) : -1;
128: int startFeature = scan(buffer, 0, FEATURE_START_TAG);
129:
130: while (startComment != -1 && startFeature > startComment
131: && startFeature < endComment) {
132: startFeature = scan(buffer, endComment, FEATURE_START_TAG);
133: startComment = scan(buffer, endComment, COMMENT_START_TAG);
134: endComment = startComment > -1 ? scan(buffer, startComment,
135: COMMENT_END_TAG) : -1;
136: }
137:
138: if (startFeature == -1)
139: return;
140:
141: int endFeature = scan(buffer, startFeature, ">"); //$NON-NLS-1$
142:
143: if (selfVersion != null) {
144: boolean versionFound = false;
145: while (!versionFound) {
146: int startVersionWord = scan(buffer, startFeature,
147: VERSION);
148: if (startVersionWord == -1
149: || startVersionWord > endFeature)
150: return;
151: if (!Character.isWhitespace(buffer
152: .charAt(startVersionWord - 1))) {
153: startFeature = startVersionWord + VERSION.length();
154: continue;
155: }
156: //Verify that the word version found is the actual attribute
157: int endVersionWord = startVersionWord
158: + VERSION.length();
159: while (Character.isWhitespace(buffer
160: .charAt(endVersionWord))
161: && endVersionWord < endFeature) {
162: endVersionWord++;
163: }
164: if (endVersionWord > endFeature) { //version has not been found
165: System.err
166: .println("Could not find the tag 'version' in the feature header, file: " + featureFilePath); //$NON-NLS-1$
167: return;
168: }
169:
170: if (buffer.charAt(endVersionWord) != '=') {
171: startFeature = endVersionWord;
172: continue;
173: }
174:
175: int startVersionId = scan(buffer, startVersionWord + 1,
176: BACKSLASH);
177: int endVersionId = scan(buffer, startVersionId + 1,
178: BACKSLASH);
179: buffer.replace(startVersionId + 1, endVersionId,
180: selfVersion);
181: endFeature = endFeature
182: + (selfVersion.length() - (endVersionId - startVersionId));
183: contentChanged = true;
184: versionFound = true;
185: }
186: }
187:
188: int startElement = endFeature;
189: int startId = 0;
190: while (true) {
191: int startPlugin = scan(buffer, startElement + 1,
192: PLUGIN_START_TAG);
193: int startInclude = scan(buffer, startElement + 1,
194: INCLUDES_START_TAG);
195:
196: if (startPlugin == -1 && startInclude == -1)
197: break;
198:
199: startComment = scan(buffer, startElement + 1,
200: COMMENT_START_TAG);
201: endComment = startComment > -1 ? scan(buffer, startComment,
202: COMMENT_END_TAG) : -1;
203:
204: int foundElement = -1;
205: boolean isPlugin = false;
206:
207: //Find which of a plugin or a feature is referenced first
208: if (startPlugin == -1 || startInclude == -1) {
209: foundElement = startPlugin != -1 ? startPlugin
210: : startInclude;
211: isPlugin = (startPlugin != -1 ? true : false);
212: } else {
213: if (startPlugin < startInclude) {
214: foundElement = startPlugin;
215: isPlugin = true;
216: } else {
217: foundElement = startInclude;
218: isPlugin = false;
219: }
220: }
221:
222: if (startComment != -1 && foundElement > startComment
223: && foundElement < endComment) {
224: startElement = endComment;
225: continue;
226: }
227:
228: int startElementId, endElementId;
229: int startVersionWord, startVersionId, endVersionId;
230:
231: startId = scan(buffer, foundElement, ID);
232: startVersionWord = scan(buffer, foundElement, VERSION);
233: // Which comes first, version or id.
234: if (startId < startVersionWord) {
235: startElementId = scan(buffer, startId + 1, BACKSLASH);
236: endElementId = scan(buffer, startElementId + 1,
237: BACKSLASH);
238:
239: // search for version again since the id could have "version" in it.
240: startVersionWord = scan(buffer, endElementId + 1,
241: VERSION);
242: startVersionId = scan(buffer, startVersionWord + 1,
243: BACKSLASH);
244: endVersionId = scan(buffer, startVersionId + 1,
245: BACKSLASH);
246: } else {
247: startVersionId = scan(buffer, startVersionWord + 1,
248: BACKSLASH);
249: endVersionId = scan(buffer, startVersionId + 1,
250: BACKSLASH);
251:
252: // search for id again since the version qualifier could contain "id"
253: startId = scan(buffer, endVersionId + 1, ID);
254: startElementId = scan(buffer, startId + 1, BACKSLASH);
255: endElementId = scan(buffer, startElementId + 1,
256: BACKSLASH);
257: }
258:
259: if (startId == -1 || startVersionWord == -1)
260: break;
261:
262: char[] elementId = new char[endElementId - startElementId
263: - 1];
264: buffer.getChars(startElementId + 1, endElementId,
265: elementId, 0);
266:
267: char[] versionId = new char[endVersionId - startVersionId
268: - 1];
269: buffer.getChars(startVersionId + 1, endVersionId,
270: versionId, 0);
271:
272: if (!new String(versionId).equals(GENERIC_VERSION_NUMBER)
273: && !new String(versionId).endsWith(QUALIFIER)) {
274: startElement = startVersionId;
275: continue;
276: }
277:
278: startVersionId++;
279: String replacementVersion = null;
280: Version v = new Version(new String(versionId));
281: String lookupKey = new String(elementId) + ':'
282: + v.getMajor() + '.' + v.getMinor() + '.'
283: + v.getMicro();
284: if (isPlugin) {
285: replacementVersion = (String) pluginIds.get(lookupKey);
286: } else {
287: replacementVersion = (String) featureIds.get(lookupKey);
288: }
289: if (replacementVersion == null) {
290: System.err
291: .println("Could not find " + new String(elementId)); //$NON-NLS-1$
292: } else {
293: buffer.replace(startVersionId, endVersionId,
294: replacementVersion);
295: contentChanged = true;
296: }
297: int change = endVersionId - startVersionId
298: - replacementVersion.length();
299: startElement = (endElementId > endVersionId) ? endElementId
300: - change : endVersionId - change;
301: }
302:
303: if (!contentChanged)
304: return;
305:
306: try {
307: OutputStreamWriter w = new OutputStreamWriter(
308: new BufferedOutputStream(new FileOutputStream(
309: featureFilePath)), UTF_8);
310: w.write(buffer.toString());
311: w.close();
312: } catch (FileNotFoundException e) {
313: // ignore
314: } catch (IOException e) {
315: throw new BuildException(e);
316: }
317: }
318:
319: private int scan(StringBuffer buf, int start, String targetName) {
320: return scan(buf, start, new String[] { targetName });
321: }
322:
323: private int scan(StringBuffer buf, int start, String[] targets) {
324: for (int i = start; i < buf.length(); i++) {
325: for (int j = 0; j < targets.length; j++) {
326: if (i < buf.length() - targets[j].length()) {
327: String match = buf.substring(i, i
328: + targets[j].length());
329: if (targets[j].equalsIgnoreCase(match))
330: return i;
331: }
332: }
333: }
334: return -1;
335: }
336:
337: private StringBuffer readFile(File targetName) throws IOException {
338: InputStreamReader reader = new InputStreamReader(
339: new BufferedInputStream(new FileInputStream(targetName)),
340: UTF_8);
341: StringBuffer result = new StringBuffer();
342: char[] buf = new char[4096];
343: int count;
344: try {
345: count = reader.read(buf, 0, buf.length);
346: while (count != -1) {
347: result.append(buf, 0, count);
348: count = reader.read(buf, 0, buf.length);
349: }
350: } finally {
351: try {
352: reader.close();
353: } catch (IOException e) {
354: // ignore exceptions here
355: }
356: }
357: return result;
358: }
359: }
|