0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: *
0005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
0006: * (C) 2002, Institut de Recherche pour le Développement
0007: *
0008: * This library is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU Lesser General Public
0010: * License as published by the Free Software Foundation; either
0011: * version 2.1 of the License, or (at your option) any later version.
0012: *
0013: * This library is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016: * Lesser General Public License for more details.
0017: */
0018: package org.geotools.referencing.wkt;
0019:
0020: // J2SE dependencies
0021: import java.io.BufferedReader;
0022: import java.text.ParseException;
0023: import java.text.ParsePosition;
0024: import java.util.ArrayList;
0025: import java.util.Collections;
0026: import java.util.HashMap;
0027: import java.util.LinkedHashMap;
0028: import java.util.List;
0029: import java.util.Locale;
0030: import java.util.Map;
0031: import javax.units.NonSI;
0032: import javax.units.SI;
0033: import javax.units.Unit;
0034:
0035: // OpenGIS dependencies
0036: import org.opengis.metadata.citation.Citation;
0037: import org.opengis.parameter.ParameterDescriptor;
0038: import org.opengis.parameter.ParameterNotFoundException;
0039: import org.opengis.parameter.ParameterValue;
0040: import org.opengis.parameter.ParameterValueGroup;
0041: import org.opengis.referencing.FactoryException;
0042: import org.opengis.referencing.IdentifiedObject;
0043: import org.opengis.referencing.NoSuchIdentifierException;
0044: import org.opengis.referencing.crs.CRSFactory;
0045: import org.opengis.referencing.crs.CompoundCRS;
0046: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0047: import org.opengis.referencing.crs.DerivedCRS;
0048: import org.opengis.referencing.crs.EngineeringCRS;
0049: import org.opengis.referencing.crs.GeocentricCRS;
0050: import org.opengis.referencing.crs.GeographicCRS;
0051: import org.opengis.referencing.crs.ProjectedCRS;
0052: import org.opengis.referencing.crs.VerticalCRS;
0053: import org.opengis.referencing.cs.AxisDirection;
0054: import org.opengis.referencing.cs.CSFactory;
0055: import org.opengis.referencing.cs.CoordinateSystem;
0056: import org.opengis.referencing.cs.CoordinateSystemAxis;
0057: import org.opengis.referencing.datum.Datum; // For javadoc
0058: import org.opengis.referencing.datum.DatumFactory;
0059: import org.opengis.referencing.datum.Ellipsoid;
0060: import org.opengis.referencing.datum.EngineeringDatum;
0061: import org.opengis.referencing.datum.GeodeticDatum;
0062: import org.opengis.referencing.datum.PrimeMeridian;
0063: import org.opengis.referencing.datum.VerticalDatum;
0064: import org.opengis.referencing.datum.VerticalDatumType;
0065: import org.opengis.referencing.operation.MathTransform;
0066: import org.opengis.referencing.operation.MathTransformFactory;
0067: import org.opengis.referencing.operation.NoninvertibleTransformException;
0068: import org.opengis.referencing.operation.OperationMethod;
0069:
0070: // Geotools dependencies
0071: import org.geotools.factory.Hints;
0072: import org.geotools.metadata.iso.citation.Citations;
0073: import org.geotools.referencing.ReferencingFactoryFinder;
0074: import org.geotools.referencing.NamedIdentifier;
0075: import org.geotools.referencing.datum.BursaWolfParameters;
0076: import org.geotools.referencing.datum.DefaultGeodeticDatum;
0077: import org.geotools.referencing.datum.DefaultPrimeMeridian;
0078: import org.geotools.referencing.datum.DefaultVerticalDatum;
0079: import org.geotools.referencing.cs.AbstractCS;
0080: import org.geotools.referencing.cs.DefaultCoordinateSystemAxis;
0081: import org.geotools.referencing.factory.ReferencingFactoryContainer;
0082: import org.geotools.resources.Arguments;
0083: import org.geotools.resources.i18n.Errors;
0084: import org.geotools.resources.i18n.ErrorKeys;
0085:
0086: /**
0087: * Parser for
0088: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
0089: * Known Text</cite> (WKT)</A>. This parser can parse {@linkplain MathTransform math transform}
0090: * objects as well, which is part of the WKT's {@code FITTED_CS} element.
0091: *
0092: * @since 2.0
0093: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/wkt/Parser.java $
0094: * @version $Id: Parser.java 27520 2007-10-16 19:00:52Z desruisseaux $
0095: * @author Remi Eve
0096: * @author Martin Desruisseaux
0097: *
0098: * @see <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">Well Know Text specification</A>
0099: * @see <A HREF="http://gdal.velocet.ca/~warmerda/wktproblems.html">OGC WKT Coordinate System Issues</A>
0100: */
0101: public class Parser extends MathTransformParser {
0102: /**
0103: * {@code true} in order to allows the non-standard Oracle syntax. Oracle put the Bursa-Wolf
0104: * parameters straight into the {@code DATUM} elements, without enclosing them in a
0105: * {@code TOWGS84} element.
0106: */
0107: private static final boolean ALLOW_ORACLE_SYNTAX = true;
0108:
0109: /**
0110: * The mapping between WKT element name and the object class to be created.
0111: * Will be created by {@link #getTypeMap} only when first needed. Keys must
0112: * be upper case.
0113: */
0114: private static Map/*<String,Class<?>>*/TYPES;
0115:
0116: /**
0117: * The factory to use for creating {@linkplain Datum datum}.
0118: */
0119: protected final DatumFactory datumFactory;
0120:
0121: /**
0122: * The factory to use for creating {@linkplain CoordinateSystem coordinate systems}.
0123: */
0124: protected final CSFactory csFactory;
0125:
0126: /**
0127: * The factory to use for creating {@linkplain CoordinateReferenceSystem
0128: * coordinate reference systems}.
0129: */
0130: protected final CRSFactory crsFactory;
0131:
0132: /**
0133: * Set of helper methods working on factories. Will be constructed
0134: * only the first time it is needed.
0135: *
0136: * @todo This field is a workaround for a limitation in current {@link CRSFactory}
0137: * interface. We should remove this field if the limitation is fixed in GeoAPI.
0138: */
0139: private transient ReferencingFactoryContainer factories;
0140:
0141: /**
0142: * The list of {@linkplain AxisDirection axis directions} from their name.
0143: */
0144: private final Map directions;
0145:
0146: /**
0147: * Constructs a parser using the default set of symbols and factories.
0148: */
0149: public Parser() {
0150: this (Symbols.DEFAULT);
0151: }
0152:
0153: /**
0154: * Constructs a parser for the specified set of symbols using default factories.
0155: *
0156: * @param symbols The symbols for parsing and formatting numbers.
0157: *
0158: * @todo Pass hints in argument.
0159: */
0160: public Parser(final Symbols symbols) {
0161: this (symbols, ReferencingFactoryFinder.getDatumFactory(null),
0162: ReferencingFactoryFinder.getCSFactory(null),
0163: ReferencingFactoryFinder.getCRSFactory(null),
0164: ReferencingFactoryFinder.getMathTransformFactory(null));
0165: }
0166:
0167: /**
0168: * Constructs a parser for the specified set of symbols using the specified set of factories.
0169: *
0170: * @param symbols The symbols for parsing and formatting numbers.
0171: * @param factories The factories to use.
0172: */
0173: public Parser(final Symbols symbols,
0174: final ReferencingFactoryContainer factories) {
0175: this (symbols, factories.getDatumFactory(), factories
0176: .getCSFactory(), factories.getCRSFactory(), factories
0177: .getMathTransformFactory());
0178: this .factories = factories;
0179: }
0180:
0181: /**
0182: * Constructs a parser for the specified set of symbols using the specified factories.
0183: *
0184: * @param symbols The symbols for parsing and formatting numbers.
0185: * @param datumFactory The factory to use for creating {@linkplain Datum datum}.
0186: * @param csFactory The factory to use for creating {@linkplain CoordinateSystem
0187: * coordinate systems}.
0188: * @param crsFactory The factory to use for creating {@linkplain CoordinateReferenceSystem
0189: * coordinate reference systems}.
0190: * @param mtFactory The factory to use for creating {@linkplain MathTransform
0191: * math transform} objects.
0192: */
0193: public Parser(final Symbols symbols,
0194: final DatumFactory datumFactory, final CSFactory csFactory,
0195: final CRSFactory crsFactory,
0196: final MathTransformFactory mtFactory) {
0197: super (symbols, mtFactory);
0198: this .datumFactory = datumFactory;
0199: this .csFactory = csFactory;
0200: this .crsFactory = crsFactory;
0201: final AxisDirection[] values = AxisDirection.values();
0202: directions = new HashMap((int) Math
0203: .ceil((values.length + 1) / 0.75f), 0.75f);
0204: for (int i = 0; i < values.length; i++) {
0205: directions.put(values[i].name().trim().toUpperCase(),
0206: values[i]);
0207: }
0208: }
0209:
0210: /**
0211: * Parses a coordinate reference system element.
0212: *
0213: * @param text The text to be parsed.
0214: * @return The coordinate reference system.
0215: * @throws ParseException if the string can't be parsed.
0216: */
0217: public CoordinateReferenceSystem parseCoordinateReferenceSystem(
0218: final String text) throws ParseException {
0219: final Element element = getTree(text, new ParsePosition(0));
0220: final CoordinateReferenceSystem crs = parseCoordinateReferenceSystem(element);
0221: element.close();
0222: return crs;
0223: }
0224:
0225: /**
0226: * Parses a coordinate reference system element.
0227: *
0228: * @param parent The parent element.
0229: * @return The next element as a {@link CoordinateReferenceSystem} object.
0230: * @throws ParseException if the next element can't be parsed.
0231: */
0232: private CoordinateReferenceSystem parseCoordinateReferenceSystem(
0233: final Element element) throws ParseException {
0234: final Object key = element.peek();
0235: if (key instanceof Element) {
0236: final String keyword = ((Element) key).keyword.trim()
0237: .toUpperCase(symbols.locale);
0238: CoordinateReferenceSystem r = null;
0239: try {
0240: if ("GEOGCS".equals(keyword))
0241: return r = parseGeoGCS(element);
0242: if ("PROJCS".equals(keyword))
0243: return r = parseProjCS(element);
0244: if ("GEOCCS".equals(keyword))
0245: return r = parseGeoCCS(element);
0246: if ("VERT_CS".equals(keyword))
0247: return r = parseVertCS(element);
0248: if ("LOCAL_CS".equals(keyword))
0249: return r = parseLocalCS(element);
0250: if ("COMPD_CS".equals(keyword))
0251: return r = parseCompdCS(element);
0252: if ("FITTED_CS".equals(keyword))
0253: return r = parseFittedCS(element);
0254: } finally {
0255: // Work around for simulating post-conditions in Java.
0256: assert isValid(r, keyword) : element;
0257: }
0258: }
0259: throw element.parseFailed(null, Errors.format(
0260: ErrorKeys.UNKNOW_TYPE_$1, key));
0261: }
0262:
0263: /**
0264: * Parses the next element in the specified <cite>Well Know Text</cite> (WKT) tree.
0265: *
0266: * @param element The element to be parsed.
0267: * @return The object.
0268: * @throws ParseException if the element can't be parsed.
0269: *
0270: * @todo All sequences of <code>if ("FOO".equals(keyword))</code> in this method
0271: * and other methods of this class and subclasses, could be optimized with
0272: * a {@code switch} statement.
0273: */
0274: protected Object parse(final Element element) throws ParseException {
0275: final Object key = element.peek();
0276: if (key instanceof Element) {
0277: final String keyword = ((Element) key).keyword.trim()
0278: .toUpperCase(symbols.locale);
0279: Object r = null;
0280: try {
0281: if ("AXIS".equals(keyword))
0282: return r = parseAxis(element, SI.METER, true);
0283: if ("PRIMEM".equals(keyword))
0284: return r = parsePrimem(element, NonSI.DEGREE_ANGLE);
0285: if ("TOWGS84".equals(keyword))
0286: return r = parseToWGS84(element);
0287: if ("SPHEROID".equals(keyword))
0288: return r = parseSpheroid(element);
0289: if ("VERT_DATUM".equals(keyword))
0290: return r = parseVertDatum(element);
0291: if ("LOCAL_DATUM".equals(keyword))
0292: return r = parseLocalDatum(element);
0293: if ("DATUM".equals(keyword))
0294: return r = parseDatum(element,
0295: DefaultPrimeMeridian.GREENWICH);
0296: r = parseMathTransform(element, false);
0297: if (r != null) {
0298: return r;
0299: }
0300: } finally {
0301: // Work around for simulating post-conditions in Java.
0302: assert isValid(r, keyword) : element;
0303: }
0304: }
0305: return parseCoordinateReferenceSystem(element);
0306: }
0307:
0308: /**
0309: * Checks if the parsed object is of the expected type. This is also a way to check
0310: * the consistency of the {@link #TYPES} map.
0311: */
0312: private static boolean isValid(final Object parsed,
0313: final String keyword) {
0314: if (parsed == null) {
0315: // Required in order to avoid AssertionError in place of ParseException.
0316: return true;
0317: }
0318: final Class type = getClassOf(keyword);
0319: return type != null && type.isInstance(parsed);
0320: }
0321:
0322: /**
0323: * Returns the properties to be given to the parsed object. This method is invoked
0324: * automatically by the parser for the {@linkplain Element#isRoot root element} only.
0325: * This method expect on input the properties parsed from the {@code AUTHORITY} element,
0326: * and returns on output the properties to give to the object to be created. The default
0327: * implementation returns the {@code properties} map unchanged. Subclasses may override
0328: * this method in order to add or change properties.
0329: * <p>
0330: * <strong>Example:</strong> if a subclass want to add automatically an authority code when
0331: * no {@code AUTHORITY} element was explicitly set in the WKT, then it may test for the
0332: * {@link IdentifiedObject#IDENTIFIERS_KEY} key and add automatically an entry if this
0333: * key was missing.
0334: *
0335: * @param properties The properties parsed from the WKT file. Entries can be added, removed
0336: * or modified directly in this map.
0337: * @return The properties to be given to the parsed object. This is usually {@code properties}
0338: * (maybe after modifications), but could also be a new map.
0339: *
0340: * @since 2.3
0341: */
0342: protected Map alterProperties(final Map properties) {
0343: return properties;
0344: }
0345:
0346: /**
0347: * Parses an <strong>optional</strong> "AUTHORITY" element.
0348: * This element has the following pattern:
0349: *
0350: * <blockquote><code>
0351: * AUTHORITY["<name>", "<code>"]
0352: * </code></blockquote>
0353: *
0354: * @param parent The parent element.
0355: * @param name The name of the parent object being parsed.
0356: * @return A properties map with the parent name and the optional autority code.
0357: * @throws ParseException if the "AUTHORITY" can't be parsed.
0358: */
0359: private Map parseAuthority(final Element parent, final String name)
0360: throws ParseException {
0361: final boolean isRoot = parent.isRoot();
0362: final Element element = parent.pullOptionalElement("AUTHORITY");
0363: Map properties;
0364: if (element == null) {
0365: if (isRoot) {
0366: properties = new HashMap(4);
0367: properties.put(IdentifiedObject.NAME_KEY, name);
0368: } else {
0369: properties = Collections.singletonMap(
0370: IdentifiedObject.NAME_KEY, name);
0371: }
0372: } else {
0373: final String auth = element.pullString("name");
0374: final String code = element.pullString("code");
0375: element.close();
0376: final Citation authority = Citations.fromName(auth);
0377: properties = new HashMap(4);
0378: properties.put(IdentifiedObject.NAME_KEY,
0379: new NamedIdentifier(authority, name));
0380: properties.put(IdentifiedObject.IDENTIFIERS_KEY,
0381: new NamedIdentifier(authority, code));
0382: }
0383: if (isRoot) {
0384: properties = alterProperties(properties);
0385: }
0386: return properties;
0387: }
0388:
0389: /**
0390: * Parses an "UNIT" element.
0391: * This element has the following pattern:
0392: *
0393: * <blockquote><code>
0394: * UNIT["<name>", <conversion factor> {,<authority>}]
0395: * </code></blockquote>
0396: *
0397: * @param parent The parent element.
0398: * @param unit The contextual unit. Usually {@link SI#METRE} or {@link SI#RADIAN}.
0399: * @return The "UNIT" element as an {@link Unit} object.
0400: * @throws ParseException if the "UNIT" can't be parsed.
0401: *
0402: * @todo Authority code is currently ignored. We may consider to create a subclass of
0403: * {@link Unit} which implements {@link IdentifiedObject} in a future version.
0404: */
0405: private Unit parseUnit(final Element parent, final Unit unit)
0406: throws ParseException {
0407: final Element element = parent.pullElement("UNIT");
0408: final String name = element.pullString("name");
0409: final double factor = element.pullDouble("factor");
0410: final Map properties = parseAuthority(element, name);
0411: element.close();
0412: return (factor != 1) ? unit.multiply(factor) : unit;
0413: }
0414:
0415: /**
0416: * Parses an "AXIS" element.
0417: * This element has the following pattern:
0418: *
0419: * <blockquote><code>
0420: * AXIS["<name>", NORTH | SOUTH | EAST | WEST | UP | DOWN | OTHER]
0421: * </code></blockquote>
0422: *
0423: * Note: there is no AUTHORITY element for AXIS element in OGC specification. However, we
0424: * accept it anyway in order to make the parser more tolerant to non-100% compliant
0425: * WKT. Note that AXIS is really the only element without such AUTHORITY clause and
0426: * the EPSG database provides authority code for all axis.
0427: *
0428: * @param parent The parent element.
0429: * @param unit The contextual unit. Usually {@link NonSI#DEGREE_ANGLE} or {@link SI#METRE}.
0430: * @param required {@code true} if the axis is mandatory,
0431: * or {@code false} if it is optional.
0432: * @return The "AXIS" element as a {@link CoordinateSystemAxis} object, or {@code null}
0433: * if the axis was not required and there is no axis object.
0434: * @throws ParseException if the "AXIS" element can't be parsed.
0435: */
0436: private CoordinateSystemAxis parseAxis(final Element parent,
0437: final Unit unit, final boolean required)
0438: throws ParseException {
0439: final Element element;
0440: if (required) {
0441: element = parent.pullElement("AXIS");
0442: } else {
0443: element = parent.pullOptionalElement("AXIS");
0444: if (element == null) {
0445: return null;
0446: }
0447: }
0448: final String name = element.pullString("name");
0449: final Element orientation = element
0450: .pullVoidElement("orientation");
0451: final Map properties = parseAuthority(element, name); // See javadoc
0452: element.close();
0453: final AxisDirection direction = (AxisDirection) directions
0454: .get(orientation.keyword.trim().toUpperCase());
0455: if (direction == null) {
0456: throw element.parseFailed(null, Errors.format(
0457: ErrorKeys.UNKNOW_TYPE_$1, orientation));
0458: }
0459: try {
0460: return createAxis(properties, name, direction, unit);
0461: } catch (FactoryException exception) {
0462: throw element.parseFailed(exception, null);
0463: }
0464: }
0465:
0466: /**
0467: * Creates an axis. If the name matches one of pre-defined axis, the pre-defined one
0468: * will be returned. This replacement help to get more success when comparing a CS
0469: * built from WKT against a CS built from one of Geotools's constants.
0470: *
0471: * @param properties Name and other properties to give to the new object.
0472: * If {@code null}, the abbreviation will be used as the axis name.
0473: * @param abbreviation The coordinate axis abbreviation.
0474: * @param direction The axis direction.
0475: * @param unit The coordinate axis unit.
0476: * @throws FactoryException if the axis can't be created.
0477: */
0478: private CoordinateSystemAxis createAxis(Map properties,
0479: final String abbreviation, final AxisDirection direction,
0480: final Unit unit) throws FactoryException {
0481: final CoordinateSystemAxis candidate = DefaultCoordinateSystemAxis
0482: .getPredefined(abbreviation, direction);
0483: if (candidate != null && unit.equals(candidate.getUnit())) {
0484: return candidate;
0485: }
0486: if (properties == null) {
0487: properties = Collections.singletonMap(
0488: IdentifiedObject.NAME_KEY, abbreviation);
0489: }
0490: return csFactory.createCoordinateSystemAxis(properties,
0491: abbreviation, direction, unit);
0492: }
0493:
0494: /**
0495: * Parses a "PRIMEM" element. This element has the following pattern:
0496: *
0497: * <blockquote><code>
0498: * PRIMEM["<name>", <longitude> {,<authority>}]
0499: * </code></blockquote>
0500: *
0501: * @param parent The parent element.
0502: * @param angularUnit The contextual unit.
0503: * @return The "PRIMEM" element as a {@link PrimeMeridian} object.
0504: * @throws ParseException if the "PRIMEM" element can't be parsed.
0505: */
0506: private PrimeMeridian parsePrimem(final Element parent,
0507: final Unit angularUnit) throws ParseException {
0508: final Element element = parent.pullElement("PRIMEM");
0509: final String name = element.pullString("name");
0510: final double longitude = element.pullDouble("longitude");
0511: final Map properties = parseAuthority(element, name);
0512: element.close();
0513: try {
0514: return datumFactory.createPrimeMeridian(properties,
0515: longitude, angularUnit);
0516: } catch (FactoryException exception) {
0517: throw element.parseFailed(exception, null);
0518: }
0519: }
0520:
0521: /**
0522: * Parses an <strong>optional</strong> "TOWGS84" element.
0523: * This element has the following pattern:
0524: *
0525: * <blockquote><code>
0526: * TOWGS84[<dx>, <dy>, <dz>, <ex>, <ey>, <ez>, <ppm>]
0527: * </code></blockquote>
0528: *
0529: * @param parent The parent element.
0530: * @return The "TOWGS84" element as a {@link BursaWolfParameters} object,
0531: * or {@code null} if no "TOWGS84" has been found.
0532: * @throws ParseException if the "TOWGS84" can't be parsed.
0533: */
0534: private static BursaWolfParameters parseToWGS84(final Element parent)
0535: throws ParseException {
0536: final Element element = parent.pullOptionalElement("TOWGS84");
0537: if (element == null) {
0538: return null;
0539: }
0540: final BursaWolfParameters info = new BursaWolfParameters(
0541: DefaultGeodeticDatum.WGS84);
0542: info.dx = element.pullDouble("dx");
0543: info.dy = element.pullDouble("dy");
0544: info.dz = element.pullDouble("dz");
0545: if (element.peek() != null) {
0546: info.ex = element.pullDouble("ex");
0547: info.ey = element.pullDouble("ey");
0548: info.ez = element.pullDouble("ez");
0549: info.ppm = element.pullDouble("ppm");
0550: }
0551: element.close();
0552: return info;
0553: }
0554:
0555: /**
0556: * Parses a "SPHEROID" element. This element has the following pattern:
0557: *
0558: * <blockquote><code>
0559: * SPHEROID["<name>", <semi-major axis>, <inverse flattening> {,<authority>}]
0560: * </code></blockquote>
0561: *
0562: * @param parent The parent element.
0563: * @return The "SPHEROID" element as an {@link Ellipsoid} object.
0564: * @throws ParseException if the "SPHEROID" element can't be parsed.
0565: */
0566: private Ellipsoid parseSpheroid(final Element parent)
0567: throws ParseException {
0568: Element element = parent.pullElement("SPHEROID");
0569: String name = element.pullString("name");
0570: double semiMajorAxis = element.pullDouble("semiMajorAxis");
0571: double inverseFlattening = element
0572: .pullDouble("inverseFlattening");
0573: Map properties = parseAuthority(element, name);
0574: element.close();
0575: if (inverseFlattening == 0) {
0576: // Inverse flattening null is an OGC convention for a sphere.
0577: inverseFlattening = Double.POSITIVE_INFINITY;
0578: }
0579: try {
0580: return datumFactory.createFlattenedSphere(properties,
0581: semiMajorAxis, inverseFlattening, SI.METER);
0582: } catch (FactoryException exception) {
0583: throw element.parseFailed(exception, null);
0584: }
0585: }
0586:
0587: /**
0588: * Parses a "PROJECTION" element. This element has the following pattern:
0589: *
0590: * <blockquote><code>
0591: * PROJECTION["<name>" {,<authority>}]
0592: * </code></blockquote>
0593: *
0594: * @param parent The parent element.
0595: * @param ellipsoid The ellipsoid, or {@code null} if none.
0596: * @param linearUnit The linear unit of the parent PROJCS element, or {@code null}.
0597: * @param angularUnit The angular unit of the parent GEOCS element, or {@code null}.
0598: * @return The "PROJECTION" element as a {@link ParameterValueGroup} object.
0599: * @throws ParseException if the "PROJECTION" element can't be parsed.
0600: */
0601: private ParameterValueGroup parseProjection(final Element parent,
0602: final Ellipsoid ellipsoid, final Unit linearUnit,
0603: final Unit angularUnit) throws ParseException {
0604: final Element element = parent.pullElement("PROJECTION");
0605: final String classification = element.pullString("name");
0606: final Map properties = parseAuthority(element, classification);
0607: element.close();
0608: /*
0609: * Set the list of parameters. NOTE: Parameters are defined in
0610: * the parent Element (usually a "PROJCS" element), not in this
0611: * "PROJECTION" element.
0612: *
0613: * We will set the semi-major and semi-minor parameters from the
0614: * ellipsoid first. If those values were explicitly specified in
0615: * a "PARAMETER" statement, they will overwrite the values inferred
0616: * from the ellipsoid.
0617: */
0618: final ParameterValueGroup parameters;
0619: try {
0620: parameters = mtFactory.getDefaultParameters(classification);
0621: } catch (NoSuchIdentifierException exception) {
0622: throw element.parseFailed(exception, null);
0623: }
0624: Element param = parent;
0625: try {
0626: if (ellipsoid != null) {
0627: final Unit axisUnit = ellipsoid.getAxisUnit();
0628: parameters.parameter("semi_major").setValue(
0629: ellipsoid.getSemiMajorAxis(), axisUnit);
0630: parameters.parameter("semi_minor").setValue(
0631: ellipsoid.getSemiMinorAxis(), axisUnit);
0632: }
0633: while ((param = parent.pullOptionalElement("PARAMETER")) != null) {
0634: final String paramName = param.pullString("name");
0635: final double paramValue = param.pullDouble("value");
0636: final ParameterValue parameter = parameters
0637: .parameter(paramName);
0638: final Unit expected = ((ParameterDescriptor) parameter
0639: .getDescriptor()).getUnit();
0640: if (expected != null && !Unit.ONE.equals(expected)) {
0641: if (linearUnit != null
0642: && SI.METER.isCompatible(expected)) {
0643: parameter.setValue(paramValue, linearUnit);
0644: continue;
0645: }
0646: if (angularUnit != null
0647: && SI.RADIAN.isCompatible(expected)) {
0648: parameter.setValue(paramValue, angularUnit);
0649: continue;
0650: }
0651: }
0652: parameter.setValue(paramValue);
0653: }
0654: } catch (ParameterNotFoundException exception) {
0655: throw param.parseFailed(exception, Errors.format(
0656: ErrorKeys.UNEXPECTED_PARAMETER_$1, exception
0657: .getParameterName()));
0658: }
0659: return parameters;
0660: }
0661:
0662: /**
0663: * Parses a "DATUM" element. This element has the following pattern:
0664: *
0665: * <blockquote><code>
0666: * DATUM["<name>", <spheroid> {,<to wgs84>} {,<authority>}]
0667: * </code></blockquote>
0668: *
0669: * @param parent The parent element.
0670: * @param meridian the prime meridian.
0671: * @return The "DATUM" element as a {@link GeodeticDatum} object.
0672: * @throws ParseException if the "DATUM" element can't be parsed.
0673: */
0674: private GeodeticDatum parseDatum(final Element parent,
0675: final PrimeMeridian meridian) throws ParseException {
0676: Element element = parent.pullElement("DATUM");
0677: String name = element.pullString("name");
0678: Ellipsoid ellipsoid = parseSpheroid(element);
0679: BursaWolfParameters toWGS84 = parseToWGS84(element); // Optional; may be null.
0680: Map properties = parseAuthority(element, name);
0681: if (ALLOW_ORACLE_SYNTAX && (toWGS84 == null)
0682: && (element.peek() instanceof Number)) {
0683: toWGS84 = new BursaWolfParameters(
0684: DefaultGeodeticDatum.WGS84);
0685: toWGS84.dx = element.pullDouble("dx");
0686: toWGS84.dy = element.pullDouble("dy");
0687: toWGS84.dz = element.pullDouble("dz");
0688: toWGS84.ex = element.pullDouble("ex");
0689: toWGS84.ey = element.pullDouble("ey");
0690: toWGS84.ez = element.pullDouble("ez");
0691: toWGS84.ppm = element.pullDouble("ppm");
0692: }
0693: element.close();
0694: if (toWGS84 != null) {
0695: if (!(properties instanceof HashMap)) {
0696: properties = new HashMap(properties);
0697: }
0698: properties
0699: .put(DefaultGeodeticDatum.BURSA_WOLF_KEY, toWGS84);
0700: }
0701: try {
0702: return datumFactory.createGeodeticDatum(properties,
0703: ellipsoid, meridian);
0704: } catch (FactoryException exception) {
0705: throw element.parseFailed(exception, null);
0706: }
0707: }
0708:
0709: /**
0710: * Parses a "VERT_DATUM" element. This element has the following pattern:
0711: *
0712: * <blockquote><code>
0713: * VERT_DATUM["<name>", <datum type> {,<authority>}]
0714: * </code></blockquote>
0715: *
0716: * @param parent The parent element.
0717: * @return The "VERT_DATUM" element as a {@link VerticalDatum} object.
0718: * @throws ParseException if the "VERT_DATUM" element can't be parsed.
0719: */
0720: private VerticalDatum parseVertDatum(final Element parent)
0721: throws ParseException {
0722: final Element element = parent.pullElement("VERT_DATUM");
0723: final String name = element.pullString("name");
0724: final int datum = element.pullInteger("datum");
0725: final Map properties = parseAuthority(element, name);
0726: element.close();
0727: final VerticalDatumType type = DefaultVerticalDatum
0728: .getVerticalDatumTypeFromLegacyCode(datum);
0729: if (type == null) {
0730: throw element.parseFailed(null, Errors.format(
0731: ErrorKeys.UNKNOW_TYPE_$1, new Integer(datum)));
0732: }
0733: try {
0734: return datumFactory.createVerticalDatum(properties, type);
0735: } catch (FactoryException exception) {
0736: throw element.parseFailed(exception, null);
0737: }
0738: }
0739:
0740: /**
0741: * Parses a "LOCAL_DATUM" element. This element has the following pattern:
0742: *
0743: * <blockquote><code>
0744: * LOCAL_DATUM["<name>", <datum type> {,<authority>}]
0745: * </code></blockquote>
0746: *
0747: * @param parent The parent element.
0748: * @return The "LOCAL_DATUM" element as an {@link EngineeringDatum} object.
0749: * @throws ParseException if the "LOCAL_DATUM" element can't be parsed.
0750: *
0751: * @todo The vertical datum type is currently ignored.
0752: */
0753: private EngineeringDatum parseLocalDatum(final Element parent)
0754: throws ParseException {
0755: final Element element = parent.pullElement("LOCAL_DATUM");
0756: final String name = element.pullString("name");
0757: final int datum = element.pullInteger("datum");
0758: final Map properties = parseAuthority(element, name);
0759: element.close();
0760: try {
0761: return datumFactory.createEngineeringDatum(properties);
0762: } catch (FactoryException exception) {
0763: throw element.parseFailed(exception, null);
0764: }
0765: }
0766:
0767: /**
0768: * Parses a "LOCAL_CS" element.
0769: * This element has the following pattern:
0770: *
0771: * <blockquote><code>
0772: * LOCAL_CS["<name>", <local datum>, <unit>, <axis>, {,<axis>}* {,<authority>}]
0773: * </code></blockquote>
0774: *
0775: * @param parent The parent element.
0776: * @return The "LOCAL_CS" element as an {@link EngineeringCRS} object.
0777: * @throws ParseException if the "LOCAL_CS" element can't be parsed.
0778: *
0779: * @todo The coordinate system used is always a Geotools implementation, since we don't
0780: * know which method to invokes in the {@link CSFactory} (is it a cartesian
0781: * coordinate system? a spherical one? etc.).
0782: */
0783: private EngineeringCRS parseLocalCS(final Element parent)
0784: throws ParseException {
0785: Element element = parent.pullElement("LOCAL_CS");
0786: String name = element.pullString("name");
0787: EngineeringDatum datum = parseLocalDatum(element);
0788: Unit linearUnit = parseUnit(element, SI.METER);
0789: CoordinateSystemAxis axis = parseAxis(element, linearUnit, true);
0790: List list = new ArrayList();
0791: do {
0792: list.add(axis);
0793: axis = parseAxis(element, linearUnit, false);
0794: } while (axis != null);
0795: final Map properties = parseAuthority(element, name);
0796: element.close();
0797: final CoordinateSystem cs;
0798: cs = new AbstractCS(Collections.singletonMap("name", name),
0799: (CoordinateSystemAxis[]) list
0800: .toArray(new CoordinateSystemAxis[list.size()]));
0801: try {
0802: return crsFactory.createEngineeringCRS(properties, datum,
0803: cs);
0804: } catch (FactoryException exception) {
0805: throw element.parseFailed(exception, null);
0806: }
0807: }
0808:
0809: /**
0810: * Parses a "GEOCCS" element.
0811: * This element has the following pattern:
0812: *
0813: * <blockquote><code>
0814: * GEOCCS["<name>", <datum>, <prime meridian>, <linear unit>
0815: * {,<axis> ,<axis> ,<axis>} {,<authority>}]
0816: * </code></blockquote>
0817: *
0818: * @param parent The parent element.
0819: * @return The "GEOCCS" element as a {@link GeocentricCRS} object.
0820: * @throws ParseException if the "GEOCCS" element can't be parsed.
0821: */
0822: private GeocentricCRS parseGeoCCS(final Element parent)
0823: throws ParseException {
0824: final Element element = parent.pullElement("GEOCCS");
0825: final String name = element.pullString("name");
0826: final Map properties = parseAuthority(element, name);
0827: final PrimeMeridian meridian = parsePrimem(element,
0828: NonSI.DEGREE_ANGLE);
0829: final GeodeticDatum datum = parseDatum(element, meridian);
0830: final Unit linearUnit = parseUnit(element, SI.METER);
0831: CoordinateSystemAxis axis0, axis1, axis2;
0832: axis0 = parseAxis(element, linearUnit, false);
0833: try {
0834: if (axis0 != null) {
0835: axis1 = parseAxis(element, linearUnit, true);
0836: axis2 = parseAxis(element, linearUnit, true);
0837: } else {
0838: // Those default values are part of WKT specification.
0839: axis0 = createAxis(null, "X", AxisDirection.OTHER,
0840: linearUnit);
0841: axis1 = createAxis(null, "Y", AxisDirection.EAST,
0842: linearUnit);
0843: axis2 = createAxis(null, "Z", AxisDirection.NORTH,
0844: linearUnit);
0845: }
0846: element.close();
0847: return crsFactory.createGeocentricCRS(properties, datum,
0848: csFactory.createCartesianCS(properties, axis0,
0849: axis1, axis2));
0850: } catch (FactoryException exception) {
0851: throw element.parseFailed(exception, null);
0852: }
0853: }
0854:
0855: /**
0856: * Parses an <strong>optional</strong> "VERT_CS" element.
0857: * This element has the following pattern:
0858: *
0859: * <blockquote><code>
0860: * VERT_CS["<name>", <vert datum>, <linear unit>, {<axis>,} {,<authority>}]
0861: * </code></blockquote>
0862: *
0863: * @param parent The parent element.
0864: * @return The "VERT_CS" element as a {@link VerticalCRS} object.
0865: * @throws ParseException if the "VERT_CS" element can't be parsed.
0866: */
0867: private VerticalCRS parseVertCS(final Element parent)
0868: throws ParseException {
0869: final Element element = parent.pullElement("VERT_CS");
0870: if (element == null) {
0871: return null;
0872: }
0873: String name = element.pullString("name");
0874: VerticalDatum datum = parseVertDatum(element);
0875: Unit linearUnit = parseUnit(element, SI.METER);
0876: CoordinateSystemAxis axis = parseAxis(element, linearUnit,
0877: false);
0878: Map properties = parseAuthority(element, name);
0879: element.close();
0880: try {
0881: if (axis == null) {
0882: axis = createAxis(null, "Z", AxisDirection.UP,
0883: linearUnit);
0884: }
0885: return crsFactory.createVerticalCRS(properties, datum,
0886: csFactory.createVerticalCS(Collections
0887: .singletonMap("name", name), axis));
0888: } catch (FactoryException exception) {
0889: throw element.parseFailed(exception, null);
0890: }
0891: }
0892:
0893: /**
0894: * Parses a "GEOGCS" element. This element has the following pattern:
0895: *
0896: * <blockquote><code>
0897: * GEOGCS["<name>", <datum>, <prime meridian>, <angular unit> {,<twin axes>} {,<authority>}]
0898: * </code></blockquote>
0899: *
0900: * @param parent The parent element.
0901: * @return The "GEOGCS" element as a {@link GeographicCRS} object.
0902: * @throws ParseException if the "GEOGCS" element can't be parsed.
0903: */
0904: private GeographicCRS parseGeoGCS(final Element parent)
0905: throws ParseException {
0906: Element element = parent.pullElement("GEOGCS");
0907: String name = element.pullString("name");
0908: Map properties = parseAuthority(element, name);
0909: Unit angularUnit = parseUnit(element, SI.RADIAN);
0910: PrimeMeridian meridian = parsePrimem(element, angularUnit);
0911: GeodeticDatum datum = parseDatum(element, meridian);
0912: CoordinateSystemAxis axis0 = parseAxis(element, angularUnit,
0913: false);
0914: CoordinateSystemAxis axis1;
0915: try {
0916: if (axis0 != null) {
0917: axis1 = parseAxis(element, angularUnit, true);
0918: } else {
0919: // Those default values are part of WKT specification.
0920: axis0 = createAxis(null, "Lon", AxisDirection.EAST,
0921: angularUnit);
0922: axis1 = createAxis(null, "Lat", AxisDirection.NORTH,
0923: angularUnit);
0924: }
0925: element.close();
0926: return crsFactory.createGeographicCRS(properties, datum,
0927: csFactory.createEllipsoidalCS(properties, axis0,
0928: axis1));
0929: } catch (FactoryException exception) {
0930: throw element.parseFailed(exception, null);
0931: }
0932: }
0933:
0934: /**
0935: * Parses a "PROJCS" element.
0936: * This element has the following pattern:
0937: *
0938: * <blockquote><code>
0939: * PROJCS["<name>", <geographic cs>, <projection>, {<parameter>,}*,
0940: * <linear unit> {,<twin axes>}{,<authority>}]
0941: * </code></blockquote>
0942: *
0943: * @param parent The parent element.
0944: * @return The "PROJCS" element as a {@link ProjectedCRS} object.
0945: * @throws ParseException if the "GEOGCS" element can't be parsed.
0946: */
0947: private ProjectedCRS parseProjCS(final Element parent)
0948: throws ParseException {
0949: Element element = parent.pullElement("PROJCS");
0950: String name = element.pullString("name");
0951: Map properties = parseAuthority(element, name);
0952: GeographicCRS geoCRS = parseGeoGCS(element);
0953: Ellipsoid ellipsoid = ((GeodeticDatum) geoCRS.getDatum())
0954: .getEllipsoid();
0955: Unit linearUnit = parseUnit(element, SI.METER);
0956: Unit angularUnit = geoCRS.getCoordinateSystem().getAxis(0)
0957: .getUnit();
0958: ParameterValueGroup projection = parseProjection(element,
0959: ellipsoid, linearUnit, angularUnit);
0960: CoordinateSystemAxis axis0 = parseAxis(element, linearUnit,
0961: false);
0962: CoordinateSystemAxis axis1;
0963: try {
0964: if (axis0 != null) {
0965: axis1 = parseAxis(element, linearUnit, true);
0966: } else {
0967: // Those default values are part of WKT specification.
0968: axis0 = createAxis(null, "X", AxisDirection.EAST,
0969: linearUnit);
0970: axis1 = createAxis(null, "Y", AxisDirection.NORTH,
0971: linearUnit);
0972: }
0973: element.close();
0974: if (factories == null) {
0975: final Hints hints = new Hints(Collections.EMPTY_MAP);
0976: hints.put(Hints.DATUM_FACTORY, datumFactory);
0977: hints.put(Hints.CS_FACTORY, csFactory);
0978: hints.put(Hints.CRS_FACTORY, crsFactory);
0979: hints.put(Hints.MATH_TRANSFORM_FACTORY, mtFactory);
0980: factories = ReferencingFactoryContainer.instance(hints);
0981: }
0982: return factories.createProjectedCRS(properties, geoCRS,
0983: null, projection, csFactory.createCartesianCS(
0984: properties, axis0, axis1));
0985: } catch (FactoryException exception) {
0986: throw element.parseFailed(exception, null);
0987: }
0988: }
0989:
0990: /**
0991: * Parses a "COMPD_CS" element.
0992: * This element has the following pattern:
0993: *
0994: * <blockquote><code>
0995: * COMPD_CS["<name>", <head cs>, <tail cs> {,<authority>}]
0996: * </code></blockquote>
0997: *
0998: * @param parent The parent element.
0999: * @return The "COMPD_CS" element as a {@link CompoundCRS} object.
1000: * @throws ParseException if the "COMPD_CS" element can't be parsed.
1001: */
1002: private CompoundCRS parseCompdCS(final Element parent)
1003: throws ParseException {
1004: final CoordinateReferenceSystem[] CRS = new CoordinateReferenceSystem[2];
1005: Element element = parent.pullElement("COMPD_CS");
1006: String name = element.pullString("name");
1007: Map properties = parseAuthority(element, name);
1008: CRS[0] = parseCoordinateReferenceSystem(element);
1009: CRS[1] = parseCoordinateReferenceSystem(element);
1010: element.close();
1011: try {
1012: return crsFactory.createCompoundCRS(properties, CRS);
1013: } catch (FactoryException exception) {
1014: throw element.parseFailed(exception, null);
1015: }
1016: }
1017:
1018: /**
1019: * Parses a "FITTED_CS" element.
1020: * This element has the following pattern:
1021: *
1022: * <blockquote><code>
1023: * FITTED_CS["<name>", <to base>, <base cs>]
1024: * </code></blockquote>
1025: *
1026: * @param parent The parent element.
1027: * @return The "FITTED_CS" element as a {@link CompoundCRS} object.
1028: * @throws ParseException if the "COMPD_CS" element can't be parsed.
1029: */
1030: private DerivedCRS parseFittedCS(final Element parent)
1031: throws ParseException {
1032: Element element = parent.pullElement("FITTED_CS");
1033: String name = element.pullString("name");
1034: Map properties = parseAuthority(element, name);
1035: final MathTransform toBase = parseMathTransform(element, true);
1036: final CoordinateReferenceSystem base = parseCoordinateReferenceSystem(element);
1037: final OperationMethod method = getOperationMethod();
1038: element.close();
1039: /*
1040: * WKT provides no informations about the underlying CS of a derived CRS.
1041: * We have to guess some reasonable one with arbitrary units. We try to
1042: * construct the one which contains as few information as possible, in
1043: * order to avoid providing wrong informations.
1044: */
1045: final CoordinateSystemAxis[] axis = new CoordinateSystemAxis[toBase
1046: .getSourceDimensions()];
1047: final StringBuffer buffer = new StringBuffer(name);
1048: buffer.append(" axis ");
1049: final int start = buffer.length();
1050: try {
1051: for (int i = 0; i < axis.length; i++) {
1052: final String number = String.valueOf(i);
1053: buffer.setLength(start);
1054: buffer.append(number);
1055: axis[i] = csFactory.createCoordinateSystemAxis(
1056: Collections.singletonMap(
1057: IdentifiedObject.NAME_KEY, buffer
1058: .toString()), number,
1059: AxisDirection.OTHER, Unit.ONE);
1060: }
1061: return crsFactory.createDerivedCRS(properties, method,
1062: base, toBase.inverse(), new AbstractCS(properties,
1063: axis));
1064: } catch (FactoryException exception) {
1065: throw element.parseFailed(exception, null);
1066: } catch (NoninvertibleTransformException exception) {
1067: throw element.parseFailed(exception, null);
1068: }
1069: }
1070:
1071: /**
1072: * Returns the class of the specified WKT element. For example this method returns
1073: * <code>{@linkplain ProjectedCRS}.class</code> for element "{@code PROJCS}".
1074: *
1075: * @param element The WKT element name.
1076: * @return The GeoAPI class of the specified element, or {@code null} if unknow.
1077: */
1078: public static Class getClassOf(String element) {
1079: if (element == null) {
1080: return null;
1081: }
1082: element = element.trim().toUpperCase(Locale.US);
1083: final Class type = (Class) getTypeMap().get(element);
1084: assert type == null || type.equals(MathTransform.class)
1085: || element.equals(getNameOf(type)) : type;
1086: return type;
1087: }
1088:
1089: /**
1090: * Returns the WKT name of the specified object type. For example this method returns
1091: * "{@code PROJCS}" for type <code>{@linkplain ProjectedCRS}.class</code>.
1092: *
1093: * @param type The GeoAPI class of the specified element.
1094: * @return The WKT element name, or {@code null} if unknow.
1095: *
1096: * @since 2.4
1097: */
1098: public static String getNameOf(final Class/*<?>*/type) {
1099: if (type != null) {
1100: for (final java.util.Iterator it = getTypeMap().entrySet()
1101: .iterator(); it.hasNext();) {
1102: final Map.Entry/*<String,Class<?>>*/entry = (Map.Entry) it
1103: .next();
1104: final Class candidate = (Class) entry.getValue();
1105: if (candidate.isAssignableFrom(type)) {
1106: return (String) entry.getKey();
1107: }
1108: }
1109: }
1110: return null;
1111: }
1112:
1113: /**
1114: * Returns the type map.
1115: */
1116: private static Map getTypeMap() {
1117: if (TYPES == null) {
1118: final Map map = new LinkedHashMap/*<String,Class<?>>*/(25);
1119: map.put("GEOGCS", GeographicCRS.class);
1120: map.put("PROJCS", ProjectedCRS.class);
1121: map.put("GEOCCS", GeocentricCRS.class);
1122: map.put("VERT_CS", VerticalCRS.class);
1123: map.put("LOCAL_CS", EngineeringCRS.class);
1124: map.put("COMPD_CS", CompoundCRS.class);
1125: map.put("FITTED_CS", DerivedCRS.class);
1126: map.put("AXIS", CoordinateSystemAxis.class);
1127: map.put("PRIMEM", PrimeMeridian.class);
1128: map.put("TOWGS84", BursaWolfParameters.class);
1129: map.put("SPHEROID", Ellipsoid.class);
1130: map.put("VERT_DATUM", VerticalDatum.class);
1131: map.put("LOCAL_DATUM", EngineeringDatum.class);
1132: map.put("DATUM", GeodeticDatum.class);
1133: map.put("PARAM_MT", MathTransform.class);
1134: map.put("CONCAT_MT", MathTransform.class);
1135: map.put("INVERSE_MT", MathTransform.class);
1136: map.put("PASSTHROUGH_MT", MathTransform.class);
1137: TYPES = map; // Sets the field only once completed, in order to avoid synchronisation.
1138: // It is not a big deal in current implementation if two Maps are created.
1139: }
1140: return TYPES;
1141: }
1142:
1143: /**
1144: * Read WKT strings from the {@linkplain System#in standard input stream} and
1145: * reformat them to the {@linkplain System#out standard output stream}. The
1146: * input is read until it reach the end-of-file ({@code [Ctrl-Z]} if
1147: * reading from the keyboard), or until an unparsable WKT has been hit.
1148: * Optional arguments are:
1149: *
1150: * <TABLE CELLPADDING='0' CELLSPACING='0'>
1151: * <TR><TD NOWRAP><CODE>-authority</CODE> <VAR><name></VAR></TD>
1152: * <TD NOWRAP> The authority to prefer when choosing WKT entities names.</TD></TR>
1153: * <TR><TD NOWRAP><CODE>-indentation</CODE> <VAR><value></VAR></TD>
1154: * <TD NOWRAP> Set the indentation (0 for output on a single line)</TD></TR>
1155: * <TR><TD NOWRAP><CODE>-encoding</CODE> <VAR><code></VAR></TD>
1156: * <TD NOWRAP> Set the character encoding</TD></TR>
1157: * <TR><TD NOWRAP><CODE>-locale</CODE> <VAR><language></VAR></TD>
1158: * <TD NOWRAP> Set the language for the output (e.g. "fr" for French)</TD></TR>
1159: * </TABLE>
1160: */
1161: public static void main(String[] args) {
1162: final Arguments arguments = new Arguments(args);
1163: final Integer indentation = arguments
1164: .getOptionalInteger(Formattable.INDENTATION);
1165: final String authority = arguments
1166: .getOptionalString("-authority");
1167: args = arguments.getRemainingArguments(0);
1168: if (indentation != null) {
1169: Formattable.setIndentation(indentation.intValue());
1170: }
1171: final BufferedReader in = new BufferedReader(Arguments
1172: .getReader(System.in));
1173: try {
1174: final Parser parser = new Parser();
1175: if (authority != null) {
1176: parser.setAuthority(Citations.fromName(authority));
1177: }
1178: parser.reformat(in, arguments.out, arguments.err);
1179: } catch (Exception exception) {
1180: exception.printStackTrace(arguments.err);
1181: }
1182: // Do not close 'in', since it is the standard input stream.
1183: }
1184: }
|