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