/*******************************************************************************
* Copyright (c) 2012 Google, Inc.
* 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:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package com.windowtester.eclipse.ui.convert.preprocessor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A preprocessor that processes files line by line as opposed to an XML based
* preprocessor that would follow the XML based structure.
*/
public abstract class LineBasedPreprocessor
{
protected final List<LineProcessor> lineProcessors = new ArrayList<LineProcessor>(7);
private File currentFile;
/**
* Preprocess the specified file if it exists. If no changes are necessary, then the
* file is not rewritten.
*
* @param file the file to be processed
* @return <code>true</code> if the file exists and was modified
*/
public boolean processIfExists(File file) throws IOException {
return file.exists() && process(file);
}
/**
* Preprocess all files in the specified directory recursively. If no changes are necessary, then the file is not
* modified.
*
* @param dir the directory to be recursively processed
* @return <code>true</code> if one or more files were modified
*/
public boolean processAll(File dir) throws IOException {
boolean modified = false;
String[] names = dir.list();
for (int i = 0; i < names.length; i++) {
File child = new File(dir, names[i]);
if (child.isDirectory()) {
if (processAll(child))
modified = true;
}
else {
if (process(child))
modified = true;
}
}
return modified;
}
/**
* Preprocess the specified file. If no changes were necessary, the the file is not modified.
*
* @param file the file to be processed
* @return <code>true</code> if the file was modified
*/
public boolean process(File file) throws IOException {
currentFile = file;
String content = process0(file);
currentFile = null;
if (content == null)
return false;
FileWriter writer = new FileWriter(file);
try {
writer.write(content);
}
finally {
writer.close();
}
return true;
}
/**
* Answer the file being currently processed or <code>null</code> if none
*/
public File getCurrentFile() {
return currentFile;
}
/**
* For testing purposes, preprocess the specified file and return the preprocessed
* content.
*
* @param file the file to be processed
* @return the preprocessed content or <code>null</code> if no change.
* @throws FileNotFoundException
* @throws IOException
*/
public final String process0(File file) throws IOException {
FileReader fileReader = new FileReader(file);
try {
return process(fileReader);
}
finally {
fileReader.close();
}
}
/**
* For testing purposes, preprocess the specified reader content and return the
* preprocessed content.
*
* @param file the file to be processed
* @return the preprocessed content or <code>null</code> if no change.
* @throws FileNotFoundException
* @throws IOException
*/
public final String process(Reader reader) throws IOException {
for (LineProcessor proc : lineProcessors)
proc.reset();
boolean modified = false;
StringWriter stringWriter = new StringWriter(4096);
PrintWriter writer = new PrintWriter(stringWriter);
LineNumberReader lineReader = new LineNumberReader(new BufferedReader(reader));
startProcess(lineReader, writer);
while (true) {
String oldLine = lineReader.readLine();
if (oldLine == null)
break;
String newLine = processLine(oldLine);
modified = modified || !oldLine.equals(newLine);
if (newLine != null)
writer.println(newLine);
}
endProcess(writer);
String content = stringWriter.toString();
if (!modified)
content = null;
return content;
}
/**
* Called before the processing begins. Subclasses may override
* @param lineReader the reader containing the content to be processed
* @param writer the writer to hold the processed content.
*/
protected void startProcess(LineNumberReader lineReader, PrintWriter writer) {
}
/**
* Called after the processing has finished. Subclasses may override
* @param writer the writer holding the processed content.
*/
protected void endProcess(PrintWriter writer) {
}
/**
* Preprocess the specified line.
*
* @param oldLine the original line
* @return the new line or <code>null</code> if the line should be removed
*/
protected final String processLine(String line) {
for (LineProcessor proc : lineProcessors) {
line = proc.process(line);
if (line == null)
return null;
}
return line;
}
//============================================================================
protected interface LineProcessor
{
public void reset();
public String process(String line);
}
/**
* Replace a source pattern with the replacement text
*/
protected class LineReplacement
implements LineProcessor
{
private final Pattern pattern;
private final String replacement;
public LineReplacement(Pattern pattern, String replacement) {
this.pattern = pattern;
this.replacement = replacement;
}
public void reset() {
}
public String process(String line) {
Matcher matcher = pattern.matcher(line);
if (matcher.find())
line = matcher.replaceAll(replacement);
return line;
}
}
/**
* Remove duplicate lines
*/
protected class RemoveLines
implements LineProcessor
{
private final Pattern pattern;
public RemoveLines(Pattern pattern) {
this.pattern = pattern;
}
public void reset() {
}
public String process(String line) {
if (pattern.matcher(line).find())
return null;
return line;
}
}
/**
* Remove duplicate lines
*/
protected class RemoveDuplicateLines
implements LineProcessor
{
private final Pattern pattern;
private boolean found;
public RemoveDuplicateLines(Pattern pattern) {
this.pattern = pattern;
}
public void reset() {
found = false;
}
public String process(String line) {
if (pattern.matcher(line).find()) {
if (found)
return null;
found = true;
}
return line;
}
}
}