| /* |
| * Copyright 2005 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.util.location; |
| |
| import java.lang.ref.WeakReference; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.xml.transform.SourceLocator; |
| import javax.xml.transform.TransformerException; |
| |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * Location-related utility methods. |
| * |
| * @version $Id$ |
| * @since 2.1.8 |
| */ |
| public class LocationUtils { |
| |
| /** |
| * The string representation of an unknown location: "<code>[unknown location]</code>". |
| */ |
| public static final String UNKNOWN_STRING = "[unknown location]"; |
| |
| private static List finders = new ArrayList(); |
| |
| /** |
| * An finder or object locations |
| * |
| * @since 2.1.8 |
| */ |
| public interface LocationFinder { |
| /** |
| * Get the location of an object |
| * @param obj the object for which to find a location |
| * @param description and optional description to be added to the object's location |
| * @return the object's location or <code>null</code> if object's class isn't handled |
| * by this finder. |
| */ |
| Location getLocation(Object obj, String description); |
| } |
| |
| private LocationUtils() { |
| // Forbid instanciation |
| } |
| |
| /** |
| * Builds a string representation of a location, in the |
| * "<code><em>descripton</em> - <em>uri</em>:<em>line</em>:<em>column</em></code>" |
| * format (e.g. "<code>foo - file://path/to/file.xml:3:40</code>"). For {@link Location#UNKNOWN an unknown location}, returns |
| * {@link #UNKNOWN_STRING}. |
| * |
| * @return the string representation |
| */ |
| public static String toString(Location location) { |
| StringBuffer result = new StringBuffer(); |
| |
| String description = location.getDescription(); |
| if (description != null) { |
| result.append(description).append(" - "); |
| } |
| |
| String uri = location.getURI(); |
| if (uri != null) { |
| result.append(uri).append(':').append(location.getLineNumber()).append(':').append(location.getColumnNumber()); |
| } else { |
| result.append(UNKNOWN_STRING); |
| } |
| |
| return result.toString(); |
| } |
| |
| /** |
| * Parse a location string of the form "<code><em>uri</em>:<em>line</em>:<em>column</em></code>" (e.g. |
| * "<code>path/to/file.xml:3:40</code>") to a Location object. Additionally, a description may |
| * also optionally be present, separated with an hyphen (e.g. "<code>foo - path/to/file.xml:3.40</code>"). |
| * |
| * @param text the text to parse |
| * @return the location (possibly <code>null</code> if text was null or in an incorrect format) |
| */ |
| public static LocationImpl parse(String text) throws IllegalArgumentException { |
| if (text == null || text.length() == 0) { |
| return null; |
| } |
| |
| // Do we have a description? |
| String description; |
| int uriStart = text.lastIndexOf(" - "); // lastIndexOf to allow the separator to be in the description |
| if (uriStart > -1) { |
| description = text.substring(0, uriStart); |
| uriStart += 3; // strip " - " |
| } else { |
| description = null; |
| uriStart = 0; |
| } |
| |
| try { |
| int colSep = text.lastIndexOf(':'); |
| if (colSep > -1) { |
| int column = Integer.parseInt(text.substring(colSep + 1)); |
| |
| int lineSep = text.lastIndexOf(':', colSep - 1); |
| if (lineSep > -1) { |
| int line = Integer.parseInt(text.substring(lineSep + 1, colSep)); |
| return new LocationImpl(description, text.substring(uriStart, lineSep), line, column); |
| } |
| } else { |
| // unkonwn? |
| if (text.endsWith(UNKNOWN_STRING)) { |
| return LocationImpl.UNKNOWN; |
| } |
| } |
| } catch(Exception e) { |
| // Ignore: handled below |
| } |
| |
| return LocationImpl.UNKNOWN; |
| } |
| |
| /** |
| * Checks if a location is known, i.e. it is not null nor equal to {@link Location#UNKNOWN}. |
| * |
| * @param location the location to check |
| * @return <code>true</code> if the location is known |
| */ |
| public static boolean isKnown(Location location) { |
| return location != null && !Location.UNKNOWN.equals(location); |
| } |
| |
| /** |
| * Checks if a location is unknown, i.e. it is either null or equal to {@link Location#UNKNOWN}. |
| * |
| * @param location the location to check |
| * @return <code>true</code> if the location is unknown |
| */ |
| public static boolean isUnknown(Location location) { |
| return location == null || Location.UNKNOWN.equals(location); |
| } |
| |
| /** |
| * Add a {@link LocationFinder} to the list of finders that will be queried for an object's |
| * location by {@link #getLocation(Object, String)}. |
| * <p> |
| * <b>Important:</b> LocationUtils internally stores a weak reference to the finder. This |
| * avoids creating strong links between the classloader holding this class and the finder's |
| * classloader, which can cause some weird memory leaks if the finder's classloader is to |
| * be reloaded. Therefore, you <em>have</em> to keep a strong reference to the finder in the |
| * calling code, e.g.: |
| * <pre> |
| * private static LocationUtils.LocationFinder myFinder = |
| * new LocationUtils.LocationFinder() { |
| * public Location getLocation(Object obj, String desc) { |
| * ... |
| * } |
| * }; |
| * |
| * static { |
| * LocationUtils.addFinder(myFinder); |
| * } |
| * </pre> |
| * |
| * @param finder the location finder to add |
| */ |
| public static void addFinder(LocationFinder finder) { |
| if (finder == null) { |
| return; |
| } |
| |
| synchronized(LocationFinder.class) { |
| // Update a clone of the current finder list to avoid breaking |
| // any iteration occuring in another thread. |
| List newFinders = new ArrayList(finders); |
| newFinders.add(new WeakReference(finder)); |
| finders = newFinders; |
| } |
| } |
| |
| /** |
| * Get the location of an object. Some well-known located classes built in the JDK are handled |
| * by this method. Handling of other located classes can be handled by adding new location finders. |
| * |
| * @param obj the object of which to get the location |
| * @return the object's location, or {@link Location#UNKNOWN} if no location could be found |
| */ |
| public static Location getLocation(Object obj) { |
| return getLocation(obj, null); |
| } |
| |
| /** |
| * Get the location of an object. Some well-known located classes built in the JDK are handled |
| * by this method. Handling of other located classes can be handled by adding new location finders. |
| * |
| * @param obj the object of which to get the location |
| * @param description an optional description of the object's location, used if a Location object |
| * has to be created. |
| * @return the object's location, or {@link Location#UNKNOWN} if no location could be found |
| */ |
| public static Location getLocation(Object obj, String description) { |
| if (obj instanceof Locatable) { |
| return ((Locatable)obj).getLocation(); |
| } |
| |
| // Check some well-known locatable exceptions |
| if (obj instanceof SAXParseException) { |
| SAXParseException spe = (SAXParseException)obj; |
| if (spe.getSystemId() != null) { |
| return new LocationImpl(description, spe.getSystemId(), spe.getLineNumber(), spe.getColumnNumber()); |
| } else { |
| return Location.UNKNOWN; |
| } |
| } |
| |
| if (obj instanceof TransformerException) { |
| TransformerException ex = (TransformerException)obj; |
| SourceLocator locator = ex.getLocator(); |
| if (locator != null && locator.getSystemId() != null) { |
| return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber()); |
| } else { |
| return Location.UNKNOWN; |
| } |
| } |
| |
| if (obj instanceof Locator) { |
| Locator locator = (Locator)obj; |
| if (locator.getSystemId() != null) { |
| return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber()); |
| } else { |
| return Location.UNKNOWN; |
| } |
| } |
| |
| List currentFinders = finders; // Keep the current list |
| int size = currentFinders.size(); |
| for (int i = 0; i < size; i++) { |
| WeakReference ref = (WeakReference)currentFinders.get(i); |
| LocationFinder finder = (LocationFinder)ref.get(); |
| if (finder == null) { |
| // This finder was garbage collected: update finders |
| synchronized(LocationFinder.class) { |
| // Update a clone of the current list to avoid breaking current iterations |
| List newFinders = new ArrayList(finders); |
| newFinders.remove(ref); |
| finders = newFinders; |
| } |
| } |
| |
| Location result = finder.getLocation(obj, description); |
| if (result != null) { |
| return result; |
| } |
| } |
| |
| return Location.UNKNOWN; |
| } |
| } |