001: /* *****************************************************************************
002: * CompilationError.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2007 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.compiler;
011:
012: import java.io.File;
013: import java.util.*;
014: import org.jdom.Element;
015: import org.jdom.JDOMException;
016: import org.xml.sax.SAXParseException;
017: import org.openlaszlo.utils.ChainedException;
018: import org.openlaszlo.xml.internal.XMLUtils;
019: import org.openlaszlo.sc.parser.ParseException;
020:
021: /** Represents an error that occurred during the invocation of the
022: * interface compiler.
023: */
024: public class CompilationError extends RuntimeException {
025: /** The name of the file within which the error occurred. */
026: private String mPathname = null;
027: /** The line number of the error, or null. */
028: private Integer mLineNumber = null;
029: /** The line number of the error, or null. */
030: private Integer mColumnNumber = null;
031: /** The element at which the error occurred, or null. */
032: private Element mElement = null;
033: private String mAttribute = null;
034: /** Prefix to remove from pathnames. */
035: private String mFileBase = "";
036: private List errors = new Vector();
037:
038: /** A suggested solution for an error */
039: private String solutionMessage = "";
040:
041: /** Set this to true to throw compilation errors that have a
042: * cause, instead of wrapping them in instances of
043: * CompilationError. This is useful for debugging the
044: * compiler. */
045: public static boolean ThrowCompilationErrors = false;
046:
047: /** Constructs an instance.
048: * @param message a string
049: */
050: public CompilationError(String message) {
051: super (message);
052: }
053:
054: /** Constructs an instance.
055: * @param message a string
056: * @param element the element within which the error occurred
057: */
058: public CompilationError(String message, Element element) {
059: super (message);
060: initElement(element, null);
061: }
062:
063: /** Constructs an instance.
064: * @param element the element within which the error occurred
065: * @param cause the chained cause of the error
066: */
067: public CompilationError(Element element, Throwable cause) {
068: super (getCauseMessage(cause));
069: initElement(element, cause);
070: }
071:
072: public CompilationError(Element element, String attribute,
073: Throwable cause) {
074: super (getCauseMessage(cause));
075: initElement(element, attribute, cause);
076: }
077:
078: public CompilationError(Throwable cause, String solution) {
079: this (cause);
080: this .solutionMessage = solution;
081: }
082:
083: /** Constructs an instance.
084: * @param cause the chained cause of the error
085: */
086: public CompilationError(Throwable cause) {
087: super (getCauseMessage(cause));
088: SAXParseException se = null; // is there a SAXParseException with more location info?
089: if (cause instanceof ParseException) {
090: this .initPathname(((ParseException) cause).getPathname());
091: this .setLineNumber(((ParseException) cause).getBeginLine());
092: this .setColumnNumber(((ParseException) cause)
093: .getBeginColumn());
094: } else if (cause instanceof JDOMException) {
095: JDOMException je = (JDOMException) cause;
096: if (je.getCause() instanceof SAXParseException) {
097: se = (SAXParseException) je.getCause();
098: }
099: } else if (cause instanceof SAXParseException) {
100: se = (SAXParseException) cause;
101: }
102: if (se != null) {
103: initPathname(se.getPublicId());
104: setLineNumber(se.getLineNumber());
105: setColumnNumber(se.getColumnNumber());
106: }
107: }
108:
109: /** The constructors use this. */
110: private static String getCauseMessage(Throwable cause) {
111: if (ThrowCompilationErrors) {
112: cause.printStackTrace();
113: throw new ChainedException(cause);
114: }
115: if (cause instanceof JDOMException
116: && ((JDOMException) cause).getCause() != null)
117: cause = ((JDOMException) cause).getCause();
118: String message = cause.getMessage();
119: if (cause instanceof java.io.FileNotFoundException) {
120: return
121: /* (non-Javadoc)
122: * @i18n.test
123: * @org-mes="file not found: " + p[0]
124: */
125: org.openlaszlo.i18n.LaszloMessages.getMessage(
126: CompilationError.class.getName(), "051018-120",
127: new Object[] { message });
128: } else if (cause instanceof java.lang.NumberFormatException) {
129: return
130: /* (non-Javadoc)
131: * @i18n.test
132: * @org-mes="invalid number: " + p[0]
133: */
134: org.openlaszlo.i18n.LaszloMessages.getMessage(
135: CompilationError.class.getName(), "051018-129",
136: new Object[] { message });
137: } else if (cause instanceof org.openlaszlo.compiler.ViewSchema.ColorFormatException) {
138: return
139: /* (non-Javadoc)
140: * @i18n.test
141: * @org-mes="invalid color: " + p[0]
142: */
143: org.openlaszlo.i18n.LaszloMessages.getMessage(
144: CompilationError.class.getName(), "051018-138",
145: new Object[] { message });
146: } else if (cause instanceof ParseException) {
147: return ((ParseException) cause).getMessage(false);
148: } else {
149: /*String name = cause.getClass().getName();
150: if (name.lastIndexOf('.') > 0)
151: name = name.substring(name.lastIndexOf('.')+1);
152: if (name.indexOf('$') < 0)
153: message = name + ": " + message;*/
154: return message;
155: }
156: }
157:
158: public void attachErrors(List errors) {
159: this .errors.addAll(errors);
160: }
161:
162: /** Set the 'solution message', a hint as to what might have
163: caused this error.
164: @param sol a helpful message to be appended to the error message
165: */
166: public void setSolution(String sol) {
167: this .solutionMessage = sol;
168: }
169:
170: /** Initializes this instance's element to the specified
171: * parameter. May be called at most once, and only if the element
172: * wasn't initialized in the constructor.
173: * @param element the element at which the error occurred
174: */
175:
176: void initElement(Element element) {
177: initElement(element, null);
178: }
179:
180: void initElement(Element element, Throwable cause) {
181: if (this .mElement != null && mElement != element) {
182: throw new IllegalStateException(
183: /* (non-Javadoc)
184: * @i18n.test
185: * @org-mes="initElement called twice, on " + p[0] + " and " + p[1]
186: */
187: org.openlaszlo.i18n.LaszloMessages.getMessage(
188: CompilationError.class.getName(), "051018-182",
189: new Object[] { mElement, element }));
190: }
191: this .mElement = element;
192:
193: // Prefer the script compiler's suggestion of what line number
194: // the error occurred on
195: if (cause instanceof ParseException) {
196: this .initPathname(((ParseException) cause).getPathname());
197: this .setLineNumber(((ParseException) cause).getBeginLine());
198: this .setColumnNumber(((ParseException) cause)
199: .getBeginColumn());
200: } else {
201: this .initPathname(Parser.getSourceMessagePathname(element));
202: this .setLineNumber(Parser.getSourceLocation(element,
203: Parser.LINENO).intValue());
204: this .setColumnNumber(Parser.getSourceLocation(element,
205: Parser.COLNO).intValue());
206: }
207: }
208:
209: void initElement(Element element, String attribute, Throwable cause) {
210: initElement(element, cause);
211: this .mAttribute = attribute;
212: }
213:
214: /** Returns the element at which the error occurred.
215: * @return the element at which the error occurred
216: */
217: public Element getElement() {
218: return this .mElement;
219: }
220:
221: public String getSolutionMessage() {
222: return solutionMessage;
223: }
224:
225: public void setFileBase(String fileBase) {
226: this .mFileBase = fileBase;
227: }
228:
229: /** Initializes this instance's pathname to the specified
230: * parameter. May be called at most once, and only if the pathname
231: * wasn't initialized in the constructor.
232: * @param pathname the name of the file at which the error occurred
233: */
234: public void initPathname(String pathname) {
235: if (mPathname != null
236: && mPathname.intern() != pathname.intern()) {
237: throw new IllegalStateException(
238: /* (non-Javadoc)
239: * @i18n.test
240: * @org-mes="initPathname called twice, on " + p[0] + " and " + p[1]
241: */
242: org.openlaszlo.i18n.LaszloMessages.getMessage(
243: CompilationError.class.getName(), "051018-232",
244: new Object[] { mPathname, pathname }));
245: }
246: this .mPathname = pathname;
247: }
248:
249: /** Returns the name of the file within which this error
250: * occurred.
251: * @return the name of the file within which the error occurred
252: */
253: public String getPathname() {
254: return mPathname;
255: }
256:
257: // TODO: [2003-01-16] Once ScriptElementCompiler is fixed not to
258: // use this method, it can be removed.
259: public void setPathname(String pathname) {
260: mPathname = pathname;
261: }
262:
263: /** Returns the column number at which this error occurred, or null
264: * if this can't be determined.
265: * @return the line number at which the error occurred
266: */
267: public Integer getColumnNumber() {
268: return mColumnNumber;
269: }
270:
271: public void setColumnNumber(int columnNumber) {
272: mColumnNumber = new Integer(columnNumber);
273: }
274:
275: public void initColumnNumber(int columnNumber) {
276: if (mColumnNumber != null
277: && mColumnNumber.intValue() != columnNumber)
278: throw new IllegalStateException(
279: /* (non-Javadoc)
280: * @i18n.test
281: * @org-mes="initColumnNumber called twice, on " + p[0] + " and " + p[1]
282: */
283: org.openlaszlo.i18n.LaszloMessages.getMessage(
284: CompilationError.class.getName(), "051018-271",
285: new Object[] { mColumnNumber,
286: new Integer(columnNumber) }));
287: setColumnNumber(columnNumber);
288: }
289:
290: /** Returns the line number at which this error occurred, or null
291: * if this can't be determined.
292: * @return the line number at which the error occurred
293: */
294: public Integer getLineNumber() {
295: return mLineNumber;
296: }
297:
298: public void setLineNumber(int lineNumber) {
299: mLineNumber = new Integer(lineNumber);
300: }
301:
302: public void initLineNumber(int lineNumber) {
303: if (mLineNumber != null && mLineNumber.intValue() != lineNumber)
304: throw new IllegalStateException(
305: /* (non-Javadoc)
306: * @i18n.test
307: * @org-mes="initLineNumber called twice, on " + p[0] + " and " + p[1]
308: */
309: org.openlaszlo.i18n.LaszloMessages
310: .getMessage(CompilationError.class.getName(),
311: "051018-295", new Object[] { mLineNumber,
312: new Integer(lineNumber) }));
313: setLineNumber(lineNumber);
314: }
315:
316: /** Return a message describing the error.
317: * @param base pathname prefix to strip out from error messages
318: * @return a message describing the error
319: */
320: public String getMessage(File base) {
321: return getMessage(base.getAbsolutePath() + File.separator);
322: }
323:
324: /** Return a message describing the error.
325: * @param base pathname prefix to strip out from error messages
326: * @return a message describing the error
327: */
328: public String getMessage(String base) {
329: String errmsg = _getMessage();
330:
331: if (base != null && errmsg.startsWith(base)) {
332: // remove base string prefix
333: return errmsg.substring(base.length());
334: } else {
335: return errmsg;
336: }
337: }
338:
339: /** Return a message describing the error.
340: * @return a message describing the error
341: */
342: private String _getMessage() {
343: String message = "";
344: if (mPathname != null && super .getMessage() != null
345: && !super .getMessage().startsWith(mPathname)) {
346: message += mPathname + ":";
347: if (mLineNumber != null) {
348: message += mLineNumber + ":";
349: if (mColumnNumber != null) {
350: message += mColumnNumber + ":";
351: }
352: }
353: message += " ";
354: }
355:
356: String errorText = super .getMessage();
357: // super.getMessage() seems to be returning null for Tomcat
358: // xerces/JDOM errors [hqm 11-2003]
359: if (errorText == null) {
360: errorText = "";
361: }
362: if (!solutionMessage.equals("") && !errorText.equals("")
363: && !errorText.endsWith(".")
364: && !errorText.endsWith(". ")) {
365: errorText += ". ";
366: }
367: if (!solutionMessage.equals("") && !errorText.endsWith(" ")) {
368: errorText += " ";
369: }
370: return message + errorText + getSolutionMessage();
371: }
372:
373: public String getMessage() {
374: return getMessage(mFileBase + File.separator);
375: }
376:
377: public String toHTML() {
378: // todo: properly quote the filename
379: String sourceFile = getPathname();
380: String message = getMessage();
381: String solution = "<font color=\"green\">"
382: + getSolutionMessage() + "</font>";
383: if (sourceFile != null && message.startsWith(sourceFile)) {
384: message = "<A HREF=\"" + sourceFile + "\">" + sourceFile
385: + "</A>" + message.substring(sourceFile.length());
386: }
387: return "<HTML><HEAD>" + "<TITLE>" + "Compilation Error"
388: + "</TITLE>" + "</HEAD><BODY>" + message + "<p>"
389: + solution + "</BODY></HTML>";
390: }
391:
392: public String toXML() {
393: StringBuffer buffer = new StringBuffer();
394: buffer.append("<error>");
395: buffer.append(XMLUtils.escapeXml(getMessage()));
396: for (Iterator iter = errors.iterator(); iter.hasNext();)
397: buffer.append(((CompilationError) iter.next()).toXML());
398: buffer.append("</error>");
399: return buffer.toString();
400: }
401:
402: /**
403: * @return the error message w/out the filename and line, col numbers
404: * XXX This doesn't seem to work.
405: */
406: public String getErrorMessage() {
407: return super.getMessage();
408: }
409: }
|