Source Code Cross Referenced for Preprocessor.java in  » GIS » GeoTools-2.4.1 » org » geotools » referencing » wkt » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.referencing.wkt 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        // J2SE dependencies
021:        import java.io.IOException;
022:        import java.io.Serializable;
023:        import java.io.Writer;
024:        import java.text.FieldPosition;
025:        import java.text.Format;
026:        import java.text.ParseException;
027:        import java.text.ParsePosition;
028:        import java.util.Collections;
029:        import java.util.Iterator;
030:        import java.util.Locale;
031:        import java.util.Map;
032:        import java.util.Set;
033:        import java.util.TreeMap;
034:
035:        // OpenGIS dependencies
036:        import org.opengis.referencing.FactoryException;
037:        import org.opengis.referencing.IdentifiedObject;
038:        import org.opengis.referencing.NoSuchIdentifierException;
039:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
040:        import org.opengis.referencing.operation.MathTransform;
041:
042:        // Geotools dependencies
043:        import org.geotools.io.TableWriter;
044:        import org.geotools.referencing.Console;
045:        import org.geotools.resources.Utilities;
046:        import org.geotools.resources.i18n.Errors;
047:        import org.geotools.resources.i18n.ErrorKeys;
048:        import org.geotools.resources.i18n.Vocabulary;
049:        import org.geotools.resources.i18n.VocabularyKeys;
050:
051:        /**
052:         * A parser that performs string replacements before to delegate the work to an other parser.
053:         * String replacements are specified through calls to the {@link #addDefinition addDefinition}
054:         * method. In the example below, the {@code WGS84} string in the {@linkplain #parseObject
055:         * parseObject} call is expanded into the full <code>GEOGCS["WGS84", ...</code> string before
056:         * to be parsed.
057:         *
058:         * <blockquote><code>
059:         * {@linkplain #addDefinition addDefinition}("WGS84", "GEOGCS[\"WGS84\", DATUM[</code> ...<i>etc</i>... <code>]]<BR>
060:         * {@linkplain #parseObject parseObject}("PROJCS[\"Mercator_1SP\", <strong>WGS84</strong>, PROJECTION[</code> ...<i>etc</i>... <code>]]")</code>
061:         * </blockquote>
062:         *
063:         * @since 2.1
064:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/wkt/Preprocessor.java $
065:         * @version $Id: Preprocessor.java 22295 2006-10-20 00:58:17Z desruisseaux $
066:         * @author Martin Desruisseaux
067:         */
068:        public class Preprocessor extends Format {
069:            /**
070:             * The WKT parser, usually a {@link Parser} object.
071:             */
072:            protected final Format parser;
073:
074:            /**
075:             * The set of objects defined by calls to {@link #addDefinition}.
076:             */
077:            private final Map definitions/*<String,Definition>*/= new TreeMap();
078:
079:            /**
080:             * The unmodifiable set of keys in the {@link #definitions} map. Will be constructed
081:             * only when first needed.
082:             */
083:            private transient Set names;
084:
085:            /**
086:             * A linked list of informations about the replacements performed by {@link #substitutes}.
087:             * Those informations are used by {@link #parseObject(String,Class)} in order to adjust
088:             * {@linkplain ParseException#getErrorOffset error offset} in case of failure.
089:             */
090:            private transient Replacement replacements;
091:
092:            /**
093:             * The initial offset of the line in process of being parsed. This is a helper field
094:             * for use by {@link AbstractConsole} only, in order to produce more accurate information in
095:             * case of {@link ParseException}. This field has no impact on the object returned as a result
096:             * of successful parsing.
097:             */
098:            transient int offset = 0;
099:
100:            /**
101:             * Creates a new preprocessor that delegates the work to the specified parser.
102:             *
103:             * @param parser The WKT parser, usually a {@link Parser} object.
104:             */
105:            public Preprocessor(final Format parser) {
106:                this .parser = parser;
107:            }
108:
109:            /**
110:             * Formats the specified object. This method delegates the work to the
111:             * {@linkplain #parser parser} given at construction time.
112:             *
113:             * @param object     The object to format.
114:             * @param toAppendTo Where the text is to be appended.
115:             * @param position   Identification of a field in the formatted text.
116:             * @return The string buffer passed in as {@code toAppendTo},
117:             *         with formatted text appended
118:             */
119:            public StringBuffer format(final Object object,
120:                    final StringBuffer toAppendTo, final FieldPosition position) {
121:                return parser.format(object, toAppendTo, position);
122:            }
123:
124:            /**
125:             * Parses the specified Well Know Text starting at the specified position.
126:             * The default implementation delegates the work to
127:             * <code>{@link #parseObject(String) parseObject}(wkt.substring(position.getIndex()))</code>.
128:             *
129:             * @param  wkt The text to parse.
130:             * @param  position The index of the first character to parse.
131:             * @return The parsed object, or {@code null} in case of failure.
132:             */
133:            public Object parseObject(final String wkt,
134:                    final ParsePosition position) {
135:                /*
136:                 * NOTE:  the other way around (parseObject(String) invoking
137:                 * parseObject(String,ParsePosition) like the default Format
138:                 * implementation) is not pratical. Among other problems, it
139:                 * doesn't provide any accurate error message.
140:                 */
141:                final int start = position.getIndex();
142:                try {
143:                    return parseObject(wkt.substring(start));
144:                } catch (ParseException exception) {
145:                    position.setIndex(start);
146:                    position.setErrorIndex(exception.getErrorOffset() + start);
147:                    return null;
148:                }
149:            }
150:
151:            /**
152:             * Parses the specified Well Know Text without restriction on the expected type.
153:             * The default implementation delegates the work to
154:             * <code>{@link #parseObject(String,Class) parseObject}(wkt, Object.class)</code>.
155:             *
156:             * @param  wkt The text to parse.
157:             * @return The parsed object.
158:             * @throws ParseException if the text can't be parsed.
159:             */
160:            public Object parseObject(final String wkt) throws ParseException {
161:                try {
162:                    return parseObject(wkt, Object.class);
163:                } catch (FactoryException cause) {
164:                    final ParseException e = new ParseException(cause
165:                            .getLocalizedMessage(), 0);
166:                    e.initCause(cause);
167:                    throw e;
168:                }
169:            }
170:
171:            /**
172:             * Parses the specified text and ensure that the resulting object is of the specified type.
173:             * The text can be any of the following:
174:             * <BR>
175:             * <UL>
176:             *   <LI>A name declared in some previous call to
177:             *       <code>{@linkplain #addDefinition addDefinition}(name, ...)</code>.</LI>
178:             *   <LI>A Well Know Text, which may contains itself shortcuts declared in
179:             *       previous call to {@code addDefinition}. This text is given to
180:             *       the underlying {@link #parser}.</LI>
181:             *   <LI>Any services provided by subclasses. For example a subclass way recognize
182:             *       some authority code like {@code EPSG:6326}.</LI>
183:             * </UL>
184:             *
185:             * @param  text The text, as a name, a WKT to parse, or an authority code.
186:             * @param  type The expected type for the object to be parsed (usually a
187:             *         <code>{@linkplain CoordinateReferenceSystem}.class</code> or
188:             *         <code>{@linkplain MathTransform}.class</code>).
189:             * @return The object.
190:             * @throws ParseException if parsing the specified WKT failed.
191:             * @throws FactoryException if the object is not of the expected type.
192:             */
193:            public Object parseObject(String text, final Class type)
194:                    throws ParseException, FactoryException {
195:                Object value;
196:                final Definition def = (Definition) definitions.get(text);
197:                if (def != null) {
198:                    value = def.asObject;
199:                    if (type.isAssignableFrom(value.getClass())) {
200:                        return value;
201:                    }
202:                } else if (!isIdentifier(text)) {
203:                    /*
204:                     * The specified string was not found in the definitions map. Try to parse it as a
205:                     * WKT, but only if it contains more than a single word. This later condition exists
206:                     * only in order to produces a more accurate error message (WKT parsing of a single
207:                     * word is garantee to fail). In any case, the definitions map is not updated since
208:                     * this method is not invoked from the SET instruction.
209:                     */
210:                    text = substitute(text);
211:                    value = forwardParse(text);
212:                    final Class actualType = value.getClass();
213:                    if (type.isAssignableFrom(actualType)) {
214:                        return value;
215:                    }
216:                    throw new FactoryException(Errors.format(
217:                            ErrorKeys.ILLEGAL_CLASS_$2, Utilities
218:                                    .getShortName(actualType), Utilities
219:                                    .getShortName(type)));
220:                }
221:                throw new NoSuchIdentifierException(Errors.format(
222:                        ErrorKeys.NO_SUCH_AUTHORITY_CODE_$2, Utilities
223:                                .getShortName(type), text), text);
224:            }
225:
226:            /**
227:             * Parses a WKT. This method delegates the work to the {@link #parser}, but
228:             * catch the exception in case of failure. The exception is rethrown with the
229:             * {@linkplain ParseException#getErrorIndex error index} adjusted in order to
230:             * point to the character in the original text (before substitutions).
231:             *
232:             * @param  text The WKT to parse.
233:             * @return The object.
234:             * @throws ParseException if the parsing failed.
235:             */
236:            private Object forwardParse(final String text)
237:                    throws ParseException {
238:                try {
239:                    return parser.parseObject(text);
240:                } catch (ParseException exception) {
241:                    int shift = 0;
242:                    int errorOffset = exception.getErrorOffset();
243:                    for (Replacement r = replacements; r != null; r = r.next) {
244:                        if (errorOffset < r.lower) {
245:                            break;
246:                        }
247:                        if (errorOffset < r.upper) {
248:                            errorOffset = r.lower;
249:                            break;
250:                        }
251:                        shift += r.shift;
252:                    }
253:                    final ParseException adjusted = new ParseException(
254:                            exception.getLocalizedMessage(), errorOffset
255:                                    - shift);
256:                    adjusted.setStackTrace(exception.getStackTrace());
257:                    adjusted.initCause(exception.getCause());
258:                    throw adjusted;
259:                }
260:            }
261:
262:            /**
263:             * For every definition key found in the given string, substitute
264:             * the key by its value. The replacement will not be performed if
265:             * the key was found between two quotation marks.
266:             *
267:             * @param  text The string to process.
268:             * @return The string with all keys replaced by their values.
269:             */
270:            private String substitute(final String text) {
271:                Replacement last;
272:                replacements = last = new Replacement(0, 0, offset);
273:                StringBuffer buffer = null;
274:                for (final Iterator it = definitions.entrySet().iterator(); it
275:                        .hasNext();) {
276:                    final Map.Entry entry = (Map.Entry) it.next();
277:                    final String name = (String) entry.getKey();
278:                    final Definition def = (Definition) entry.getValue();
279:                    int index = (buffer != null) ? buffer.indexOf(name) : text
280:                            .indexOf(name);
281:                    while (index >= 0) {
282:                        /*
283:                         * An occurence of the text to substitute was found. First, make sure
284:                         * that the occurence found is a full word  (e.g. if the occurence to
285:                         * search is "WGS84", do not accept "TOWGS84").
286:                         */
287:                        final int upper = index + name.length();
288:                        final CharSequence cs = (buffer != null) ? (CharSequence) buffer
289:                                : (CharSequence) text;
290:                        if ((index == 0 || !Character.isJavaIdentifierPart(cs
291:                                .charAt(index - 1)))
292:                                && (upper == cs.length() || !Character
293:                                        .isJavaIdentifierPart(cs.charAt(upper)))) {
294:                            /*
295:                             * Count the number of quotes before the text to substitute. If this
296:                             * number is odd, then the text is between quotes and should not be
297:                             * substituted.
298:                             */
299:                            int count = 0;
300:                            for (int scan = index; --scan >= 0;) {
301:                                scan = (buffer != null) ? buffer.lastIndexOf(
302:                                        "\"", scan) : text.lastIndexOf('"',
303:                                        scan);
304:                                if (scan < 0) {
305:                                    break;
306:                                }
307:                                count++;
308:                            }
309:                            if ((count & 1) == 0) {
310:                                /*
311:                                 * An even number of quotes was found before the text to substitute.
312:                                 * Performs the substitution and keep trace of this replacement in a
313:                                 * chained list of 'Replacement' objects.
314:                                 */
315:                                if (buffer == null) {
316:                                    buffer = new StringBuffer(text);
317:                                    assert buffer.indexOf(name, index) == index;
318:                                }
319:                                final String value = def.asString;
320:                                buffer.replace(index, upper, value);
321:                                final int change = value.length()
322:                                        - name.length();
323:                                last = last.next = new Replacement(index, index
324:                                        + value.length(), change);
325:                                index = buffer.indexOf(name, index + change);
326:                                // Note: it is okay to skip the text we just replaced, since the
327:                                //       'definitions' map do not contains nested definitions.
328:                                continue;
329:                            }
330:                        }
331:                        /*
332:                         * The substitution was not performed because the text found was not a word,
333:                         * or was between quotes. Search the next occurence.
334:                         */
335:                        index += name.length();
336:                        index = (buffer != null) ? buffer.indexOf(name, index)
337:                                : text.indexOf(name, index);
338:                    }
339:                }
340:                return (buffer != null) ? buffer.toString() : text;
341:            }
342:
343:            /**
344:             * Adds a predefined Well Know Text (WKT). The {@code value} argument given to this method
345:             * can contains itself other definitions specified in some previous calls to this method.
346:             *
347:             * @param  name The name for the definition to be added.
348:             * @param  value The Well Know Text (WKT) represented by the name.
349:             * @throws IllegalArgumentException if the name is invalid.
350:             * @throws ParseException if the WKT can't be parsed.
351:             */
352:            public void addDefinition(final String name, String value)
353:                    throws ParseException {
354:                if (value == null || value.trim().length() == 0) {
355:                    throw new IllegalArgumentException(Errors
356:                            .format(ErrorKeys.MISSING_WKT_DEFINITION));
357:                }
358:                if (!isIdentifier(name)) {
359:                    throw new IllegalArgumentException(Errors.format(
360:                            ErrorKeys.ILLEGAL_IDENTIFIER_$1, name));
361:                }
362:                value = substitute(value);
363:                final Definition newDef = new Definition(value,
364:                        forwardParse(value));
365:                final Definition oldDef = (Definition) definitions.put(name,
366:                        newDef);
367:            }
368:
369:            /**
370:             * Removes a definition set in some previous call to
371:             * <code>{@linkplain #addDefinition addDefinition}(name, ...)</code>.
372:             *
373:             * @param name The name of the definition to remove.
374:             */
375:            public void removeDefinition(final String name) {
376:                definitions.remove(name);
377:            }
378:
379:            /**
380:             * Returns an unmodifiable set which contains all definition's names given to the
381:             * <code>{@linkplain #addDefinition addDefinition}(name, ...)</code> method. The
382:             * elements in this set are sorted in alphabetical order.
383:             */
384:            public Set getDefinitionNames() {
385:                if (names == null) {
386:                    names = Collections.unmodifiableSet(definitions.keySet());
387:                }
388:                return names;
389:            }
390:
391:            /**
392:             * Prints to the specified stream a table of all definitions.
393:             * The content of this table is inferred from the values given to the
394:             * {@link #addDefinition} method.
395:             *
396:             * @param  out writer The output stream where to write the table.
397:             * @throws IOException if an error occured while writting to the output stream.
398:             */
399:            public void printDefinitions(final Writer out) throws IOException {
400:                final Locale locale = null;
401:                final Vocabulary resources = Vocabulary.getResources(locale);
402:                final TableWriter table = new TableWriter(out, " \u2502 ");
403:                table.setMultiLinesCells(true);
404:                table.writeHorizontalSeparator();
405:                table.write(resources.getString(VocabularyKeys.NAME));
406:                table.nextColumn();
407:                table.write(resources.getString(VocabularyKeys.TYPE));
408:                table.nextColumn();
409:                table.write(resources.getString(VocabularyKeys.DESCRIPTION));
410:                table.nextLine();
411:                table.writeHorizontalSeparator();
412:                for (final Iterator it = definitions.entrySet().iterator(); it
413:                        .hasNext();) {
414:                    final Map.Entry entry = (Map.Entry) it.next();
415:                    final Object object = ((Definition) entry.getValue()).asObject;
416:                    table.write(String.valueOf(entry.getKey()));
417:                    table.nextColumn();
418:                    table.write(Utilities.getShortClassName(object));
419:                    table.nextColumn();
420:                    if (object instanceof  IdentifiedObject) {
421:                        table.write(((IdentifiedObject) object).getName()
422:                                .getCode());
423:                    }
424:                    table.nextLine();
425:                }
426:                table.writeHorizontalSeparator();
427:                table.flush();
428:            }
429:
430:            /**
431:             * Returns {@code true} if the specified text is a valid identifier.
432:             */
433:            private static boolean isIdentifier(final String text) {
434:                for (int i = text.length(); --i >= 0;) {
435:                    final char c = text.charAt(i);
436:                    if (!Character.isJavaIdentifierPart(c) && c != ':') {
437:                        return false;
438:                    }
439:                }
440:                return true;
441:            }
442:
443:            /**
444:             * An entry for the {@link Console#definitions} map. This entry contains a definition
445:             * as a well know text (WKT), and the parsed value for this WKT (usually a
446:             * {@linkplain CoordinateReferenceSystem} or a {@linkplain MathTransform} object).
447:             */
448:            private static final class Definition implements  Serializable {
449:                /**
450:                 * The definition as a string. This string should not contains anymore
451:                 * shortcut to substitute by an other WKT (i.e. compound definitions
452:                 * must be resolved before to construct a {@code Definition} object).
453:                 */
454:                public final String asString;
455:
456:                /**
457:                 * The definition as an object (usually a {@linkplain CoordinateReferenceSystem}
458:                 * or a {@linkplain MathTransform} object).
459:                 */
460:                public final Object asObject;
461:
462:                /**
463:                 * Constructs a new definition.
464:                 */
465:                public Definition(final String asString, final Object asObject) {
466:                    this .asString = asString;
467:                    this .asObject = asObject;
468:                }
469:            }
470:
471:            /**
472:             * Contains informations about the index changes induced by a replacement in a string.
473:             * All index refer to the string <strong>after</strong> the replacement. The substring
474:             * at index between {@link #lower} inclusive and {@link #upper} exclusive is the replacement
475:             * string. The {@link #shift} is the difference between the replacement substring length and
476:             * the replaced substring length.
477:             */
478:            private static final class Replacement {
479:                /** The lower index in the target string, inclusive. */
480:                public final int lower;
481:                /** The upper index in the target string, exclusive. */
482:                public final int upper;
483:                /** The shift from source string to target string.   */
484:                public final int shift;
485:                /** The next element in the linked list.             */
486:                public Replacement next;
487:
488:                /** Constructs a new index shift initialized with the given values. */
489:                public Replacement(final int lower, final int upper,
490:                        final int shift) {
491:                    this .lower = lower;
492:                    this .upper = upper;
493:                    this .shift = shift;
494:                }
495:
496:                /**
497:                 * Returns a string representation for debugging purpose.
498:                 */
499:                public String toString() {
500:                    final StringBuffer buffer = new StringBuffer();
501:                    for (Replacement r = this ; r != null; r = r.next) {
502:                        if (r != this ) {
503:                            buffer.append(", ");
504:                        }
505:                        buffer.append('[').append(r.lower).append("..").append(
506:                                r.upper).append("] \u2192 ").append(r.shift);
507:                    }
508:                    return buffer.toString();
509:                }
510:            }
511:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.