View Javadoc

1   /*
2    * Copyright 2005 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  package org.apache.struts.flow.core.location;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  
22  import org.apache.commons.lang.exception.ExceptionUtils;
23  import org.apache.commons.lang.exception.NestableException;
24  
25  /***
26   * A cascading and located <code>Exception</code>. It is also {@link MultiLocatable} to easily build
27   * stack traces.
28   *
29   * @since 2.1.8
30   * @version $Id: LocatedException.java 291771 2005-09-26 22:32:55Z vgritsenko $
31   */
32  public class LocatedException extends NestableException
33                                implements LocatableException, MultiLocatable {
34  
35      private List locations;
36  
37      public LocatedException(String message) {
38          this(message, null, null);
39      }
40  
41      public LocatedException(String message, Throwable cause) {
42          this(message, cause, null);
43      }
44  
45      public LocatedException(String message, Location location) {
46          this(message, null, location);
47      }
48  
49      public LocatedException(String message, Throwable cause, Location location) {
50          super(message, cause);
51          ensureCauseChainIsSet(cause);
52          addCauseLocations(this, cause);
53          addLocation(location);
54      }
55  
56      /***
57       * Crawl the cause chain and ensure causes are properly set using {@ link Throwable#initCause}.
58       * This is needed because some exceptions (e.g. SAXException) don't have a {@ link Throwable#getCause}
59       * that is used to print stacktraces.
60       */
61      public static void ensureCauseChainIsSet(Throwable thr) {
62          // Loop either until null or encountering exceptions that use this method.
63          while (thr != null && !(thr instanceof LocatedRuntimeException) && !(thr instanceof LocatedException)) {
64              Throwable parent = ExceptionUtils.getCause(thr);
65              if (thr.getCause() == null && parent != null) {
66                  thr.initCause(parent);
67              }
68              thr = parent;
69          }
70      }
71  
72      /***
73       * Add to the location stack all locations of an exception chain. This allows to have all possible
74       * location information in the stacktrace, as some exceptions like SAXParseException don't output
75       * their location in printStackTrace().
76       * <p>
77       * Traversal of the call chain stops at the first <code>Locatable</code> exception which is supposed
78       * to handle the loction of its causes by itself.
79       * <p>
80       * This method is static as a convenience for {@link LocatedRuntimeException other implementations}
81       * of locatable exceptions.
82       *
83       * @param self the current locatable exception
84       * @param cause a cause of <code>self</code>
85       */
86      public static void addCauseLocations(MultiLocatable self, Throwable cause) {
87          if (cause == null || cause instanceof Locatable) {
88              // Locatable handles its location itself
89              return;
90          }
91          // Add parent location first
92          addCauseLocations(self, ExceptionUtils.getCause(cause));
93          // then ourselve's
94          Location loc = LocationUtils.getLocation(cause);
95          if (LocationUtils.isKnown(loc)) {
96              // Get the exception's short name
97              String name = cause.getClass().getName();
98              int pos = name.lastIndexOf('.');
99              if (pos != -1) {
100                 name = name.substring(pos+1);
101             }
102             loc = new LocationImpl("[" + name + "]", loc.getURI(), loc.getLineNumber(), loc.getColumnNumber());
103             self.addLocation(loc);
104         }
105     }
106 
107     public Location getLocation() {
108         return locations == null ? null : (Location)locations.get(0);
109     }
110 
111     public List getLocations() {
112         return locations == null ? Collections.EMPTY_LIST : locations;
113     }
114 
115     public String getRawMessage() {
116         return super.getMessage();
117     }
118 
119     /***
120      * Standard way of building the message of a {@link LocatableException}, as a Java-like
121      * stack trace of locations.
122      *
123      * @param message the exception's message, given by <code>super.getMessage()</code> (can be null)
124      * @param locations the location list (can be null)
125      *
126      * @return the message, or <code>null</code> no message and locations were given.
127      */
128     public static String getMessage(String message, List locations) {
129         if (locations == null || locations.isEmpty()) {
130             return message;
131         }
132 
133         // Produce a Java-like stacktrace with locations
134         StringBuffer buf = message == null ? new StringBuffer() : new StringBuffer(message);
135         for (int i = 0; i < locations.size(); i++) {
136             buf.append("\n\tat ").append(LocationUtils.toString((Location)locations.get(i)));
137         }
138         return buf.toString();
139     }
140 
141     public String getMessage() {
142         return getMessage(super.getMessage(), locations);
143     }
144 
145     public void addLocation(Location loc) {
146         if (LocationUtils.isUnknown(loc))
147             return;
148 
149         if (locations == null) {
150             this.locations = new ArrayList(1); // Start small
151         }
152         locations.add(LocationImpl.get(loc));
153     }
154 }