1. /*
  2. * Copyright 2000-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: ListingErrorHandler.java,v 1.7 2004/02/17 04:21:14 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.utils;
  20. import java.io.BufferedReader;
  21. import java.io.InputStream;
  22. import java.io.InputStreamReader;
  23. import java.io.PrintWriter;
  24. import java.net.URL;
  25. import java.net.URLConnection;
  26. import javax.xml.transform.ErrorListener;
  27. import javax.xml.transform.SourceLocator;
  28. import javax.xml.transform.TransformerException;
  29. import com.sun.org.apache.xml.internal.res.XMLErrorResources;
  30. import com.sun.org.apache.xml.internal.res.XMLMessages;
  31. import org.xml.sax.ErrorHandler;
  32. import org.xml.sax.SAXException;
  33. import org.xml.sax.SAXParseException;
  34. /**
  35. * Sample implementation of similar SAX ErrorHandler and JAXP ErrorListener.
  36. *
  37. * <p>This implementation is suitable for various use cases, and
  38. * provides some basic configuration API's as well to control
  39. * when we re-throw errors, etc.</p>
  40. *
  41. * @author shane_curcuru@us.ibm.com
  42. * @version $Id: ListingErrorHandler.java,v 1.7 2004/02/17 04:21:14 minchau Exp $
  43. * @xsl.usage general
  44. */
  45. public class ListingErrorHandler implements ErrorHandler, ErrorListener
  46. {
  47. protected PrintWriter m_pw = null;
  48. /**
  49. * Constructor ListingErrorHandler; user-supplied PrintWriter.
  50. */
  51. public ListingErrorHandler(PrintWriter pw)
  52. {
  53. if (null == pw)
  54. throw new NullPointerException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ERRORHANDLER_CREATED_WITH_NULL_PRINTWRITER, null));
  55. // "ListingErrorHandler created with null PrintWriter!");
  56. m_pw = pw;
  57. }
  58. /**
  59. * Constructor ListingErrorHandler; uses System.err.
  60. */
  61. public ListingErrorHandler()
  62. {
  63. m_pw = new PrintWriter(System.err, true);
  64. }
  65. /* ======== Implement org.xml.sax.ErrorHandler ======== */
  66. /**
  67. * Receive notification of a warning.
  68. *
  69. * <p>SAX parsers will use this method to report conditions that
  70. * are not errors or fatal errors as defined by the XML 1.0
  71. * recommendation. The default behaviour is to take no action.</p>
  72. *
  73. * <p>The SAX parser must continue to provide normal parsing events
  74. * after invoking this method: it should still be possible for the
  75. * application to process the document through to the end.</p>
  76. *
  77. * <p>Filters may use this method to report other, non-XML warnings
  78. * as well.</p>
  79. *
  80. * @param exception The warning information encapsulated in a
  81. * SAX parse exception.
  82. * @exception org.xml.sax.SAXException Any SAX exception, possibly
  83. * wrapping another exception; only if setThrowOnWarning is true.
  84. * @see org.xml.sax.SAXParseException
  85. */
  86. public void warning (SAXParseException exception)
  87. throws SAXException
  88. {
  89. logExceptionLocation(m_pw, exception);
  90. // Note: should we really call .toString() below, since
  91. // sometimes the message is not properly set?
  92. m_pw.println("warning: " + exception.getMessage());
  93. m_pw.flush();
  94. if (getThrowOnWarning())
  95. throw exception;
  96. }
  97. /**
  98. * Receive notification of a recoverable error.
  99. *
  100. * <p>This corresponds to the definition of "error" in section 1.2
  101. * of the W3C XML 1.0 Recommendation. For example, a validating
  102. * parser would use this callback to report the violation of a
  103. * validity constraint. The default behaviour is to take no
  104. * action.</p>
  105. *
  106. * <p>The SAX parser must continue to provide normal parsing events
  107. * after invoking this method: it should still be possible for the
  108. * application to process the document through to the end. If the
  109. * application cannot do so, then the parser should report a fatal
  110. * error even if the XML 1.0 recommendation does not require it to
  111. * do so.</p>
  112. *
  113. * <p>Filters may use this method to report other, non-XML errors
  114. * as well.</p>
  115. *
  116. * @param exception The error information encapsulated in a
  117. * SAX parse exception.
  118. * @exception org.xml.sax.SAXException Any SAX exception, possibly
  119. * wrapping another exception; only if setThrowOnErroris true.
  120. * @see org.xml.sax.SAXParseException
  121. */
  122. public void error (SAXParseException exception)
  123. throws SAXException
  124. {
  125. logExceptionLocation(m_pw, exception);
  126. m_pw.println("error: " + exception.getMessage());
  127. m_pw.flush();
  128. if (getThrowOnError())
  129. throw exception;
  130. }
  131. /**
  132. * Receive notification of a non-recoverable error.
  133. *
  134. * <p>This corresponds to the definition of "fatal error" in
  135. * section 1.2 of the W3C XML 1.0 Recommendation. For example, a
  136. * parser would use this callback to report the violation of a
  137. * well-formedness constraint.</p>
  138. *
  139. * <p>The application must assume that the document is unusable
  140. * after the parser has invoked this method, and should continue
  141. * (if at all) only for the sake of collecting addition error
  142. * messages: in fact, SAX parsers are free to stop reporting any
  143. * other events once this method has been invoked.</p>
  144. *
  145. * @param exception The error information encapsulated in a
  146. * SAX parse exception.
  147. * @exception org.xml.sax.SAXException Any SAX exception, possibly
  148. * wrapping another exception; only if setThrowOnFatalError is true.
  149. * @see org.xml.sax.SAXParseException
  150. */
  151. public void fatalError (SAXParseException exception)
  152. throws SAXException
  153. {
  154. logExceptionLocation(m_pw, exception);
  155. m_pw.println("fatalError: " + exception.getMessage());
  156. m_pw.flush();
  157. if (getThrowOnFatalError())
  158. throw exception;
  159. }
  160. /* ======== Implement javax.xml.transform.ErrorListener ======== */
  161. /**
  162. * Receive notification of a warning.
  163. *
  164. * <p>{@link javax.xml.transform.Transformer} can use this method to report
  165. * conditions that are not errors or fatal errors. The default behaviour
  166. * is to take no action.</p>
  167. *
  168. * <p>After invoking this method, the Transformer must continue with
  169. * the transformation. It should still be possible for the
  170. * application to process the document through to the end.</p>
  171. *
  172. * @param exception The warning information encapsulated in a
  173. * transformer exception.
  174. *
  175. * @throws javax.xml.transform.TransformerException only if
  176. * setThrowOnWarning is true.
  177. *
  178. * @see javax.xml.transform.TransformerException
  179. */
  180. public void warning(TransformerException exception)
  181. throws TransformerException
  182. {
  183. logExceptionLocation(m_pw, exception);
  184. m_pw.println("warning: " + exception.getMessage());
  185. m_pw.flush();
  186. if (getThrowOnWarning())
  187. throw exception;
  188. }
  189. /**
  190. * Receive notification of a recoverable error.
  191. *
  192. * <p>The transformer must continue to try and provide normal transformation
  193. * after invoking this method. It should still be possible for the
  194. * application to process the document through to the end if no other errors
  195. * are encountered.</p>
  196. *
  197. * @param exception The error information encapsulated in a
  198. * transformer exception.
  199. *
  200. * @throws javax.xml.transform.TransformerException only if
  201. * setThrowOnError is true.
  202. *
  203. * @see javax.xml.transform.TransformerException
  204. */
  205. public void error(TransformerException exception)
  206. throws TransformerException
  207. {
  208. logExceptionLocation(m_pw, exception);
  209. m_pw.println("error: " + exception.getMessage());
  210. m_pw.flush();
  211. if (getThrowOnError())
  212. throw exception;
  213. }
  214. /**
  215. * Receive notification of a non-recoverable error.
  216. *
  217. * <p>The transformer must continue to try and provide normal transformation
  218. * after invoking this method. It should still be possible for the
  219. * application to process the document through to the end if no other errors
  220. * are encountered, but there is no guarantee that the output will be
  221. * useable.</p>
  222. *
  223. * @param exception The error information encapsulated in a
  224. * transformer exception.
  225. *
  226. * @throws javax.xml.transform.TransformerException only if
  227. * setThrowOnError is true.
  228. *
  229. * @see javax.xml.transform.TransformerException
  230. */
  231. public void fatalError(TransformerException exception)
  232. throws TransformerException
  233. {
  234. logExceptionLocation(m_pw, exception);
  235. m_pw.println("error: " + exception.getMessage());
  236. m_pw.flush();
  237. if (getThrowOnError())
  238. throw exception;
  239. }
  240. /* ======== Implement worker methods ======== */
  241. /**
  242. * Print out location information about the exception.
  243. *
  244. * Cribbed from DefaultErrorHandler.printLocation()
  245. * @param pw PrintWriter to send output to
  246. * @param exception TransformerException or SAXParseException
  247. * to log information about
  248. */
  249. public static void logExceptionLocation(PrintWriter pw, Throwable exception)
  250. {
  251. if (null == pw)
  252. pw = new PrintWriter(System.err, true);
  253. SourceLocator locator = null;
  254. Throwable cause = exception;
  255. // Try to find the locator closest to the cause.
  256. do
  257. {
  258. // Find the current locator, if one present
  259. if(cause instanceof SAXParseException)
  260. {
  261. // A SAXSourceLocator is a Xalan helper class
  262. // that implements both a SourceLocator and a SAX Locator
  263. //@todo check that the new locator actually has
  264. // as much or more information as the
  265. // current one already does
  266. locator = new SAXSourceLocator((SAXParseException)cause);
  267. }
  268. else if (cause instanceof TransformerException)
  269. {
  270. SourceLocator causeLocator = ((TransformerException)cause).getLocator();
  271. if(null != causeLocator)
  272. {
  273. locator = causeLocator;
  274. }
  275. }
  276. // Then walk back down the chain of exceptions
  277. if(cause instanceof TransformerException)
  278. cause = ((TransformerException)cause).getCause();
  279. else if(cause instanceof WrappedRuntimeException)
  280. cause = ((WrappedRuntimeException)cause).getException();
  281. else if(cause instanceof SAXException)
  282. cause = ((SAXException)cause).getException();
  283. else
  284. cause = null;
  285. }
  286. while(null != cause);
  287. // Formatting note: mimic javac-like errors:
  288. // path\filename:123: message-here
  289. // systemId:L=1;C=2: message-here
  290. if(null != locator)
  291. {
  292. String id = (locator.getPublicId() != locator.getPublicId())
  293. ? locator.getPublicId()
  294. : (null != locator.getSystemId())
  295. ? locator.getSystemId() : "SystemId-Unknown";
  296. pw.print(id + ":Line=" + locator.getLineNumber()
  297. + ";Column=" + locator.getColumnNumber()+": ");
  298. pw.println("exception:" + exception.getMessage());
  299. pw.println("root-cause:"
  300. + ((null != cause) ? cause.getMessage() : "null"));
  301. logSourceLine(pw, locator);
  302. }
  303. else
  304. {
  305. pw.print("SystemId-Unknown:locator-unavailable: ");
  306. pw.println("exception:" + exception.getMessage());
  307. pw.println("root-cause:"
  308. + ((null != cause) ? cause.getMessage() : "null"));
  309. }
  310. }
  311. /**
  312. * Print out the specific source line that caused the exception,
  313. * if possible to load it.
  314. *
  315. * @author shane_curcuru@us.ibm.com
  316. * @param pw PrintWriter to send output to
  317. * @param locator Xalan wrapper for either a JAXP or a SAX
  318. * source location object
  319. */
  320. public static void logSourceLine(PrintWriter pw, SourceLocator locator)
  321. {
  322. if (null == locator)
  323. return;
  324. if (null == pw)
  325. pw = new PrintWriter(System.err, true);
  326. String url = locator.getSystemId();
  327. // Bail immediately if we get SystemId-Unknown
  328. //@todo future improvement: attempt to get resource
  329. // from a publicId if possible
  330. if (null == url)
  331. {
  332. pw.println("line: (No systemId; cannot read file)");
  333. pw.println();
  334. return;
  335. }
  336. //@todo attempt to get DOM backpointer or other ids
  337. try
  338. {
  339. int line = locator.getLineNumber();
  340. int column = locator.getColumnNumber();
  341. pw.println("line: " + getSourceLine(url, line));
  342. StringBuffer buf = new StringBuffer("line: ");
  343. for (int i = 1; i < column; i++)
  344. {
  345. buf.append(' ');
  346. }
  347. buf.append('^');
  348. pw.println(buf.toString());
  349. }
  350. catch (Exception e)
  351. {
  352. pw.println("line: logSourceLine unavailable due to: " + e.getMessage());
  353. pw.println();
  354. }
  355. }
  356. /**
  357. * Return the specific source line that caused the exception,
  358. * if possible to load it; allow exceptions to be thrown.
  359. *
  360. * @author shane_curcuru@us.ibm.com
  361. */
  362. protected static String getSourceLine(String sourceUrl, int lineNum)
  363. throws Exception
  364. {
  365. URL url = null;
  366. // Get a URL from the sourceUrl
  367. try
  368. {
  369. // Try to get a URL from it as-is
  370. url = new URL(sourceUrl);
  371. }
  372. catch (java.net.MalformedURLException mue)
  373. {
  374. int indexOfColon = sourceUrl.indexOf(':');
  375. int indexOfSlash = sourceUrl.indexOf('/');
  376. if ((indexOfColon != -1)
  377. && (indexOfSlash != -1)
  378. && (indexOfColon < indexOfSlash))
  379. {
  380. // The url is already absolute, but we could not get
  381. // the system to form it, so bail
  382. throw mue;
  383. }
  384. else
  385. {
  386. // The url is relative, so attempt to get absolute
  387. url = new URL(SystemIDResolver.getAbsoluteURI(sourceUrl));
  388. // If this fails, allow the exception to propagate
  389. }
  390. }
  391. String line = null;
  392. InputStream is = null;
  393. BufferedReader br = null;
  394. try
  395. {
  396. // Open the URL and read to our specified line
  397. URLConnection uc = url.openConnection();
  398. is = uc.getInputStream();
  399. br = new BufferedReader(new InputStreamReader(is));
  400. // Not the most efficient way, but it works
  401. // (Feel free to patch to seek to the appropriate line)
  402. for (int i = 1; i <= lineNum; i++)
  403. {
  404. line = br.readLine();
  405. }
  406. }
  407. // Allow exceptions to propagate from here, but ensure
  408. // streams are closed!
  409. finally
  410. {
  411. br.close();
  412. is.close();
  413. }
  414. // Return whatever we found
  415. return line;
  416. }
  417. /* ======== Implement settable properties ======== */
  418. /**
  419. * User-settable behavior: when to re-throw exceptions.
  420. *
  421. * <p>This allows per-instance configuration of
  422. * ListingErrorHandlers. You can ask us to either throw
  423. * an exception when we're called for various warning /
  424. * error / fatalErrors, or simply log them and continue.</p>
  425. *
  426. * @param t if we should throw an exception on warnings
  427. */
  428. public void setThrowOnWarning(boolean b)
  429. {
  430. throwOnWarning = b;
  431. }
  432. /**
  433. * User-settable behavior: when to re-throw exceptions.
  434. *
  435. * @return if we throw an exception on warnings
  436. */
  437. public boolean getThrowOnWarning()
  438. {
  439. return throwOnWarning;
  440. }
  441. /** If we should throw exception on warnings; default:false. */
  442. protected boolean throwOnWarning = false;
  443. /**
  444. * User-settable behavior: when to re-throw exceptions.
  445. *
  446. * <p>This allows per-instance configuration of
  447. * ListingErrorHandlers. You can ask us to either throw
  448. * an exception when we're called for various warning /
  449. * error / fatalErrors, or simply log them and continue.</p>
  450. *
  451. * <p>Note that the behavior of many parsers/transformers
  452. * after an error is not necessarily defined!</p>
  453. *
  454. * @param t if we should throw an exception on errors
  455. */
  456. public void setThrowOnError(boolean b)
  457. {
  458. throwOnError = b;
  459. }
  460. /**
  461. * User-settable behavior: when to re-throw exceptions.
  462. *
  463. * @return if we throw an exception on errors
  464. */
  465. public boolean getThrowOnError()
  466. {
  467. return throwOnError;
  468. }
  469. /** If we should throw exception on errors; default:true. */
  470. protected boolean throwOnError = true;
  471. /**
  472. * User-settable behavior: when to re-throw exceptions.
  473. *
  474. * <p>This allows per-instance configuration of
  475. * ListingErrorHandlers. You can ask us to either throw
  476. * an exception when we're called for various warning /
  477. * error / fatalErrors, or simply log them and continue.</p>
  478. *
  479. * <p>Note that the behavior of many parsers/transformers
  480. * after a fatalError is not necessarily defined, most
  481. * products will probably barf if you continue.</p>
  482. *
  483. * @param t if we should throw an exception on fatalErrors
  484. */
  485. public void setThrowOnFatalError(boolean b)
  486. {
  487. throwOnFatalError = b;
  488. }
  489. /**
  490. * User-settable behavior: when to re-throw exceptions.
  491. *
  492. * @return if we throw an exception on fatalErrors
  493. */
  494. public boolean getThrowOnFatalError()
  495. {
  496. return throwOnFatalError;
  497. }
  498. /** If we should throw exception on fatalErrors; default:true. */
  499. protected boolean throwOnFatalError = true;
  500. }