package com.fr.third.antlr; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; // assumes one source file for now -- may need to change if ANTLR allows // file inclusion in the future // TODO optimize the output using line ranges for input/output files // currently this writes one mapping per line public class PrintWriterWithSMAP extends PrintWriter { private int currentOutputLine = 1; private int currentSourceLine = 0; private Map sourceMap = new HashMap(); private boolean lastPrintCharacterWasCR = false; private boolean mapLines = false; private boolean mapSingleSourceLine = false; private boolean anythingWrittenSinceMapping = false; public PrintWriterWithSMAP(OutputStream out) { super(out); } public PrintWriterWithSMAP(OutputStream out, boolean autoFlush) { super(out, autoFlush); } public PrintWriterWithSMAP(Writer out) { super(out); } public PrintWriterWithSMAP(Writer out, boolean autoFlush) { super(out, autoFlush); } public void startMapping(int sourceLine) { mapLines = true; if (sourceLine != JavaCodeGenerator.CONTINUE_LAST_MAPPING) currentSourceLine = sourceLine; } public void startSingleSourceLineMapping(int sourceLine) { mapSingleSourceLine = true; mapLines = true; if (sourceLine != JavaCodeGenerator.CONTINUE_LAST_MAPPING) currentSourceLine = sourceLine; } public void endMapping() { mapLine(false); mapLines = false; mapSingleSourceLine = false; } protected void mapLine(boolean incrementOutputLineCount) { if (mapLines && anythingWrittenSinceMapping) { Integer sourceLine = new Integer(currentSourceLine); Integer outputLine = new Integer(currentOutputLine); List outputLines = (List)sourceMap.get(sourceLine); if (outputLines == null) { outputLines = new ArrayList(); sourceMap.put(sourceLine,outputLines); } if (!outputLines.contains(outputLine)) outputLines.add(outputLine); } if (incrementOutputLineCount) currentOutputLine++; if (!mapSingleSourceLine) currentSourceLine++; anythingWrittenSinceMapping = false; } public void dump(PrintWriter smapWriter, String targetClassName, String grammarFile) { smapWriter.println("SMAP"); smapWriter.println(targetClassName + ".java"); smapWriter.println('G'); smapWriter.println("*S G"); smapWriter.println("*F"); smapWriter.println("+ 0 " + grammarFile); smapWriter.println(grammarFile); smapWriter.println("*L"); List sortedSourceLines = new ArrayList(sourceMap.keySet()); Collections.sort(sortedSourceLines); for (Iterator i = sortedSourceLines.iterator(); i.hasNext();) { Integer sourceLine = (Integer)i.next(); List outputLines = (List)sourceMap.get(sourceLine); for (Iterator j = outputLines.iterator(); j.hasNext();) { Integer outputLine = (Integer)j.next(); smapWriter.println(sourceLine + ":" + outputLine); } } smapWriter.println("*E"); smapWriter.close(); } public void write(char[] buf, int off, int len) { int stop = off+len; for(int i = off; i < stop; i++) { checkChar(buf[i]); } super.write(buf,off,len); } // after testing, may want to inline this public void checkChar(int c) { if (lastPrintCharacterWasCR && c != '\n') mapLine(true); else if (c == '\n') mapLine(true); else if (!Character.isWhitespace((char)c)) anythingWrittenSinceMapping = true; lastPrintCharacterWasCR = (c == '\r'); } public void write(int c) { checkChar(c); super.write(c); } public void write(String s, int off, int len) { int stop = off+len; for(int i = off; i < stop; i++) { checkChar(s.charAt(i)); } super.write(s,off,len); } // PrintWriter delegates write(char[]) to write(char[], int, int) // PrintWriter delegates write(String) to write(String, int, int) // dependent on current impl of PrintWriter, which directly // dumps a newline sequence to the target file w/o going through // the other write methods. public void println() { mapLine(true); super.println(); lastPrintCharacterWasCR = false; } public Map getSourceMap() { return sourceMap; } public int getCurrentOutputLine() { return currentOutputLine; } }