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