001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2004, 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: package org.geotools.referencing.wkt;
019:
020: import java.text.DecimalFormat;
021: import java.text.NumberFormat;
022: import java.util.Locale;
023:
024: /**
025: * The set of symbols to use for WKT parsing and formatting.
026: *
027: * @since 2.1
028: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/wkt/Symbols.java $
029: * @version $Id: Symbols.java 27520 2007-10-16 19:00:52Z desruisseaux $
030: * @author Martin Desruisseaux
031: */
032: public class Symbols {
033: /**
034: * The default set of symbols.
035: */
036: public static final Symbols DEFAULT = new Symbols(Locale.US);
037: // DONT't invoke the default constructor for this one.
038:
039: /**
040: * A set of symbols with parameters between square brackets, like {@code [...]}.
041: */
042: public static final Symbols SQUARE_BRACKETS = DEFAULT;
043:
044: /**
045: * A set of symbols with parameters between parentheses,
046: * like {@code (...)}.
047: */
048: public static final Symbols CURLY_BRACKETS = new Symbols();
049: static {
050: CURLY_BRACKETS.open = '(';
051: CURLY_BRACKETS.close = ')';
052: }
053:
054: /* ----------------------------------------------------------
055: * NOTE: Consider all fields below as final.
056: * It is not only in order to make construction easier.
057: * If the informations provided by those fields became
058: * needed outside of this package, then we need to make
059: * them private and declare accessors instead.
060: * ---------------------------------------------------------- */
061:
062: /**
063: * The locale for querying localizable information.
064: */
065: final Locale locale;
066:
067: /**
068: * The character used for opening brace.
069: * Usually {@code '['}, but {@code '('} is legal as well.
070: */
071: char open = '[';
072:
073: /**
074: * The character used for closing brace.
075: * Usually {@code ']'}, but {@code ')'} is legal as well.
076: */
077: char close = ']';
078:
079: /**
080: * The character used for opening an array or enumeration.
081: * Usually {@code '{'}.
082: */
083: final char openArray = '{';
084:
085: /**
086: * The character used for closing an array or enumeration.
087: * Usually {@code '}'}.
088: */
089: final char closeArray = '}';
090:
091: /**
092: * The character used for quote.
093: * Usually {@code '"'}.
094: */
095: final char quote = '"';
096:
097: /**
098: * The character used as a separator. Usually {@code ','}, but would need
099: * to be changed if a non-English locale is used for formatting numbers.
100: */
101: char separator = ',';
102:
103: /**
104: * The character used for space.
105: * Usually <code>' '</code>, but could be a no-break space too if unicode is allowed.
106: */
107: final char space = ' ';
108:
109: /**
110: * List of caracters acceptable as opening bracket. The closing bracket must
111: * be the character in the {@code closingBrackets} array at the same index
112: * than the opening bracket.
113: */
114: final char[] openingBrackets = { '[', '(' };
115:
116: /**
117: * List of caracters acceptable as closing bracket.
118: */
119: final char[] closingBrackets = { ']', ')' };
120:
121: /**
122: * The object to use for parsing and formatting numbers.
123: *
124: * <STRONG>Note:</STRONG> {@link NumberFormat} object are usually not thread safe.
125: * Consequently, each instances of {@link Parser} or {@link Formatter} must use a
126: * clone of this object, not this object directly (unless they synchronize on it).
127: */
128: final NumberFormat numberFormat;
129:
130: /**
131: * Creates a new instance initialized to the default symbols.
132: */
133: private Symbols() {
134: locale = Locale.US;
135: numberFormat = DEFAULT.numberFormat;
136: }
137:
138: /**
139: * Creates a new set of symbols for the specified locale.
140: */
141: public Symbols(final Locale locale) {
142: this .locale = locale;
143: numberFormat = NumberFormat.getNumberInstance(locale);
144: numberFormat.setGroupingUsed(false);
145: numberFormat.setMinimumFractionDigits(1);
146: numberFormat.setMaximumFractionDigits(20);
147: /*
148: * The "maximum fraction digits" seems hight for the precision of floating
149: * points (even in double precision), but this is because the above format
150: * do not uses the scientific notation. For example 1E-5 is always formatted
151: * as 0.00001, and 1E-340 would actually need a maximum fraction digits of
152: * 340. For most parameters, such low values should not occurs and may be
153: * rounding error for the 0 value. For semi-major and semi-minor axis, we
154: * often want to avoid exponential notation as well.
155: */
156: if (numberFormat instanceof DecimalFormat) {
157: final char decimalSeparator = ((DecimalFormat) numberFormat)
158: .getDecimalFormatSymbols().getDecimalSeparator();
159: if (decimalSeparator == ',') {
160: separator = ';';
161: }
162: }
163: }
164:
165: /**
166: * Returns {@code true} if the specified WKT contains at least one {@code AXIS[...]} element.
167: * This method tries to make a quick checks taking in account a minimal set of WKT syntax rules.
168: *
169: * @since 2.4
170: */
171: public boolean containsAxis(final CharSequence wkt) {
172: return indexOf(wkt, "AXIS", 0) >= 0;
173: }
174:
175: /**
176: * Returns the index after the specified element in the specified WKT, or -1 if not found.
177: * The element must be followed (ignoring spaces) by an opening bracket. If found, this
178: * method returns the index of the opening bracket after the element.
179: *
180: * @param wkt The WKT to parse.
181: * @param element The element to search. Must contains only uppercase letters.
182: * @param index The index to start the search from.
183: */
184: private int indexOf(final CharSequence wkt, final String element,
185: int index) {
186: assert element.equals(element.trim().toUpperCase(locale)) : element;
187: assert element.indexOf(quote) < 0 : element;
188: boolean isQuoting = false;
189: final int elementLength = element.length();
190: final int length = wkt.length();
191: if (index < length) {
192: char c = wkt.charAt(index);
193: search: while (true) {
194: // Do not parse any content between quotes.
195: if (c == quote) {
196: isQuoting = !isQuoting;
197: }
198: if (isQuoting || !Character.isJavaIdentifierStart(c)) {
199: if (++index == length) {
200: break search;
201: }
202: c = wkt.charAt(index);
203: continue;
204: }
205: // Checks if we have a match.
206: for (int j = 0; j < elementLength; j++) {
207: c = Character.toUpperCase(c);
208: if (c != element.charAt(j)) {
209: // No match. Skip all remaining letters and resume the search.
210: while (Character.isJavaIdentifierPart(c)) {
211: if (++index == length) {
212: break search;
213: }
214: c = wkt.charAt(index);
215: }
216: continue search;
217: }
218: if (++index == length) {
219: break search;
220: }
221: c = wkt.charAt(index);
222: }
223: // Checks if the next character (ignoring space) is an opening brace.
224: while (Character.isWhitespace(c)) {
225: if (++index == length) {
226: break search;
227: }
228: c = wkt.charAt(index);
229: }
230: for (int i = 0; i < openingBrackets.length; i++) {
231: if (c == openingBrackets[i]) {
232: return index;
233: }
234: }
235: }
236: }
237: return -1;
238: }
239: }
|