001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: DefaultErrorHandler.java 524806 2007-04-02 15:51:39Z zongaro $
020     */
021    package org.apache.xml.utils;
022    
023    import java.io.PrintStream;
024    import java.io.PrintWriter;
025    
026    import javax.xml.transform.ErrorListener;
027    import javax.xml.transform.SourceLocator;
028    import javax.xml.transform.TransformerException;
029    
030    import org.apache.xml.res.XMLErrorResources;
031    import org.apache.xml.res.XMLMessages;
032    
033    import org.xml.sax.ErrorHandler;
034    import org.xml.sax.SAXException;
035    import org.xml.sax.SAXParseException;
036    
037    
038    /**
039     * Implement SAX error handler for default reporting.
040     * @xsl.usage general
041     */
042    public class DefaultErrorHandler implements ErrorHandler, ErrorListener
043    {
044      PrintWriter m_pw;
045    
046      /**
047       * if this flag is set to true, we will rethrow the exception on
048       * the error() and fatalError() methods. If it is false, the errors 
049       * are reported to System.err. 
050       */
051      boolean m_throwExceptionOnError = true;
052    
053      /**
054       * Constructor DefaultErrorHandler
055       */
056      public DefaultErrorHandler(PrintWriter pw)
057      {
058        m_pw = pw;
059      }
060      
061      /**
062       * Constructor DefaultErrorHandler
063       */
064      public DefaultErrorHandler(PrintStream pw)
065      {
066        m_pw = new PrintWriter(pw, true);
067      }
068      
069      /**
070       * Constructor DefaultErrorHandler
071       */
072      public DefaultErrorHandler()
073      {
074        this(true);
075      }
076    
077      /**
078       * Constructor DefaultErrorHandler
079       */
080      public DefaultErrorHandler(boolean throwExceptionOnError)
081      {
082        // Defer creation of a PrintWriter until it's actually needed
083        m_throwExceptionOnError = throwExceptionOnError;
084      }
085    
086      /**
087       * Retrieve <code>java.io.PrintWriter</code> to which errors are being
088       * directed.
089       * @return The <code>PrintWriter</code> installed via the constructor
090       *         or the default <code>PrintWriter</code>
091       */
092      public PrintWriter getErrorWriter() {
093        // Defer creating the java.io.PrintWriter until an error needs to be
094        // reported.
095        if (m_pw == null) {
096          m_pw = new PrintWriter(System.err, true);
097        }
098        return m_pw;
099      }
100    
101      /**
102       * Receive notification of a warning.
103       *
104       * <p>SAX parsers will use this method to report conditions that
105       * are not errors or fatal errors as defined by the XML 1.0
106       * recommendation.  The default behaviour is to take no action.</p>
107       *
108       * <p>The SAX parser must continue to provide normal parsing events
109       * after invoking this method: it should still be possible for the
110       * application to process the document through to the end.</p>
111       *
112       * @param exception The warning information encapsulated in a
113       *                  SAX parse exception.
114       * @throws SAXException Any SAX exception, possibly
115       *            wrapping another exception.
116       */
117      public void warning(SAXParseException exception) throws SAXException
118      {
119        PrintWriter pw = getErrorWriter();
120    
121        printLocation(pw, exception);
122        pw.println("Parser warning: " + exception.getMessage());
123      }
124    
125      /**
126       * Receive notification of a recoverable error.
127       *
128       * <p>This corresponds to the definition of "error" in section 1.2
129       * of the W3C XML 1.0 Recommendation.  For example, a validating
130       * parser would use this callback to report the violation of a
131       * validity constraint.  The default behaviour is to take no
132       * action.</p>
133       *
134       * <p>The SAX parser must continue to provide normal parsing events
135       * after invoking this method: it should still be possible for the
136       * application to process the document through to the end.  If the
137       * application cannot do so, then the parser should report a fatal
138       * error even if the XML 1.0 recommendation does not require it to
139       * do so.</p>
140       *
141       * @param exception The error information encapsulated in a
142       *                  SAX parse exception.
143       * @throws SAXException Any SAX exception, possibly
144       *            wrapping another exception.
145       */
146      public void error(SAXParseException exception) throws SAXException
147      {
148        //printLocation(exception);
149        // getErrorWriter().println(exception.getMessage());
150    
151        throw exception;
152      }
153    
154      /**
155       * Receive notification of a non-recoverable error.
156       *
157       * <p>This corresponds to the definition of "fatal error" in
158       * section 1.2 of the W3C XML 1.0 Recommendation.  For example, a
159       * parser would use this callback to report the violation of a
160       * well-formedness constraint.</p>
161       *
162       * <p>The application must assume that the document is unusable
163       * after the parser has invoked this method, and should continue
164       * (if at all) only for the sake of collecting addition error
165       * messages: in fact, SAX parsers are free to stop reporting any
166       * other events once this method has been invoked.</p>
167       *
168       * @param exception The error information encapsulated in a
169       *                  SAX parse exception.
170       * @throws SAXException Any SAX exception, possibly
171       *            wrapping another exception.
172       */
173      public void fatalError(SAXParseException exception) throws SAXException
174      {
175        // printLocation(exception);
176        // getErrorWriter().println(exception.getMessage());
177    
178        throw exception;
179      }
180      
181      /**
182       * Receive notification of a warning.
183       *
184       * <p>SAX parsers will use this method to report conditions that
185       * are not errors or fatal errors as defined by the XML 1.0
186       * recommendation.  The default behaviour is to take no action.</p>
187       *
188       * <p>The SAX parser must continue to provide normal parsing events
189       * after invoking this method: it should still be possible for the
190       * application to process the document through to the end.</p>
191       *
192       * @param exception The warning information encapsulated in a
193       *                  SAX parse exception.
194       * @throws javax.xml.transform.TransformerException Any SAX exception, possibly
195       *            wrapping another exception.
196       * @see javax.xml.transform.TransformerException
197       */
198      public void warning(TransformerException exception) throws TransformerException
199      {
200        PrintWriter pw = getErrorWriter();
201    
202        printLocation(pw, exception);
203        pw.println(exception.getMessage());
204      }
205    
206      /**
207       * Receive notification of a recoverable error.
208       *
209       * <p>This corresponds to the definition of "error" in section 1.2
210       * of the W3C XML 1.0 Recommendation.  For example, a validating
211       * parser would use this callback to report the violation of a
212       * validity constraint.  The default behaviour is to take no
213       * action.</p>
214       *
215       * <p>The SAX parser must continue to provide normal parsing events
216       * after invoking this method: it should still be possible for the
217       * application to process the document through to the end.  If the
218       * application cannot do so, then the parser should report a fatal
219       * error even if the XML 1.0 recommendation does not require it to
220       * do so.</p>
221       *
222       * @param exception The error information encapsulated in a
223       *                  SAX parse exception.
224       * @throws javax.xml.transform.TransformerException Any SAX exception, possibly
225       *            wrapping another exception.
226       * @see javax.xml.transform.TransformerException
227       */
228      public void error(TransformerException exception) throws TransformerException
229      {
230        // If the m_throwExceptionOnError flag is true, rethrow the exception.
231        // Otherwise report the error to System.err.
232        if (m_throwExceptionOnError)
233          throw exception;
234        else
235        {
236          PrintWriter pw = getErrorWriter();
237    
238          printLocation(pw, exception);
239          pw.println(exception.getMessage());
240        }
241      }
242    
243      /**
244       * Receive notification of a non-recoverable error.
245       *
246       * <p>This corresponds to the definition of "fatal error" in
247       * section 1.2 of the W3C XML 1.0 Recommendation.  For example, a
248       * parser would use this callback to report the violation of a
249       * well-formedness constraint.</p>
250       *
251       * <p>The application must assume that the document is unusable
252       * after the parser has invoked this method, and should continue
253       * (if at all) only for the sake of collecting addition error
254       * messages: in fact, SAX parsers are free to stop reporting any
255       * other events once this method has been invoked.</p>
256       *
257       * @param exception The error information encapsulated in a
258       *                  SAX parse exception.
259       * @throws javax.xml.transform.TransformerException Any SAX exception, possibly
260       *            wrapping another exception.
261       * @see javax.xml.transform.TransformerException
262       */
263      public void fatalError(TransformerException exception) throws TransformerException
264      {
265        // If the m_throwExceptionOnError flag is true, rethrow the exception.
266        // Otherwise report the error to System.err.
267        if (m_throwExceptionOnError)
268          throw exception;
269        else
270        {
271          PrintWriter pw = getErrorWriter();
272    
273          printLocation(pw, exception);
274          pw.println(exception.getMessage());
275        }
276      }
277      
278      public static void ensureLocationSet(TransformerException exception)
279      {
280        // SourceLocator locator = exception.getLocator();
281        SourceLocator locator = null;
282        Throwable cause = exception;
283        
284        // Try to find the locator closest to the cause.
285        do
286        {
287          if(cause instanceof SAXParseException)
288          {
289            locator = new SAXSourceLocator((SAXParseException)cause);
290          }
291          else if (cause instanceof TransformerException)
292          {
293            SourceLocator causeLocator = ((TransformerException)cause).getLocator();
294            if(null != causeLocator)
295              locator = causeLocator;
296          }
297          
298          if(cause instanceof TransformerException)
299            cause = ((TransformerException)cause).getCause();
300          else if(cause instanceof SAXException)
301            cause = ((SAXException)cause).getException();
302          else
303            cause = null;
304        }
305        while(null != cause);
306        
307        exception.setLocator(locator);
308      }
309      
310      public static void printLocation(PrintStream pw, TransformerException exception)
311      {
312        printLocation(new PrintWriter(pw), exception);
313      }
314      
315      public static void printLocation(java.io.PrintStream pw, org.xml.sax.SAXParseException exception)
316      {
317        printLocation(new PrintWriter(pw), exception);
318      }
319      
320      public static void printLocation(PrintWriter pw, Throwable exception)
321      {
322        SourceLocator locator = null;
323        Throwable cause = exception;
324        
325        // Try to find the locator closest to the cause.
326        do
327        {
328          if(cause instanceof SAXParseException)
329          {
330            locator = new SAXSourceLocator((SAXParseException)cause);
331          }
332          else if (cause instanceof TransformerException)
333          {
334            SourceLocator causeLocator = ((TransformerException)cause).getLocator();
335            if(null != causeLocator)
336              locator = causeLocator;
337          }
338          if(cause instanceof TransformerException)
339            cause = ((TransformerException)cause).getCause();
340          else if(cause instanceof WrappedRuntimeException)
341            cause = ((WrappedRuntimeException)cause).getException();
342          else if(cause instanceof SAXException)
343            cause = ((SAXException)cause).getException();
344          else
345            cause = null;
346        }
347        while(null != cause);
348            
349        if(null != locator)
350        {
351          // getErrorWriter().println("Parser fatal error: "+exception.getMessage());
352          String id = (null != locator.getPublicId() )
353                      ? locator.getPublicId()
354                        : (null != locator.getSystemId())
355                          ? locator.getSystemId() : XMLMessages.createXMLMessage(XMLErrorResources.ER_SYSTEMID_UNKNOWN, null); //"SystemId Unknown";
356    
357          pw.print(id + "; " +XMLMessages.createXMLMessage("line", null) + locator.getLineNumber()
358                             + "; " +XMLMessages.createXMLMessage("column", null) + locator.getColumnNumber()+"; ");
359        }
360        else
361          pw.print("("+XMLMessages.createXMLMessage(XMLErrorResources.ER_LOCATION_UNKNOWN, null)+")");
362      }
363    }