001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2001, Institut de Recherche pour le Développement
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * This package contains documentation from OpenGIS specifications.
019: * OpenGIS consortium's work is fully acknowledged here.
020: */
021: package org.geotools.referencing.wkt;
022:
023: // J2SE dependencies
024: import java.util.Locale;
025: import java.util.prefs.Preferences;
026:
027: // OpenGIS dependencies
028: import org.opengis.metadata.citation.Citation;
029: import org.opengis.parameter.GeneralParameterValue;
030: import org.opengis.referencing.cs.CoordinateSystem;
031:
032: // Geotools dependencies
033: import org.geotools.metadata.iso.citation.Citations;
034: import org.geotools.resources.Utilities;
035: import org.geotools.resources.i18n.Errors;
036: import org.geotools.resources.i18n.ErrorKeys;
037:
038: /**
039: * Base class for all object formattable as
040: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
041: * Known Text</cite> (WKT)</A>.
042: *
043: * @since 2.0
044: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/wkt/Formattable.java $
045: * @version $Id: Formattable.java 24701 2007-03-07 22:25:42Z desruisseaux $
046: * @author Martin Desruisseaux
047: *
048: * @see <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">Well Know Text specification</A>
049: * @see <A HREF="http://gdal.velocet.ca/~warmerda/wktproblems.html">OGC WKT Coordinate System Issues</A>
050: */
051: public class Formattable {
052: /**
053: * The "Indentation" preference name.
054: *
055: * @todo this string is also hard-coded in AffineTransform2D, because we
056: * don't want to make it public (neither {@link #getIndentation}).
057: */
058: static final String INDENTATION = "Indentation";
059:
060: /**
061: * The formatter for the {@link #toWKT()} method.
062: * Will be constructed only when first needed.
063: */
064: private static Formatter FORMATTER;
065:
066: /**
067: * Default constructor.
068: */
069: protected Formattable() {
070: }
071:
072: /**
073: * Returns a string representation for this object. The default implementation returns
074: * the same string similar than {@link #toWKT()}, except that no exception is thrown if
075: * the string contains non-standard keywords. For example the
076: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">WKT
077: * specification</A> do not defines any keyword for {@linkplain CoordinateSystem coordinate
078: * system} objects. If this object is an instance of
079: * {@link org.geotools.referencing.cs.DefaultCartesianCS}, then the WKT will
080: * be formatted as <code>"CartesianCS[AXIS["</code>...<code>"], AXIS["</code>...<code>"],
081: * </code><i>etc.</i><code>]"</code>.
082: */
083: public String toString() {
084: return toWKT(Citations.OGC, getIndentation(), false);
085: }
086:
087: /**
088: * Returns a
089: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
090: * Known Text</cite> (WKT)</A> using a default indentation. The default indentation is read from
091: * {@linkplain Preferences user preferences}. It can be set from the command line using the
092: * following syntax:
093: *
094: * <blockquote>
095: * {@code java org.geotools.referencing.wkt.Formattable -identation=}<var><preferred
096: * indentation></var>
097: * </blockquote>
098: *
099: * @return The Well Know Text for this object.
100: * @throws UnformattableObjectException If this object can't be formatted as WKT.
101: * A formatting may fails because an object is too complex for the WKT format capability
102: * (for example an {@linkplain org.geotools.referencing.crs.DefaultEngineeringCRS
103: * engineering CRS} with different unit for each axis), or because only some specific
104: * implementations can be formatted as WKT.
105: */
106: public String toWKT() throws UnformattableObjectException {
107: return toWKT(getIndentation());
108: }
109:
110: /**
111: * Returns a
112: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
113: * Known Text</cite> (WKT)</A> for this object using the specified indentation.
114: *
115: * @param indentation The amount of spaces to use in indentation for WKT formatting,
116: * or 0 for formatting the whole WKT on a single line.
117: * @return The Well Know Text for this object.
118: * @throws UnformattableObjectException If this object can't be formatted as WKT.
119: * A formatting may fails because an object is too complex for the WKT format capability
120: * (for example an {@linkplain org.geotools.referencing.crs.DefaultEngineeringCRS
121: * engineering CRS} with different unit for each axis), or because only some specific
122: * implementations can be formatted as WKT.
123: */
124: public String toWKT(final int indentation)
125: throws UnformattableObjectException {
126: return toWKT(Citations.OGC, indentation);
127: }
128:
129: /**
130: * Returns a
131: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
132: * Known Text</cite> (WKT)</A> for this object using the specified indentation and authority.
133: *
134: * @param authority The authority to prefer when choosing WKT entities names.
135: * @param indentation The amount of spaces to use in indentation for WKT formatting,
136: * or 0 for formatting the whole WKT on a single line.
137: * @return The Well Know Text for this object.
138: * @throws UnformattableObjectException If this object can't be formatted as WKT.
139: * A formatting may fails because an object is too complex for the WKT format capability
140: * (for example an {@linkplain org.geotools.referencing.crs.DefaultEngineeringCRS
141: * engineering CRS} with different unit for each axis), or because only some specific
142: * implementations can be formatted as WKT.
143: */
144: public String toWKT(final Citation authority, final int indentation)
145: throws UnformattableObjectException {
146: return toWKT(authority, indentation, true);
147: }
148:
149: /**
150: * Returns a WKT for this object using the specified indentation and authority.
151: * If {@code strict} is true, then an exception is thrown if the WKT contains
152: * invalid keywords.
153: */
154: private String toWKT(final Citation authority,
155: final int indentation, final boolean strict)
156: throws UnformattableObjectException {
157: if (authority == null) {
158: throw new IllegalArgumentException(Errors.format(
159: ErrorKeys.NULL_ARGUMENT_$1, "authority"));
160: }
161: // No need to synchronize. This is not a big deal
162: // if two formatters co-exist for a short time.
163: Formatter formatter = FORMATTER;
164: if (formatter == null || formatter.indentation != indentation
165: || formatter.authority != authority) {
166: formatter = new Formatter(Symbols.DEFAULT, indentation);
167: formatter.authority = authority;
168: FORMATTER = formatter;
169: }
170: synchronized (formatter) {
171: try {
172: if (this instanceof GeneralParameterValue) {
173: // Special processing for parameter values, which is formatted
174: // directly in 'Formatter'. Note that in GeoAPI, this interface
175: // doesn't share the same parent interface than other interfaces.
176: formatter.append((GeneralParameterValue) this );
177: } else {
178: formatter.append(this );
179: }
180: if (strict && formatter.isInvalidWKT()) {
181: final Class unformattable = formatter
182: .getUnformattableClass();
183: throw new UnformattableObjectException(
184: formatter.warning, unformattable);
185: }
186: return formatter.toString();
187: } finally {
188: formatter.clear();
189: }
190: }
191: }
192:
193: /**
194: * Format the inner part of a
195: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
196: * Known Text</cite> (WKT)</A> element. This method is automatically invoked by
197: * {@link Formatter#append(Formattable)}. Element name and authority code must not be
198: * formatted here. For example for a {@code GEOGCS} element
199: * ({@link org.geotools.referencing.crs.DefaultGeographicCRS}), the formatter will invokes
200: * this method for completing the WKT at the insertion point show below:
201: *
202: * <pre>
203: * GEOGCS["WGS 84", AUTHORITY["EPSG","4326"]]
204: * |
205: * (insertion point)
206: * </pre>
207: *
208: * The default implementation declares that this object produces an invalid WKT.
209: * Subclasses must override this method for proper WKT formatting and should
210: * <strong>not</strong> invoke {@code super.formatWKT(formatter)} if they can
211: * use a valid WKT syntax.
212: *
213: * @param formatter The formatter to use.
214: * @return The name of the WKT element type (e.g. {@code "GEOGCS"}).
215: *
216: * @see #toWKT
217: * @see #toString
218: */
219: protected String formatWKT(final Formatter formatter) {
220: Class type = getClass();
221: formatter.setInvalidWKT(type);
222: Class[] interfaces = type.getInterfaces();
223: for (int i = 0; i < interfaces.length; i++) {
224: final Class candidate = interfaces[i];
225: if (candidate.getName().startsWith(
226: "org.opengis.referencing.")) {
227: type = candidate;
228: break;
229: }
230: }
231: return Utilities.getShortName(type);
232: }
233:
234: /**
235: * Returns the default indentation.
236: */
237: static int getIndentation() {
238: try {
239: return Preferences.userNodeForPackage(Formattable.class)
240: .getInt(INDENTATION, 2);
241: } catch (SecurityException ignore) {
242: // Ignore. Will fallback on the default indentation.
243: return 2;
244: }
245: }
246:
247: /**
248: * Set the default value for indentation.
249: *
250: * @throws SecurityException if a security manager is present and
251: * it denies <code>RuntimePermission("preferences")</code>.
252: */
253: static void setIndentation(final int indentation)
254: throws SecurityException {
255: Preferences.userNodeForPackage(Formattable.class).putInt(
256: INDENTATION, indentation);
257: }
258: }
|