/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.referencing.wkt; import java.util.prefs.Preferences; import org.opengis.metadata.citation.Citation; import org.opengis.parameter.GeneralParameterValue; import org.geotools.metadata.iso.citation.Citations; import org.geotools.resources.Classes; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * Base class for all object formattable as * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A>. * * @since 2.0 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * * @see <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">Well Know Text specification</A> * @see <A HREF="http://gdal.velocet.ca/~warmerda/wktproblems.html">OGC WKT Coordinate System Issues</A> */ public class Formattable { /** * The "Indentation" preference name. * * @todo this string is also hard-coded in AffineTransform2D, because we * don't want to make it public (neither {@link #getIndentation}). */ static final String INDENTATION = "Indentation"; /** * The formatter for the {@link #toWKT()} method. */ private static final ThreadLocal<Formatter> FORMATTER = new ThreadLocal<Formatter>(); /** * The indentation value to give to {@link #toWKT(int)} method for formatting the complete * object on a single line. * * @since 2.6 */ public static final int SINGLE_LINE = 0; /** * Default constructor. */ protected Formattable() { } /** * Returns a string representation for this object. The default implementation returns * the same string similar than {@link #toWKT()}, except that no exception is thrown if * the string contains non-standard keywords. For example the * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">WKT * specification</A> do not defines any keyword for * {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate system} objects. If this * object is an instance of {@link org.geotools.referencing.cs.DefaultCartesianCS}, then the * WKT will be formatted as <code>"CartesianCS[AXIS["</code>...<code>"], AXIS["</code>...<code>"], * </code><i>etc.</i><code>]"</code>. */ @Override public String toString() { return toWKT(Citations.OGC, getIndentation(), false); } /** * Returns a * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A> using a default indentation. The default indentation is read from * {@linkplain Preferences user preferences}. * * @return The Well Know Text for this object. * @throws UnformattableObjectException If this object can't be formatted as WKT. * A formatting may fails because an object is too complex for the WKT format capability * (for example an {@linkplain org.geotools.referencing.crs.DefaultEngineeringCRS * engineering CRS} with different unit for each axis), or because only some specific * implementations can be formatted as WKT. */ public String toWKT() throws UnformattableObjectException { return toWKT(getIndentation()); } /** * Returns a * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A> for this object using the specified indentation. * * @param indentation The amount of spaces to use in indentation for WKT formatting, * or {@link #SINGLE_LINE} for formatting the whole WKT on a single line. * @return The Well Know Text for this object. * @throws UnformattableObjectException If this object can't be formatted as WKT. * A formatting may fails because an object is too complex for the WKT format capability * (for example an {@linkplain org.geotools.referencing.crs.DefaultEngineeringCRS * engineering CRS} with different unit for each axis), or because only some specific * implementations can be formatted as WKT. */ public String toWKT(final int indentation) throws UnformattableObjectException { return toWKT(Citations.OGC, indentation); } /** * Returns a * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A> for this object using the specified indentation and authority. * * @param authority The authority to prefer when choosing WKT entities names. * @param indentation The amount of spaces to use in indentation for WKT formatting, * or {@link #SINGLE_LINE} for formatting the whole WKT on a single line. * @return The Well Know Text for this object. * @throws UnformattableObjectException If this object can't be formatted as WKT. * A formatting may fails because an object is too complex for the WKT format capability * (for example an {@linkplain org.geotools.referencing.crs.DefaultEngineeringCRS * engineering CRS} with different unit for each axis), or because only some specific * implementations can be formatted as WKT. */ public String toWKT(final Citation authority, final int indentation) throws UnformattableObjectException { return toWKT(authority, indentation, true); } /** * Returns a WKT for this object using the specified indentation and authority. * If {@code strict} is true, then an exception is thrown if the WKT contains * invalid keywords. */ private String toWKT(final Citation authority, final int indentation, final boolean strict) throws UnformattableObjectException { if (authority == null) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NULL_ARGUMENT_$1, "authority")); } Formatter formatter = FORMATTER.get(); if (formatter == null || formatter.indentation != indentation || formatter.getAuthority() != authority) { formatter = new Formatter(Symbols.DEFAULT, indentation); formatter.setAuthority(authority); FORMATTER.set(formatter); } try { if (this instanceof GeneralParameterValue) { // Special processing for parameter values, which is formatted // directly in 'Formatter'. Note that in GeoAPI, this interface // doesn't share the same parent interface than other interfaces. formatter.append((GeneralParameterValue) this); } else { formatter.append(this); } if (strict && formatter.isInvalidWKT()) { final Class unformattable = formatter.getUnformattableClass(); throw new UnformattableObjectException(formatter.warning, unformattable); } return formatter.toString(); } finally { formatter.clear(); } } /** * Format the inner part of a * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well * Known Text</cite> (WKT)</A> element. This method is automatically invoked by * {@link Formatter#append(Formattable)}. Element name and authority code must not be * formatted here. For example for a {@code GEOGCS} element * ({@link org.geotools.referencing.crs.DefaultGeographicCRS}), the formatter will invokes * this method for completing the WKT at the insertion point show below: * * <pre> *   GEOGCS["WGS 84", AUTHORITY["EPSG","4326"]] *   | *   (insertion point) * </pre> * * The default implementation declares that this object produces an invalid WKT. * Subclasses must override this method for proper WKT formatting and should * <strong>not</strong> invoke {@code super.formatWKT(formatter)} if they can * use a valid WKT syntax. * * @param formatter The formatter to use. * @return The name of the WKT element type (e.g. {@code "GEOGCS"}). * * @see #toWKT * @see #toString */ protected String formatWKT(final Formatter formatter) { Class type = getClass(); formatter.setInvalidWKT(type); Class[] interfaces = type.getInterfaces(); for (int i=0; i<interfaces.length; i++) { final Class candidate = interfaces[i]; if (candidate.getName().startsWith("org.opengis.referencing.")) { type = candidate; break; } } return Classes.getShortName(type); } /** * Returns the default indentation. */ static int getIndentation() { try { return Preferences.userNodeForPackage(Formattable.class).getInt(INDENTATION, 2); } catch (SecurityException ignore) { // Ignore. Will fallback on the default indentation. return 2; } } /** * Set the default value for indentation. * * @throws SecurityException if a security manager is present and * it denies <code>RuntimePermission("preferences")</code>. */ static void setIndentation(final int indentation) throws SecurityException { Preferences.userNodeForPackage(Formattable.class).putInt(INDENTATION, indentation); } /** * Cleans up the thread local set in this thread. They can prevent web applications from * proper shutdown */ public static void cleanupThreadLocals() { FORMATTER.remove(); } }