com.caucho.java.LineMap Maven / Gradle / Ivy
Show all versions of resin-kernel Show documentation
/*
* Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.java;
import com.caucho.util.CharBuffer;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
/**
* LineMap maps generated code lines back to the source lines.
*
* Resin uses LineMap to tell the user the line in the JSP or XSL
* file that is broken, as opposed to the generated Java line.
*/
public class LineMap implements Serializable {
private String _dstFilename;
private String _srcFilename;
private String _srcType = "JSP";
private String _lastSrcFilename;
private ArrayList _lines = new ArrayList();
/**
* Null-arg constructor for serialization.
*/
public LineMap()
{
}
public LineMap(String dstFilename, String srcFilename)
{
int tail = dstFilename.lastIndexOf('/');
if (tail < 0)
_dstFilename = dstFilename;
else
dstFilename = dstFilename.substring(tail + 1);
_srcFilename = srcFilename;
_lastSrcFilename = _srcFilename;
}
public LineMap(String dstFilename)
{
int tail = dstFilename.lastIndexOf('/');
if (tail < 0)
_dstFilename = dstFilename;
else
_dstFilename = dstFilename.substring(tail + 1);
}
public void setSourceType(String type)
{
_srcType = type;
}
public String getSourceType()
{
return _srcType;
}
public String getDestFilename()
{
return _dstFilename;
}
public String getLastSourceFilename()
{
return _lastSrcFilename;
}
/**
* Adds a new line map entry.
*
* LineMap assumes that dstLine increases monotonically.
*
* @param srcFilename the source filename, e.g. the included filename
* @param srcLine the source line, e.g. the line in the included file
* @param dstLine the line of the generated file.
*
* @return true if a new entry is needed
*/
public boolean add(String srcFilename, int srcLine, int dstLine)
{
return add(srcFilename, srcLine, dstLine, false);
}
public boolean add(String srcFilename,
int srcLine,
int dstLine,
boolean isPreferLast)
{
_lastSrcFilename = srcFilename;
if (_lines.size() > 0) {
Line line = _lines.get(_lines.size() - 1);
if (line.add(srcFilename, srcLine, dstLine, isPreferLast)) {
if (_lines.size() > 1) {
Line prevLine = _lines.get(_lines.size() - 2);
if (prevLine.merge(line)) {
_lines.remove(_lines.size() - 1);
}
}
return true;
}
if (line.getLastDestinationLine() + 1 < dstLine) {
_lines.add(new Line(line.getLastSourceLine(),
line.getSourceFilename(),
1,
line.getLastDestinationLine() + 1,
dstLine - line.getLastDestinationLine()));
}
}
_lines.add(new Line(srcFilename, srcLine, dstLine));
return true;
}
/**
* Adds a line from the smap
*/
public void addLine(int startLine, String sourceFile, int repeatCount,
int outputLine, int outputIncrement)
{
_lines.add(new Line(startLine, sourceFile, repeatCount,
outputLine, outputIncrement));
}
public void add(int srcLine, int dstLine)
{
add(_lastSrcFilename, srcLine, dstLine);
}
public Iterator iterator()
{
return _lines.iterator();
}
public int size()
{
return _lines.size();
}
public Line get(int i)
{
return _lines.get(i);
}
public Line getLine(int line)
{
for (int i = 0; i < _lines.size(); i++) {
Line map = _lines.get(i);
if (map._dstLine <= line && line <= map.getLastDestinationLine()) {
return map;
}
}
return null;
}
/**
* Converts an error in the generated file to a CompileError based on
* the source.
*/
public String convertError(String filename, int line,
int column, String message)
{
String srcFilename = null;
int destLine = 0;
int srcLine = 0;
for (int i = 0; i < _lines.size(); i++) {
Line map = _lines.get(i);
if (filename != null && ! filename.endsWith(_dstFilename)) {
}
else if (map._dstLine <= line && line <= map.getLastDestinationLine()) {
srcFilename = map._srcFilename;
srcLine = map.getSourceLine(line);
}
}
if (srcFilename != null)
return srcFilename + ":" + srcLine + ": " + message;
else
return filename + ":" + line + ": " + message;
}
public String convertLine(String filename, int line)
{
Line bestLine = getLine(line);
if (bestLine != null)
return bestLine.getSourceFilename() + ":" + bestLine.getSourceLine(line);
else
return filename + ":" + line;
}
/**
* Filter a stack trace, replacing names.
*/
public void printStackTrace(Throwable e, OutputStream os)
{
CharArrayWriter writer = new CharArrayWriter();
PrintWriter pw = new PrintWriter(writer);
e.printStackTrace(pw);
pw.close();
char []array = writer.toCharArray();
CharBuffer cb = filter(array);
if (os != null) {
byte []b = cb.toString().getBytes();
try {
os.write(b, 0, b.length);
} catch (IOException e1) {
}
} else
System.out.println(cb);
}
/**
* Filter a stack trace, replacing names.
*/
public void printStackTrace(Throwable e, PrintWriter os)
{
CharArrayWriter writer = new CharArrayWriter();
PrintWriter pw = new PrintWriter(writer);
e.printStackTrace(pw);
pw.close();
char []array = writer.toCharArray();
CharBuffer cb = filter(array);
if (os != null)
os.print(cb.toString());
else
System.out.println(cb);
}
/**
* Parses a Java stack trace, converting files and line numbers when
* possible.
*/
private CharBuffer filter(char []array)
{
CharBuffer buf = new CharBuffer();
CharBuffer fun = new CharBuffer();
CharBuffer file = new CharBuffer();
int i = 0;
while (i < array.length) {
fun.clear();
file.clear();
int start = i;
int end;
for (end = i; end < array.length && array[end] != '\n'; end++) {
}
for (; i < end && Character.isWhitespace(array[i]); i++) {
fun.append(array[i]);
}
// skip 'at'
for (; i < end && ! Character.isWhitespace(array[i]); i++) {
fun.append(array[i]);
}
if (! fun.endsWith("at")) {
for (i = start; i < end; i++) {
buf.append(array[i]);
}
i = end + 1;
buf.append('\n');
continue;
}
for (; i < end && Character.isWhitespace(array[i]); i++) {
}
fun.clear();
for (; i < end && ! Character.isWhitespace(array[i]) &&
array[i] != '('; i++) {
fun.append(array[i]);
}
if (i < end && array[i] == '(')
i++;
for (; i < end && ! Character.isWhitespace(array[i]) &&
array[i] != ':' && array[i] != ')'; i++) {
file.append(array[i]);
}
int line = -1;
if (i < end && array[i] == ':') {
line = 0;
for (i++; i < end && array[i] >= '0' && array[i] <= '9'; i++) {
line = 10 * line + array[i] - '0';
}
}
for (; i < end && ! Character.isWhitespace(array[i]) &&
array[i] != ':' && array[i] != ')'; i++) {
file.append(array[i]);
}
buf.append("\tat ");
buf.append(fun);
buf.append("(");
String dstFile = file.toString();
if (dstFile.equals(_dstFilename)) {
convertError(buf, line);
}
else {
buf.append(file);
if (line > 0) {
buf.append(":");
buf.append(line);
}
}
buf.append(array, i, end - i);
buf.append('\n');
i = end + 1;
}
return buf;
}
/**
* Maps a destination line to an error location.
*
* @param buf CharBuffer to write the error location
* @param line generated source line to convert.
*/
private void convertError(CharBuffer buf, int line)
{
String srcFilename = null;
int destLine = 0;
int srcLine = 0;
int srcTailLine = Integer.MAX_VALUE;
for (int i = 0; i < _lines.size(); i++) {
Line map = (Line) _lines.get(i);
if (map._dstLine <= line && line <= map.getLastDestinationLine()) {
srcFilename = map._srcFilename;
destLine = map._dstLine;
srcLine = map.getSourceLine(line);
break;
}
}
if (srcFilename != null) {
}
else if (_lines.size() > 0)
srcFilename = ((Line) _lines.get(0))._srcFilename;
else
srcFilename = "";
buf.append(srcFilename);
if (line >= 0) {
buf.append(":");
buf.append(srcLine + (line - destLine));
}
}
public static class Line implements Serializable {
String _srcFilename;
int _srcLine;
int _dstLine;
int _dstIncrement = 1;
int _repeat = 1;
/**
* Constructor for serialization.
*/
public Line()
{
}
Line(String srcFilename, int srcLine, int dstLine)
{
_srcFilename = srcFilename;
_srcLine = srcLine;
_dstLine = dstLine;
}
Line(int srcLine, String srcFilename, int repeat,
int dstLine, int dstIncrement)
{
_srcFilename = srcFilename;
_srcLine = srcLine;
_dstLine = dstLine;
_repeat = repeat;
_dstIncrement = dstIncrement;
}
/**
* Tries to add a new location.
*/
boolean add(String srcFilename, int srcLine, int dstLine,
boolean isPreferLast)
{
if (_srcFilename != null
&& (! _srcFilename.equals(srcFilename) || srcFilename == null))
return false;
if (dstLine <= _dstLine) {
// php/180u
if (! isPreferLast)
return true;
else if (_dstIncrement == 1 && _repeat == 1) {
_srcLine = srcLine;
return true;
}
else if (_repeat > 1) {
_repeat--;
return false;
}
else if (_dstIncrement > 1) {
_dstIncrement--;
return false;
}
else
return true;
}
if (srcLine == _srcLine) {
_dstIncrement = dstLine - _dstLine + 1;
return true;
}
else if (dstLine - _dstLine == (srcLine - _srcLine) * _dstIncrement) {
_repeat = srcLine - _srcLine + 1;
return true;
}
else if (srcLine == _srcLine + 1 && _repeat == 1) {
_dstIncrement = dstLine - _dstLine;
return false;
}
/*
else if (_repeat == 1 && _dstIncrement <= 1) {
_dstIncrement = dstLine - 1 - _dstLine;
if (_dstIncrement < 0)
_dstIncrement = 1;
return true;
}
*/
return false;
}
/**
* Tries to merge two lines
*/
boolean merge(Line next)
{
if (_srcFilename != null && ! _srcFilename.equals(next._srcFilename))
return false;
else if (_dstIncrement != next._dstIncrement)
return false;
else if (getLastDestinationLine() + 1 != next._dstLine)
return false;
else if (getLastSourceLine() + 1 != next._srcLine)
return false;
else {
_repeat += next._repeat;
return true;
}
}
public String getSourceFilename()
{
return _srcFilename;
}
public int getSourceLine()
{
return _srcLine;
}
/**
* Returns the source line.
*/
public int getSourceLine(int dstLine)
{
return _srcLine + (dstLine - _dstLine) / _dstIncrement;
}
public int getRepeatCount()
{
return _repeat;
}
public int getDestinationLine()
{
return _dstLine;
}
public int getLastSourceLine()
{
return _srcLine + _repeat - 1;
}
public int getLastDestinationLine()
{
return _dstLine + _dstIncrement * _repeat - 1;
}
public int getDestinationIncrement()
{
return _dstIncrement;
}
public String toString()
{
return "Line[src:" + _srcFilename + ":" + _srcLine + ",dst:" + _dstLine + "]";
}
}
}