/*******************************************************************************
* Copyright (c) 2008 Scott Stanchfield.
* 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:
* Based on the ANTLR parser generator by Terence Parr, http://antlr.org
* Ric Klaren <klaren@cs.utwente.nl>
* Scott Stanchfield - Modifications for XML Parsing
*******************************************************************************/
package com.javadude.antxr;
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.List;
import java.util.Map;
// assumes one source file for now -- may need to change if ANTXR 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<Integer, List<Integer>> sourceMap = new HashMap<Integer, List<Integer>>();
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<Integer> outputLines = sourceMap.get(sourceLine);
if (outputLines == null) {
outputLines = new ArrayList<Integer>();
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<Integer> sortedSourceLines = new ArrayList<Integer>(sourceMap.keySet());
Collections.sort(sortedSourceLines);
for (Integer sourceLine : sortedSourceLines) {
List<Integer> outputLines = sourceMap.get(sourceLine);
for (Integer outputLine : outputLines) {
smapWriter.println(sourceLine + ":" + outputLine);
}
}
smapWriter.println("*E");
smapWriter.close();
}
@Override
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');
}
@Override
public void write(int c) {
checkChar(c);
super.write(c);
}
@Override
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.
@Override
public void println() {
mapLine(true);
super.println();
lastPrintCharacterWasCR = false;
}
public Map<Integer, List<Integer>> getSourceMap() {
return sourceMap;
}
}