001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.java;
031:
032: import com.caucho.util.CharBuffer;
033:
034: import java.io.CharArrayWriter;
035: import java.io.IOException;
036: import java.io.OutputStream;
037: import java.io.PrintWriter;
038: import java.io.Serializable;
039: import java.util.ArrayList;
040: import java.util.Iterator;
041:
042: /**
043: * LineMap maps generated code lines back to the source lines.
044: *
045: * <p>Resin uses LineMap to tell the user the line in the JSP or XSL
046: * file that is broken, as opposed to the generated Java line.
047: */
048: public class LineMap implements Serializable {
049: private String _dstFilename;
050: private String _srcFilename;
051:
052: private String _srcType = "JSP";
053:
054: private String _lastSrcFilename;
055:
056: private ArrayList<Line> _lines = new ArrayList<Line>();
057:
058: /**
059: * Null-arg constructor for serialization.
060: */
061: public LineMap() {
062: }
063:
064: public LineMap(String dstFilename, String srcFilename) {
065: int tail = dstFilename.lastIndexOf('/');
066: if (tail < 0)
067: _dstFilename = dstFilename;
068: else
069: dstFilename = dstFilename.substring(tail + 1);
070:
071: _srcFilename = srcFilename;
072: _lastSrcFilename = _srcFilename;
073: }
074:
075: public LineMap(String dstFilename) {
076: int tail = dstFilename.lastIndexOf('/');
077: if (tail < 0)
078: _dstFilename = dstFilename;
079: else
080: _dstFilename = dstFilename.substring(tail + 1);
081: }
082:
083: public void setSourceType(String type) {
084: _srcType = type;
085: }
086:
087: public String getSourceType() {
088: return _srcType;
089: }
090:
091: public String getDestFilename() {
092: return _dstFilename;
093: }
094:
095: public String getLastSourceFilename() {
096: return _lastSrcFilename;
097: }
098:
099: /**
100: * Adds a new line map entry.
101: *
102: * <p>LineMap assumes that dstLine increases monotonically.
103: *
104: * @param srcFilename the source filename, e.g. the included filename
105: * @param srcLine the source line, e.g. the line in the included file
106: * @param dstLine the line of the generated file.
107: *
108: * @return true if a new entry is needed
109: */
110: public boolean add(String srcFilename, int srcLine, int dstLine) {
111: _lastSrcFilename = srcFilename;
112:
113: if (_lines.size() > 0) {
114: Line line = _lines.get(_lines.size() - 1);
115:
116: if (line.add(srcFilename, srcLine, dstLine)) {
117: if (_lines.size() > 1) {
118: Line prevLine = _lines.get(_lines.size() - 2);
119:
120: if (prevLine.merge(line)) {
121: _lines.remove(_lines.size() - 1);
122: }
123: }
124:
125: return true;
126: }
127:
128: if (line.getLastDestinationLine() + 1 < dstLine) {
129: _lines.add(new Line(line.getLastSourceLine(), line
130: .getSourceFilename(), 1, line
131: .getLastDestinationLine() + 1, dstLine
132: - line.getLastDestinationLine()));
133: }
134: }
135:
136: _lines.add(new Line(srcFilename, srcLine, dstLine));
137:
138: return true;
139: }
140:
141: /**
142: * Adds a line from the smap
143: */
144: public void addLine(int startLine, String sourceFile,
145: int repeatCount, int outputLine, int outputIncrement) {
146: _lines.add(new Line(startLine, sourceFile, repeatCount,
147: outputLine, outputIncrement));
148: }
149:
150: public void add(int srcLine, int dstLine) {
151: add(_lastSrcFilename, srcLine, dstLine);
152: }
153:
154: public Iterator<Line> iterator() {
155: return _lines.iterator();
156: }
157:
158: public int size() {
159: return _lines.size();
160: }
161:
162: public Line get(int i) {
163: return _lines.get(i);
164: }
165:
166: public Line getLine(int line) {
167: for (int i = 0; i < _lines.size(); i++) {
168: Line map = _lines.get(i);
169:
170: if (map._dstLine <= line
171: && line <= map.getLastDestinationLine()) {
172: return map;
173: }
174: }
175:
176: return null;
177: }
178:
179: /**
180: * Converts an error in the generated file to a CompileError based on
181: * the source.
182: */
183: public String convertError(String filename, int line, int column,
184: String message) {
185: String srcFilename = null;
186: int destLine = 0;
187: int srcLine = 0;
188:
189: for (int i = 0; i < _lines.size(); i++) {
190: Line map = _lines.get(i);
191:
192: if (filename != null && !filename.endsWith(_dstFilename)) {
193: } else if (map._dstLine <= line
194: && line <= map.getLastDestinationLine()) {
195: srcFilename = map._srcFilename;
196: srcLine = map.getSourceLine(line);
197: }
198: }
199:
200: if (srcFilename != null)
201: return srcFilename + ":" + srcLine + ": " + message;
202: else
203: return filename + ":" + line + ": " + message;
204: }
205:
206: public String convertLine(String filename, int line) {
207: Line bestLine = getLine(line);
208:
209: if (bestLine != null)
210: return bestLine.getSourceFilename() + ":"
211: + bestLine.getSourceLine(line);
212: else
213: return filename + ":" + line;
214: }
215:
216: /**
217: * Filter a stack trace, replacing names.
218: */
219: public void printStackTrace(Throwable e, OutputStream os) {
220: CharArrayWriter writer = new CharArrayWriter();
221: PrintWriter pw = new PrintWriter(writer);
222:
223: e.printStackTrace(pw);
224:
225: pw.close();
226: char[] array = writer.toCharArray();
227:
228: CharBuffer cb = filter(array);
229:
230: if (os != null) {
231: byte[] b = cb.toString().getBytes();
232:
233: try {
234: os.write(b, 0, b.length);
235: } catch (IOException e1) {
236: }
237: } else
238: System.out.println(cb);
239: }
240:
241: /**
242: * Filter a stack trace, replacing names.
243: */
244: public void printStackTrace(Throwable e, PrintWriter os) {
245: CharArrayWriter writer = new CharArrayWriter();
246: PrintWriter pw = new PrintWriter(writer);
247:
248: e.printStackTrace(pw);
249:
250: pw.close();
251: char[] array = writer.toCharArray();
252:
253: CharBuffer cb = filter(array);
254:
255: if (os != null)
256: os.print(cb.toString());
257: else
258: System.out.println(cb);
259: }
260:
261: /**
262: * Parses a Java stack trace, converting files and line numbers when
263: * possible.
264: */
265: private CharBuffer filter(char[] array) {
266: CharBuffer buf = new CharBuffer();
267: CharBuffer fun = new CharBuffer();
268: CharBuffer file = new CharBuffer();
269:
270: int i = 0;
271: while (i < array.length) {
272: fun.clear();
273: file.clear();
274: int start = i;
275: int end;
276: for (end = i; end < array.length && array[end] != '\n'; end++) {
277: }
278:
279: for (; i < end && Character.isWhitespace(array[i]); i++) {
280: fun.append(array[i]);
281: }
282:
283: // skip 'at'
284: for (; i < end && !Character.isWhitespace(array[i]); i++) {
285: fun.append(array[i]);
286: }
287:
288: if (!fun.endsWith("at")) {
289: for (i = start; i < end; i++) {
290: buf.append(array[i]);
291: }
292: i = end + 1;
293:
294: buf.append('\n');
295:
296: continue;
297: }
298:
299: for (; i < end && Character.isWhitespace(array[i]); i++) {
300: }
301:
302: fun.clear();
303: for (; i < end && !Character.isWhitespace(array[i])
304: && array[i] != '('; i++) {
305: fun.append(array[i]);
306: }
307:
308: if (i < end && array[i] == '(')
309: i++;
310:
311: for (; i < end && !Character.isWhitespace(array[i])
312: && array[i] != ':' && array[i] != ')'; i++) {
313: file.append(array[i]);
314: }
315:
316: int line = -1;
317: if (i < end && array[i] == ':') {
318: line = 0;
319: for (i++; i < end && array[i] >= '0' && array[i] <= '9'; i++) {
320: line = 10 * line + array[i] - '0';
321: }
322: }
323:
324: for (; i < end && !Character.isWhitespace(array[i])
325: && array[i] != ':' && array[i] != ')'; i++) {
326: file.append(array[i]);
327: }
328:
329: buf.append("\tat ");
330: buf.append(fun);
331: buf.append("(");
332: String dstFile = file.toString();
333:
334: if (dstFile.equals(_dstFilename)) {
335: convertError(buf, line);
336: } else {
337: buf.append(file);
338: if (line > 0) {
339: buf.append(":");
340: buf.append(line);
341: }
342: }
343: buf.append(array, i, end - i);
344: buf.append('\n');
345: i = end + 1;
346: }
347:
348: return buf;
349: }
350:
351: /**
352: * Maps a destination line to an error location.
353: *
354: * @param buf CharBuffer to write the error location
355: * @param line generated source line to convert.
356: */
357: private void convertError(CharBuffer buf, int line) {
358: String srcFilename = null;
359: int destLine = 0;
360: int srcLine = 0;
361: int srcTailLine = Integer.MAX_VALUE;
362:
363: for (int i = 0; i < _lines.size(); i++) {
364: Line map = (Line) _lines.get(i);
365:
366: if (map._dstLine <= line
367: && line <= map.getLastDestinationLine()) {
368: srcFilename = map._srcFilename;
369: destLine = map._dstLine;
370: srcLine = map.getSourceLine(line);
371: break;
372: }
373: }
374:
375: if (srcFilename != null) {
376: } else if (_lines.size() > 0)
377: srcFilename = ((Line) _lines.get(0))._srcFilename;
378: else
379: srcFilename = "";
380:
381: buf.append(srcFilename);
382: if (line >= 0) {
383: buf.append(":");
384: buf.append(srcLine + (line - destLine));
385: }
386: }
387:
388: public static class Line implements Serializable {
389: String _srcFilename;
390: int _srcLine;
391:
392: int _dstLine;
393: int _dstIncrement = 1;
394:
395: int _repeat = 1;
396:
397: /**
398: * Constructor for serialization.
399: */
400: public Line() {
401: }
402:
403: Line(String srcFilename, int srcLine, int dstLine) {
404: _srcFilename = srcFilename;
405: _srcLine = srcLine;
406: _dstLine = dstLine;
407: }
408:
409: Line(int srcLine, String srcFilename, int repeat, int dstLine,
410: int dstIncrement) {
411: _srcFilename = srcFilename;
412: _srcLine = srcLine;
413: _dstLine = dstLine;
414: _repeat = repeat;
415: _dstIncrement = dstIncrement;
416: }
417:
418: /**
419: * Tries to add a new location.
420: */
421: boolean add(String srcFilename, int srcLine, int dstLine) {
422: if (_srcFilename != null
423: && (!_srcFilename.equals(srcFilename) || srcFilename == null))
424: return false;
425:
426: // XXX:
427: if (dstLine <= _dstLine) {
428: return true;
429: }
430:
431: if (srcLine == _srcLine) {
432: _dstIncrement = dstLine - _dstLine + 1;
433:
434: return true;
435: } else if (dstLine - _dstLine == (srcLine - _srcLine)
436: * _dstIncrement) {
437: _repeat = srcLine - _srcLine + 1;
438:
439: return true;
440: } else if (srcLine == _srcLine + 1 && _repeat == 1) {
441: _dstIncrement = dstLine - _dstLine;
442:
443: return false;
444: }
445: /*
446: else if (_repeat == 1 && _dstIncrement <= 1) {
447: _dstIncrement = dstLine - 1 - _dstLine;
448: if (_dstIncrement < 0)
449: _dstIncrement = 1;
450:
451: return true;
452: }
453: */
454:
455: return false;
456: }
457:
458: /**
459: * Tries to merge two lines
460: */
461: boolean merge(Line next) {
462: if (_srcFilename != null
463: && !_srcFilename.equals(next._srcFilename))
464: return false;
465:
466: else if (_dstIncrement != next._dstIncrement)
467: return false;
468: else if (getLastDestinationLine() + 1 != next._dstLine)
469: return false;
470: else if (getLastSourceLine() + 1 != next._srcLine)
471: return false;
472: else {
473: _repeat += next._repeat;
474:
475: return true;
476: }
477: }
478:
479: public String getSourceFilename() {
480: return _srcFilename;
481: }
482:
483: public int getSourceLine() {
484: return _srcLine;
485: }
486:
487: /**
488: * Returns the source line.
489: */
490: public int getSourceLine(int dstLine) {
491: return _srcLine + (dstLine - _dstLine) / _dstIncrement;
492: }
493:
494: public int getRepeatCount() {
495: return _repeat;
496: }
497:
498: public int getDestinationLine() {
499: return _dstLine;
500: }
501:
502: public int getLastSourceLine() {
503: return _srcLine + _repeat - 1;
504: }
505:
506: public int getLastDestinationLine() {
507: return _dstLine + _dstIncrement * _repeat - 1;
508: }
509:
510: public int getDestinationIncrement() {
511: return _dstIncrement;
512: }
513:
514: public String toString() {
515: return "Line[src:" + _srcFilename + ":" + _srcLine
516: + ",dst:" + _dstLine + "]";
517: }
518: }
519: }
|