/*
* *************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
* *************************************************************************************
*/
package com.espertech.esper.util;
import java.io.*;
import java.util.*;
/**
* Tools to transform the ANTLR-generated parser (EsperEPL2GrammarParser.java) method "specialStateTransition"
* to move case-checking to separate methods. This is required as ANTLR 3.2 generates
* a method that results in more then 64k bytecode. Through the simple refactoring that moves each case-check
* to a separate method the bytecode size gets much smaller then 64k for the method. Also see file "etc/antlrtool.(sh, cmd)".
*/
public class ParserTool {
private final static String NEWLINE = System.getProperty("line.separator");
public static void main(String[] args) {
if (args.length == 0) {
throw new RuntimeException("Required filename parameter is not provided");
}
String filename = args[0];
List<String> lines = readFile(filename);
System.out.println("Read " + lines.size() + " lines read from file " + filename);
List<String> commentRemovedLines = new ArrayList<String>();
for (String line : lines) {
int indexComment = line.indexOf("// ");
if (indexComment != -1) {
line = line.substring(0, indexComment);
}
if (line.trim().length() == 0) {
continue;
}
commentRemovedLines.add(line);
}
// transform the state transition method
transformSpecialStateTransition(commentRemovedLines);
// transform the DFA197_transitionS
Map<String, List<String>> files = transformDFA197Array(filename, commentRemovedLines);
List<String> staticFinals = moveStaticFinalToInterface(commentRemovedLines);
String[] filenameSplit = filename.split("\\.java");
files.put(filenameSplit[0] + "_Const.java", staticFinals);
for (Map.Entry<String, List<String>> linesPerFile : files.entrySet()) {
String filenameToWrite = linesPerFile.getKey();
writeFile(filenameToWrite, linesPerFile.getValue());
System.out.println("Wrote " + linesPerFile.getValue().size() + " lines to file " + filenameToWrite);
}
}
public static Map<String, List<String>> transformDFA197Array(String filename, List<String> lines) {
String regexDFA = "(.*)static final String\\[\\] DFA(.*)_transitionS(.*)";
int startDFA = findLineNumRegEx(lines, regexDFA, 0);
if (startDFA == -1) {
throw new RuntimeException("Failed to find line: " + regexDFA);
}
String dfaNum = lines.get(startDFA).split("DFA")[1].substring(0,3);
int endDFA = findMethodEndLine(lines, startDFA);
if (endDFA == -1) {
throw new RuntimeException("Failed to end of array declaration: " + regexDFA);
}
// build new class
List<String> dfaDeclBuf = new ArrayList<String>();
dfaDeclBuf.add("package com.espertech.esper.epl.generated;");
dfaDeclBuf.add("public class EsperEPL2GrammarParser_DFAS {");
List<String> dfaDeclaration = getLinesBetween(lines, startDFA, endDFA);
dfaDeclBuf.addAll(dfaDeclaration);
dfaDeclBuf.add("};");
dfaDeclBuf.add("}");
// remove from old, replace in old
replaceLines(lines, startDFA, endDFA + 1, Collections.<String>emptyList());
replaceContent(lines, "DFA" + dfaNum + "_transitionS", "EsperEPL2GrammarParser_DFAS.DFA" + dfaNum + "_transitionS");
String[] filenameSplit = filename.split("\\.java");
Map<String, List<String>> items = new LinkedHashMap<String, List<String>>();
items.put(filename, lines);
items.put(filenameSplit[0] + "_DFAS.java", dfaDeclBuf);
return items;
}
public static String transform(String text) {
List<String> lines = textToLines(text);
transformSpecialStateTransition(lines);
return linesToText(lines);
}
private static List<String> moveStaticFinalToInterface(List<String> lines) {
Iterator<String> it = lines.iterator();
ArrayList<String> bitsets = new ArrayList<String>();
for (;it.hasNext();) {
String line = it.next();
if (line.contains("public static final BitSet")) {
it.remove();
bitsets.add(line);
}
}
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.contains("public class EsperEPL2GrammarParser extends Parser")) {
lines.set(i, "public class EsperEPL2GrammarParser extends Parser implements EsperEPL2GrammarParser_Const {");
}
}
bitsets.add(0, "package com.espertech.esper.epl.generated;" + NEWLINE);
bitsets.add(1, "import org.antlr.runtime.BitSet;" + NEWLINE);
bitsets.add(2, "public interface EsperEPL2GrammarParser_Const {" + NEWLINE);
bitsets.add("}" + NEWLINE);
return bitsets;
}
private static void transformSpecialStateTransition(List<String> inputbuf) {
int startLineNum = findLineNumStartWith(inputbuf, "public int specialStateTransition", 0);
if (startLineNum == -1) {
return;
}
int endLineNum = findMethodEndLine(inputbuf, startLineNum);
if (endLineNum == -1) {
return;
}
int firstCaseLineNum = findLineNumStartWith(inputbuf, "case 0", startLineNum);
int caseEndLineNum = findLineNumStartWith(inputbuf, "if (state.backtracking>0)", startLineNum);
List<String> headerLines = getLinesBetween(inputbuf, startLineNum, firstCaseLineNum);
List<String> trailerLines = getLinesBetween(inputbuf, caseEndLineNum, endLineNum + 1);
List<List<String>> caseHandlers = new ArrayList<List<String>>();
List<List<String>> methods = new ArrayList<List<String>>();
int caseNum = 0;
while(true) {
int caseLineNum = findLineNumStartWith(inputbuf, "case " + caseNum + " :", startLineNum);
if (caseLineNum == -1) {
break;
}
int breakLineNum = findLineNumStartWith(inputbuf, "break", caseLineNum);
List<String> method = new ArrayList<String>();
method.add(" private int sst_" + caseNum + "() {");
method.add(" int s = -1;");
method.addAll(getLinesBetween(inputbuf, caseLineNum+1, breakLineNum - 1));
method.add(" return s;");
method.add(" }");
methods.add(method);
List<String> caseHandler = new ArrayList<String>();
caseHandler.add(" case " + caseNum + ": ");
caseHandler.add(" s = " + "sst_" + caseNum + "();");
caseHandler.add(" if ( s>=0 ) return s;");
caseHandler.add(" break;");
caseHandlers.add(caseHandler);
caseNum++;
}
List<String> replacementMethod = new ArrayList<String>();
replacementMethod.addAll(headerLines); // add header
for (List<String> caseHandler : caseHandlers) {
replacementMethod.addAll(caseHandler);
}
replacementMethod.add(" }"); // add trailer
replacementMethod.addAll(trailerLines);
for (List<String> method : methods) { // add methods
replacementMethod.addAll(method);
}
replaceLines(inputbuf, startLineNum, endLineNum + 1, replacementMethod);
}
private static void replaceContent(List<String> lines, String toReplace, String replaceWith) {
List<String> result = new ArrayList<String>();
for (String line : lines) {
result.add(line.replace(toReplace, replaceWith));
}
lines.clear();
lines.addAll(result);
}
private static void replaceLines(List<String> input, int start, int end, List<String> replacement) {
int count = end - start;
for (int i = 0; i < count; i++) {
input.remove(start);
}
for (int i = 0; i < replacement.size(); i++) {
input.add(start + i, replacement.get(i));
}
}
private static List<String> getLinesBetween(List<String> input, int start, int end) {
List<String> result = new ArrayList<String>();
for (int i = start; i < end; i++) {
result.add(input.get(i));
}
return result;
}
private static int findMethodEndLine(List<String> lines, int lineNum) {
int open = 1;
int current = lineNum + 1;
while(current < lines.size()) {
String line = lines.get(current);
for (int c = 0; c < line.length(); c++) {
if (line.charAt(c) == '{') {
open++;
}
if (line.charAt(c) == '}') {
open--;
if (open == 0) {
return current;
}
}
}
current++;
}
return -1;
}
private static int findLineNumStartWith(List<String> lines, String name, int startIndex) {
for (int i = startIndex; i < lines.size(); i++) {
if (lines.get(i).trim().startsWith(name)) {
return i;
}
}
return -1;
}
private static int findLineNumRegEx(List<String> lines, String regex, int startIndex) {
for (int i = startIndex; i < lines.size(); i++) {
if (lines.get(i).trim().matches(regex)) {
return i;
}
}
return -1;
}
private static void readFile(BufferedReader reader, List<String> list) throws IOException
{
String text;
// repeat until all lines is read
while ((text = reader.readLine()) != null)
{
list.add(text);
}
}
public static List<String> readFile(InputStream is)
{
InputStreamReader isr = new InputStreamReader(is);
try {
return readFile(isr);
}
finally {
try {
isr.close();
}
catch (IOException e) {
// fine
}
}
}
public static List<String> readFile(Reader reader)
{
List<String> list = new ArrayList<String>();
BufferedReader bufferedReader = null;
try
{
bufferedReader = new BufferedReader(reader);
readFile(bufferedReader, list);
}
catch (FileNotFoundException e)
{
throw new RuntimeException("File not found: " + e.getMessage(), e);
}
catch (IOException e)
{
throw new RuntimeException("IO Error reading file: " + e.getMessage(), e);
}
finally
{
try
{
if (bufferedReader != null)
{
bufferedReader.close();
}
}
catch (IOException e)
{
}
}
return list;
}
public static List<String> readFile(String file)
{
FileReader fileReader = null;
try {
fileReader = new FileReader(file);
return readFile(fileReader);
}
catch (FileNotFoundException e)
{
throw new RuntimeException("File not found: " + e.getMessage(), e);
}
finally {
if (fileReader != null) {
try {
fileReader.close();
}
catch (IOException e) {
// fine
}
}
}
}
public static String linesToText(List<String> lines) {
StringWriter writer = new StringWriter();
for (String line : lines) {
writer.append(line)
.append(System.getProperty(
"line.separator"));
}
return writer.toString();
}
public static List<String> textToLines(String input)
{
BufferedReader reader = null;
List<String> list = new ArrayList<String>();
try
{
reader = new BufferedReader(new StringReader(input));
String text = null;
// repeat until all lines is read
while ((text = reader.readLine()) != null)
{
list.add(text);
}
}
catch (FileNotFoundException e)
{
throw new RuntimeException("File not found: " + e.getMessage(), e);
}
catch (IOException e)
{
throw new RuntimeException("IO Error reading file: " + e.getMessage(), e);
}
finally
{
try
{
if (reader != null)
{
reader.close();
}
}
catch (IOException e)
{
}
}
return list;
}
public static void writeFile(String file, List<String> lines) {
FileWriter writer = null;
try {
writer = new FileWriter(file, false);
for (String line : lines) {
writer.append(line)
.append(System.getProperty(
"line.separator"));
}
}
catch (IOException ex) {
throw new RuntimeException("File write problem: " + ex.getMessage(), ex);
}
finally {
if (writer != null) {
try
{
writer.close();
}
catch (IOException e)
{
}
}
}
}
}