Package org.geotools.gml.producer

Source Code of org.geotools.gml.producer.FeatureTransformer$FeatureTranslator

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2003-2008, Open Source Geospatial Foundation (OSGeo)
*   
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    Lesser General Public License for more details.
*/
package org.geotools.gml.producer;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureReader;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollectionIteration;
import org.geotools.feature.type.DateUtil;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.gml.producer.GeometryTransformer.GeometryTranslator;
import org.geotools.referencing.CRS;
import org.geotools.xml.transform.TransformerBase;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.geometry.BoundingBox;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.NamespaceSupport;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;


/**
* FeatureTransformer provides a mechanism for converting Feature objects into
* (hopefully) valid gml. This is a work in progress, so please be patient. A
* simple example of how to use this class follows:
* <pre>
*    SimpleFeatureCollection collection; // can also use FeatureReader!!
*   OutputStream out;
*    FeatureTransformer ft = new FeatureTransformer();
*    // set the indentation to 4 spaces
*   ft.setIndentation(4);
*    // this will allow Features with the FeatureType which has the namespace
*   // "http://somewhere.org" to be prefixed with xxx...
*   ft.getFeatureNamespaces().declarePrefix("xxx","http://somewhere.org");
*    // transform
*   ft.transform(collection,out);
* </pre>
* <b>The above example assumes a homogenous collection of Features whose
* FeatureType has the namespace "http://somewhere.org"</b> but note that not
* all DataSources currently provide FeatureTypes with a namespace... There
* are two other mechanisms for prefixing your Features.<br>
* 1) Map a specific FeatureType <b>by identity</b> to prefix and nsURI
* <pre>
*   FeatureType fc;
*   FeatureTransformer ft = new FeatureTransformer();
*   ft.getFeatureTypeNamespaces().declareNamespace(fc,"xxx","http://somewhere.org");
* </pre>
* 2) Provide a default namespace for any Features whose FeatureType either has
* an empty namespace, OR, has not been mapped using the previous method. This
* is basically a catch-all mechanism.
* <pre>
*   FeatureTransformer ft = new FeatureTransformer();
*   ft.getFeatureTypeNamespaces().declareDefaultNamespace("xxx","http://somewhere.org");
* </pre>
* <br/> The collectionNamespace and prefix property refers to the prefix and
* namespace given to the document root and defualts to
* wfs,http://www.opengis.wfs.
*
* @author Ian Schneider
* @author Chris Holmes, TOPP
*
*
* @source $URL$
* @version $Id$
*
* @todo Add support for schemaLocation
*/
public class FeatureTransformer extends TransformerBase {
    /** The logger for the filter module. */
    private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.gml");
    private static Set gmlAtts;
    private String collectionPrefix = "wfs";
    private String collectionNamespace = "http://www.opengis.net/wfs";
    private NamespaceSupport nsLookup = new NamespaceSupport();
    private FeatureTypeNamespaces featureTypeNamespaces = new FeatureTypeNamespaces(nsLookup);
    private SchemaLocationSupport schemaLocation = new SchemaLocationSupport();
    private int maxFeatures = -1;
    private boolean prefixGml = false;
    private boolean featureBounding = false;
    private boolean collectionBounding = true;
    private String srsName;
    private String lockId;
    private int numDecimals = 4;

    public void setCollectionNamespace(String nsURI) {
        collectionNamespace = nsURI;
    }

    public String getCollectionNamespace() {
        return collectionNamespace;
    }

    public void setCollectionPrefix(String prefix) {
        this.collectionPrefix = prefix;
    }

    public String getCollectionPrefix() {
        return collectionPrefix;
    }

    /**
     * Sets the number of decimals to be used in the geometry coordinates of
     * the response.  This allows for more efficient results, since often the
     * storage format itself won't specify as many decimal places as the
     * response might want.  The default is 4, but should generally be set by
     * the user of this class.
     *
     * @param numDecimals the number of significant digits past the decimal to
     *        include in the response.
     */
    public void setNumDecimals(int numDecimals) {
        this.numDecimals = numDecimals;
    }

    public NamespaceSupport getFeatureNamespaces() {
        return nsLookup;
    }

    public FeatureTypeNamespaces getFeatureTypeNamespaces() {
        return featureTypeNamespaces;
    }

    public void addSchemaLocation(String nsURI, String uri) {
        schemaLocation.setLocation(nsURI, uri);
    }

    /**
     * Used to set the srsName attribute of the Geometries to be turned to xml.
     * The srsName is applied to all the geometries and is not done on a case by case
     * basis.
     *
     * @param srsName Spatial Reference System Name
     *
     * @task REVISIT: once we have better srs support in our feature model this
     *       should be rethought, as it's a rather blunt approach.
     *
     * @see CRS#toSRS(CoordinateReferenceSystem, boolean)
     */
    public void setSrsName(String srsName) {
        this.srsName = srsName;
    }

    /**
     * Used to set a lockId attribute after a getFeatureWithLock.
     *
     * @param lockId The lockId of the lock on the WFS.
     *
     * @task REVISIT: Ian, this is probably the most wfs specific addition. If
     *       you'd like I can subclass and add it there.  It has to be added
     *       as an attribute to FeatureCollection, to report a
     *       GetFeatureWithLock
     */
    public void setLockId(String lockId) {
        this.lockId = lockId;
    }

    /**
     * If Gml Prefixing is enabled then attributes with names that could be
     * prefixed with gml, such as description, pointProperty, and name, will
     * be.  So if an attribute called name is encountered, instead of
     * prepending the default prefix (say gt2:name), it will turn out as
     * gml:name.  Right now this is fairly hacky, as the gml:name,
     * gml:description, ect., should be in the first attributes by default.
     * The actualy geometry encodings will always be prefixed with the proper
     * gml, like gml:coordinates. This only applies to attributes, that could
     * also be part of the features normal schema (for example a pointProperty
     * could be declared in the gt2  namespace, instead of a gml:pointProperty
     * it would be a gt2:pointProperty.
     *
     * @param prefixGml <tt>true</tt> if prefixing gml should be enabled.
     *        Default is disabled, no gml prefixing.
     *
     * @task REVISIT: only prefix name, description, and boundedBy if they
     *       occur in their proper places.  Right now names always get gml
     *       prefixed if the gmlPrefixing is on, which is less than ideal.
     * @task REVISIT: The other approach is to allow for generic mapping, users
     *       would set which attributes they wanted to have different
     *       prefixes.
     */
    public void setGmlPrefixing(boolean prefixGml) {
        this.prefixGml = prefixGml;

        if (prefixGml && (gmlAtts == null)) {
            gmlAtts = new HashSet();
           loadGmlAttributes( gmlAtts );
        }
    }

    /**
     * Template method for determining which attributes to prefix with gml.
     * @param gmlAtts Set of strings corresponding to element names on a type.
     */
    protected void loadGmlAttributes( Set gmlAtts ) {
       gmlAtts.add("pointProperty");
         gmlAtts.add("geometryProperty");
         gmlAtts.add("polygonProperty");
         gmlAtts.add("lineStringProperty");
         gmlAtts.add("multiPointProperty");
         gmlAtts.add("multiLineStringProperty");
         gmlAtts.add("multiPolygonProperty");
         gmlAtts.add("description");
         gmlAtts.add("name");

         //boundedBy is done in handleAttribute to make use of the writeBounds
         //code.
    }
   
    /**
     * Sets whether a gml:boundedBy element should automatically be generated
     * and included.  The element will not be updateable, and is simply
     * derived from the geometries present in the feature.
     *
     * <p>
     * Note that the <tt>setGmlPrefixing()</tt> interacts with this
     * occasionally, since it will hack in a gml prefix to a boundedBy
     * attribute included in the featureType.  If gml prefixing is on, and
     * featureBounding is on, then the bounds from the attribute will be used.
     * If gml prefixing is off, then that boundedBy attribute will
     * presumably be in its own namespace, and so the automatic gml boundedBy
     * will not conflict, so both will be printed, with the automatic one
     * deriving its bounds from the boundedBy attribute and any other
     * geometries in the feature
     * </p>
     *
     * @param featureBounding <tt>true</tt> if the bounds of the feature should
     *        be automatically calculated and included as a gml:boundedBy in
     *        the gml output.  Note this puts a good bit of bandwidth overhead
     *        on the  output.  Default is <tt>false</tt>
     */
    public void setFeatureBounding(boolean featureBounding) {
        this.featureBounding = featureBounding;
    }
   
    /**
     * If true, enables the generation of the full collection bounds. Depending on the
     * collection being generated in output, this operation can be extremely expensive.
     * <p>
     * Defaults to true (for backwards compatibility), disable explicitly if you
     * don't want feature collection bounds to be generated.
     * @param collectionBounding
     */
    public void setCollectionBounding(boolean collectionBounding) {
        this.collectionBounding = collectionBounding;
    }

    public org.geotools.xml.transform.Translator createTranslator(
        ContentHandler handler) {
        FeatureTranslator t = createTranslator(handler, collectionPrefix,
                collectionNamespace, featureTypeNamespaces, schemaLocation);
        java.util.Enumeration prefixes = nsLookup.getPrefixes();

        //setGmlPrefixing(true);
        t.setNumDecimals(numDecimals);
        t.setGmlPrefixing(prefixGml);
        t.setSrsName(srsName);
        t.setLockId(lockId);
        t.setFeatureBounding(featureBounding);
        t.setCollectionBounding(collectionBounding);

        while (prefixes.hasMoreElements()) {
            String prefix = prefixes.nextElement().toString();
            String uri = nsLookup.getURI(prefix);
            t.getNamespaceSupport().declarePrefix(prefix, uri);
        }

        return t;
    }

    /**
     * Template method for creating the translator.
     */
    protected FeatureTranslator createTranslator(
    ContentHandler handler, String prefix, String ns,
    FeatureTypeNamespaces featureTypeNamespaces, SchemaLocationSupport schemaLocationSupport
  ) {
      return new FeatureTranslator( handler, prefix, ns, featureTypeNamespaces, schemaLocationSupport );
    }
   
    public static class FeatureTypeNamespaces {
        Map lookup = new HashMap();
        NamespaceSupport nsSupport;
        String defaultPrefix = null;

        public FeatureTypeNamespaces(NamespaceSupport nsSupport) {
            this.nsSupport = nsSupport;
        }

        public void declareDefaultNamespace(String prefix, String nsURI) {
            defaultPrefix = prefix;
            nsSupport.declarePrefix(prefix, nsURI);
        }

        public void declareNamespace(FeatureType type, String prefix,
            String nsURI) {
            lookup.put(type, prefix);
            nsSupport.declarePrefix(prefix, nsURI);
        }

        public String findPrefix(FeatureType type) {
            String pre = (String) lookup.get(type);

            if (pre == null) {
                pre = defaultPrefix;
            }

            return pre;
        }

        public String toString() {
            return "FeatureTypeNamespaces[Default: " + defaultPrefix
            + ", lookUp: " + lookup.keySet() +"]";
        }
    }

    /**
     * Outputs gml without any fancy indents or newlines.
     */
    public static class FeatureTranslator extends TranslatorSupport
        implements FeatureCollectionIteration.Handler {
        String fc = "FeatureCollection";
        protected GeometryTransformer.GeometryTranslator geometryTranslator;
        String memberString;
        String currentPrefix;
        FeatureTypeNamespaces types;
        boolean prefixGml = false;
        boolean featureBounding = false;
        boolean collectionBounding = true;
        /**
         * The string representing the Spatial Reference System of the data.
         * <p>
         * This value should be determined by looking at the first
         * GeometryAttributeType encountered (but this way GeoServer can override
         * everything and be divorced from the actual (lack of) abilities of the underlying
         * DataStore).
         */
        String srsName = null;
        /**
         * Will be 0 - if unknown; 2 if normal and 3 if working with 3D coordinates.
         * <p>
         * This value will be set based on looking at the *first* GeometryAttributeType encountered,
         * a similar approach should be taken for determining the SRID name.
         *
         * @since 2.4.1
         */
        int dimension = 0;
       
        String lockId = null;
        ContentHandler handler;
        private boolean running = true;

        /**
         * Constructor with handler.
         *
         * @param handler the handler to use.
         * @param prefix prefix
         * @param ns namespace
         * @param types Capture namespace and prefix information for types
         * @param schemaLoc Schema location information
         */
        public FeatureTranslator(ContentHandler handler, String prefix,
            String ns, FeatureTypeNamespaces types,
            SchemaLocationSupport schemaLoc) {
            super(handler, prefix, ns, schemaLoc);
            geometryTranslator = createGeometryTranslator( handler );
            this.types = types;
            this.handler = handler;
            getNamespaceSupport().declarePrefix(geometryTranslator
                .getDefaultPrefix(), geometryTranslator.getDefaultNamespace());
            memberString = geometryTranslator.getDefaultPrefix()
                + ":featureMember";
        }

        /**
         * Method to be subclassed to return a custom geometry translator, mostly for gml3
         * geometry support.
         * @param handler
         * @return
         */
        protected GeometryTranslator createGeometryTranslator( ContentHandler handler ) {
          return new GeometryTransformer.GeometryTranslator( handler );
        }
        protected GeometryTranslator createGeometryTranslator( ContentHandler handler, int numDecimals ) {
          return new GeometryTransformer.GeometryTranslator( handler, numDecimals );
        }
        /**
         * @param handler
         * @param numDecimals
         * @param useDummyZ
         * @return
         */
        protected GeometryTranslator createGeometryTranslator( ContentHandler handler, int numDecimals, boolean useDummyZ ) {
            return new GeometryTransformer.GeometryTranslator( handler, numDecimals, useDummyZ );
        }
       
       /**
        * Set up a GeometryTranslator for working with content of the indicate
        * dimension.
        * <p>
        * This method can be used by code explicitly wishing to output 2D ordinates.
        *
        * @since 2.4.1
        * @param handler
        * @param numDecimals
        * @param dimension
        * @return GeometryTranslator that will delegate  a CoordinateWriter configured with the above parameters
        */
       protected GeometryTranslator createGeometryTranslator( ContentHandler handler, int numDecimals, int dimension ) {
           return new GeometryTranslator( handler, "gml",GMLUtils.GML_URL, numDecimals, false, dimension);
       }  
       
        void setGmlPrefixing(boolean prefixGml) {
            this.prefixGml = prefixGml;
        }

        void setFeatureBounding(boolean bounding) {
            this.featureBounding = bounding;
        }
       
        void setCollectionBounding(boolean collectionBounding) {
            this.collectionBounding = collectionBounding;
        }

        void setSrsName(String srsName) {
            this.srsName = srsName;
        }

        void setNumDecimals(int numDecimals) {
          geometryTranslator = createGeometryTranslator( handler, numDecimals );
        }

        void setUseDummyZ(boolean useDummyZ) {
            geometryTranslator = createGeometryTranslator(handler,
                    geometryTranslator.getNumDecimals(), useDummyZ);
        }
       
        /** If set to 3 the real z value from the coordinates will be used */
        void setDimension( int dimension ){
            geometryTranslator = createGeometryTranslator(handler, geometryTranslator.getNumDecimals(), dimension );
        }
       

        public void setLockId(String lockId) {
            this.lockId = lockId;
        }

        public FeatureTypeNamespaces getFeatureTypeNamespaces() {
          return types;
        }
       
        public void encode(Object o) throws IllegalArgumentException {
            try {
                if (o instanceof FeatureCollection) {
                    SimpleFeatureCollection fc = (SimpleFeatureCollection) o;
                    FeatureCollectionIteration.iteration(this, fc);
                } else if (o instanceof FeatureCollection[]) {
                    //Did FeatureResult[] so that we are sure they're all the same type.
                    //Could also consider collections here... 
                    FeatureCollection[] results = (FeatureCollection[]) o;
                    startFeatureCollection();
                    if(collectionBounding) {
                        ReferencedEnvelope bounds = null;
                        for (int i = 0; i < results.length; i++) {
                            ReferencedEnvelope more = results[i].getBounds();
                            if( bounds == null ){
                                bounds = new ReferencedEnvelope( more );
                            }
                            else {
                               bounds.expandToInclude(more);
                            }
                        }
                        writeBounds(bounds);
                    } else {
                        writeNullBounds();                       
                    }
                  
                    for (int i = 0; i < results.length; i++) {
                        handleFeatureIterator(DataUtilities.simple(results[i]).features());
                    }
                    endFeatureCollection();
                } else if (o instanceof FeatureReader) {
                    // THIS IS A HACK FOR QUICK USE
                     FeatureReader<SimpleFeatureType, SimpleFeature> r = (FeatureReader<SimpleFeatureType, SimpleFeature>) o;

                    startFeatureCollection();

                    handleFeatureReader(r);

                    endFeatureCollection();
//                } else if (o instanceof FeatureResults) {
//                    FeatureResults fr = (FeatureResults) o;
//                    startFeatureCollection();
//                    writeBounds(fr.getBounds());
//                    handleFeatureReader(fr.reader());
//                    endFeatureCollection();
//                } else if (o instanceof FeatureResults[]) {
//                    //Did FeatureResult[] so that we are sure they're all the same type.
//                    //Could also consider collections here... 
//                    FeatureResults[] results = (FeatureResults[]) o;
//                    Envelope bounds = new Envelope();
//
//                    for (int i = 0; i < results.length; i++) {
//                        bounds.expandToInclude(results[i].getBounds());
//                    }
//
//                    startFeatureCollection();
//                    writeBounds(bounds);
//
//                    for (int i = 0; i < results.length; i++) {
//                        handleFeatureReader(results[i].reader());
//                    }
//
//                    endFeatureCollection();
                } else {
                    throw new IllegalArgumentException("Cannot encode " + o);
                }
            } catch (IOException ioe) {
                ioe.printStackTrace(System.out);
                throw new RuntimeException("error reading FeatureResults", ioe);
            }
        }

        public void handleFeatureIterator(SimpleFeatureIterator iterator)
            throws IOException {
            try {
                while (iterator.hasNext() && running) {
                    SimpleFeature f = (SimpleFeature) iterator.next();
                    handleFeature(f);
   
                    SimpleFeatureType t = f.getFeatureType();
   
                    for (int i = 0, ii = f.getAttributeCount(); i < ii;
                            i++) {
                        AttributeDescriptor descriptor = t.getDescriptor(i);
                        Object value = f.getAttribute(i);
                        handleAttribute( descriptor, value );
                    }
                    endFeature(f);
                }
            } catch (Exception ioe) {
                throw new RuntimeException("Error reading Features", ioe);
            } finally {
                if (iterator != null) {
                    LOGGER.finer("closing reader " + iterator);
                    iterator.close();
                }
            }
        }
       
        public void handleFeatureReader(FeatureReader <SimpleFeatureType, SimpleFeature> reader)
            throws IOException {
            try {
                while (reader.hasNext() && running) {
                    SimpleFeature f = (SimpleFeature) reader.next();
                    handleFeature(f);

                    SimpleFeatureType t = f.getFeatureType();

                    for (int i = 0, ii = f.getAttributeCount(); i < ii; i++) {
                        AttributeDescriptor descriptor = t.getDescriptor(i);
                        Object value = f.getAttribute(i);
                        handleAttribute( descriptor, value );
                    }

                    endFeature(f);
                }
            } catch (Exception ioe) {
                throw new RuntimeException("Error reading Features", ioe);
            } finally {
                if (reader != null) {
                    LOGGER.finer("closing reader " + reader);
                    reader.close();
                }
            }
        }

        public void startFeatureCollection() {
            try {
                String element = (getDefaultPrefix() == null) ? fc
                                                              : (getDefaultPrefix()
                    + ":" + fc);
                AttributesImpl atts = new AttributesImpl();

                if (lockId != null) {
                    atts.addAttribute("", "lockId", "lockId", "", lockId);
                }

                contentHandler.startElement("", "", element, atts);

            } catch (SAXException se) {
                throw new RuntimeException(se);
            }
        }

        public void endFeatureCollection() {
            end(fc);
        }

        /**
         * Prints up the gml for a featurecollection.
         *
         * @param collection FeatureCollection being encoded
         */
        public void handleFeatureCollection(FeatureCollection<?,?> collection) {
            startFeatureCollection();
            if(collectionBounding)
                writeBounds(collection.getBounds());
        }

        /**
         * writes the <code>gml:boundedBy</code> element to output based on
         * <code>fc.getBounds()</code>
         *
         * @param bounds
         *
         * @throws RuntimeException if it is thorwn while writing the element
         *         or coordinates
         */
        public void writeBounds(BoundingBox bounds) {
            try {
                String boundedBy = geometryTranslator.getDefaultPrefix() + ":"
                    + "boundedBy";
              
                contentHandler.startElement("", "", boundedBy, NULL_ATTS);
               
               
                Envelope env = null;
                if (bounds != null){
                  env = new Envelope(new Coordinate(bounds.getMinX(), bounds.getMinY()),new Coordinate(bounds.getMaxX(), bounds.getMaxY()));
                }
                geometryTranslator.encode(env, srsName);
                contentHandler.endElement("", "", boundedBy);
            } catch (SAXException se) {
                throw new RuntimeException(se);
            }
        }
       
        /**
         * writes null bounds to the output
         *
         * @throws RuntimeException if it is thorwn while writing the element
         *         or coordinates
         */
        public void writeNullBounds() {
            try {
                String boundedBy = geometryTranslator.getDefaultPrefix() + ":boundedBy";
                String nullBox = geometryTranslator.getDefaultPrefix() + ":null";
              
                contentHandler.startElement("", "", boundedBy, NULL_ATTS);
                contentHandler.startElement("", "", nullBox, NULL_ATTS);
                contentHandler.characters("unknown".toCharArray(), 0, "unknown".length());
                contentHandler.endElement("", "", nullBox);
                contentHandler.endElement("", "", boundedBy);
            } catch (SAXException se) {
                throw new RuntimeException(se);
            }
        }
       
        /**
         * Sends sax for the ending of a feature collection.
         *
         * @param collection Feature collection we have just finished encoding
         */
        public void endFeatureCollection(FeatureCollection<?,?> collection) {
            endFeatureCollection();
        }

        /**
         * Sends sax for the ending of a feature.
         *
         * @param f Feature (implementation assume a SimpleFeature)
         *
         * @throws RuntimeException if something goes wrong during encode it is wrapped up as a generic runtime exception
         */
        public void endFeature(Feature f) {
            try {
                Name typeName = f.getType().getName();
                String name = typeName.getLocalPart();

                if (currentPrefix != null) {
                    name = currentPrefix + ":" + name;
                }
                contentHandler.endElement("", "", name);
                contentHandler.endElement("", "", memberString);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * handles sax for an attribute.
         *
         * @param descriptor Property descriptor
         * @param value Value being encoded for this property
         *
         * @throws RuntimeException Any problems are bundled up in a generic runtime exception
         */
        public void handleAttribute(PropertyDescriptor descriptor, Object value) {
            try {
                if (value != null) {
                    String name = descriptor.getName().getLocalPart();

                    //HACK: this should be user configurable, along with the

                    //other gml substitutions I shall add.

                    if (prefixGml //adding this in since the extra boundedBy
                            //hacking should only need to be done for the weird
                        //cite tests, and having this check before the string
                        //equals should get us better performance.  Albeit
                        //very slightly, but this method gets called millions
                            && (name.equals("boundedBy")
                            && value instanceof Geometry)) {

                        Envelope envelopeInternal = ((Geometry) value).getEnvelopeInternal();
                        CoordinateReferenceSystem crs = null;
                        if( descriptor instanceof GeometryDescriptor ){
                            GeometryDescriptor geometryDescriptor = (GeometryDescriptor) descriptor;
                            crs = geometryDescriptor.getCoordinateReferenceSystem();
                        }                    
                        ReferencedEnvelope
                            bounds = new ReferencedEnvelope( envelopeInternal, crs );
                                   
                        writeBounds( bounds);
                    } else {
                        String thisPrefix = currentPrefix;

                        if (prefixGml && gmlAtts.contains(name)) {
                            thisPrefix = "gml";
                        }

                        if (thisPrefix != null) {
                            name = thisPrefix + ":" + name;
                        }

                        contentHandler.startElement("", "", name, NULL_ATTS);

                        if (value instanceof Geometry) {
                            if( dimension == 0 ){
                                // lets look at the CRS
                                GeometryDescriptor geometryType = (GeometryDescriptor) descriptor;
                                CoordinateReferenceSystem crs = geometryType.getCoordinateReferenceSystem();
                                if( crs == null ){
                                    // I won't even bother people with a warning
                                    // (until DataStore quality has improved
                                    dimension = 2; // the most sensible default
                                   
                                }
                                else {
                                    dimension = crs.getCoordinateSystem().getDimension();
                                    // note we could check the srsName here!
                                    if( dimension == 3 ){
                                        setDimension( dimension );
                                    }
                                }
                            }
                            geometryTranslator.encode((Geometry) value, srsName);
                        } else if(value instanceof Date) {
                            String text = null;
                            if(value instanceof java.sql.Date)
                                text = DateUtil.serializeSqlDate((java.sql.Date) value);
                            else if(value instanceof java.sql.Time)
                                text = DateUtil.serializeSqlTime((java.sql.Time) value);
                            else
                                text = DateUtil.serializeDateTime((Date) value);
                            contentHandler.characters(text.toCharArray(), 0,
                                    text.length());
                        } else {
                            String text = value.toString();
                            contentHandler.characters(text.toCharArray(), 0,
                                text.length());
                        }

                        contentHandler.endElement("", "", name);
                    }
                }

                //REVISIT: xsi:nillable is the proper xml way to handle nulls,
                //but OGC people are fine with just leaving it out.      
            } catch (Exception e) {
                throw new IllegalStateException("Could not transform "+descriptor.getName()+":"+e, e );
            }
        }

        /**
         * Handles sax for a feature.
         * <p>
         * Please take care when considering the prefix/namespace for the feature. It is defined
         * by either:
         * <ul>
         * <li>the FeatureType using type.getName().getNamespaceURI() and the user data entry for
         * "prefix".</li>
         * <li>FeatureTypeNamespaces as provided to the constructor</li>
         * </ul>
         *
         * @param f Feature being encoded
         *
         * @throws RuntimeException Used to report any troubles during encoding
         */
        public void handleFeature(Feature f) {
            try {
                contentHandler.startElement("", "", memberString, NULL_ATTS);

                FeatureType type = f.getType();
                String name = type.getName().getLocalPart();
                String namespaceURI = type.getName().getNamespaceURI();
                if( namespaceURI != null ){
                    currentPrefix = getNamespaceSupport().getPrefix( namespaceURI );
                    if( currentPrefix == null ){
                        currentPrefix = (String) type.getUserData().get("prefix");
                        if( currentPrefix != null ){
                            getNamespaceSupport().declarePrefix(currentPrefix, namespaceURI );
                        }
                    }
                }
               
                if (currentPrefix == null) {
                    currentPrefix = types.findPrefix(type);
                }

                if (currentPrefix == null ){
                    throw new IllegalStateException("FeatureType namespace/prefix unknown for " + name + "look up in: " + types);
                }
                else if ( currentPrefix.length() == 0 ) {
                    // must be the default prefix
                }
                else {
                    name = currentPrefix + ":" + name;
                }

                Attributes fidAtts = encodeFeatureId( f );

                contentHandler.startElement("", "", name, fidAtts);

                // encode the bounds if requested and the bounds are not missing or empty
                if (featureBounding && f.getBounds() != null && !f.getBounds().isEmpty()) {
                    //HACK pt.2 see line 511, if the cite stuff wanted to hack
                    //in a boundedBy geometry, we don't want to do it twice.
                    //So if
                    if (prefixGml && (f.getProperty("boundedBy") != null)) {
                        //do nothing, since our hack will handle it.
                    } else {
                        writeBounds(f.getBounds());
                    }
                }
            } catch (Exception e) {
                throw new IllegalStateException("Could not transform "+f.getIdentifier()+" :"+e, e );
            }
        }
       
        protected Attributes encodeFeatureId( Feature f ) {
          AttributesImpl fidAtts = new org.xml.sax.helpers.AttributesImpl();
            String fid = f.getIdentifier().getID();

            if (fid != null) {
                fidAtts.addAttribute("", "fid", "fid", "fids", fid);
            }
           
            return fidAtts;
        }
       
    }

   
}
TOP

Related Classes of org.geotools.gml.producer.FeatureTransformer$FeatureTranslator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.