~ubuntu-branches/ubuntu/natty/jts/natty

« back to all changes in this revision

Viewing changes to src/com/vividsolutions/jts/io/WKTReader.java

  • Committer: Bazaar Package Importer
  • Author(s): Wolfgang Baer
  • Date: 2005-08-07 14:12:35 UTC
  • Revision ID: james.westby@ubuntu.com-20050807141235-7hy3ll3xpq79djcb
Tags: upstream-1.6
ImportĀ upstreamĀ versionĀ 1.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
 
 
3
/*
 
4
 * The JTS Topology Suite is a collection of Java classes that
 
5
 * implement the fundamental operations required to validate a given
 
6
 * geo-spatial data set to a known topological specification.
 
7
 *
 
8
 * Copyright (C) 2001 Vivid Solutions
 
9
 *
 
10
 * This library is free software; you can redistribute it and/or
 
11
 * modify it under the terms of the GNU Lesser General Public
 
12
 * License as published by the Free Software Foundation; either
 
13
 * version 2.1 of the License, or (at your option) any later version.
 
14
 *
 
15
 * This library is distributed in the hope that it will be useful,
 
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * Lesser General Public License for more details.
 
19
 *
 
20
 * You should have received a copy of the GNU Lesser General Public
 
21
 * License along with this library; if not, write to the Free Software
 
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
23
 *
 
24
 * For more information, contact:
 
25
 *
 
26
 *     Vivid Solutions
 
27
 *     Suite #1A
 
28
 *     2328 Government Street
 
29
 *     Victoria BC  V8T 5G5
 
30
 *     Canada
 
31
 *
 
32
 *     (250)385-6040
 
33
 *     www.vividsolutions.com
 
34
 */
 
35
package com.vividsolutions.jts.io;
 
36
 
 
37
import com.vividsolutions.jts.geom.*;
 
38
import com.vividsolutions.jts.io.ParseException;
 
39
 
 
40
import com.vividsolutions.jts.util.Assert;
 
41
import java.io.IOException;
 
42
import java.io.Reader;
 
43
import java.io.StreamTokenizer;
 
44
import java.io.StringReader;
 
45
import java.util.ArrayList;
 
46
 
 
47
/**
 
48
 *  Converts a Well-Known Text string to a <code>Geometry</code>.
 
49
 * <p>
 
50
 *  The <code>WKTReader</code> allows
 
51
 *  extracting <code>Geometry</code> objects from either input streams or
 
52
 *  internal strings. This allows it to function as a parser to read <code>Geometry</code>
 
53
 *  objects from text blocks embedded in other data formats (e.g. XML). <P>
 
54
 * <p>
 
55
 * The Well-known
 
56
 *  Text format is defined in the <A HREF="http://www.opengis.org/techno/specs.htm">
 
57
 *  OpenGIS Simple Features Specification for SQL</A> . <P>
 
58
 * <p>
 
59
 *  <B>Note: </B> There is an inconsistency in the SFS. The WKT grammar states
 
60
 *  that <code>MultiPoints</code> are represented by <code>MULTIPOINT ( ( x y), (x y) )</code>
 
61
 *  , but the examples show <code>MultiPoint</code>s as <code>MULTIPOINT ( x y, x y )</code>
 
62
 *  . Other implementations follow the latter syntax, so JTS will adopt it as
 
63
 *  well.
 
64
 *
 
65
 *  A <code>WKTReader</code> is parameterized by a <code>GeometryFactory</code>
 
66
 *  , to allow it to create <code>Geometry</code> objects of the appropriate
 
67
 *  implementation. In particular, the <code>GeometryFactory</code> will
 
68
 *  determine the <code>PrecisionModel</code> and <code>SRID</code> that is
 
69
 *  used. <P>
 
70
 *
 
71
 *  The <code>WKTReader</code> will convert the input numbers to the precise
 
72
 *  internal representation.
 
73
 *
 
74
 *  Reads non-standard "LINEARRING" tags.
 
75
 *
 
76
 *@version 1.6
 
77
 */
 
78
public class WKTReader {
 
79
  private GeometryFactory geometryFactory;
 
80
  private PrecisionModel precisionModel;
 
81
 
 
82
  /**
 
83
   * Creates a WKTReader that creates objects using a basic GeometryFactory.
 
84
   */
 
85
  public WKTReader() {
 
86
    this(new GeometryFactory());
 
87
  }
 
88
 
 
89
  /**
 
90
   *  Creates a <code>WKTReader</code> that creates objects using the given
 
91
   *  <code>GeometryFactory</code>.
 
92
   *
 
93
   *@param  geometryFactory  the factory used to create <code>Geometry</code>s.
 
94
   */
 
95
  public WKTReader(GeometryFactory geometryFactory) {
 
96
    this.geometryFactory = geometryFactory;
 
97
    precisionModel = geometryFactory.getPrecisionModel();
 
98
  }
 
99
 
 
100
  
 
101
 
 
102
        /**
 
103
     * Converts a Well-known Text representation to a <code>Geometry</code>.
 
104
     * 
 
105
     * @param wellKnownText
 
106
     *            one or more <Geometry Tagged Text>strings (see the OpenGIS
 
107
     *            Simple Features Specification) separated by whitespace
 
108
     * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
 
109
     * @throws ParseException
 
110
     *             if a parsing problem occurs
 
111
         */
 
112
  public Geometry read(String wellKnownText) throws ParseException {
 
113
    StringReader reader = new StringReader(wellKnownText);
 
114
    try {
 
115
      return read(reader);
 
116
    }
 
117
    finally {
 
118
      reader.close();
 
119
    }
 
120
  }
 
121
 
 
122
  /**
 
123
   *  Converts a Well-known Text representation to a <code>Geometry</code>.
 
124
   *
 
125
   *@param  reader           a Reader which will return a <Geometry Tagged Text>
 
126
   *      string (see the OpenGIS Simple Features Specification)
 
127
   *@return                  a <code>Geometry</code> read from <code>reader</code>
 
128
   *@throws  ParseException  if a parsing problem occurs
 
129
   */
 
130
  public Geometry read(Reader reader) throws ParseException {
 
131
    StreamTokenizer tokenizer = new StreamTokenizer(reader);
 
132
    try {
 
133
      return readGeometryTaggedText(tokenizer);
 
134
    }
 
135
    catch (IOException e) {
 
136
      throw new ParseException(e.toString());
 
137
    }
 
138
  }
 
139
 
 
140
  /**
 
141
   *  Returns the next array of <code>Coordinate</code>s in the stream.
 
142
   *
 
143
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
144
   *      format. The next element returned by the stream should be "(" (the
 
145
   *      beginning of "(x1 y1, x2 y2, ..., xn yn)") or "EMPTY".
 
146
   *@return                  the next array of <code>Coordinate</code>s in the
 
147
   *      stream, or an empty array if "EMPTY" is the next element returned by
 
148
   *      the stream.
 
149
   *@throws  IOException     if an I/O error occurs
 
150
   *@throws  ParseException  if an unexpected token was encountered
 
151
   */
 
152
  private Coordinate[] getCoordinates(StreamTokenizer tokenizer)
 
153
      throws IOException, ParseException
 
154
  {
 
155
    String nextToken = getNextEmptyOrOpener(tokenizer);
 
156
    if (nextToken.equals("EMPTY")) {
 
157
      return new Coordinate[]{};
 
158
    }
 
159
    ArrayList coordinates = new ArrayList();
 
160
    coordinates.add(getPreciseCoordinate(tokenizer));
 
161
    nextToken = getNextCloserOrComma(tokenizer);
 
162
    while (nextToken.equals(",")) {
 
163
      coordinates.add(getPreciseCoordinate(tokenizer));
 
164
      nextToken = getNextCloserOrComma(tokenizer);
 
165
    }
 
166
    Coordinate[] array = new Coordinate[coordinates.size()];
 
167
    return (Coordinate[]) coordinates.toArray(array);
 
168
  }
 
169
 
 
170
  private Coordinate getPreciseCoordinate(StreamTokenizer tokenizer)
 
171
      throws IOException, ParseException
 
172
  {
 
173
    Coordinate coord = new Coordinate();
 
174
    coord.x = getNextNumber(tokenizer);
 
175
    coord.y = getNextNumber(tokenizer);
 
176
    if (isNumberNext(tokenizer)) {
 
177
        coord.z = getNextNumber(tokenizer);
 
178
    }
 
179
    precisionModel.makePrecise(coord);
 
180
    return coord;
 
181
  }
 
182
  private boolean isNumberNext(StreamTokenizer tokenizer) throws IOException {
 
183
      try {
 
184
          return tokenizer.nextToken() == StreamTokenizer.TT_NUMBER;
 
185
      }
 
186
      finally {
 
187
          tokenizer.pushBack();
 
188
      }
 
189
  }
 
190
  /**
 
191
   *  Returns the next number in the stream.
 
192
   *
 
193
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
194
   *      format. The next token must be a number.
 
195
   *@return                  the next number in the stream
 
196
   *@throws  ParseException  if the next token is not a number
 
197
   *@throws  IOException     if an I/O error occurs
 
198
   */
 
199
  private double getNextNumber(StreamTokenizer tokenizer) throws IOException,
 
200
      ParseException {
 
201
    int type = tokenizer.nextToken();
 
202
    switch (type) {
 
203
      case StreamTokenizer.TT_EOF:
 
204
        throw new ParseException("Expected number but encountered end of stream");
 
205
      case StreamTokenizer.TT_EOL:
 
206
        throw new ParseException("Expected number but encountered end of line");
 
207
      case StreamTokenizer.TT_NUMBER:
 
208
        return tokenizer.nval;
 
209
      case StreamTokenizer.TT_WORD:
 
210
        throw new ParseException("Expected number but encountered word: " +
 
211
            tokenizer.sval);
 
212
      case '(':
 
213
        throw new ParseException("Expected number but encountered '('");
 
214
      case ')':
 
215
        throw new ParseException("Expected number but encountered ')'");
 
216
      case ',':
 
217
        throw new ParseException("Expected number but encountered ','");
 
218
    }
 
219
    Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: "
 
220
         + type);
 
221
    return 0;
 
222
  }
 
223
 
 
224
  /**
 
225
   *  Returns the next "EMPTY" or "(" in the stream as uppercase text.
 
226
   *
 
227
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
228
   *      format. The next token must be "EMPTY" or "(".
 
229
   *@return                  the next "EMPTY" or "(" in the stream as uppercase
 
230
   *      text.
 
231
   *@throws  ParseException  if the next token is not "EMPTY" or "("
 
232
   *@throws  IOException     if an I/O error occurs
 
233
   */
 
234
  private String getNextEmptyOrOpener(StreamTokenizer tokenizer) throws IOException, ParseException {
 
235
    String nextWord = getNextWord(tokenizer);
 
236
    if (nextWord.equals("EMPTY") || nextWord.equals("(")) {
 
237
      return nextWord;
 
238
    }
 
239
    throw new ParseException("Expected 'EMPTY' or '(' but encountered '" +
 
240
        nextWord + "'");
 
241
  }
 
242
 
 
243
  /**
 
244
   *  Returns the next ")" or "," in the stream.
 
245
   *
 
246
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
247
   *      format. The next token must be ")" or ",".
 
248
   *@return                  the next ")" or "," in the stream
 
249
   *@throws  ParseException  if the next token is not ")" or ","
 
250
   *@throws  IOException     if an I/O error occurs
 
251
   */
 
252
  private String getNextCloserOrComma(StreamTokenizer tokenizer) throws IOException, ParseException {
 
253
    String nextWord = getNextWord(tokenizer);
 
254
    if (nextWord.equals(",") || nextWord.equals(")")) {
 
255
      return nextWord;
 
256
    }
 
257
    throw new ParseException("Expected ')' or ',' but encountered '" + nextWord
 
258
         + "'");
 
259
  }
 
260
 
 
261
  /**
 
262
   *  Returns the next ")" in the stream.
 
263
   *
 
264
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
265
   *      format. The next token must be ")".
 
266
   *@return                  the next ")" in the stream
 
267
   *@throws  ParseException  if the next token is not ")"
 
268
   *@throws  IOException     if an I/O error occurs
 
269
   */
 
270
  private String getNextCloser(StreamTokenizer tokenizer) throws IOException, ParseException {
 
271
    String nextWord = getNextWord(tokenizer);
 
272
    if (nextWord.equals(")")) {
 
273
      return nextWord;
 
274
    }
 
275
    throw new ParseException("Expected ')' but encountered '" + nextWord + "'");
 
276
  }
 
277
 
 
278
  /**
 
279
   *  Returns the next word in the stream as uppercase text.
 
280
   *
 
281
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
282
   *      format. The next token must be a word.
 
283
   *@return                  the next word in the stream as uppercase text
 
284
   *@throws  ParseException  if the next token is not a word
 
285
   *@throws  IOException     if an I/O error occurs
 
286
   */
 
287
  private String getNextWord(StreamTokenizer tokenizer) throws IOException, ParseException {
 
288
    int type = tokenizer.nextToken();
 
289
    switch (type) {
 
290
      case StreamTokenizer.TT_EOF:
 
291
        throw new ParseException("Expected word but encountered end of stream");
 
292
      case StreamTokenizer.TT_EOL:
 
293
        throw new ParseException("Expected word but encountered end of line");
 
294
      case StreamTokenizer.TT_NUMBER:
 
295
        throw new ParseException("Expected word but encountered number: " +
 
296
            tokenizer.nval);
 
297
      case StreamTokenizer.TT_WORD:
 
298
        return tokenizer.sval.toUpperCase();
 
299
      case '(':
 
300
        return "(";
 
301
      case ')':
 
302
        return ")";
 
303
      case ',':
 
304
        return ",";
 
305
    }
 
306
    Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: " + type);
 
307
    return null;
 
308
  }
 
309
 
 
310
  /**
 
311
   *  Creates a <code>Geometry</code> using the next token in the stream.
 
312
   *
 
313
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
314
   *      format. The next tokens must form a &lt;Geometry Tagged Text&gt;.
 
315
   *@return                  a <code>Geometry</code> specified by the next token
 
316
   *      in the stream
 
317
   *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
 
318
   *      shell and holes do not form closed linestrings, or if an unexpected
 
319
   *      token was encountered
 
320
   *@throws  IOException     if an I/O error occurs
 
321
   */
 
322
  private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
323
    String type = getNextWord(tokenizer);
 
324
    if (type.equals("POINT")) {
 
325
      return readPointText(tokenizer);
 
326
    }
 
327
    else if (type.equals("LINESTRING")) {
 
328
      return readLineStringText(tokenizer);
 
329
    }
 
330
    else if (type.equals("LINEARRING")) {
 
331
      return readLinearRingText(tokenizer);
 
332
    }
 
333
    else if (type.equals("POLYGON")) {
 
334
      return readPolygonText(tokenizer);
 
335
    }
 
336
    else if (type.equals("MULTIPOINT")) {
 
337
      return readMultiPointText(tokenizer);
 
338
    }
 
339
    else if (type.equals("MULTILINESTRING")) {
 
340
      return readMultiLineStringText(tokenizer);
 
341
    }
 
342
    else if (type.equals("MULTIPOLYGON")) {
 
343
      return readMultiPolygonText(tokenizer);
 
344
    }
 
345
    else if (type.equals("GEOMETRYCOLLECTION")) {
 
346
      return readGeometryCollectionText(tokenizer);
 
347
    }
 
348
    throw new ParseException("Unknown type: " + type);
 
349
  }
 
350
 
 
351
  /**
 
352
   *  Creates a <code>Point</code> using the next token in the stream.
 
353
   *
 
354
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
355
   *      format. The next tokens must form a &lt;Point Text&gt;.
 
356
   *@return                  a <code>Point</code> specified by the next token in
 
357
   *      the stream
 
358
   *@throws  IOException     if an I/O error occurs
 
359
   *@throws  ParseException  if an unexpected token was encountered
 
360
   */
 
361
  private Point readPointText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
362
    String nextToken = getNextEmptyOrOpener(tokenizer);
 
363
    if (nextToken.equals("EMPTY")) {
 
364
      return geometryFactory.createPoint((Coordinate)null);
 
365
    }
 
366
    Point point = geometryFactory.createPoint(getPreciseCoordinate(tokenizer));
 
367
    getNextCloser(tokenizer);
 
368
    return point;
 
369
  }
 
370
 
 
371
  /**
 
372
   *  Creates a <code>LineString</code> using the next token in the stream.
 
373
   *
 
374
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
375
   *      format. The next tokens must form a &lt;LineString Text&gt;.
 
376
   *@return                  a <code>LineString</code> specified by the next
 
377
   *      token in the stream
 
378
   *@throws  IOException     if an I/O error occurs
 
379
   *@throws  ParseException  if an unexpected token was encountered
 
380
   */
 
381
  private LineString readLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
382
    return geometryFactory.createLineString(getCoordinates(tokenizer));
 
383
  }
 
384
 
 
385
  /**
 
386
   *  Creates a <code>LinearRing</code> using the next token in the stream.
 
387
   *
 
388
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
389
   *      format. The next tokens must form a &lt;LineString Text&gt;.
 
390
   *@return                  a <code>LinearRing</code> specified by the next
 
391
   *      token in the stream
 
392
   *@throws  IOException     if an I/O error occurs
 
393
   *@throws  ParseException  if the coordinates used to create the <code>LinearRing</code>
 
394
   *      do not form a closed linestring, or if an unexpected token was
 
395
   *      encountered
 
396
   */
 
397
  private LinearRing readLinearRingText(StreamTokenizer tokenizer)
 
398
    throws IOException, ParseException
 
399
  {
 
400
    return geometryFactory.createLinearRing(getCoordinates(tokenizer));
 
401
  }
 
402
 
 
403
  /**
 
404
   *  Creates a <code>MultiPoint</code> using the next token in the stream.
 
405
   *
 
406
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
407
   *      format. The next tokens must form a &lt;MultiPoint Text&gt;.
 
408
   *@return                  a <code>MultiPoint</code> specified by the next
 
409
   *      token in the stream
 
410
   *@throws  IOException     if an I/O error occurs
 
411
   *@throws  ParseException  if an unexpected token was encountered
 
412
   */
 
413
  private MultiPoint readMultiPointText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
414
    return geometryFactory.createMultiPoint(toPoints(getCoordinates(tokenizer)));
 
415
  }
 
416
 
 
417
  /**
 
418
   *  Creates an array of <code>Point</code>s having the given <code>Coordinate</code>
 
419
   *  s.
 
420
   *
 
421
   *@param  coordinates  the <code>Coordinate</code>s with which to create the
 
422
   *      <code>Point</code>s
 
423
   *@return              <code>Point</code>s created using this <code>WKTReader</code>
 
424
   *      s <code>GeometryFactory</code>
 
425
   */
 
426
  private Point[] toPoints(Coordinate[] coordinates) {
 
427
    ArrayList points = new ArrayList();
 
428
    for (int i = 0; i < coordinates.length; i++) {
 
429
      points.add(geometryFactory.createPoint(coordinates[i]));
 
430
    }
 
431
    return (Point[]) points.toArray(new Point[]{});
 
432
  }
 
433
 
 
434
  /**
 
435
   *  Creates a <code>Polygon</code> using the next token in the stream.
 
436
   *
 
437
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
438
   *      format. The next tokens must form a &lt;Polygon Text&gt;.
 
439
   *@return                  a <code>Polygon</code> specified by the next token
 
440
   *      in the stream
 
441
   *@throws  ParseException  if the coordinates used to create the <code>Polygon</code>
 
442
   *      shell and holes do not form closed linestrings, or if an unexpected
 
443
   *      token was encountered.
 
444
   *@throws  IOException     if an I/O error occurs
 
445
   */
 
446
  private Polygon readPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
447
    String nextToken = getNextEmptyOrOpener(tokenizer);
 
448
    if (nextToken.equals("EMPTY")) {
 
449
        return geometryFactory.createPolygon(geometryFactory.createLinearRing(
 
450
            new Coordinate[]{}), new LinearRing[]{});
 
451
    }
 
452
    ArrayList holes = new ArrayList();
 
453
    LinearRing shell = readLinearRingText(tokenizer);
 
454
    nextToken = getNextCloserOrComma(tokenizer);
 
455
    while (nextToken.equals(",")) {
 
456
      LinearRing hole = readLinearRingText(tokenizer);
 
457
      holes.add(hole);
 
458
      nextToken = getNextCloserOrComma(tokenizer);
 
459
    }
 
460
    LinearRing[] array = new LinearRing[holes.size()];
 
461
    return geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
 
462
  }
 
463
 
 
464
  /**
 
465
   *  Creates a <code>MultiLineString</code> using the next token in the stream.
 
466
   *
 
467
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
468
   *      format. The next tokens must form a &lt;MultiLineString Text&gt;.
 
469
   *@return                  a <code>MultiLineString</code> specified by the
 
470
   *      next token in the stream
 
471
   *@throws  IOException     if an I/O error occurs
 
472
   *@throws  ParseException  if an unexpected token was encountered
 
473
   */
 
474
  private com.vividsolutions.jts.geom.MultiLineString readMultiLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
475
    String nextToken = getNextEmptyOrOpener(tokenizer);
 
476
    if (nextToken.equals("EMPTY")) {
 
477
      return geometryFactory.createMultiLineString(new LineString[]{});
 
478
    }
 
479
    ArrayList lineStrings = new ArrayList();
 
480
    LineString lineString = readLineStringText(tokenizer);
 
481
    lineStrings.add(lineString);
 
482
    nextToken = getNextCloserOrComma(tokenizer);
 
483
    while (nextToken.equals(",")) {
 
484
      lineString = readLineStringText(tokenizer);
 
485
      lineStrings.add(lineString);
 
486
      nextToken = getNextCloserOrComma(tokenizer);
 
487
    }
 
488
    LineString[] array = new LineString[lineStrings.size()];
 
489
    return geometryFactory.createMultiLineString((LineString[]) lineStrings.toArray(array));
 
490
  }
 
491
 
 
492
  /**
 
493
   *  Creates a <code>MultiPolygon</code> using the next token in the stream.
 
494
   *
 
495
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
496
   *      format. The next tokens must form a &lt;MultiPolygon Text&gt;.
 
497
   *@return                  a <code>MultiPolygon</code> specified by the next
 
498
   *      token in the stream, or if if the coordinates used to create the
 
499
   *      <code>Polygon</code> shells and holes do not form closed linestrings.
 
500
   *@throws  IOException     if an I/O error occurs
 
501
   *@throws  ParseException  if an unexpected token was encountered
 
502
   */
 
503
  private MultiPolygon readMultiPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
504
    String nextToken = getNextEmptyOrOpener(tokenizer);
 
505
    if (nextToken.equals("EMPTY")) {
 
506
      return geometryFactory.createMultiPolygon(new Polygon[]{});
 
507
    }
 
508
    ArrayList polygons = new ArrayList();
 
509
    Polygon polygon = readPolygonText(tokenizer);
 
510
    polygons.add(polygon);
 
511
    nextToken = getNextCloserOrComma(tokenizer);
 
512
    while (nextToken.equals(",")) {
 
513
      polygon = readPolygonText(tokenizer);
 
514
      polygons.add(polygon);
 
515
      nextToken = getNextCloserOrComma(tokenizer);
 
516
    }
 
517
    Polygon[] array = new Polygon[polygons.size()];
 
518
    return geometryFactory.createMultiPolygon((Polygon[]) polygons.toArray(array));
 
519
  }
 
520
 
 
521
  /**
 
522
   *  Creates a <code>GeometryCollection</code> using the next token in the
 
523
   *  stream.
 
524
   *
 
525
   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
 
526
   *      format. The next tokens must form a &lt;GeometryCollection Text&gt;.
 
527
   *@return                  a <code>GeometryCollection</code> specified by the
 
528
   *      next token in the stream
 
529
   *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
 
530
   *      shell and holes do not form closed linestrings, or if an unexpected
 
531
   *      token was encountered
 
532
   *@throws  IOException     if an I/O error occurs
 
533
   */
 
534
  private GeometryCollection readGeometryCollectionText(StreamTokenizer tokenizer) throws IOException, ParseException {
 
535
    String nextToken = getNextEmptyOrOpener(tokenizer);
 
536
    if (nextToken.equals("EMPTY")) {
 
537
      return geometryFactory.createGeometryCollection(new Geometry[]{});
 
538
    }
 
539
    ArrayList geometries = new ArrayList();
 
540
    Geometry geometry = readGeometryTaggedText(tokenizer);
 
541
    geometries.add(geometry);
 
542
    nextToken = getNextCloserOrComma(tokenizer);
 
543
    while (nextToken.equals(",")) {
 
544
      geometry = readGeometryTaggedText(tokenizer);
 
545
      geometries.add(geometry);
 
546
      nextToken = getNextCloserOrComma(tokenizer);
 
547
    }
 
548
    Geometry[] array = new Geometry[geometries.size()];
 
549
    return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
 
550
  }
 
551
}
 
552