Revision: 5663
          http://sourceforge.net/p/jump-pilot/code/5663
Author:   edso
Date:     2018-01-04 18:09:57 +0000 (Thu, 04 Jan 2018)
Log Message:
-----------
JML/GML Reader/Writer add EPSG SRID support via WFS gml:boundedBy->gml:Box tag
JML/GML Reader
  - add SAX Locator support to have errors sport line numbers/columns
  - reordered parsing methods into a meaningful order
  - reverted to BasicFeature/FeatureSchema until Schema editing via plugin is 
finished for FlexibleSchema
JML/GML Writer
  - minimized space indention for better readability and to save disk space
  - is now cancelable via TaskMonitor button
  - reports feature count written so far now

Modified Paths:
--------------
    core/trunk/src/com/vividsolutions/jump/io/GMLInputTemplate.java
    core/trunk/src/com/vividsolutions/jump/io/GMLReader.java
    core/trunk/src/com/vividsolutions/jump/io/GMLWriter.java

Modified: core/trunk/src/com/vividsolutions/jump/io/GMLInputTemplate.java
===================================================================
--- core/trunk/src/com/vividsolutions/jump/io/GMLInputTemplate.java     
2018-01-04 18:05:26 UTC (rev 5662)
+++ core/trunk/src/com/vividsolutions/jump/io/GMLInputTemplate.java     
2018-01-04 18:09:57 UTC (rev 5663)
@@ -66,6 +66,7 @@
     boolean loaded = false;
     String collectionTag;
     String featureTag;
+    String crsTag;
     ArrayList<ColumnDescription> columnDefinitions = new ArrayList<>(); //list 
of type ColumnDescription
 
     private XMLReader xr;
@@ -75,6 +76,7 @@
     private boolean havecollectionTag = false;
     private boolean havefeatureTag = false;
     private boolean havegeometryElement = false;
+    private boolean havecrsTag = false;
 
     //for the jcs column definition
     private int columnDef_valueType = 0; // 0 - undef, 1 = body, 2 = attribute
@@ -550,6 +552,14 @@
                 return;
             }
 
+            if (qName.equalsIgnoreCase("CRSElement")) {
+              tagBody = tagBody.trim();
+              crsTag = tagBody;
+              havecrsTag = true;
+
+              return;
+            }
+
             if (qName.equalsIgnoreCase("name")) {
                 columnDef_columnName = tagBody.trim();
             }

Modified: core/trunk/src/com/vividsolutions/jump/io/GMLReader.java
===================================================================
--- core/trunk/src/com/vividsolutions/jump/io/GMLReader.java    2018-01-04 
18:05:26 UTC (rev 5662)
+++ core/trunk/src/com/vividsolutions/jump/io/GMLReader.java    2018-01-04 
18:09:57 UTC (rev 5663)
@@ -44,6 +44,7 @@
 
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 import org.xml.sax.XMLReader;
@@ -58,12 +59,15 @@
 import com.vividsolutions.jts.geom.Polygon;
 import com.vividsolutions.jts.geom.PrecisionModel;
 import com.vividsolutions.jump.I18N;
+import com.vividsolutions.jump.coordsys.CoordinateSystem;
 import com.vividsolutions.jump.feature.AttributeType;
+import com.vividsolutions.jump.feature.BasicFeature;
 import com.vividsolutions.jump.feature.Feature;
 import com.vividsolutions.jump.feature.FeatureCollection;
 import com.vividsolutions.jump.feature.FeatureDataset;
-import com.vividsolutions.jump.feature.FlexibleFeature;
-import com.vividsolutions.jump.feature.FlexibleFeatureSchema;
+import com.vividsolutions.jump.feature.FeatureSchema;
+//import com.vividsolutions.jump.feature.FlexibleFeature;
+//import com.vividsolutions.jump.feature.FlexibleFeatureSchema;
 import com.vividsolutions.jump.task.DummyTaskMonitor;
 import com.vividsolutions.jump.task.TaskMonitor;
 import com.vividsolutions.jump.task.TaskMonitorSupport;
@@ -246,7 +250,8 @@
 public class GMLReader extends DefaultHandler implements JUMPReader, 
TaskMonitorSupport {
 
   private static int STATE_GET_COLUMNS = 3;
-  private Collection<Exception> exceptions;
+  private Collection<Exception> exceptions = new ArrayList<Exception>();
+  private Locator locator;
 
   /**
    * STATE MEANING <br>
@@ -263,6 +268,7 @@
   private static int STATE_PARSE_GEOM_SIMPLE = 4;
   private static int STATE_WAIT_COLLECTION_TAG = 1;
   private static int STATE_WAIT_FEATURE_TAG = 2;
+  private static int STATE_WAIT_CRS_TAG = 8;
 
   private final static List<String> simpleGeoms = new ArrayList<>();
   private final static List<String> multiGeoms = new ArrayList<>();
@@ -283,9 +289,9 @@
   private int STATE = STATE_INIT; // list of points
   private Point apoint;
   private Feature currentFeature;
-  private int currentGeometryNumb = 1;
+//  private int currentGeometryNumb = 1;
   private FeatureCollection fc;
-  private FlexibleFeatureSchema fcmd; // list of geometries
+  private FeatureSchema fcmd; // list of geometries
   private Geometry finalGeometry; // list of geometrycollections - list of 
list of
                           // geometry
   private String current_geom_qname = "";
@@ -346,6 +352,11 @@
     xr.setErrorHandler(this);
   }
 
+  // get current location for throw errors
+  public void setDocumentLocator(Locator locator) {
+      this.locator = locator;
+  }
+
   /**
    * parse SRID information in geometry tags
    * 
@@ -395,261 +406,23 @@
     }
   }
 
-  /**
-   * SAX HANDLER - move to state 0
-   */
-  public void endDocument() {
-    // System.out.println("End document");
-    STATE = STATE_INIT;
-  }
 
-  /**
-   * SAX handler - handle state information and transitions based on ending
-   * elements Most of the work of the parser is done here.
-   *
-   * @param uri
-   *          Description of the Parameter
-   * @param name
-   *          Description of the Parameter
-   * @param qName
-   *          Description of the Parameter
-   * @exception SAXException
-   *              Description of the Exception
-   */
-  public void endElement(String uri, String name, String qName)
-      throws SAXException {
-    
-    // allow cancellation
-    if (getTaskMonitor().isCancelRequested()) throw new 
SAXCancelledException();
-    
-    try {
-      int index;
 
-      // System.out.println("End element: " + qName);
-      if (STATE == STATE_INIT) {
-        tagBody = new StringBuffer();
 
-        return; // something wrong
-      }
 
-      if (STATE > STATE_GET_COLUMNS) {
-        if (isMultiGeometryTag(qName)) {
-          if (STATE == STATE_PARSE_GEOM_NESTED) {
-            STATE = STATE_PARSE_GEOM_SIMPLE; // finished - no action. geometry
-                                             // is correct
-          } else {
-            // build the geometry that was in that collection
-            Geometry g;
+  private boolean compareToIgnoreCaseWithOptionalGmlColonPrefix( String 
value1, String value2 ){
+    if (value1.equalsIgnoreCase(value2))
+      return true;
 
-            g = geometryFactory.buildGeometry(geometry);
-            geometry = (ArrayList) recursivegeometry.get(STATE
-                - STATE_PARSE_GEOM_NESTED - 1);
-            geometry.add(g);
-            recursivegeometry.remove(STATE - STATE_PARSE_GEOM_NESTED);
-            g = null;
-
-            STATE--;
-          }
-        }
-
-        //System.out.println("wrap-element: " + qName);
-        // finalize geometry and add to feature
-        if (GMLinput.isGeometryElement(qName)) {
-          tagBody = new StringBuffer();
-          STATE = STATE_GET_COLUMNS;
-
-          // -- [sstein] 14.March.2009
-          // read LinearRings even if we don't have polygons
-          if ( linearRing != null ) {
-            geometry.add(linearRing);
-            linearRing = null;
-          }
-
-          if (regex_geomMultiPoint.matcher(current_geom_qname).matches())
-            finalGeometry = geometryFactory
-                .createMultiPoint(geometry.toArray(new Point[0]));
-          else if 
(regex_geomMultiLineString.matcher(current_geom_qname).matches())
-            finalGeometry = geometryFactory
-                .createMultiLineString((geometry.toArray(new LineString[0])));
-          else if 
(regex_geomMultiPolygon.matcher(current_geom_qname).matches())
-            finalGeometry = geometryFactory
-                .createMultiPolygon((geometry.toArray(new Polygon[0])));
-//          else if (current_geom_qname.matches("^(?i)(gml:)?linearring$"))
-//            finalGeometry = (Geometry) geometry.get(0);
-          else
-            finalGeometry = geometryFactory.buildGeometry(geometry);
-
-          // System.out.println("end geom: "+finalGeometry.toString() );
-          currentFeature.setGeometry(finalGeometry);
-          currentGeometryNumb++;
-
-          // reset multi qname
-          current_geom_qname = "";
-          return;
-        }
-        
-        
-        //System.out.println("geom-element: " + qName);
-        // these correspond to <coord><X>0.0</X><Y>0.0</Y></coord>
-        if ((qName.compareToIgnoreCase("X") == 0)
-            || (qName.compareToIgnoreCase("gml:X") == 0)) {
-          singleCoordinate.x = Double.parseDouble(tagBody.toString());
-        } else if ((qName.compareToIgnoreCase("Y") == 0)
-            || (qName.compareToIgnoreCase("gml:y") == 0)) {
-          singleCoordinate.y = Double.parseDouble(tagBody.toString());
-        } else if ((qName.compareToIgnoreCase("Z") == 0)
-            || (qName.compareToIgnoreCase("gml:z") == 0)) {
-          singleCoordinate.z = Double.parseDouble(tagBody.toString());
-        } else if ((qName.compareToIgnoreCase("COORD") == 0)
-            || (qName.compareToIgnoreCase("gml:coord") == 0)) {
-          pointList.add(new Coordinate(singleCoordinate)); // remember it
-        }
-        // this corresponds to
-        // <gml:coordinates>1195156.78946687,382069.533723461</gml:coordinates>
-        else if ((qName.compareToIgnoreCase("COORDINATES") == 0)
-            || (qName.compareToIgnoreCase("gml:coordinates") == 0)) {
-          // tagBody has a wack-load of points in it - we need
-          // to parse them into the pointList list.
-          // assume that the x,y,z coordinate are "," separated, and the points
-          // are " " separated
-          parsePoints(tagBody.toString(), geometryFactory);
-        } else if ((qName.compareToIgnoreCase("linearring") == 0)
-            || (qName.compareToIgnoreCase("gml:linearring") == 0)) {
-          Coordinate[] c = new Coordinate[0];
-
-          c = pointList.toArray(c);
-
-          linearRing = geometryFactory.createLinearRing(c);
-        } else if ((qName.compareToIgnoreCase("outerBoundaryIs") == 0)
-            || (qName.compareToIgnoreCase("gml:outerBoundaryIs") == 0)) {
-          outerBoundary = linearRing;
-          linearRing = null;
-        } else if ((qName.compareToIgnoreCase("innerBoundaryIs") == 0)
-            || (qName.compareToIgnoreCase("gml:innerBoundaryIs") == 0)) {
-          innerBoundaries.add(linearRing);
-          linearRing = null;
-        } else if ((qName.compareToIgnoreCase("polygon") == 0)
-            || (qName.compareToIgnoreCase("gml:polygon") == 0)) {
-          // LinearRing[] lrs = new LinearRing[1];
-          LinearRing[] lrs = new LinearRing[0];
-          lrs = innerBoundaries.toArray(lrs);
-          polygon = geometryFactory.createPolygon(outerBoundary, lrs);
-          geometry.add(polygon);
-        } else if ((qName.compareToIgnoreCase("linestring") == 0)
-            || (qName.compareToIgnoreCase("gml:linestring") == 0)) {
-          Coordinate[] c = new Coordinate[0];
-
-          c = pointList.toArray(c);
-
-          lineString = geometryFactory.createLineString(c);
-          geometry.add(lineString);
-        } else if ((qName.compareToIgnoreCase("point") == 0)
-            || (qName.compareToIgnoreCase("gml:point") == 0)) {
-          apoint = geometryFactory
-              .createPoint(pointList.size() > 0 ? pointList.get(0) : null);
-          geometry.add(apoint);
-        }
-      } else if (STATE == STATE_GET_COLUMNS) {
-        if (qName.compareToIgnoreCase(GMLinput.featureTag) == 0) {
-          tagBody = new StringBuffer();
-          STATE = STATE_WAIT_FEATURE_TAG;
-
-          // System.out.println("end feature");
-          // create a feature and put it inside the featurecollection
-          if (currentFeature.getGeometry() == null) {
-            Geometry g = currentFeature.getGeometry();
-
-            if (g != null) {
-              //System.out.println(g.toString());
-            }
-
-            throw new ParseException("no geometry specified in feature");
-          }
-
-          fc.add(currentFeature);
-          report(fc.size());
-
-          currentFeature = null;
-
-          return;
-        } else {
-          // check to see if this was a tag we want to store as a column
-          // DB: added 2nd check for GML like <a><b></b></a>
-          // the "b" tag is the "lastStartTag_qName" for "</b>" and "</a>" we
-          // only need to
-          // process it once.
-          try {
-            if (((index = GMLinput.match(lastStartTag_qName, 
lastStartTag_atts)) > -1)
-                && (lastStartTag_qName.equalsIgnoreCase(qName))) {
-              // System.out.println("value of " +
-              // GMLinput.columnName(index)+" : " +
-              // GMLinput.getColumnValue(index,tagBody, lastStartTag_atts) );
-
-              // if the column already has a value and multiItems support is
-              // turned on ..and its type ==object
-              if ((multiItemsAsLists)
-                  && (currentFeature.getAttribute(GMLinput.columnName(index)) 
!= null)
-                  && (((ColumnDescription) (GMLinput.columnDefinitions
-                      .get(index))).type == AttributeType.OBJECT)) {
-                Object oldValue = currentFeature.getAttribute(GMLinput
-                    .columnName(index));
-                if (oldValue instanceof List) {
-                  // already a list there - just stuff another thing in!
-                  ((List) oldValue).add(GMLinput.getColumnValue(index,
-                      tagBody.toString(), lastStartTag_atts));
-                } else {
-                  // no list currently there - make a list and replace
-                  List l = new ArrayList();
-                  l.add(oldValue);
-                  l.add(GMLinput.getColumnValue(index, tagBody.toString(),
-                      lastStartTag_atts)); // new value
-                  currentFeature.setAttribute(GMLinput.columnName(index), l);
-                }
-              } else // handle normally
-              {
-                currentFeature.setAttribute(GMLinput.columnName(index),
-                    GMLinput.getColumnValue(index, tagBody.toString(),
-                        lastStartTag_atts));
-              }
-            }
-          } catch (Exception e) {
-            // dont actually do anything with the parse problem - just ignore
-            // it,
-            // we cannot send it back because the function its overiding doesnt
-            // allow
-            e.printStackTrace();
-          }
-
-          tagBody = new StringBuffer();
-        }
-      } else if (STATE == STATE_WAIT_FEATURE_TAG) {
-        if (qName.compareToIgnoreCase(GMLinput.collectionTag) == 0) {
-          STATE = STATE_INIT; // finish
-
-          // System.out.println("DONE!");
-          tagBody = new StringBuffer();
-
-          return;
-        }
-      } else if (STATE == STATE_WAIT_COLLECTION_TAG) {
-        tagBody = new StringBuffer();
-
-        return; // still look for start collection tag
-      }
-    } catch (Exception e) {
-      e.printStackTrace();
-      throw new SAXException(e.getMessage());
-    }
+    // retry making sure both are prefixed
+    String prefix = "gml:";
+    if (!value1.toLowerCase().startsWith(prefix))
+        value1 = prefix + value1;
+    if (!value2.toLowerCase().startsWith(prefix))
+        value2 = prefix + value2;
+    return value1.equalsIgnoreCase(value2);
   }
 
-  public void error(SAXParseException exception) throws SAXException {
-    throw exception;
-  }
-
-  public void fatalError(SAXParseException exception) throws SAXException {
-    throw exception;
-  }
-
   /**
    * Main Entry - load in a GML file
    *
@@ -847,7 +620,7 @@
     if (getTaskMonitor().isCancelRequested()) throw new 
SAXCancelledException();
     
     try {
-      // System.out.println("Start element: " + qName);
+      //System.out.println("Start element: " + qName);
       tagBody = new StringBuffer();
       lastStartTag_uri = uri;
       lastStartTag_name = name;
@@ -854,6 +627,9 @@
       lastStartTag_qName = qName;
       lastStartTag_atts = atts;
 
+//      if (STATE == STATE_WAIT_COLLECTION_TAG)
+//        System.out.println("(STATE == STATE_WAIT_COLLECTION_TAG)");
+      
       if (STATE == STATE_INIT) {
         return; // something wrong
       }
@@ -866,17 +642,47 @@
 
         return;
       }
+      
+      if ((STATE == STATE_WAIT_FEATURE_TAG)
+          && compareToIgnoreCaseWithOptionalGmlColonPrefix(qName, 
GMLinput.crsTag) ) {
+        // found the crs1 tag
 
+        return;
+      }
+
       if ((STATE == STATE_WAIT_FEATURE_TAG)
+          && compareToIgnoreCaseWithOptionalGmlColonPrefix(qName, "Box") ) {
+        // found the crs2 box tag
+
+        // fetch srid id
+        for (int i = 0; i < atts.getLength(); i++) {
+          String attName = atts.getQName(i);
+          if (attName.equalsIgnoreCase("srsName")){
+            String attValue = atts.getValue(i);
+            String sridString = attValue.substring(attValue.lastIndexOf("#") + 
1);
+
+            try {
+              SRID = Integer.valueOf(sridString);
+            } catch (NumberFormatException e) {
+              addParseException("srid '"+sridString+"'is not a number.", e);
+            }
+          }
+        }
+        STATE = STATE_WAIT_FEATURE_TAG;
+
+        return;
+      }
+
+      if ((STATE == STATE_WAIT_FEATURE_TAG)
           && (qName.compareToIgnoreCase(GMLinput.featureTag) == 0)) {
         // found the feature tag
         // System.out.println("found feature");
-        currentFeature = new FlexibleFeature(fcmd);
+        currentFeature = new BasicFeature(fcmd);
         STATE = STATE_GET_COLUMNS;
-        SRID = 0;// default SRID (reset for each feature, but should be 
constant
-                 // for a featurecollection)
-        if (geometryFactory.getSRID() != SRID)
-          geometryFactory = new GeometryFactory(new PrecisionModel(), SRID);
+//        SRID = 0;// default SRID (reset for each feature, but should be 
constant
+//                 // for a featurecollection)
+//        if (geometryFactory.getSRID() != SRID)
+//          geometryFactory = new GeometryFactory(new PrecisionModel(), SRID);
 
         return;
       }
@@ -956,14 +762,281 @@
       }
 
     } catch (Exception e) {
+      e.printStackTrace(System.err);
       throw new SAXException(e.getMessage());
     }
   }
 
+  /**
+   * SAX handler - handle state information and transitions based on ending
+   * elements Most of the work of the parser is done here.
+   *
+   * @param uri
+   *          Description of the Parameter
+   * @param name
+   *          Description of the Parameter
+   * @param qName
+   *          Description of the Parameter
+   * @exception SAXException
+   *              Description of the Exception
+   */
+  public void endElement(String uri, String name, String qName)
+      throws SAXException {
+    
+    // allow cancellation
+    if (getTaskMonitor().isCancelRequested()) throw new 
SAXCancelledException();
+    
+    try {
+      int index;
+
+      // System.out.println("End element: " + qName);
+      if (STATE == STATE_INIT) {
+        tagBody = new StringBuffer();
+
+        return; // something wrong
+      }
+
+      if (STATE > STATE_GET_COLUMNS) {
+        if (isMultiGeometryTag(qName)) {
+          if (STATE == STATE_PARSE_GEOM_NESTED) {
+            STATE = STATE_PARSE_GEOM_SIMPLE; // finished - no action. geometry
+                                             // is correct
+          } else {
+            // build the geometry that was in that collection
+            Geometry g;
+
+            g = geometryFactory.buildGeometry(geometry);
+            geometry = (ArrayList) recursivegeometry.get(STATE
+                - STATE_PARSE_GEOM_NESTED - 1);
+            geometry.add(g);
+            recursivegeometry.remove(STATE - STATE_PARSE_GEOM_NESTED);
+            g = null;
+
+            STATE--;
+          }
+        }
+
+        //System.out.println("wrap-element: " + qName);
+        // finalize geometry and add to feature
+        if (GMLinput.isGeometryElement(qName)) {
+          tagBody = new StringBuffer();
+          STATE = STATE_GET_COLUMNS;
+
+          // -- [sstein] 14.March.2009
+          // read LinearRings even if we don't have polygons
+          if ( linearRing != null ) {
+            geometry.add(linearRing);
+            linearRing = null;
+          }
+
+          if (regex_geomMultiPoint.matcher(current_geom_qname).matches())
+            finalGeometry = geometryFactory
+                .createMultiPoint(geometry.toArray(new Point[0]));
+          else if 
(regex_geomMultiLineString.matcher(current_geom_qname).matches())
+            finalGeometry = geometryFactory
+                .createMultiLineString((geometry.toArray(new LineString[0])));
+          else if 
(regex_geomMultiPolygon.matcher(current_geom_qname).matches())
+            finalGeometry = geometryFactory
+                .createMultiPolygon((geometry.toArray(new Polygon[0])));
+//          else if (current_geom_qname.matches("^(?i)(gml:)?linearring$"))
+//            finalGeometry = (Geometry) geometry.get(0);
+          else
+            finalGeometry = geometryFactory.buildGeometry(geometry);
+
+          // System.out.println("end geom: "+finalGeometry.toString() );
+          currentFeature.setGeometry(finalGeometry);
+//          currentGeometryNumb++;
+
+          // reset multi qname
+          current_geom_qname = "";
+          return;
+        }
+        
+        
+        //System.out.println("geom-element: " + qName);
+        // these correspond to <coord><X>0.0</X><Y>0.0</Y></coord>
+        if ((qName.compareToIgnoreCase("X") == 0)
+            || (qName.compareToIgnoreCase("gml:X") == 0)) {
+          singleCoordinate.x = Double.parseDouble(tagBody.toString());
+        } else if ((qName.compareToIgnoreCase("Y") == 0)
+            || (qName.compareToIgnoreCase("gml:y") == 0)) {
+          singleCoordinate.y = Double.parseDouble(tagBody.toString());
+        } else if ((qName.compareToIgnoreCase("Z") == 0)
+            || (qName.compareToIgnoreCase("gml:z") == 0)) {
+          singleCoordinate.z = Double.parseDouble(tagBody.toString());
+        } else if ((qName.compareToIgnoreCase("COORD") == 0)
+            || (qName.compareToIgnoreCase("gml:coord") == 0)) {
+          pointList.add(new Coordinate(singleCoordinate)); // remember it
+        }
+        // this corresponds to
+        // <gml:coordinates>1195156.78946687,382069.533723461</gml:coordinates>
+        else if ((qName.compareToIgnoreCase("COORDINATES") == 0)
+            || (qName.compareToIgnoreCase("gml:coordinates") == 0)) {
+          // tagBody has a wack-load of points in it - we need
+          // to parse them into the pointList list.
+          // assume that the x,y,z coordinate are "," separated, and the points
+          // are " " separated
+          parsePoints(tagBody.toString(), geometryFactory);
+        } else if ((qName.compareToIgnoreCase("linearring") == 0)
+            || (qName.compareToIgnoreCase("gml:linearring") == 0)) {
+          Coordinate[] c = new Coordinate[0];
+
+          c = pointList.toArray(c);
+
+          linearRing = geometryFactory.createLinearRing(c);
+        } else if ((qName.compareToIgnoreCase("outerBoundaryIs") == 0)
+            || (qName.compareToIgnoreCase("gml:outerBoundaryIs") == 0)) {
+          outerBoundary = linearRing;
+          linearRing = null;
+        } else if ((qName.compareToIgnoreCase("innerBoundaryIs") == 0)
+            || (qName.compareToIgnoreCase("gml:innerBoundaryIs") == 0)) {
+          innerBoundaries.add(linearRing);
+          linearRing = null;
+        } else if ((qName.compareToIgnoreCase("polygon") == 0)
+            || (qName.compareToIgnoreCase("gml:polygon") == 0)) {
+          // LinearRing[] lrs = new LinearRing[1];
+          LinearRing[] lrs = new LinearRing[0];
+          lrs = innerBoundaries.toArray(lrs);
+          polygon = geometryFactory.createPolygon(outerBoundary, lrs);
+          geometry.add(polygon);
+        } else if ((qName.compareToIgnoreCase("linestring") == 0)
+            || (qName.compareToIgnoreCase("gml:linestring") == 0)) {
+          Coordinate[] c = new Coordinate[0];
+
+          c = pointList.toArray(c);
+
+          lineString = geometryFactory.createLineString(c);
+          geometry.add(lineString);
+        } else if ((qName.compareToIgnoreCase("point") == 0)
+            || (qName.compareToIgnoreCase("gml:point") == 0)) {
+          apoint = geometryFactory
+              .createPoint(pointList.size() > 0 ? pointList.get(0) : null);
+          geometry.add(apoint);
+        }
+      } else if (STATE == STATE_GET_COLUMNS) {
+        if (qName.compareToIgnoreCase(GMLinput.featureTag) == 0) {
+          tagBody = new StringBuffer();
+          STATE = STATE_WAIT_FEATURE_TAG;
+
+          // System.out.println("end feature");
+          // create a feature and put it inside the featurecollection
+          if (currentFeature.getGeometry() == null) {
+            Geometry g = currentFeature.getGeometry();
+
+            if (g != null) {
+              //System.out.println(g.toString());
+            }
+
+            throw new ParseException("no geometry specified in feature");
+          }
+
+          fc.add(currentFeature);
+          report(fc.size());
+
+          currentFeature = null;
+
+          return;
+        } else {
+          // check to see if this was a tag we want to store as a column
+          // DB: added 2nd check for GML like <a><b></b></a>
+          // the "b" tag is the "lastStartTag_qName" for "</b>" and "</a>" we
+          // only need to
+          // process it once.
+          try {
+            if (((index = GMLinput.match(lastStartTag_qName, 
lastStartTag_atts)) > -1)
+                && (lastStartTag_qName.equalsIgnoreCase(qName))) {
+              // System.out.println("value of " +
+              // GMLinput.columnName(index)+" : " +
+              // GMLinput.getColumnValue(index,tagBody, lastStartTag_atts) );
+
+              // if the column already has a value and multiItems support is
+              // turned on ..and its type ==object
+              if ((multiItemsAsLists)
+                  && (currentFeature.getAttribute(GMLinput.columnName(index)) 
!= null)
+                  && (((ColumnDescription) (GMLinput.columnDefinitions
+                      .get(index))).type == AttributeType.OBJECT)) {
+                Object oldValue = currentFeature.getAttribute(GMLinput
+                    .columnName(index));
+                if (oldValue instanceof List) {
+                  // already a list there - just stuff another thing in!
+                  ((List) oldValue).add(GMLinput.getColumnValue(index,
+                      tagBody.toString(), lastStartTag_atts));
+                } else {
+                  // no list currently there - make a list and replace
+                  List l = new ArrayList();
+                  l.add(oldValue);
+                  l.add(GMLinput.getColumnValue(index, tagBody.toString(),
+                      lastStartTag_atts)); // new value
+                  currentFeature.setAttribute(GMLinput.columnName(index), l);
+                }
+              } else // handle normally
+              {
+                currentFeature.setAttribute(GMLinput.columnName(index),
+                    GMLinput.getColumnValue(index, tagBody.toString(),
+                        lastStartTag_atts));
+              }
+            }
+          } catch (Exception e) {
+            // dont actually do anything with the parse problem - just ignore
+            // it,
+            // we cannot send it back because the function its overiding doesnt
+            // allow
+            e.printStackTrace();
+          }
+
+          tagBody = new StringBuffer();
+        }
+      } else if (STATE == STATE_WAIT_FEATURE_TAG) {
+        if (qName.compareToIgnoreCase(GMLinput.collectionTag) == 0) {
+          STATE = STATE_INIT; // finish
+
+          // System.out.println("DONE!");
+          tagBody = new StringBuffer();
+
+          return;
+        }
+      } else if (STATE == STATE_WAIT_COLLECTION_TAG) {
+        tagBody = new StringBuffer();
+
+        return; // still look for start collection tag
+      } else if (STATE == STATE_WAIT_CRS_TAG) {
+        if (compareToIgnoreCaseWithOptionalGmlColonPrefix(qName, "Box")) {
+          STATE = STATE_WAIT_COLLECTION_TAG;
+        }
+        return; // still looking for <gml:Box/>
+      }
+      
+      
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw new SAXException(e.getMessage());
+    }
+  }
+
+  /**
+   * SAX HANDLER - move to state 0
+   */
+  public void endDocument() {
+    // System.out.println("End document");
+    
+    // apply srid if not unset
+    if (SRID > 0)
+      fc.getFeatureSchema().setCoordinateSystem(new CoordinateSystem("", SRID, 
null));
+    STATE = STATE_INIT;
+  }
+
   // //////////////////////////////////////////////////////////////////
   // Error handlers.
   // //////////////////////////////////////////////////////////////////
   public void warning(SAXParseException exception) throws SAXException {
+    exceptions.add(exception);
+  }
+
+  public void error(SAXParseException exception) throws SAXException {
+    exceptions.add(exception);
+  }
+
+  public void fatalError(SAXParseException exception) throws SAXException {
     throw exception;
   }
 
@@ -1111,19 +1184,26 @@
       int semicolonLoc = srsName.lastIndexOf(':');
       if (semicolonLoc == -1)
         return 0;
-      srsName = srsName.substring(semicolonLoc + 1).trim();
-      return Integer.parseInt(srsName);
-    } catch (Exception e) {
+      return Integer.parseInt(srsName.substring(semicolonLoc + 1).trim());
+    } catch (NumberFormatException e) {
+      addParseException( "srid '"+srsName+"'is not a number.", e);
       return 0;
     }
   }
 
+  protected void addException(Exception e){
+    exceptions.add(e);
+  }
+
+  protected void addParseException(String message, Exception cause) {
+    exceptions.add(
+        new ParseException(message, streamName, locator.getLineNumber(), 
locator.getColumnNumber(), cause));
+  }
+
   /**
    * @return exceptions collected during the reading process.
    */
   public Collection<Exception> getExceptions() {
-    if (exceptions == null)
-      exceptions = new ArrayList<>();
     return exceptions;
   }
 

Modified: core/trunk/src/com/vividsolutions/jump/io/GMLWriter.java
===================================================================
--- core/trunk/src/com/vividsolutions/jump/io/GMLWriter.java    2018-01-04 
18:05:26 UTC (rev 5662)
+++ core/trunk/src/com/vividsolutions/jump/io/GMLWriter.java    2018-01-04 
18:09:57 UTC (rev 5663)
@@ -37,6 +37,7 @@
 import java.io.FileReader;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
+import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
@@ -44,11 +45,19 @@
 
 import org.apache.commons.lang3.StringEscapeUtils;
 
+import com.vividsolutions.jts.geom.Envelope;
 import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jump.I18N;
+import com.vividsolutions.jump.coordsys.CoordinateSystem;
 import com.vividsolutions.jump.feature.AttributeType;
 import com.vividsolutions.jump.feature.Feature;
 import com.vividsolutions.jump.feature.FeatureCollection;
 import com.vividsolutions.jump.feature.FeatureSchema;
+import com.vividsolutions.jump.task.DummyTaskMonitor;
+import com.vividsolutions.jump.task.TaskMonitor;
+import com.vividsolutions.jump.task.TaskMonitorSupport;
+import com.vividsolutions.jump.task.TaskMonitorUtil;
+import com.vividsolutions.jump.util.Timer;
 
 
 /**
@@ -109,18 +118,18 @@
  * </pre>
  * 
  */
-public class GMLWriter implements JUMPWriter {
+public class GMLWriter implements JUMPWriter, TaskMonitorSupport {
 
     // Standard tags for the auto-generated outputTemplate.
-    private static String standard_geom = "geometry";
-    private static String standard_feature = "feature";
-    private static String standard_featureCollection = "featureCollection";
+    private static final String standard_geom = "geometry";
+    private static final String standard_feature = "feature";
+    private static final String standard_featureCollection = 
"featureCollection";
     private GMLOutputTemplate outputTemplate = null;
     private GMLGeometryWriter geometryWriter = new GMLGeometryWriter();
 
     /** constructor**/
     public GMLWriter() {
-        geometryWriter.setLinePrefix("                ");
+        geometryWriter.setLinePrefix("      ");
     }
 
     /**
@@ -146,7 +155,7 @@
 
         if (dp.getProperty("TemplateFile") == null) {
             //we're going create the output template
-            gmlTemplate = 
GMLWriter.makeOutputTemplate(featureCollection.getFeatureSchema());
+            gmlTemplate = makeOutputTemplate(featureCollection);
         } else {
             // load the template
             java.io.Reader r;
@@ -192,7 +201,29 @@
 
         buffWriter.write(outputTemplate.headerText);
 
-        for (Iterator t = featureCollection.iterator(); t.hasNext();) {
+        // write a bounded by box definition setting an srid for the whole 
dataset
+        // Workaround or convenience as OGC writes in the WFS 2.0 standard:
+        //   11.3.6 Inheritance rules for srsName values
+        if (getSrid(featureCollection) > 0){
+          Envelope env = featureCollection.getEnvelope();
+          DecimalFormat df = new DecimalFormat("#,##0.00");
+          df.setGroupingUsed(false);
+          String envString = df.format(env.getMinX()) + "," + 
df.format(env.getMinY()) + " " + df.format(env.getMaxX())
+              + "," + df.format(env.getMaxY());
+          buffWriter.write(
+            "  <gml:boundedBy>\n" +
+            "    <gml:Box 
srsName=\"http://www.opengis.net/gml/srs/epsg.xml#"+getSrid(featureCollection)+"\">\n"
 +
+            "      <gml:coordinates decimal=\".\" cs=\",\" ts=\" \">" + 
envString + "</gml:coordinates>\n" +
+            "    </gml:Box>\n" +
+            "  </gml:boundedBy>\n");
+        }
+
+        long milliSeconds = 0;
+        int count = 0;
+        int size = featureCollection.size();
+        TaskMonitorUtil.report(getTaskMonitor(),
+            I18N.getMessage("Writer.writing-features"));
+        for (Iterator t = featureCollection.iterator(); t.hasNext() && 
!getTaskMonitor().isCancelRequested();) {
             f = (Feature) t.next();
 
             for (int u = 0; u < outputTemplate.featureText.size(); u++) {
@@ -207,6 +238,14 @@
 
             buffWriter.write(outputTemplate.featureTextfooter);
             buffWriter.write("\n");
+            
+            long now = Timer.milliSecondsSince(0);
+            count++;
+            // show status every .5s
+            if (now - 500 >= milliSeconds) {
+              milliSeconds = now;
+              TaskMonitorUtil.report(getTaskMonitor(), count, size, "");
+            }
         }
 
         buffWriter.write(outputTemplate.footerText);
@@ -224,41 +263,22 @@
         // this should take care of really _all_ XML1.0 invalid chars
         // see 
https://commons.apache.org/proper/commons-lang/javadocs/api-3.5/org/apache/commons/lang3/StringEscapeUtils.html#escapeXml10-java.lang.String-
         return StringEscapeUtils.escapeXml10(s);
+    }
 
-        
-        // kept for reference
-//        StringBuilder sb = new StringBuilder(s);
-//        char c;
-//
-//        for (int t = 0; t < sb.length(); t++) {
-//            c = sb.charAt(t);
-//
-//            if (c == '<') {
-//                sb.replace(t, t + 1, "&lt;");
-//            }
-//
-//            if (c == '>') {
-//                sb.replace(t, t + 1, "&gt;");
-//            }
-//
-//            if (c == '&') {
-//                sb.replace(t, t + 1, "&amp;");
-//            }
-//
-//            if (c == '\'') {
-//                sb.replace(t, t + 1, "&apos;");
-//            }
-//
-//            if (c == '"') {
-//                sb.replace(t, t + 1, "&quot;");
-//            }
-//
-//            if ((int)c < 20 && c != '\t' && c != '\n' && c != '\r') {
-//                sb.replace(t, t + 1, "");
-//            }
-//        }
-//
-//        return sb.toString();
+    /**
+     * utility function to retrieve srid from a feature collection or -1 if 
none
+     * 
+     * @param featureCollection
+     * @return srid
+     */
+    private static int getSrid( FeatureCollection featureCollection ){
+      Integer srid = -1;
+      CoordinateSystem cs = 
featureCollection.getFeatureSchema().getCoordinateSystem();
+      if (!cs.equals(CoordinateSystem.UNSPECIFIED)) {
+        srid = cs.getEPSGCode();
+      }
+
+      return srid.intValue();
     }
 
     /**
@@ -327,7 +347,7 @@
     }
     
     // precompile pattern for performance reasons
-    Pattern closingTagPattern = Pattern.compile(">$");
+    static Pattern closingTagPattern = Pattern.compile(">$");
     
     private void evaluateToken(Feature f, String pre, String token, Writer 
writer)
         throws Exception {
@@ -399,11 +419,11 @@
         return attribute.toString();
     }
 
+    private static final SimpleDateFormat dateFormatter = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
     protected String format(Date date) {
         return dateFormatter.format(date);
     }
-    
-    private SimpleDateFormat dateFormatter = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
 
     /**
      * Given a FeatureSchema, make an output template
@@ -410,7 +430,7 @@
      * in the JCS  format
      * @param fcmd input featureSchema
      */
-    private static GMLOutputTemplate makeOutputTemplate(FeatureSchema fcmd) {
+    private static GMLOutputTemplate makeOutputTemplate(FeatureCollection fc) {
         GMLOutputTemplate result;
         String inputTemplate;
         int t;
@@ -418,16 +438,17 @@
         String colText;
         String colCode;
         String colHeader;
+        FeatureSchema fcmd = fc.getFeatureSchema();
 
         result = new GMLOutputTemplate();
 
-        inputTemplate = makeInputTemplate(fcmd);
+        inputTemplate = makeInputTemplate(fc);
 
         result.setHeaderText(
             "<?xml version='1.0' encoding='UTF-8'?>\n<JCSDataFile 
xmlns:gml=\"http://www.opengis.net/gml\"; 
xmlns:xsi=\"http://www.w3.org/2000/10/XMLSchema-instance\"; >\n" +
             inputTemplate + "<" + standard_featureCollection + ">\n");
 
-        colHeader = "     <" + standard_feature + "> \n";
+        colHeader = "  <" + standard_feature + "> \n";
 
         for (t = 0; t < fcmd.getAttributeCount(); t++) {
             colName = fcmd.getAttributeName(t);
@@ -434,23 +455,23 @@
 
             if (t != fcmd.getGeometryIndex()) {
                 //not geometry
-                colText = colHeader + "          <property name=\"" + 
escapeXML(colName) +
+                colText = colHeader + "    <property name=\"" + 
escapeXML(colName) +
                     "\">";
                 colCode = "=column " + colName;
                 colHeader = "</property>\n";
             } else {
                 //geometry
-                colText = colHeader + "          <" + standard_geom + ">\n";
+                colText = colHeader + "    <" + standard_geom + ">\n";
                 colCode = "=geometry";
-                colHeader = "          </" + standard_geom + ">\n";
+                colHeader = "    </" + standard_geom + ">\n";
             }
 
             result.addItem(colText, colCode);
         }
 
-        result.setFeatureFooter(colHeader + "     </" + standard_feature +
+        result.setFeatureFooter(colHeader + "  </" + standard_feature +
             ">\n");
-        result.setFooterText("     </" + standard_featureCollection +
+        result.setFooterText("  </" + standard_featureCollection +
             ">\n</JCSDataFile>\n");
 
         return result;
@@ -463,15 +484,21 @@
      *
      * @param fcmd the featureSchema to describe
      */
-    private static String makeInputTemplate(FeatureSchema fcmd) {
-        String result;
+    private static String makeInputTemplate(FeatureCollection fc) {
+        String result = "";
         int t;
+        FeatureSchema fcmd = fc.getFeatureSchema(); 
 
-        result = "<JCSGMLInputTemplate>\n<CollectionElement>" +
-            standard_featureCollection +
-            "</CollectionElement> \n<FeatureElement>" + standard_feature +
-            "</FeatureElement>\n<GeometryElement>" + standard_geom +
-            "</GeometryElement>\n<ColumnDefinitions>\n";
+        result += "<JCSGMLInputTemplate>\n";
+        result += "<CollectionElement>" + standard_featureCollection + 
"</CollectionElement>\n";
+        result += "<FeatureElement>" + standard_feature + 
"</FeatureElement>\n";
+        result += "<GeometryElement>" + standard_geom + "</GeometryElement>\n";
+        // write a bounded by box definition setting an srid for the whole 
dataset
+        // Workaround or convenience as OGC writes in the WFS 2.0 standard:
+        //   11.3.6 Inheritance rules for srsName values
+        if (getSrid(fc) > 0)
+          result += "<CRSElement>boundedBy</CRSElement>\n";
+        result += "<ColumnDefinitions>\n";
 
         //fill in each of the column defs
         for (t = 0; t < fcmd.getAttributeCount(); t++) {
@@ -505,4 +532,14 @@
 
         return result;
     }
+
+    private TaskMonitor taskMonitor = new DummyTaskMonitor();
+
+    public void setTaskMonitor(TaskMonitor taskMonitor) {
+      this.taskMonitor = taskMonitor;
+    }
+
+    public TaskMonitor getTaskMonitor() {
+      return taskMonitor;
+    }
 }


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to