001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-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.gml.producer;
017:
018: import com.vividsolutions.jts.geom.Envelope;
019: import com.vividsolutions.jts.geom.Geometry;
020: import org.geotools.data.FeatureReader;
021: import org.geotools.feature.AttributeType;
022: import org.geotools.feature.Feature;
023: import org.geotools.feature.FeatureCollection;
024: import org.geotools.feature.FeatureCollectionIteration;
025: import org.geotools.feature.FeatureIterator;
026: import org.geotools.feature.FeatureType;
027: import org.geotools.feature.GeometryAttributeType;
028: import org.geotools.feature.type.DateUtil;
029: import org.geotools.gml.producer.GeometryTransformer.GeometryTranslator;
030: import org.geotools.xml.transform.TransformerBase;
031: import org.opengis.referencing.crs.CoordinateReferenceSystem;
032: import org.xml.sax.Attributes;
033: import org.xml.sax.ContentHandler;
034: import org.xml.sax.SAXException;
035: import org.xml.sax.helpers.AttributesImpl;
036: import org.xml.sax.helpers.NamespaceSupport;
037: import java.io.IOException;
038: import java.text.SimpleDateFormat;
039: import java.util.Date;
040: import java.util.HashMap;
041: import java.util.HashSet;
042: import java.util.Map;
043: import java.util.Set;
044: import java.util.logging.Logger;
045:
046: /**
047: * FeatureTransformer provides a mechanism for converting Feature objects into
048: * (hopefully) valid gml. This is a work in progress, so please be patient. A
049: * simple example of how to use this class follows:
050: * <pre>
051: * FeatureCollection collection; // can also use FeatureReader!!
052: * OutputStream out;
053: * FeatureTransformer ft = new FeatureTransformer();
054: * // set the indentation to 4 spaces
055: * ft.setIndentation(4);
056: * // this will allow Features with the FeatureType which has the namespace
057: * // "http://somewhere.org" to be prefixed with xxx...
058: * ft.getFeatureNamespaces().declarePrefix("xxx","http://somewhere.org");
059: * // transform
060: * ft.transform(collection,out);
061: * </pre>
062: * <b>The above example assumes a homogenous collection of Features whose
063: * FeatureType has the namespace "http://somewhere.org"</b> but note that not
064: * all DataSources currently provide FeatureTypes with a namespace... There
065: * are two other mechanisms for prefixing your Features.<br>
066: * 1) Map a specific FeatureType <b>by identity</b> to prefix and nsURI
067: * <pre>
068: * FeatureType fc;
069: * FeatureTransformer ft = new FeatureTransformer();
070: * ft.getFeatureTypeNamespaces().declareNamespace(fc,"xxx","http://somewhere.org");
071: * </pre>
072: * 2) Provide a default namespace for any Features whose FeatureType either has
073: * an empty namespace, OR, has not been mapped using the previous method. This
074: * is basically a catch-all mechanism.
075: * <pre>
076: * FeatureTransformer ft = new FeatureTransformer();
077: * ft.getFeatureTypeNamespaces().declareDefaultNamespace("xxx","http://somewhere.org");
078: * </pre>
079: * <br/> The collectionNamespace and prefix property refers to the prefix and
080: * namespace given to the document root and defualts to
081: * wfs,http://www.opengis.wfs.
082: *
083: * @author Ian Schneider
084: * @author Chris Holmes, TOPP
085: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/gml/producer/FeatureTransformer.java $
086: * @version $Id: FeatureTransformer.java 29502 2008-02-28 09:06:25Z jgarnett $
087: *
088: * @todo Add support for schemaLocation
089: */
090: public class FeatureTransformer extends TransformerBase {
091: /** The logger for the filter module. */
092: private static final Logger LOGGER = org.geotools.util.logging.Logging
093: .getLogger("org.geotools.gml");
094: private static Set gmlAtts;
095: private String collectionPrefix = "wfs";
096: private String collectionNamespace = "http://www.opengis.net/wfs";
097: private NamespaceSupport nsLookup = new NamespaceSupport();
098: private FeatureTypeNamespaces featureTypeNamespaces = new FeatureTypeNamespaces(
099: nsLookup);
100: private SchemaLocationSupport schemaLocation = new SchemaLocationSupport();
101: private int maxFeatures = -1;
102: private boolean prefixGml = false;
103: private boolean featureBounding = false;
104: private String srsName;
105: private String lockId;
106: private int numDecimals = 4;
107:
108: public void setCollectionNamespace(String nsURI) {
109: collectionNamespace = nsURI;
110: }
111:
112: public String getCollectionNamespace() {
113: return collectionNamespace;
114: }
115:
116: public void setCollectionPrefix(String prefix) {
117: this .collectionPrefix = prefix;
118: }
119:
120: public String getCollectionPrefix() {
121: return collectionPrefix;
122: }
123:
124: /**
125: * Sets the number of decimals to be used in the geometry coordinates of
126: * the response. This allows for more efficient results, since often the
127: * storage format itself won't specify as many decimal places as the
128: * response might want. The default is 4, but should generally be set by
129: * the user of this class.
130: *
131: * @param numDecimals the number of significant digits past the decimal to
132: * include in the response.
133: */
134: public void setNumDecimals(int numDecimals) {
135: this .numDecimals = numDecimals;
136: }
137:
138: public NamespaceSupport getFeatureNamespaces() {
139: return nsLookup;
140: }
141:
142: public FeatureTypeNamespaces getFeatureTypeNamespaces() {
143: return featureTypeNamespaces;
144: }
145:
146: public void addSchemaLocation(String nsURI, String uri) {
147: schemaLocation.setLocation(nsURI, uri);
148: }
149:
150: /**
151: * Used to set the srsName attribute of the Geometries to be turned to xml.
152: * For now we can only have all with the same srsName.
153: *
154: * @param srsName This SRS names hould agree with the GML specification; example "EPSG:4326"
155: * @task REVISIT: once we have better srs support in our feature model this
156: * should be rethought, as it's a rather blunt approach.
157: */
158: public void setSrsName(String srsName) {
159: this .srsName = srsName;
160: }
161:
162: /**
163: * Used to set a lockId attribute after a getFeatureWithLock.
164: *
165: * @param lockId The lockId of the lock on the WFS.
166: *
167: * @task REVISIT: Ian, this is probably the most wfs specific addition. If
168: * you'd like I can subclass and add it there. It has to be added
169: * as an attribute to FeatureCollection, to report a
170: * GetFeatureWithLock
171: */
172: public void setLockId(String lockId) {
173: this .lockId = lockId;
174: }
175:
176: /**
177: * If Gml Prefixing is enabled then attributes with names that could be
178: * prefixed with gml, such as description, pointProperty, and name, will
179: * be. So if an attribute called name is encountered, instead of
180: * prepending the default prefix (say gt2:name), it will turn out as
181: * gml:name. Right now this is fairly hacky, as the gml:name,
182: * gml:description, ect., should be in the first attributes by default.
183: * The actualy geometry encodings will always be prefixed with the proper
184: * gml, like gml:coordinates. This only applies to attributes, that could
185: * also be part of the features normal schema (for example a pointProperty
186: * could be declared in the gt2 namespace, instead of a gml:pointProperty
187: * it would be a gt2:pointProperty.
188: *
189: * @param prefixGml <tt>true</tt> if prefixing gml should be enabled.
190: * Default is disabled, no gml prefixing.
191: *
192: * @task REVISIT: only prefix name, description, and boundedBy if they
193: * occur in their proper places. Right now names always get gml
194: * prefixed if the gmlPrefixing is on, which is less than ideal.
195: * @task REVISIT: The other approach is to allow for generic mapping, users
196: * would set which attributes they wanted to have different
197: * prefixes.
198: */
199: public void setGmlPrefixing(boolean prefixGml) {
200: this .prefixGml = prefixGml;
201:
202: if (prefixGml && (gmlAtts == null)) {
203: gmlAtts = new HashSet();
204: loadGmlAttributes(gmlAtts);
205: }
206: }
207:
208: /**
209: * Template method for determining which attributes to prefix with gml.
210: * @param gmlAtts Set of strings corresponding to element names on a type.
211: */
212: protected void loadGmlAttributes(Set gmlAtts) {
213: gmlAtts.add("pointProperty");
214: gmlAtts.add("geometryProperty");
215: gmlAtts.add("polygonProperty");
216: gmlAtts.add("lineStringProperty");
217: gmlAtts.add("multiPointProperty");
218: gmlAtts.add("multiLineStringProperty");
219: gmlAtts.add("multiPolygonProperty");
220: gmlAtts.add("description");
221: gmlAtts.add("name");
222:
223: //boundedBy is done in handleAttribute to make use of the writeBounds
224: //code.
225: }
226:
227: /**
228: * Sets whether a gml:boundedBy element should automatically be generated
229: * and included. The element will not be updateable, and is simply
230: * derived from the geometries present in the feature.
231: *
232: * <p>
233: * Note that the <tt>setGmlPrefixing()</tt> interacts with this
234: * occasionally, since it will hack in a gml prefix to a boundedBy
235: * attribute included in the featureType. If gml prefixing is on, and
236: * featureBounding is on, then the bounds from the attribute will be used.
237: * If gml prefixing is off, then that boundedBy attribute will
238: * presumably be in its own namespace, and so the automatic gml boundedBy
239: * will not conflict, so both will be printed, with the automatic one
240: * deriving its bounds from the boundedBy attribute and any other
241: * geometries in the feature
242: * </p>
243: *
244: * @param featureBounding <tt>true</tt> if the bounds of the feature should
245: * be automatically calculated and included as a gml:boundedBy in
246: * the gml output. Note this puts a good bit of bandwidth overhead
247: * on the output. Default is <tt>false</tt>
248: */
249: public void setFeatureBounding(boolean featureBounding) {
250: this .featureBounding = featureBounding;
251: }
252:
253: public org.geotools.xml.transform.Translator createTranslator(
254: ContentHandler handler) {
255: FeatureTranslator t = createTranslator(handler,
256: collectionPrefix, collectionNamespace,
257: featureTypeNamespaces, schemaLocation);
258: java.util.Enumeration prefixes = nsLookup.getPrefixes();
259:
260: //setGmlPrefixing(true);
261: t.setNumDecimals(numDecimals);
262: t.setGmlPrefixing(prefixGml);
263: t.setSrsName(srsName);
264: t.setLockId(lockId);
265: t.setFeatureBounding(featureBounding);
266:
267: while (prefixes.hasMoreElements()) {
268: String prefix = prefixes.nextElement().toString();
269: String uri = nsLookup.getURI(prefix);
270: t.getNamespaceSupport().declarePrefix(prefix, uri);
271: }
272:
273: return t;
274: }
275:
276: /**
277: * Template method for creating the translator.
278: */
279: protected FeatureTranslator createTranslator(
280: ContentHandler handler, String prefix, String ns,
281: FeatureTypeNamespaces featureTypeNamespaces,
282: SchemaLocationSupport schemaLocationSupport) {
283: return new FeatureTranslator(handler, prefix, ns,
284: featureTypeNamespaces, schemaLocationSupport);
285: }
286:
287: public static class FeatureTypeNamespaces {
288: Map lookup = new HashMap();
289: NamespaceSupport nsSupport;
290: String defaultPrefix = null;
291:
292: public FeatureTypeNamespaces(NamespaceSupport nsSupport) {
293: this .nsSupport = nsSupport;
294: }
295:
296: public void declareDefaultNamespace(String prefix, String nsURI) {
297: defaultPrefix = prefix;
298: nsSupport.declarePrefix(prefix, nsURI);
299: }
300:
301: public void declareNamespace(FeatureType type, String prefix,
302: String nsURI) {
303: lookup.put(type, prefix);
304: nsSupport.declarePrefix(prefix, nsURI);
305: }
306:
307: public String findPrefix(FeatureType type) {
308: String pre = (String) lookup.get(type);
309:
310: if (pre == null) {
311: pre = defaultPrefix;
312: }
313:
314: return pre;
315: }
316:
317: public String toString() {
318: return "FeatureTypeNamespaces[Default: " + defaultPrefix
319: + ", lookUp: " + lookup;
320: }
321: }
322:
323: /**
324: * Outputs gml without any fancy indents or newlines.
325: */
326: public static class FeatureTranslator extends TranslatorSupport
327: implements FeatureCollectionIteration.Handler {
328: String fc = "FeatureCollection";
329: protected GeometryTransformer.GeometryTranslator geometryTranslator;
330: String memberString;
331: String currentPrefix;
332: FeatureTypeNamespaces types;
333: boolean prefixGml = false;
334: boolean featureBounding = false;
335: /**
336: * The string representing the Spatial Reference System of the data.
337: * <p>
338: * This value should be determined by looking at the first
339: * GeometryAttributeType encountered (but this way GeoServer can override
340: * everything and be divorced from the actual (lack of) abilities of the underlying
341: * DataStore).
342: */
343: String srsName = null;
344: /**
345: * Will be 0 - if unknown; 2 if normal and 3 if working with 3D coordinates.
346: * <p>
347: * This value will be set based on looking at the *first* GeometryAttributeType encountered,
348: * a similar approach should be taken for determining the SRID name.
349: *
350: * @since 2.4.1
351: */
352: int dimension = 0;
353: String lockId = null;
354: ContentHandler handler;
355: private boolean running = true;
356:
357: /**
358: * Constructor with handler.
359: *
360: * @param handler the handler to use.
361: * @param prefix DOCUMENT ME!
362: * @param ns DOCUMENT ME!
363: * @param types DOCUMENT ME!
364: * @param schemaLoc DOCUMENT ME!
365: */
366: public FeatureTranslator(ContentHandler handler, String prefix,
367: String ns, FeatureTypeNamespaces types,
368: SchemaLocationSupport schemaLoc) {
369: super (handler, prefix, ns, schemaLoc);
370: geometryTranslator = createGeometryTranslator(handler);
371: this .types = types;
372: this .handler = handler;
373: getNamespaceSupport().declarePrefix(
374: geometryTranslator.getDefaultPrefix(),
375: geometryTranslator.getDefaultNamespace());
376: memberString = geometryTranslator.getDefaultPrefix()
377: + ":featureMember";
378: }
379:
380: /**
381: * Method to be subclassed to return a custom geometry translator, mostly for gml3
382: * geometry support.
383: * @param handler
384: * @return
385: */
386: protected GeometryTranslator createGeometryTranslator(
387: ContentHandler handler) {
388: return new GeometryTransformer.GeometryTranslator(handler);
389: }
390:
391: protected GeometryTranslator createGeometryTranslator(
392: ContentHandler handler, int numDecimals) {
393: return new GeometryTransformer.GeometryTranslator(handler,
394: numDecimals);
395: }
396:
397: /**
398: * @deprecated Use of DummyZ is not known
399: * @param handler
400: * @param numDecimals
401: * @param useDummyZ
402: * @return
403: */
404: protected GeometryTranslator createGeometryTranslator(
405: ContentHandler handler, int numDecimals,
406: boolean useDummyZ) {
407: return new GeometryTransformer.GeometryTranslator(handler,
408: numDecimals, useDummyZ);
409: }
410:
411: /**
412: * @since 2.4.1
413: * @param handler
414: * @param numDecimals
415: * @param dimension
416: * @return GeometryTranslator that will delegate a CoordinateWriter configured with the above parameters
417: */
418: protected GeometryTranslator createGeometryTranslator(
419: ContentHandler handler, int numDecimals, int dimension) {
420: return new GeometryTranslator(handler, "gml",
421: GMLUtils.GML_URL, numDecimals, false, dimension);
422: }
423:
424: void setGmlPrefixing(boolean prefixGml) {
425: this .prefixGml = prefixGml;
426: }
427:
428: void setFeatureBounding(boolean bounding) {
429: this .featureBounding = bounding;
430: }
431:
432: void setSrsName(String srsName) {
433: this .srsName = srsName;
434: this .dimension = 0; // will be determined by the first GeometryAttributeType encountered
435: }
436:
437: void setNumDecimals(int numDecimals) {
438: geometryTranslator = createGeometryTranslator(handler,
439: numDecimals);
440: }
441:
442: void setUseDummyZ(boolean useDummyZ) {
443: geometryTranslator = createGeometryTranslator(handler,
444: geometryTranslator.getNumDecimals(), useDummyZ);
445: }
446:
447: /** If set to 3 the real z value from the coordinates will be used */
448: void setDimension(int dimension) {
449: geometryTranslator = createGeometryTranslator(handler,
450: geometryTranslator.getNumDecimals(), dimension);
451: }
452:
453: public void setLockId(String lockId) {
454: this .lockId = lockId;
455: }
456:
457: public FeatureTypeNamespaces getFeatureTypeNamespaces() {
458: return types;
459: }
460:
461: public void encode(Object o) throws IllegalArgumentException {
462: try {
463: if (o instanceof FeatureCollection) {
464: FeatureCollection fc = (FeatureCollection) o;
465: FeatureCollectionIteration.iteration(this , fc);
466: } else if (o instanceof FeatureCollection[]) {
467: //Did FeatureResult[] so that we are sure they're all the same type.
468: //Could also consider collections here...
469: FeatureCollection[] results = (FeatureCollection[]) o;
470: Envelope bounds = new Envelope();
471:
472: for (int i = 0; i < results.length; i++) {
473: bounds.expandToInclude(results[i].getBounds());
474: }
475:
476: startFeatureCollection();
477: writeBounds(bounds);
478:
479: for (int i = 0; i < results.length; i++) {
480: handleFeatureIterator(results[i].features());
481: }
482: endFeatureCollection();
483: } else if (o instanceof FeatureReader) {
484: // THIS IS A HACK FOR QUICK USE
485: FeatureReader r = (FeatureReader) o;
486:
487: startFeatureCollection();
488:
489: handleFeatureReader(r);
490:
491: endFeatureCollection();
492: // } else if (o instanceof FeatureResults) {
493: // FeatureResults fr = (FeatureResults) o;
494: // startFeatureCollection();
495: // writeBounds(fr.getBounds());
496: // handleFeatureReader(fr.reader());
497: // endFeatureCollection();
498: // } else if (o instanceof FeatureResults[]) {
499: // //Did FeatureResult[] so that we are sure they're all the same type.
500: // //Could also consider collections here...
501: // FeatureResults[] results = (FeatureResults[]) o;
502: // Envelope bounds = new Envelope();
503: //
504: // for (int i = 0; i < results.length; i++) {
505: // bounds.expandToInclude(results[i].getBounds());
506: // }
507: //
508: // startFeatureCollection();
509: // writeBounds(bounds);
510: //
511: // for (int i = 0; i < results.length; i++) {
512: // handleFeatureReader(results[i].reader());
513: // }
514: //
515: // endFeatureCollection();
516: } else {
517: throw new IllegalArgumentException("Cannot encode "
518: + o);
519: }
520: } catch (IOException ioe) {
521: ioe.printStackTrace(System.out);
522: throw new RuntimeException(
523: "error reading FeatureResults", ioe);
524: }
525: }
526:
527: public void handleFeatureIterator(FeatureIterator iterator)
528: throws IOException {
529: try {
530: while (iterator.hasNext() && running) {
531: Feature f = iterator.next();
532: handleFeature(f);
533:
534: FeatureType t = f.getFeatureType();
535:
536: for (int i = 0, ii = f.getNumberOfAttributes(); i < ii; i++) {
537: handleAttribute(t.getAttributeType(i), f
538: .getAttribute(i));
539: }
540: endFeature(f);
541: }
542: } catch (Exception ioe) {
543: throw new RuntimeException("Error reading Features",
544: ioe);
545: } finally {
546: if (iterator != null) {
547: LOGGER.finer("closing reader " + iterator);
548: iterator.close();
549: }
550: }
551: }
552:
553: public void handleFeatureReader(FeatureReader reader)
554: throws IOException {
555: try {
556: while (reader.hasNext() && running) {
557: Feature f = reader.next();
558: handleFeature(f);
559:
560: FeatureType t = f.getFeatureType();
561:
562: for (int i = 0, ii = f.getNumberOfAttributes(); i < ii; i++) {
563: handleAttribute(t.getAttributeType(i), f
564: .getAttribute(i));
565: }
566:
567: endFeature(f);
568: }
569: } catch (Exception ioe) {
570: throw new RuntimeException("Error reading Features",
571: ioe);
572: } finally {
573: if (reader != null) {
574: LOGGER.finer("closing reader " + reader);
575: reader.close();
576: }
577: }
578: }
579:
580: public void startFeatureCollection() {
581: try {
582: String element = (getDefaultPrefix() == null) ? fc
583: : (getDefaultPrefix() + ":" + fc);
584: AttributesImpl atts = new AttributesImpl();
585:
586: if (lockId != null) {
587: atts.addAttribute("", "lockId", "lockId", "",
588: lockId);
589: }
590:
591: contentHandler.startElement("", "", element, atts);
592:
593: } catch (SAXException se) {
594: throw new RuntimeException(se);
595: }
596: }
597:
598: public void endFeatureCollection() {
599: end(fc);
600: }
601:
602: /**
603: * Prints up the gml for a featurecollection.
604: *
605: * @param collection DOCUMENT ME!
606: */
607: public void handleFeatureCollection(FeatureCollection collection) {
608: startFeatureCollection();
609: writeBounds(collection.getBounds());
610: }
611:
612: /**
613: * writes the <code>gml:boundedBy</code> element to output based on
614: * <code>fc.getBounds()</code>
615: *
616: * @param bounds
617: *
618: * @throws RuntimeException if it is thorwn while writing the element
619: * or coordinates
620: */
621: public void writeBounds(Envelope bounds) {
622: try {
623: String boundedBy = geometryTranslator
624: .getDefaultPrefix()
625: + ":" + "boundedBy";
626:
627: contentHandler.startElement("", "", boundedBy,
628: NULL_ATTS);
629: geometryTranslator.encode(bounds, srsName);
630: contentHandler.endElement("", "", boundedBy);
631: } catch (SAXException se) {
632: throw new RuntimeException(se);
633: }
634: }
635:
636: /**
637: * Sends sax for the ending of a feature collection.
638: *
639: * @param collection DOCUMENT ME!
640: */
641: public void endFeatureCollection(FeatureCollection collection) {
642: endFeatureCollection();
643: }
644:
645: /**
646: * Sends sax for the ending of a feature.
647: *
648: * @param f DOCUMENT ME!
649: *
650: * @throws RuntimeException DOCUMENT ME!
651: */
652: public void endFeature(Feature f) {
653: try {
654: String name = f.getFeatureType().getTypeName();
655:
656: if (currentPrefix != null) {
657: name = currentPrefix + ":" + name;
658: }
659:
660: contentHandler.endElement("", "", name);
661: contentHandler.endElement("", "", memberString);
662: } catch (Exception e) {
663: throw new RuntimeException(e);
664: }
665: }
666:
667: /**
668: * handles sax for an attribute.
669: *
670: * @param type DOCUMENT ME!
671: * @param value DOCUMENT ME!
672: *
673: * @throws RuntimeException DOCUMENT ME!
674: */
675: public void handleAttribute(AttributeType type, Object value) {
676: try {
677: if (value != null) {
678: String name = type.getName();
679:
680: //HACK: this should be user configurable, along with the
681:
682: //other gml substitutions I shall add.
683:
684: if (prefixGml //adding this in since the extra boundedBy
685: //hacking should only need to be done for the weird
686: //cite tests, and having this check before the string
687: //equals should get us better performance. Albeit
688: //very slightly, but this method gets called millions
689: && (name.equals("boundedBy") && Geometry.class
690: .isAssignableFrom(value.getClass()))) {
691:
692: writeBounds(((Geometry) value)
693: .getEnvelopeInternal());
694: } else {
695: String this Prefix = currentPrefix;
696:
697: if (prefixGml && gmlAtts.contains(name)) {
698: this Prefix = "gml";
699: }
700:
701: if (this Prefix != null) {
702: name = this Prefix + ":" + name;
703: }
704:
705: contentHandler.startElement("", "", name,
706: NULL_ATTS);
707:
708: if (Geometry.class.isAssignableFrom(value
709: .getClass())) {
710: if (dimension == 0) {
711: // lets look at the CRS
712: GeometryAttributeType geometryType = (GeometryAttributeType) type;
713: CoordinateReferenceSystem crs = geometryType
714: .getCoordinateSystem();
715: if (crs == null) {
716: // I won't even bother people with a warning
717: // (until DataStore quality has improved
718: dimension = 2; // the most sensible default
719:
720: } else {
721: dimension = crs
722: .getCoordinateSystem()
723: .getDimension();
724: // note we could check the srsName here!
725: if (dimension == 3) {
726: setDimension(dimension);
727: }
728: }
729: }
730: geometryTranslator.encode((Geometry) value,
731: srsName);
732: } else if (value instanceof Date) {
733: String text = null;
734: if (value instanceof java.sql.Date)
735: text = DateUtil
736: .serializeSqlDate((java.sql.Date) value);
737: else if (value instanceof java.sql.Time)
738: text = DateUtil
739: .serializeSqlTime((java.sql.Time) value);
740: else
741: text = DateUtil
742: .serializeDateTime((Date) value);
743: contentHandler.characters(text
744: .toCharArray(), 0, text.length());
745: } else {
746: String text = value.toString();
747: contentHandler.characters(text
748: .toCharArray(), 0, text.length());
749: }
750:
751: contentHandler.endElement("", "", name);
752: }
753: }
754:
755: //REVISIT: xsi:nillable is the proper xml way to handle nulls,
756: //but OGC people are fine with just leaving it out.
757: } catch (Exception e) {
758: throw new RuntimeException(e);
759: }
760: }
761:
762: /**
763: * Handles sax for a feature.
764: *
765: * @param f DOCUMENT ME!
766: *
767: * @throws RuntimeException DOCUMENT ME!
768: */
769: public void handleFeature(Feature f) {
770: try {
771: contentHandler.startElement("", "", memberString,
772: NULL_ATTS);
773:
774: FeatureType type = f.getFeatureType();
775: String name = type.getTypeName();
776: currentPrefix = getNamespaceSupport().getPrefix(
777: f.getFeatureType().getNamespace().toString());
778:
779: if (currentPrefix == null) {
780: currentPrefix = types
781: .findPrefix(f.getFeatureType());
782: }
783:
784: if (currentPrefix == null) {
785: throw new RuntimeException(
786: "Could not locate namespace for FeatureType : "
787: + type.getTypeName() + ":"
788: + type.getNamespace()
789: + "look up in: " + types);
790: }
791:
792: if (currentPrefix != null) {
793: name = currentPrefix + ":" + name;
794: }
795:
796: Attributes fidAtts = encodeFeatureId(f);
797:
798: contentHandler.startElement("", "", name, fidAtts);
799:
800: if (featureBounding) {
801: //HACK pt.2 see line 511, if the cite stuff wanted to hack
802: //in a boundedBy geometry, we don't want to do it twice.
803: //So if
804: if (prefixGml
805: && (f.getAttribute("boundedBy") != null)) {
806: //do nothing, since our hack will handle it.
807: } else {
808: writeBounds(f.getBounds());
809: }
810: }
811: } catch (Exception e) {
812: throw new RuntimeException(e);
813: }
814: }
815:
816: protected Attributes encodeFeatureId(Feature f) {
817: AttributesImpl fidAtts = new org.xml.sax.helpers.AttributesImpl();
818: String fid = f.getID();
819:
820: if (fid != null) {
821: fidAtts.addAttribute("", "fid", "fid", "fids", fid);
822: }
823:
824: return fidAtts;
825: }
826:
827: }
828: }
|