001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.jasper.compiler;
017:
018: import java.io.BufferedReader;
019: import java.io.IOException;
020: import java.io.StringReader;
021: import java.util.ResourceBundle;
022: import java.util.Vector;
023: import java.net.MalformedURLException;
024:
025: import org.apache.jasper.JasperException;
026: import org.xml.sax.SAXException;
027:
028: /**
029: * Class responsible for dispatching JSP parse and javac compilation errors
030: * to the configured error handler.
031: *
032: * This class is also responsible for localizing any error codes before they
033: * are passed on to the configured error handler.
034: *
035: * In the case of a Java compilation error, the compiler error message is
036: * parsed into an array of JavacErrorDetail instances, which is passed on to
037: * the configured error handler.
038: *
039: * @author Jan Luehe
040: * @author Kin-man Chung
041: */
042: public class ErrorDispatcher {
043:
044: private static final ResourceBundle bundle = ResourceBundle
045: .getBundle("org.apache.jasper.resources.messages");
046:
047: // Custom error handler
048: private ErrorHandler errHandler;
049:
050: // Indicates whether the compilation was initiated by JspServlet or JspC
051: private boolean jspcMode = false;
052:
053: /*
054: * Constructor.
055: *
056: * @param jspcMode true if compilation has been initiated by JspC, false
057: * otherwise
058: */
059: public ErrorDispatcher(boolean jspcMode) {
060: // XXX check web.xml for custom error handler
061: errHandler = new DefaultErrorHandler();
062: this .jspcMode = jspcMode;
063: }
064:
065: /*
066: * Dispatches the given JSP parse error to the configured error handler.
067: *
068: * The given error code is localized. If it is not found in the
069: * resource bundle for localized error messages, it is used as the error
070: * message.
071: *
072: * @param errCode Error code
073: */
074: public void jspError(String errCode) throws JasperException {
075: dispatch(null, errCode, null, null);
076: }
077:
078: /*
079: * Dispatches the given JSP parse error to the configured error handler.
080: *
081: * The given error code is localized. If it is not found in the
082: * resource bundle for localized error messages, it is used as the error
083: * message.
084: *
085: * @param where Error location
086: * @param errCode Error code
087: */
088: public void jspError(Mark where, String errCode)
089: throws JasperException {
090: dispatch(where, errCode, null, null);
091: }
092:
093: /*
094: * Dispatches the given JSP parse error to the configured error handler.
095: *
096: * The given error code is localized. If it is not found in the
097: * resource bundle for localized error messages, it is used as the error
098: * message.
099: *
100: * @param n Node that caused the error
101: * @param errCode Error code
102: */
103: public void jspError(Node n, String errCode) throws JasperException {
104: dispatch(n.getStart(), errCode, null, null);
105: }
106:
107: /*
108: * Dispatches the given JSP parse error to the configured error handler.
109: *
110: * The given error code is localized. If it is not found in the
111: * resource bundle for localized error messages, it is used as the error
112: * message.
113: *
114: * @param errCode Error code
115: * @param arg Argument for parametric replacement
116: */
117: public void jspError(String errCode, String arg)
118: throws JasperException {
119: dispatch(null, errCode, new Object[] { arg }, null);
120: }
121:
122: /*
123: * Dispatches the given JSP parse error to the configured error handler.
124: *
125: * The given error code is localized. If it is not found in the
126: * resource bundle for localized error messages, it is used as the error
127: * message.
128: *
129: * @param where Error location
130: * @param errCode Error code
131: * @param arg Argument for parametric replacement
132: */
133: public void jspError(Mark where, String errCode, String arg)
134: throws JasperException {
135: dispatch(where, errCode, new Object[] { arg }, null);
136: }
137:
138: /*
139: * Dispatches the given JSP parse error to the configured error handler.
140: *
141: * The given error code is localized. If it is not found in the
142: * resource bundle for localized error messages, it is used as the error
143: * message.
144: *
145: * @param n Node that caused the error
146: * @param errCode Error code
147: * @param arg Argument for parametric replacement
148: */
149: public void jspError(Node n, String errCode, String arg)
150: throws JasperException {
151: dispatch(n.getStart(), errCode, new Object[] { arg }, null);
152: }
153:
154: /*
155: * Dispatches the given JSP parse error to the configured error handler.
156: *
157: * The given error code is localized. If it is not found in the
158: * resource bundle for localized error messages, it is used as the error
159: * message.
160: *
161: * @param errCode Error code
162: * @param arg1 First argument for parametric replacement
163: * @param arg2 Second argument for parametric replacement
164: */
165: public void jspError(String errCode, String arg1, String arg2)
166: throws JasperException {
167: dispatch(null, errCode, new Object[] { arg1, arg2 }, null);
168: }
169:
170: /*
171: * Dispatches the given JSP parse error to the configured error handler.
172: *
173: * The given error code is localized. If it is not found in the
174: * resource bundle for localized error messages, it is used as the error
175: * message.
176: *
177: * @param errCode Error code
178: * @param arg1 First argument for parametric replacement
179: * @param arg2 Second argument for parametric replacement
180: * @param arg3 Third argument for parametric replacement
181: */
182: public void jspError(String errCode, String arg1, String arg2,
183: String arg3) throws JasperException {
184: dispatch(null, errCode, new Object[] { arg1, arg2, arg3 }, null);
185: }
186:
187: /*
188: * Dispatches the given JSP parse error to the configured error handler.
189: *
190: * The given error code is localized. If it is not found in the
191: * resource bundle for localized error messages, it is used as the error
192: * message.
193: *
194: * @param where Error location
195: * @param errCode Error code
196: * @param arg1 First argument for parametric replacement
197: * @param arg2 Second argument for parametric replacement
198: */
199: public void jspError(Mark where, String errCode, String arg1,
200: String arg2) throws JasperException {
201: dispatch(where, errCode, new Object[] { arg1, arg2 }, null);
202: }
203:
204: /*
205: * Dispatches the given JSP parse error to the configured error handler.
206: *
207: * The given error code is localized. If it is not found in the
208: * resource bundle for localized error messages, it is used as the error
209: * message.
210: *
211: * @param where Error location
212: * @param errCode Error code
213: * @param arg1 First argument for parametric replacement
214: * @param arg2 Second argument for parametric replacement
215: * @param arg3 Third argument for parametric replacement
216: */
217:
218: public void jspError(Mark where, String errCode, String arg1,
219: String arg2, String arg3) throws JasperException {
220: dispatch(where, errCode, new Object[] { arg1, arg2, arg3 },
221: null);
222: }
223:
224: /*
225: * Dispatches the given JSP parse error to the configured error handler.
226: *
227: * The given error code is localized. If it is not found in the
228: * resource bundle for localized error messages, it is used as the error
229: * message.
230: *
231: * @param n Node that caused the error
232: * @param errCode Error code
233: * @param arg1 First argument for parametric replacement
234: * @param arg2 Second argument for parametric replacement
235: */
236:
237: public void jspError(Node n, String errCode, String arg1,
238: String arg2) throws JasperException {
239: dispatch(n.getStart(), errCode, new Object[] { arg1, arg2 },
240: null);
241: }
242:
243: /*
244: * Dispatches the given JSP parse error to the configured error handler.
245: *
246: * The given error code is localized. If it is not found in the
247: * resource bundle for localized error messages, it is used as the error
248: * message.
249: *
250: * @param n Node that caused the error
251: * @param errCode Error code
252: * @param arg1 First argument for parametric replacement
253: * @param arg2 Second argument for parametric replacement
254: * @param arg3 Third argument for parametric replacement
255: */
256:
257: public void jspError(Node n, String errCode, String arg1,
258: String arg2, String arg3) throws JasperException {
259: dispatch(n.getStart(), errCode,
260: new Object[] { arg1, arg2, arg3 }, null);
261: }
262:
263: /*
264: * Dispatches the given parsing exception to the configured error handler.
265: *
266: * @param e Parsing exception
267: */
268: public void jspError(Exception e) throws JasperException {
269: dispatch(null, null, null, e);
270: }
271:
272: /*
273: * Dispatches the given JSP parse error to the configured error handler.
274: *
275: * The given error code is localized. If it is not found in the
276: * resource bundle for localized error messages, it is used as the error
277: * message.
278: *
279: * @param errCode Error code
280: * @param arg Argument for parametric replacement
281: * @param e Parsing exception
282: */
283: public void jspError(String errCode, String arg, Exception e)
284: throws JasperException {
285: dispatch(null, errCode, new Object[] { arg }, e);
286: }
287:
288: /*
289: * Dispatches the given JSP parse error to the configured error handler.
290: *
291: * The given error code is localized. If it is not found in the
292: * resource bundle for localized error messages, it is used as the error
293: * message.
294: *
295: * @param n Node that caused the error
296: * @param errCode Error code
297: * @param arg Argument for parametric replacement
298: * @param e Parsing exception
299: */
300: public void jspError(Node n, String errCode, String arg, Exception e)
301: throws JasperException {
302: dispatch(n.getStart(), errCode, new Object[] { arg }, e);
303: }
304:
305: /**
306: * Parses the given error message into an array of javac compilation error
307: * messages (one per javac compilation error line number).
308: *
309: * @param errMsg Error message
310: * @param fname Name of Java source file whose compilation failed
311: * @param page Node representation of JSP page from which the Java source
312: * file was generated
313: *
314: * @return Array of javac compilation errors, or null if the given error
315: * message does not contain any compilation error line numbers
316: */
317: public static JavacErrorDetail[] parseJavacErrors(String errMsg,
318: String fname, Node.Nodes page) throws JasperException,
319: IOException {
320:
321: return parseJavacMessage(errMsg, fname, page);
322: }
323:
324: /*
325: * Dispatches the given javac compilation errors to the configured error
326: * handler.
327: *
328: * @param javacErrors Array of javac compilation errors
329: */
330: public void javacError(JavacErrorDetail[] javacErrors)
331: throws JasperException {
332:
333: errHandler.javacError(javacErrors);
334: }
335:
336: /*
337: * Dispatches the given compilation error report and exception to the
338: * configured error handler.
339: *
340: * @param errorReport Compilation error report
341: * @param e Compilation exception
342: */
343: public void javacError(String errorReport, Exception e)
344: throws JasperException {
345:
346: errHandler.javacError(errorReport, e);
347: }
348:
349: //*********************************************************************
350: // Private utility methods
351:
352: /*
353: * Dispatches the given JSP parse error to the configured error handler.
354: *
355: * The given error code is localized. If it is not found in the
356: * resource bundle for localized error messages, it is used as the error
357: * message.
358: *
359: * @param where Error location
360: * @param errCode Error code
361: * @param args Arguments for parametric replacement
362: * @param e Parsing exception
363: */
364: private void dispatch(Mark where, String errCode, Object[] args,
365: Exception e) throws JasperException {
366: String file = null;
367: String errMsg = null;
368: int line = -1;
369: int column = -1;
370: boolean hasLocation = false;
371:
372: // Localize
373: if (errCode != null) {
374: errMsg = Localizer.getMessage(errCode, args);
375: } else if (e != null) {
376: // give a hint about what's wrong
377: errMsg = e.getMessage();
378: }
379:
380: // Get error location
381: if (where != null) {
382: if (jspcMode) {
383: // Get the full URL of the resource that caused the error
384: try {
385: file = where.getURL().toString();
386: } catch (MalformedURLException me) {
387: // Fallback to using context-relative path
388: file = where.getFile();
389: }
390: } else {
391: // Get the context-relative resource path, so as to not
392: // disclose any local filesystem details
393: file = where.getFile();
394: }
395: line = where.getLineNumber();
396: column = where.getColumnNumber();
397: hasLocation = true;
398: }
399:
400: // Get nested exception
401: Exception nestedEx = e;
402: if ((e instanceof SAXException)
403: && (((SAXException) e).getException() != null)) {
404: nestedEx = ((SAXException) e).getException();
405: }
406:
407: if (hasLocation) {
408: errHandler.jspError(file, line, column, errMsg, nestedEx);
409: } else {
410: errHandler.jspError(errMsg, nestedEx);
411: }
412: }
413:
414: /*
415: * Parses the given Java compilation error message, which may contain one
416: * or more compilation errors, into an array of JavacErrorDetail instances.
417: *
418: * Each JavacErrorDetail instance contains the information about a single
419: * compilation error.
420: *
421: * @param errMsg Compilation error message that was generated by the
422: * javac compiler
423: * @param fname Name of Java source file whose compilation failed
424: * @param page Node representation of JSP page from which the Java source
425: * file was generated
426: *
427: * @return Array of JavacErrorDetail instances corresponding to the
428: * compilation errors
429: */
430: private static JavacErrorDetail[] parseJavacMessage(String errMsg,
431: String fname, Node.Nodes page) throws IOException,
432: JasperException {
433:
434: Vector errVec = new Vector();
435: StringBuffer errMsgBuf = null;
436: int lineNum = -1;
437: JavacErrorDetail javacError = null;
438:
439: BufferedReader reader = new BufferedReader(new StringReader(
440: errMsg));
441:
442: /*
443: * Parse compilation errors. Each compilation error consists of a file
444: * path and error line number, followed by a number of lines describing
445: * the error.
446: */
447: String line = null;
448: while ((line = reader.readLine()) != null) {
449:
450: /*
451: * Error line number is delimited by set of colons.
452: * Ignore colon following drive letter on Windows (fromIndex = 2).
453: * XXX Handle deprecation warnings that don't have line info
454: */
455: int beginColon = line.indexOf(':', 2);
456: int endColon = line.indexOf(':', beginColon + 1);
457: if ((beginColon >= 0) && (endColon >= 0)) {
458: if (javacError != null) {
459: // add previous error to error vector
460: errVec.add(javacError);
461: }
462:
463: String lineNumStr = line.substring(beginColon + 1,
464: endColon);
465: try {
466: lineNum = Integer.parseInt(lineNumStr);
467: } catch (NumberFormatException e) {
468: // XXX
469: }
470:
471: errMsgBuf = new StringBuffer();
472:
473: // Attempt to map javac error line number to line in JSP page
474: ErrorVisitor errVisitor = new ErrorVisitor(lineNum);
475: page.visit(errVisitor);
476: Node errNode = errVisitor.getJspSourceNode();
477: if ((errNode != null) && (errNode.getStart() != null)) {
478: javacError = new JavacErrorDetail(fname, lineNum,
479: errNode.getStart().getFile(), errNode
480: .getStart().getLineNumber(),
481: errMsgBuf);
482: } else {
483: /*
484: * javac error line number cannot be mapped to JSP page
485: * line number. For example, this is the case if a
486: * scriptlet is missing a closing brace, which causes
487: * havoc with the try-catch-finally block that the code
488: * generator places around all generated code: As a result
489: * of this, the javac error line numbers will be outside
490: * the range of begin and end java line numbers that were
491: * generated for the scriptlet, and therefore cannot be
492: * mapped to the start line number of the scriptlet in the
493: * JSP page.
494: * Include just the javac error info in the error detail.
495: */
496: javacError = new JavacErrorDetail(fname, lineNum,
497: errMsgBuf);
498: }
499: }
500:
501: // Ignore messages preceding first error
502: if (errMsgBuf != null) {
503: errMsgBuf.append(line);
504: errMsgBuf.append("\n");
505: }
506: }
507:
508: // Add last error to error vector
509: if (javacError != null) {
510: errVec.add(javacError);
511: }
512:
513: reader.close();
514:
515: JavacErrorDetail[] errDetails = null;
516: if (errVec.size() > 0) {
517: errDetails = new JavacErrorDetail[errVec.size()];
518: errVec.copyInto(errDetails);
519: }
520:
521: return errDetails;
522: }
523:
524: /*
525: * Visitor responsible for mapping a line number in the generated servlet
526: * source code to the corresponding JSP node.
527: */
528: static class ErrorVisitor extends Node.Visitor {
529:
530: // Java source line number to be mapped
531: private int lineNum;
532:
533: /*
534: * JSP node whose Java source code range in the generated servlet
535: * contains the Java source line number to be mapped
536: */
537: Node found;
538:
539: /*
540: * Constructor.
541: *
542: * @param lineNum Source line number in the generated servlet code
543: */
544: public ErrorVisitor(int lineNum) {
545: this .lineNum = lineNum;
546: }
547:
548: public void doVisit(Node n) throws JasperException {
549: if ((lineNum >= n.getBeginJavaLine())
550: && (lineNum < n.getEndJavaLine())) {
551: found = n;
552: }
553: }
554:
555: /*
556: * Gets the JSP node to which the source line number in the generated
557: * servlet code was mapped.
558: *
559: * @return JSP node to which the source line number in the generated
560: * servlet code was mapped
561: */
562: public Node getJspSourceNode() {
563: return found;
564: }
565: }
566: }
|