blob: 30495d3f492ae63bad497322ef7196573830646f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 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
* Bjorn Freeman-Benson - initial API and implementation
*******************************************************************************/
package org.eclipse.debug.examples.ant.tasks;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.util.FileUtils;
/**
* Java preprocessor for code examples. Used to export source code for
* example plug-ins with parts of code missing/inserted etc., for
* various exercises.
* <p>
* The preprocessor looks for #ifdef statements in java comments, and is
* run with a set of symbols. For example:
* <pre>
* //#ifdef ex1
* ... code to insert when 'ex1' symbol is on
* //#else
* ... code to insert when not 'ex1'
* //#endif
* </pre>
* </p>
*/
public class PreProcessor extends Task {
private Vector fFileSets = new Vector();
private File fDestDir = null;
private Set fSymbols = new HashSet();
private FileUtils fUtils = FileUtils.getFileUtils();
// possible states
private static final int STATE_OUTSIDE_CONDITION = 0;
private static final int STATE_TRUE_CONDITION = 1;
private static final int STATE_FALSE_CONDITION = 2;
private static final int STATE_POST_TRUE_CONDITION = 3;
// matchers
private Matcher IF_DEF_MATCHER = Pattern.compile("#ifdef\\s+\\w+").matcher("");
private Matcher ELSE_IF_MATCHER = Pattern.compile("#elseif\\s+\\w+").matcher("");
private Matcher ELSE_MATCHER = Pattern.compile("#else$|#else\\W+").matcher("");
private Matcher END_MATCHER = Pattern.compile("#endif").matcher("");
/**
* Constructs a new preprocessor task
*/
public PreProcessor() {
}
/**
* Adds a set of files to process.
*
* @param set a set of files to process
*/
public void addFileset(FileSet set) {
fFileSets.addElement(set);
}
/**
* Sets the destination directory for processed files.
*
* @param destDir destination directory for processed files
*/
public void setDestdir(File destDir) {
fDestDir = destDir;
}
/**
* Sets the symbols that are "on" for the preprocessing.
*
* @param symbols symbols that are "on" for the preprocessing
*/
public void setSymbols(String symbols) {
String[] strings = symbols.split(",");
for (int i = 0; i < strings.length; i++) {
String string = strings[i].trim();
if (string.length() > 0) {
fSymbols.add(string);
}
}
}
public void execute() throws BuildException {
if (fSymbols.size() == 0) {
throw new BuildException("No symbols specified for preprocessor");
}
if (fFileSets.isEmpty()) {
throw new BuildException("No filesets specified for processing");
}
if (!fDestDir.exists()) {
throw new BuildException("destdir does not exist: " + fDestDir.getAbsolutePath());
}
StringBuffer buf = new StringBuffer("Symbols: ");
String[] symbols = (String[]) fSymbols.toArray(new String[fSymbols.size()]);
for (int i = 0; i < symbols.length; i++) {
String symbol = symbols[i];
buf.append(symbol);
if(i < (symbols.length -1)) {
buf.append(", ");
}
}
log(buf.toString());
Iterator fileSets = fFileSets.iterator();
while (fileSets.hasNext()) {
FileSet fileSet = (FileSet) fileSets.next();
DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
String[] includedFiles = scanner.getIncludedFiles();
File baseDir = fileSet.getDir(getProject());
for (int i = 0; i < includedFiles.length; i++) {
String fileName = includedFiles[i];
processFile(baseDir, fileName, fDestDir);
}
}
}
/**
* Process the file
* @param baseDir base directory source file is relative to
* @param fileName source file name
* @param destDir root destination directory
*/
private void processFile(File baseDir, String fileName, File destDir) throws BuildException {
File destFile = new File(destDir, fileName);
File srcFile = new File(baseDir, fileName);
File dir = destFile.getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
String contents = null;
if (fileName.endsWith(".java")) {
contents = preProcessFile(srcFile, "//#");
} else if (fileName.equals("plugin.xml")) {
contents = preProcessFile(srcFile, null);
}
if (contents == null) {
// no change, just copy file
try {
fUtils.copyFile(srcFile, destFile);
} catch (IOException e) {
throw new BuildException(e);
}
} else {
// write new file
FileWriter writer;
try {
writer = new FileWriter(destFile);
writer.write(contents);
writer.close();
} catch (IOException e) {
throw new BuildException(e);
}
}
}
/**
* Preprocesses a file
*
* @param srcFile the file to process
* @param strip chars to stip off lines in a true condition, or <code>null</code>
* @return
*/
public String preProcessFile(File srcFile, String strip) {
try {
FileReader fileReader = new FileReader(srcFile);
BufferedReader reader = new BufferedReader(fileReader);
StringBuffer buffer = new StringBuffer();
String line = reader.readLine();
String activeSymbol = null;
int state = STATE_OUTSIDE_CONDITION;
boolean changed = false;
while (line != null) {
boolean ifdef = IF_DEF_MATCHER.reset(line).find();
boolean elseif = ELSE_IF_MATCHER.reset(line).find();
boolean elze = ELSE_MATCHER.reset(line).find();
boolean endif = END_MATCHER.reset(line).find();
boolean commandLine = ifdef || elseif || elze || endif;
boolean written = false;
switch (state) {
case STATE_OUTSIDE_CONDITION:
if (ifdef) {
String condition = line.substring(IF_DEF_MATCHER.start(), IF_DEF_MATCHER.end());
String[] strings = condition.split("\\s+");
activeSymbol = strings[1].trim();
if (fSymbols.contains(activeSymbol)) {
state = STATE_TRUE_CONDITION;
} else {
state = STATE_FALSE_CONDITION;
}
} else if (elseif) {
throw new BuildException("#elseif encountered without corresponding #ifdef");
} else if (elze) {
throw new BuildException("#else encountered without corresponding #ifdef (" + srcFile.getPath() + ")");
} else if (endif) {
throw new BuildException("#endif encountered without corresponding #ifdef");
}
break;
case STATE_TRUE_CONDITION:
if (elze || elseif) {
state = STATE_POST_TRUE_CONDITION;
break;
} else if (endif) {
state = STATE_OUTSIDE_CONDITION;
break;
} else if (ifdef) {
throw new BuildException("illegal nested #ifdef");
}
case STATE_FALSE_CONDITION:
if (elseif) {
String condition = line.substring(ELSE_IF_MATCHER.start(), ELSE_IF_MATCHER.end());
String[] strings = condition.split("\\s+");
activeSymbol = strings[1].trim();
if (fSymbols.contains(activeSymbol)) {
state = STATE_TRUE_CONDITION;
} else {
state = STATE_FALSE_CONDITION;
}
} else if (elze) {
state = STATE_TRUE_CONDITION;
break;
} else if (endif) {
state = STATE_OUTSIDE_CONDITION;
break;
} else if (ifdef) {
throw new BuildException("illegal nested #ifdef");
}
case STATE_POST_TRUE_CONDITION:
if (endif) {
state = STATE_OUTSIDE_CONDITION;
break;
} else if (ifdef) {
throw new BuildException("illegal nested #ifdef");
}
}
if (!commandLine) {
if (state == STATE_OUTSIDE_CONDITION || state == STATE_TRUE_CONDITION) {
if (state == STATE_TRUE_CONDITION && strip != null) {
if (line.startsWith(strip)) {
line = line.substring(strip.length());
}
}
buffer.append(line);
buffer.append("\n");
written = true;
}
}
changed = changed || !written;
line = reader.readLine();
}
if (!changed) {
return null;
}
return buffer.toString();
} catch (IOException e) {
throw new BuildException(e);
}
}
public static void main(String[] args) {
PreProcessor processor = new PreProcessor();
processor.setSymbols("ex2");
String string = processor.preProcessFile(new File("c:\\eclipse3.1\\dev\\example.debug.core\\src\\example\\debug\\core\\launcher\\PDALaunchDelegate.java"), "//#");
//String string = processor.preProcessFile(new File("c:\\eclipse3.1\\dev\\example.debug.core\\plugin.xml"), null);
System.out.println(string);
}
}