001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.xml.gml;
017:
018: import java.io.IOException;
019: import java.net.URI;
020: import java.util.Calendar;
021: import java.util.Date;
022: import java.util.HashMap;
023: import java.util.Map;
024: import java.util.NoSuchElementException;
025: import java.util.logging.Level;
026: import java.util.logging.Logger;
027:
028: import org.geotools.data.FeatureReader;
029: import org.geotools.feature.Feature;
030: import org.geotools.feature.FeatureType;
031: import org.geotools.xml.DocumentFactory;
032: import org.geotools.xml.XMLHandlerHints;
033: import org.xml.sax.SAXException;
034:
035: /**
036: * <p>
037: * Feature Buffer ... acts as a FeatureReader by making itself as a seperate
038: * thread prior starting execution with the SAX Parser.
039: * </p>
040: *
041: * @author dzwiers
042: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/xml/src/main/java/org/geotools/xml/gml/FCBuffer.java $
043: */
044: public class FCBuffer extends Thread implements FeatureReader {
045: /** Last feature is in the buffer */
046: public static final int FINISH = -1;
047:
048: /** DOCUMENT ME! */
049: public static final int STOP = -2;
050:
051: /** DOCUMENT ME! */
052: protected static Logger logger = getLogger();
053:
054: // positive number is the number of feature to parse before yield
055:
056: /** DOCUMENT ME! */
057: protected int state = 0;
058: private Feature[] features;
059:
060: private int end;
061: private int size;
062: private int head;
063: private int timeout = 1000;
064: private URI document; // for run
065: protected SAXException exception = null;
066:
067: private FCBuffer() {
068: // should not be called
069: super ("Feature Collection Buffer");
070: }
071:
072: /**
073: *
074: * @param document
075: * @param capacity
076: * @param timeout
077: * @param ft Nullable
078: */
079: protected FCBuffer(URI document, int capacity, int timeout,
080: FeatureType ft) {
081: super ("Feature Collection Buffer");
082: features = new Feature[capacity];
083: this .timeout = timeout;
084: this .document = document;
085: end = size = head = 0;
086: this .ft = ft;
087: }
088:
089: /**
090: * Returns the logger to be used for this class.
091: *
092: * @todo Logger.setLevel(...) should not be invoked, because it override any user setting in
093: * {@code jre/lib/logging.properties}. Users should edit their properties file instead.
094: * If Geotools is too verbose below the warning level, then some log messages should
095: * probably be changed from Level.INFO to Level.FINE.
096: */
097: private static final Logger getLogger() {
098: Logger l = org.geotools.util.logging.Logging
099: .getLogger("org.geotools.xml.gml");
100: l.setLevel(Level.WARNING);
101: return l;
102: }
103:
104: /**
105: * DOCUMENT ME!
106: *
107: * @return The buffer size
108: */
109: public int getSize() {
110: return size;
111: }
112:
113: /**
114: * DOCUMENT ME!
115: *
116: * @return The buffer capacity
117: */
118: public int getCapacity() {
119: return features.length;
120: }
121:
122: /**
123: * DOCUMENT ME!
124: *
125: * @return The buffer capacity
126: */
127: public int getTimeout() {
128: return timeout;
129: }
130:
131: /**
132: * <p>
133: * Adds a feature to the buffer
134: * </p>
135: *
136: * @param f Feature to add
137: *
138: * @return false if unable to add the feature
139: */
140: protected boolean addFeature(Feature f) {
141: if (ft == null) {
142: ft = f.getFeatureType();
143: }
144:
145: if (size >= features.length) {
146: return false;
147: }
148:
149: synchronized (this ) {
150: notify();
151:
152: features[end] = f;
153: end++;
154:
155: if (end == features.length) {
156: end = 0;
157: }
158:
159: size++;
160: }
161: return true;
162: }
163:
164: /**
165: * <p>
166: * The prefered method of using this class, this will return the Feature
167: * Reader for the document specified, using the specified buffer capacity.
168: * </p>
169: *
170: * @param document URL to parse
171: * @param capacity
172: *
173: *
174: * @throws SAXException
175: */
176: public static FeatureReader getFeatureReader(URI document,
177: int capacity) throws SAXException {
178: return getFeatureReader(document, capacity, 1000, null);
179: }
180:
181: public static FeatureReader getFeatureReader(URI document,
182: int capacity, FeatureType ft) throws SAXException {
183: return getFeatureReader(document, capacity, 1000, ft);
184: }
185:
186: public static FeatureReader getFeatureReader(URI document,
187: int capacity, int timeout) throws SAXException {
188:
189: return getFeatureReader(document, capacity, timeout, null);
190: }
191:
192: public static FeatureReader getFeatureReader(URI document,
193: int capacity, int timeout, FeatureType ft)
194: throws SAXException {
195: FCBuffer fc = new FCBuffer(document, capacity, timeout, ft);
196: fc.start(); // calls run
197:
198: if (fc.exception != null) {
199: throw fc.exception;
200: }
201:
202: if (fc.getFeatureType() != null
203: && fc.getFeatureType().getDefaultGeometry() != null
204: && fc.getFeatureType().getDefaultGeometry()
205: .getCoordinateSystem() == null) {
206: // load crs
207: // Feature f = fc.peek();
208: // TODO set crs here.
209: }
210: return fc;
211: }
212:
213: protected FeatureType ft = null;
214:
215: private volatile Date lastUpdate;
216:
217: /**
218: * DOCUMENT ME!
219: *
220: * @return DOCUMENT ME!
221: *
222: * @see org.geotools.data.FeatureReader#getFeatureType()
223: */
224: public FeatureType getFeatureType() {
225: if (ft != null)
226: return ft;
227: Date d = new Date(Calendar.getInstance().getTimeInMillis()
228: + timeout);
229:
230: while ((ft == null) && ((state != FINISH) && (state != STOP))) {
231: yield(); // let the parser run ... this is being called from
232:
233: if (d.before(Calendar.getInstance().getTime())) {
234: exception = new SAXException("Timeout");
235: state = STOP;
236: }
237: }
238:
239: // the original thread
240: if ((state == FINISH) || (state == STOP)) {
241: return ft;
242: }
243:
244: return ft;
245: }
246:
247: /**
248: * @see org.geotools.data.FeatureReader#next()
249: */
250: public Feature next() throws IOException, NoSuchElementException {
251: if (exception != null) {
252: state = STOP;
253: IOException e = new IOException(exception.toString());
254: e.initCause(exception);
255: throw e;
256: }
257:
258: Feature f = null;
259: synchronized (this ) {
260: size--;
261: f = features[head++];
262: notify();
263:
264: if (head == features.length) {
265: head = 0;
266: }
267: }
268: return f;
269: }
270:
271: /**
272: * @see org.geotools.data.FeatureReader#next()
273: */
274: public Feature peek() throws IOException, NoSuchElementException {
275: if (exception != null) {
276: state = STOP;
277: IOException e = new IOException(exception.toString());
278: e.initCause(exception);
279: throw e;
280: }
281:
282: Feature f = features[head];
283: return f;
284: }
285:
286: /**
287: * @see org.geotools.data.FeatureReader#hasNext()
288: */
289: public boolean hasNext() throws IOException {
290: if (exception instanceof StopException) {
291: return false;
292: }
293:
294: if (exception != null) {
295: IOException e = new IOException(exception.toString());
296: e.initCause(exception);
297: throw e;
298: }
299:
300: logger.finest("hasNext " + size);
301:
302: resetTimer();
303:
304: while ((size <= 1) && (state != FINISH) && (state != STOP)) { //&& t>0) {
305:
306: if (exception != null) {
307: state = STOP;
308: IOException e = new IOException(exception.toString());
309: e.initCause(exception);
310: throw e;
311: }
312:
313: logger.finest("waiting for parser");
314: try {
315: synchronized (this ) {
316: wait(200);
317: }
318: } catch (InterruptedException e) {
319: //just continue;
320: }
321:
322: if (lastUpdate.before(new Date(Calendar.getInstance()
323: .getTimeInMillis()
324: - timeout))) {
325: exception = new SAXException("Timeout");
326: state = STOP;
327: }
328: }
329:
330: if (state == STOP) {
331: if (exception != null) {
332: IOException e = new IOException(exception.toString());
333: e.initCause(exception);
334: throw e;
335: }
336:
337: return false;
338: }
339:
340: if (state == FINISH) {
341: return !(size == 0);
342: }
343:
344: if (size == 0) {
345: state = STOP;
346:
347: if (exception != null) {
348: throw new IOException(exception.toString());
349: }
350:
351: throw new IOException("There was an error");
352: }
353:
354: return true;
355: }
356:
357: /**
358: * @see org.geotools.data.FeatureReader#close()
359: */
360: public void close() {
361: state = STOP; // note for the sax parser
362: interrupt();
363: }
364:
365: /**
366: * @see java.lang.Runnable#run()
367: */
368: public void run() {
369: XMLHandlerHints hints = new XMLHandlerHints();
370: initHints(hints);
371:
372: try {
373: DocumentFactory.getInstance(document, hints);
374:
375: // start parsing until buffer part full, then yield();
376: } catch (StopException e) {
377: exception = e;
378: state = STOP;
379: yield();
380: } catch (SAXException e) {
381: exception = e;
382: state = STOP;
383: yield();
384: }
385: }
386:
387: /**
388: * Called before parsing the FeatureCollection. Subclasses may override to set their custom hints.
389: */
390: protected void initHints(XMLHandlerHints hints) {
391: hints.put(XMLHandlerHints.STREAM_HINT, this );
392: hints.put(XMLHandlerHints.FLOW_HANDLER_HINT,
393: new FCFlowHandler());
394: if (this .ft != null) {
395: hints.put("DEBUG_INFO_FEATURE_TYPE_NAME", ft.getTypeName());
396: }
397: }
398:
399: /**
400: * DOCUMENT ME!
401: *
402: * @author $author$
403: * @version $Revision: 1.9 $
404: */
405: public static class StopException extends SAXException {
406: public StopException() {
407: super ("Stopping");
408: }
409: }
410:
411: public int getInternalState() {
412: return state;
413: }
414:
415: public void resetTimer() {
416: this.lastUpdate = Calendar.getInstance().getTime();
417: }
418: }
|