| /******************************************************************************* |
| * Copyright (c) 2005, 2016 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.cdt.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<FileSet> fFileSets = new Vector<>(); |
| private File fDestDir = null; |
| private Set<String> 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); |
| } |
| } |
| } |
| |
| @Override |
| 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()); |
| } |
| StringBuilder buf = new StringBuilder("Symbols: "); |
| String[] symbols = 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<FileSet> fileSets = fFileSets.iterator(); |
| while (fileSets.hasNext()) { |
| 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 (BufferedReader reader = new BufferedReader(new FileReader(srcFile))) { |
| StringBuilder buffer = new StringBuilder(); |
| 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); |
| } |
| } |