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: DTMException.java 468653 2006-10-28 07:07:05Z minchau $
020     */
021    package org.apache.xml.dtm;
022    
023    
024    import java.lang.reflect.InvocationTargetException;
025    import java.lang.reflect.Method;
026    
027    import javax.xml.transform.SourceLocator;
028    
029    import org.apache.xml.res.XMLErrorResources;
030    import org.apache.xml.res.XMLMessages;
031    
032    
033    /**
034     * This class specifies an exceptional condition that occured
035     * in the DTM module.
036     */
037    public class DTMException extends RuntimeException {
038        static final long serialVersionUID = -775576419181334734L;
039    
040        /** Field locator specifies where the error occured.
041         *  @serial */
042        SourceLocator locator;
043    
044        /**
045         * Method getLocator retrieves an instance of a SourceLocator
046         * object that specifies where an error occured.
047         *
048         * @return A SourceLocator object, or null if none was specified.
049         */
050        public SourceLocator getLocator() {
051            return locator;
052        }
053    
054        /**
055         * Method setLocator sets an instance of a SourceLocator
056         * object that specifies where an error occured.
057         *
058         * @param location A SourceLocator object, or null to clear the location.
059         */
060        public void setLocator(SourceLocator location) {
061            locator = location;
062        }
063    
064        /** Field containedException specifies a wrapped exception.  May be null.
065         *  @serial */
066        Throwable containedException;
067    
068        /**
069         * This method retrieves an exception that this exception wraps.
070         *
071         * @return An Throwable object, or null.
072         * @see #getCause
073         */
074        public Throwable getException() {
075            return containedException;
076        }
077    
078        /**
079         * Returns the cause of this throwable or <code>null</code> if the
080         * cause is nonexistent or unknown.  (The cause is the throwable that
081         * caused this throwable to get thrown.)
082         */
083        public Throwable getCause() {
084    
085            return ((containedException == this)
086                    ? null
087                    : containedException);
088        }
089    
090        /**
091         * Initializes the <i>cause</i> of this throwable to the specified value.
092         * (The cause is the throwable that caused this throwable to get thrown.)
093         *
094         * <p>This method can be called at most once.  It is generally called from
095         * within the constructor, or immediately after creating the
096         * throwable.  If this throwable was created
097         * with {@link #DTMException(Throwable)} or
098         * {@link #DTMException(String,Throwable)}, this method cannot be called
099         * even once.
100         *
101         * @param  cause the cause (which is saved for later retrieval by the
102         *         {@link #getCause()} method).  (A <tt>null</tt> value is
103         *         permitted, and indicates that the cause is nonexistent or
104         *         unknown.)
105         * @return  a reference to this <code>Throwable</code> instance.
106         * @throws IllegalArgumentException if <code>cause</code> is this
107         *         throwable.  (A throwable cannot
108         *         be its own cause.)
109         * @throws IllegalStateException if this throwable was
110         *         created with {@link #DTMException(Throwable)} or
111         *         {@link #DTMException(String,Throwable)}, or this method has already
112         *         been called on this throwable.
113         */
114        public synchronized Throwable initCause(Throwable cause) {
115    
116            if ((this.containedException == null) && (cause != null)) {
117                throw new IllegalStateException(XMLMessages.createXMLMessage(XMLErrorResources.ER_CANNOT_OVERWRITE_CAUSE, null)); //"Can't overwrite cause");
118            }
119    
120            if (cause == this) {
121                throw new IllegalArgumentException(
122                    XMLMessages.createXMLMessage(XMLErrorResources.ER_SELF_CAUSATION_NOT_PERMITTED, null)); //"Self-causation not permitted");
123            }
124    
125            this.containedException = cause;
126    
127            return this;
128        }
129    
130        /**
131         * Create a new DTMException.
132         *
133         * @param message The error or warning message.
134         */
135        public DTMException(String message) {
136    
137            super(message);
138    
139            this.containedException = null;
140            this.locator            = null;
141        }
142    
143        /**
144         * Create a new DTMException wrapping an existing exception.
145         *
146         * @param e The exception to be wrapped.
147         */
148        public DTMException(Throwable e) {
149    
150            super(e.getMessage());
151    
152            this.containedException = e;
153            this.locator            = null;
154        }
155    
156        /**
157         * Wrap an existing exception in a DTMException.
158         *
159         * <p>This is used for throwing processor exceptions before
160         * the processing has started.</p>
161         *
162         * @param message The error or warning message, or null to
163         *                use the message from the embedded exception.
164         * @param e Any exception
165         */
166        public DTMException(String message, Throwable e) {
167    
168            super(((message == null) || (message.length() == 0))
169                  ? e.getMessage()
170                  : message);
171    
172            this.containedException = e;
173            this.locator            = null;
174        }
175    
176        /**
177         * Create a new DTMException from a message and a Locator.
178         *
179         * <p>This constructor is especially useful when an application is
180         * creating its own exception from within a DocumentHandler
181         * callback.</p>
182         *
183         * @param message The error or warning message.
184         * @param locator The locator object for the error or warning.
185         */
186        public DTMException(String message, SourceLocator locator) {
187    
188            super(message);
189    
190            this.containedException = null;
191            this.locator            = locator;
192        }
193    
194        /**
195         * Wrap an existing exception in a DTMException.
196         *
197         * @param message The error or warning message, or null to
198         *                use the message from the embedded exception.
199         * @param locator The locator object for the error or warning.
200         * @param e Any exception
201         */
202        public DTMException(String message, SourceLocator locator,
203                                    Throwable e) {
204    
205            super(message);
206    
207            this.containedException = e;
208            this.locator            = locator;
209        }
210    
211        /**
212         * Get the error message with location information
213         * appended.
214         */
215        public String getMessageAndLocation() {
216    
217            StringBuffer sbuffer = new StringBuffer();
218            String       message = super.getMessage();
219    
220            if (null != message) {
221                sbuffer.append(message);
222            }
223    
224            if (null != locator) {
225                String systemID = locator.getSystemId();
226                int    line     = locator.getLineNumber();
227                int    column   = locator.getColumnNumber();
228    
229                if (null != systemID) {
230                    sbuffer.append("; SystemID: ");
231                    sbuffer.append(systemID);
232                }
233    
234                if (0 != line) {
235                    sbuffer.append("; Line#: ");
236                    sbuffer.append(line);
237                }
238    
239                if (0 != column) {
240                    sbuffer.append("; Column#: ");
241                    sbuffer.append(column);
242                }
243            }
244    
245            return sbuffer.toString();
246        }
247    
248        /**
249         * Get the location information as a string.
250         *
251         * @return A string with location info, or null
252         * if there is no location information.
253         */
254        public String getLocationAsString() {
255    
256            if (null != locator) {
257                StringBuffer sbuffer  = new StringBuffer();
258                String       systemID = locator.getSystemId();
259                int          line     = locator.getLineNumber();
260                int          column   = locator.getColumnNumber();
261    
262                if (null != systemID) {
263                    sbuffer.append("; SystemID: ");
264                    sbuffer.append(systemID);
265                }
266    
267                if (0 != line) {
268                    sbuffer.append("; Line#: ");
269                    sbuffer.append(line);
270                }
271    
272                if (0 != column) {
273                    sbuffer.append("; Column#: ");
274                    sbuffer.append(column);
275                }
276    
277                return sbuffer.toString();
278            } else {
279                return null;
280            }
281        }
282    
283        /**
284         * Print the the trace of methods from where the error
285         * originated.  This will trace all nested exception
286         * objects, as well as this object.
287         */
288        public void printStackTrace() {
289            printStackTrace(new java.io.PrintWriter(System.err, true));
290        }
291    
292        /**
293         * Print the the trace of methods from where the error
294         * originated.  This will trace all nested exception
295         * objects, as well as this object.
296         * @param s The stream where the dump will be sent to.
297         */
298        public void printStackTrace(java.io.PrintStream s) {
299            printStackTrace(new java.io.PrintWriter(s));
300        }
301    
302        /**
303         * Print the the trace of methods from where the error
304         * originated.  This will trace all nested exception
305         * objects, as well as this object.
306         * @param s The writer where the dump will be sent to.
307         */
308        public void printStackTrace(java.io.PrintWriter s) {
309    
310            if (s == null) {
311                s = new java.io.PrintWriter(System.err, true);
312            }
313    
314            try {
315                String locInfo = getLocationAsString();
316    
317                if (null != locInfo) {
318                    s.println(locInfo);
319                }
320    
321                super.printStackTrace(s);
322            } catch (Throwable e) {}
323    
324            boolean isJdk14OrHigher = false;
325            try {
326                Throwable.class.getMethod("getCause",null);
327                isJdk14OrHigher = true;
328            } catch (NoSuchMethodException nsme) {
329                // do nothing
330            }        
331    
332            // The printStackTrace method of the Throwable class in jdk 1.4 
333            // and higher will include the cause when printing the backtrace.
334            // The following code is only required when using jdk 1.3 or lower                
335            if (!isJdk14OrHigher) {
336                Throwable exception = getException();
337        
338                for (int i = 0; (i < 10) && (null != exception); i++) {
339                    s.println("---------");
340        
341                    try {
342                        if (exception instanceof DTMException) {
343                            String locInfo =
344                                ((DTMException) exception)
345                                    .getLocationAsString();
346        
347                            if (null != locInfo) {
348                                s.println(locInfo);
349                            }
350                        }
351        
352                        exception.printStackTrace(s);
353                    } catch (Throwable e) {
354                        s.println("Could not print stack trace...");
355                    }
356        
357                    try {
358                        Method meth =
359                            ((Object) exception).getClass().getMethod("getException",
360                                null);
361        
362                        if (null != meth) {
363                            Throwable prev = exception;
364        
365                            exception = (Throwable) meth.invoke(exception, null);
366        
367                            if (prev == exception) {
368                                break;
369                            }
370                        } else {
371                            exception = null;
372                        }
373                    } catch (InvocationTargetException ite) {
374                        exception = null;
375                    } catch (IllegalAccessException iae) {
376                        exception = null;
377                    } catch (NoSuchMethodException nsme) {
378                        exception = null;
379                    }
380                }
381            }
382        }
383    }