1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.struts.flow.core.location;
17
18 import java.lang.ref.WeakReference;
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import javax.xml.transform.SourceLocator;
23 import javax.xml.transform.TransformerException;
24
25 import org.xml.sax.Locator;
26 import org.xml.sax.SAXParseException;
27
28 /***
29 * Location-related utility methods.
30 *
31 * @version $Id: LocationUtils.java 280946 2005-09-14 21:43:05Z sylvain $
32 * @since 2.1.8
33 */
34 public class LocationUtils {
35
36 /***
37 * The string representation of an unknown location: "<code>[unknown location]</code>".
38 */
39 public static final String UNKNOWN_STRING = "[unknown location]";
40
41 private static List finders = new ArrayList();
42
43 /***
44 * An finder or object locations
45 *
46 * @since 2.1.8
47 */
48 public interface LocationFinder {
49 /***
50 * Get the location of an object
51 * @param obj the object for which to find a location
52 * @param description and optional description to be added to the object's location
53 * @return the object's location or <code>null</code> if object's class isn't handled
54 * by this finder.
55 */
56 Location getLocation(Object obj, String description);
57 }
58
59 private LocationUtils() {
60
61 }
62
63 /***
64 * Builds a string representation of a location, in the
65 * "<code><em>descripton</em> - <em>uri</em>:<em>line</em>:<em>column</em></code>"
66 * format (e.g. "<code>foo - file://path/to/file.xml:3:40</code>"). For {@link Location#UNKNOWN an unknown location}, returns
67 * {@link #UNKNOWN_STRING}.
68 *
69 * @return the string representation
70 */
71 public static String toString(Location location) {
72 StringBuffer result = new StringBuffer();
73
74 String description = location.getDescription();
75 if (description != null) {
76 result.append(description).append(" - ");
77 }
78
79 String uri = location.getURI();
80 if (uri != null) {
81 result.append(uri).append(':').append(location.getLineNumber()).append(':').append(location.getColumnNumber());
82 } else {
83 result.append(UNKNOWN_STRING);
84 }
85
86 return result.toString();
87 }
88
89 /***
90 * Parse a location string of the form "<code><em>uri</em>:<em>line</em>:<em>column</em></code>" (e.g.
91 * "<code>path/to/file.xml:3:40</code>") to a Location object. Additionally, a description may
92 * also optionally be present, separated with an hyphen (e.g. "<code>foo - path/to/file.xml:3.40</code>").
93 *
94 * @param text the text to parse
95 * @return the location (possibly <code>null</code> if text was null or in an incorrect format)
96 */
97 public static LocationImpl parse(String text) throws IllegalArgumentException {
98 if (text == null || text.length() == 0) {
99 return null;
100 }
101
102
103 String description;
104 int uriStart = text.lastIndexOf(" - ");
105 if (uriStart > -1) {
106 description = text.substring(0, uriStart);
107 uriStart += 3;
108 } else {
109 description = null;
110 uriStart = 0;
111 }
112
113 try {
114 int colSep = text.lastIndexOf(':');
115 if (colSep > -1) {
116 int column = Integer.parseInt(text.substring(colSep + 1));
117
118 int lineSep = text.lastIndexOf(':', colSep - 1);
119 if (lineSep > -1) {
120 int line = Integer.parseInt(text.substring(lineSep + 1, colSep));
121 return new LocationImpl(description, text.substring(uriStart, lineSep), line, column);
122 }
123 } else {
124
125 if (text.endsWith(UNKNOWN_STRING)) {
126 return LocationImpl.UNKNOWN;
127 }
128 }
129 } catch(Exception e) {
130
131 }
132
133 return LocationImpl.UNKNOWN;
134 }
135
136 /***
137 * Checks if a location is known, i.e. it is not null nor equal to {@link Location#UNKNOWN}.
138 *
139 * @param location the location to check
140 * @return <code>true</code> if the location is known
141 */
142 public static boolean isKnown(Location location) {
143 return location != null && !Location.UNKNOWN.equals(location);
144 }
145
146 /***
147 * Checks if a location is unknown, i.e. it is either null or equal to {@link Location#UNKNOWN}.
148 *
149 * @param location the location to check
150 * @return <code>true</code> if the location is unknown
151 */
152 public static boolean isUnknown(Location location) {
153 return location == null || Location.UNKNOWN.equals(location);
154 }
155
156 /***
157 * Add a {@link LocationFinder} to the list of finders that will be queried for an object's
158 * location by {@link #getLocation(Object, String)}.
159 * <p>
160 * <b>Important:</b> LocationUtils internally stores a weak reference to the finder. This
161 * avoids creating strong links between the classloader holding this class and the finder's
162 * classloader, which can cause some weird memory leaks if the finder's classloader is to
163 * be reloaded. Therefore, you <em>have</em> to keep a strong reference to the finder in the
164 * calling code, e.g.:
165 * <pre>
166 * private static LocationUtils.LocationFinder myFinder =
167 * new LocationUtils.LocationFinder() {
168 * public Location getLocation(Object obj, String desc) {
169 * ...
170 * }
171 * };
172 *
173 * static {
174 * LocationUtils.addFinder(myFinder);
175 * }
176 * </pre>
177 *
178 * @param finder the location finder to add
179 */
180 public static void addFinder(LocationFinder finder) {
181 if (finder == null) {
182 return;
183 }
184
185 synchronized(LocationFinder.class) {
186
187
188 List newFinders = new ArrayList(finders);
189 newFinders.add(new WeakReference(finder));
190 finders = newFinders;
191 }
192 }
193
194 /***
195 * Get the location of an object. Some well-known located classes built in the JDK are handled
196 * by this method. Handling of other located classes can be handled by adding new location finders.
197 *
198 * @param obj the object of which to get the location
199 * @return the object's location, or {@link Location#UNKNOWN} if no location could be found
200 */
201 public static Location getLocation(Object obj) {
202 return getLocation(obj, null);
203 }
204
205 /***
206 * Get the location of an object. Some well-known located classes built in the JDK are handled
207 * by this method. Handling of other located classes can be handled by adding new location finders.
208 *
209 * @param obj the object of which to get the location
210 * @param description an optional description of the object's location, used if a Location object
211 * has to be created.
212 * @return the object's location, or {@link Location#UNKNOWN} if no location could be found
213 */
214 public static Location getLocation(Object obj, String description) {
215 if (obj instanceof Locatable) {
216 return ((Locatable)obj).getLocation();
217 }
218
219
220 if (obj instanceof SAXParseException) {
221 SAXParseException spe = (SAXParseException)obj;
222 if (spe.getSystemId() != null) {
223 return new LocationImpl(description, spe.getSystemId(), spe.getLineNumber(), spe.getColumnNumber());
224 } else {
225 return Location.UNKNOWN;
226 }
227 }
228
229 if (obj instanceof TransformerException) {
230 TransformerException ex = (TransformerException)obj;
231 SourceLocator locator = ex.getLocator();
232 if (locator != null && locator.getSystemId() != null) {
233 return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber());
234 } else {
235 return Location.UNKNOWN;
236 }
237 }
238
239 if (obj instanceof Locator) {
240 Locator locator = (Locator)obj;
241 if (locator.getSystemId() != null) {
242 return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber());
243 } else {
244 return Location.UNKNOWN;
245 }
246 }
247
248 List currentFinders = finders;
249 int size = currentFinders.size();
250 for (int i = 0; i < size; i++) {
251 WeakReference ref = (WeakReference)currentFinders.get(i);
252 LocationFinder finder = (LocationFinder)ref.get();
253 if (finder == null) {
254
255 synchronized(LocationFinder.class) {
256
257 List newFinders = new ArrayList(finders);
258 newFinders.remove(ref);
259 finders = newFinders;
260 }
261 }
262
263 Location result = finder.getLocation(obj, description);
264 if (result != null) {
265 return result;
266 }
267 }
268
269 return Location.UNKNOWN;
270 }
271 }