Parser Class

package javaxt.geospatial.coordinate;
import javaxt.geospatial.geometry.Box;
import javaxt.geospatial.geometry.Geometry;
import javaxt.geospatial.geometry.Line;
import javaxt.geospatial.geometry.MultiLine;
import javaxt.geospatial.geometry.MultiPoint;
import javaxt.geospatial.geometry.MultiPolygon;
import javaxt.geospatial.geometry.Point;
import javaxt.geospatial.geometry.Points;
import javaxt.geospatial.geometry.Polygon;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

//******************************************************************************
//**  CoordinateParser Class - By Peter Borissow
//******************************************************************************
/**
 *   Used to parse a string containing coordinate tuples. Currently limited to
 *   2D geometries. Optimized for Lat/Long tuples.
 *
 ******************************************************************************/

public class Parser {
    
    private Geometry Geometry = null;
    
    
  //**************************************************************************
  //** Creates a new instance of CoordinateParser
  //**************************************************************************
    
    public Parser(String Coordinates){
        Geometry = getGeometry(Coordinates);
    }
    
   
    
  //**************************************************************************
  //** getGeometry
  //**************************************************************************
    
    public Geometry getGeometry(){
        return Geometry;
    }
    
    
  //**************************************************************************
  //** getGeometry
  //**************************************************************************
  /**  Private function that tries to convert a string of coordinates into a 
   *   Geometry. The string may contain coordinate tuples or gml. 
   */
    
    private Geometry getGeometry(String Coordinates){
        Coordinates = Coordinates.trim();
        if (Coordinates.startsWith("<")){ //GML or GeoRSS  input?
          
            try{
              
              //Convert Coordinates (String) into a DOM Document
                org.w3c.dom.Document doc = Parser.createDocument(Coordinates);
                
              //Get Outer Node
                Node OuterNode = Parser.getOuterNode(doc);
                
              //Get Name of the First Node
                String nodeName = getNodeName(OuterNode);
                
              //Get the srsName Attribute (may return a value if gml)
                String srsName = Parser.getAttributeValue(OuterNode,"srsName");
                

              //Point
                if (nodeName.equalsIgnoreCase("Point")){
                    if (Parser.hasChildren(OuterNode)==false){
                        Coordinates = Parser.getNodeValue(OuterNode);
                        return getGeometry(Coordinates.trim());
                        
                    }
                    else{ //gml
                        Point[] Points = getGMLCoordinates(OuterNode.getChildNodes());
                        return Points[0];
                    }
                }
                
              //Line
                if (nodeName.equalsIgnoreCase("Line")){
                    if (Parser.hasChildren(OuterNode)==false){
                        Coordinates = Parser.getNodeValue(OuterNode);
                        return getGeometry(Coordinates.trim());
                    }
                    else{ //gml? is there such a thing a "line"
                    }
                }
                
              //LineString
                if (nodeName.equalsIgnoreCase("LineString")){
                    if (Parser.hasChildren(OuterNode)==false){
                        Coordinates = Parser.getNodeValue(OuterNode);
                        return getGeometry(Coordinates.trim());
                    }
                    else{ //gml
                        Point[] Points = getGMLCoordinates(OuterNode.getChildNodes());
                        Line line = new Line(Points);
                        line.setSRS(Parser.getAttributeValue(OuterNode,"srsName"));
                        return line;
                    }
                }
                
              //Polygon
                if (nodeName.equalsIgnoreCase("Polygon")){
                    if (Parser.hasChildren(OuterNode)==false){
                        Coordinates = Parser.getNodeValue(OuterNode);
                        return getGeometry(Coordinates.trim());   
                    }
                    else{ //gml polygon
                        
                        NodeList outerBoundary = OuterNode.getChildNodes();
                        Polygon polygon = getGMLPolygon(outerBoundary);
                        polygon.setSRS(Parser.getAttributeValue(OuterNode,"srsName"));
                        return polygon;


                    }

                }
                
              //Box/Envelope
                if (nodeName.equalsIgnoreCase("Box") || nodeName.equalsIgnoreCase("Envelope")){
                    Points Points = null;
                    if (Parser.hasChildren(OuterNode)==false){ //georss box
                        Coordinates = Parser.getNodeValue(OuterNode);
                        Points = getPoints(Coordinates);
                    }
                    else{ //gml envelope or box
                        Points = new Points();
                        NodeList PointMembers = OuterNode.getChildNodes();
                        
                      //Try to retrieve points defined by upper/lower corners
                        for (int i=0; i<PointMembers.getLength(); i++){
                             Node node = PointMembers.item(i);
                             nodeName = getNodeName(node);
                             if (nodeName.equalsIgnoreCase("lowerCorner")){
                                 Points.addPoints(getPoints(Parser.getNodeValue(node)));
                             }
                             else if (nodeName.equalsIgnoreCase("upperCorner")){
                                 Points.addPoints(getPoints(Parser.getNodeValue(node)));
                             }
                        }
                        
                      //If no upper/lower tags found, try extracting coordinates using default parser
                        if (Points.getLength()<=0){    
                            Points.addPoints(getGMLCoordinates(PointMembers));
                        }
                    }
                    if (Points!=null) {
                        if (Points.getLength()==2){
                            Box box = new Box(Points.getFirstPoint(), Points.getLastPoint());
                            box.setSRS(Parser.getAttributeValue(OuterNode,"srsName"));
                            return box;
                        }
                    }
                }
                
                
              //MultiPoint
                if (nodeName.equalsIgnoreCase("MultiPoint")){
                    if (Parser.hasChildren(OuterNode)==false){
                        //what to do?  
                    }
                    else{ //gml multipoint
                        
                        
                        MultiPoint MultiPoint = new MultiPoint();
                        NodeList PointMembers = OuterNode.getChildNodes();
                        for (int i=0; i<PointMembers.getLength(); i++){
                             Node node = PointMembers.item(i);
                             if (getNodeName(node).equalsIgnoreCase("PointMember")){
                                 NodeList Points = node.getChildNodes();
                                 for (int j=0; j<Points.getLength(); j++){
                                      if (getNodeName(Points.item(j)).equalsIgnoreCase("Point")){
                                          Point[] arrPoints = getGMLCoordinates(Points.item(j).getChildNodes());
                                          MultiPoint.addPoint(arrPoints[0]);
                                      }
                                 }
                             }
                        }
                        
                        
                        MultiPoint.setSRS(Parser.getAttributeValue(OuterNode,"srsName"));
                        return MultiPoint;

                    }

                }
                
              //MultiLine
                if (nodeName.equalsIgnoreCase("MultiLineString")){
                    if (Parser.hasChildren(OuterNode)==false){
                        //what to do? 
                    }
                    else{ //gml MultiLine
                        
                        
                        MultiLine multiLine = new MultiLine();
                        NodeList LineMembers = OuterNode.getChildNodes();
                        for (int i=0; i<LineMembers.getLength(); i++){
                             Node node = LineMembers.item(i);
                             nodeName = getNodeName(node);
                             if (nodeName.equalsIgnoreCase("lineStringMember")){
                                 NodeList Lines = node.getChildNodes();
                                 for (int j=0; j<Lines.getLength(); j++){
                                      node = Lines.item(j);
                                      if (getNodeName(node).equalsIgnoreCase("LineString")){
                                          Point[] points = getGMLCoordinates(node.getChildNodes());
                                          Line line = new Line(points);
                                          multiLine.addLine(line);
                                      }
                                 }
                             }
                        }
                        
                        multiLine.setSRS(Parser.getAttributeValue(OuterNode,"srsName"));
                        return multiLine;

                    }

                }
                
                
              //MultiPolygon
                if (nodeName.equalsIgnoreCase("MultiPolygon")){
                    if (Parser.hasChildren(OuterNode)==false){
                        //what to do? 
                    }
                    else{ //gml MultiPolygon
                        
                        
                        MultiPolygon multiPolygon = new MultiPolygon();
                        multiPolygon.setSRS(srsName);
                        
                        NodeList PolygonMembers = OuterNode.getChildNodes();
                        for (int i=0; i<PolygonMembers.getLength(); i++){
                             Node node = PolygonMembers.item(i);
                             nodeName = getNodeName(node);
                             if (nodeName.equalsIgnoreCase("polygonMember")){
                                 NodeList Polygons = node.getChildNodes();
                                 for (int j=0; j<Polygons.getLength(); j++){
                                      node = Polygons.item(j);
                                      nodeName = getNodeName(node);
                                      if (nodeName.equalsIgnoreCase("Polygon")){
                                          Polygon polygon = getGMLPolygon(node.getChildNodes());
                                          multiPolygon.addPolygon(polygon);
                                      }
                                 }

                             }
                        }

                        
                        return multiPolygon;

                    }

                }
                
                
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
        else{
            Coordinates = Coordinates.toUpperCase();
            if (Coordinates.startsWith("POINT")){
                String[] pt = Coordinates.substring(Coordinates.indexOf("(")+1, Coordinates.indexOf(")")).trim().split(" ");
                return new Point(cdbl(pt[0]), cdbl(pt[1]));
            }
            else if (Coordinates.startsWith("POLYGON")){
                Coordinates = Coordinates.substring(Coordinates.indexOf("(")+1, Coordinates.indexOf(")")).trim();
                Coordinates = Coordinates.substring(Coordinates.indexOf("(")+1).trim();
                Points points = new Points();
                for (String coord : Coordinates.split(",")){
                    String[] pt = coord.trim().split(" ");
                    points.addPoint(cdbl(pt[0]), cdbl(pt[1]));
                }
                return new Polygon(points);
            }
            else if (Coordinates.startsWith("LINESTRING")){
                Coordinates = Coordinates.substring(Coordinates.indexOf("(")+1, Coordinates.indexOf(")")).trim();
                Coordinates = Coordinates.substring(Coordinates.indexOf("(")+1).trim();
                Points points = new Points();
                for (String coord : Coordinates.split(",")){
                    String[] pt = coord.trim().split(" ");
                    points.addPoint(cdbl(pt[0]), cdbl(pt[1]));
                }
                return new Line(points);
            }
            else{
                
                Points Points = getPoints(Coordinates);
                if (Points.getLength()==1){
                    return Points.getFirstPoint();
                }
                else{
                    if (Points.getFirstPoint().equals(Points.getLastPoint())){
                        return new Polygon(Points);
                    }
                    else{
                        return new Line(Points);
                    }
                }
            }
            
        }
        
        return null;
    }
    
    
  //**************************************************************************
  //** getGMLPolygon
  //**************************************************************************
  /**  Used to convert a gml nodelist into a polygon */
    
    private Polygon getGMLPolygon(NodeList outerBoundary){
        for (int i=0; i<outerBoundary.getLength(); i++){
             Node node = outerBoundary.item(i);
             String nodeName = getNodeName(node);
             
             if (nodeName.equalsIgnoreCase("outerBoundaryIs") || 
                 nodeName.equalsIgnoreCase("exterior")){
                 NodeList LinearRing = node.getChildNodes();
                 for (int j=0; j<LinearRing.getLength(); j++){
                      node = LinearRing.item(i);
                      nodeName = getNodeName(node);
                      Point[] Points = getGMLCoordinates(node.getChildNodes());
                      return new Polygon(Points);
                 }

             }
             else if(nodeName.equalsIgnoreCase("LinearRing")){
                  return new Polygon(getGMLCoordinates(node.getChildNodes()));
             }
        }
        
        return null;
    }
    
    
    
  //**************************************************************************
  //** getGMLCoordinates
  //**************************************************************************
  /**  Used to convert a gml nodelist containing coordinates to a Point array */
    
    private Point[] getGMLCoordinates(org.w3c.dom.NodeList ChildNodes){
        
        String CoordinateSeparator = ",";
        String TupleSeparator = " ";
        Points Points = new Points();
        
        for (int i=0; i<ChildNodes.getLength(); i++){
             Node Node = ChildNodes.item(i);
             String NodeName = getNodeName(Node);
             
             
           //Parse Coordinate List
             if (NodeName.equalsIgnoreCase("coordinates")){
                 
                 String cs = Parser.getAttributeValue(Node, "cs");
                 String ts = Parser.getAttributeValue(Node, "ts");
                 if (cs.length()>0) CoordinateSeparator = cs;
                 if (ts.length()>0) TupleSeparator = ts;
                 
                 String Coordinates = Parser.getNodeValue(Node).trim();
                 String[] points = Coordinates.split(TupleSeparator);
                 
                 for (int j=0; j<points.length; j++){
                      String point = points[j].trim(); 
                      if (point.length()>0){
                          String[] pt = point.split(CoordinateSeparator);
                          if (pt.length==2){ 
                              point = pt[0].trim() + "," + pt[1].trim();
                              Points.addPoints(getPoints(point));
                          }
                      }
                 }
                 
                 
             }
             
           //Parse Position List
             else if(NodeName.equalsIgnoreCase("pos") || NodeName.equalsIgnoreCase("posList")){
                 String Coordinates = Parser.getNodeValue(Node).trim();
                 //Points.addPoints(Coordinates);
                 Points.addPoints(getPoints(Coordinates));
             }
             
           //Parse Nested Coordinates
             else if (NodeName.equalsIgnoreCase("coord")){
                 NodeList Coords = Node.getChildNodes();
                 
                 String x = "";
                 String y = "";        
                 
                 for (int j=0; j<Coords.getLength(); j++){
                      Node Coord = Coords.item(j);
                      String CoordName = getNodeName(Coord);
                      if (CoordName.equalsIgnoreCase("x")){
                          x = Parser.getNodeValue(Coord).trim();
                      }
                      if (CoordName.equalsIgnoreCase("y")){
                          y = Parser.getNodeValue(Coord).trim();
                      }
                 }
                 
                 if (x.length()>0 && y.length()>0){
                     Points.addPoints(getPoints(x + "," + y));
                 }
                 
             }
             else{
                 //throw an error?
                 System.out.println(NodeName);
             }

             
        }
        
        return Points.getArray();
    }
    
  
    

    
  //**************************************************************************
  //** getPoints
  //**************************************************************************
  /**  Used to convert a string containing coordinate tuples (x,y) into an 
   *   array of points. Only use with Lat/Long coordinate tuples.
   */
    
    private Points getPoints(String strGeometry) {        
        
        strGeometry = replace(strGeometry,"\t"," ");
        strGeometry = replace(strGeometry,"\n"," ");
        strGeometry = replace(strGeometry,"\r"," ");
        strGeometry = strGeometry.trim();
        if (strGeometry.length() == 0) return null;

      //Convert Degrees, minutes, seconds symbols (37° 23" 12')
        strGeometry = replace(strGeometry,"°",":");
        strGeometry = replace(strGeometry,"\"",":");
        strGeometry = replace(strGeometry,"'","");



      //Convert All Deliminators into Whitespaces
        String wd = "";
        String Char = "";
        for (int i=0; i<strGeometry.length(); i++){ //i=1; i<=len(strGeometry)
             Char = strGeometry.substring(i, i+1); //mid(strGeometry,i,1);                    
             
             if (Case(Char.toLowerCase(), new String[]{":", ".", "+", "-", "n", "s", "e", "w", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"})){
                    wd = wd + Char;               
             }
             else {
                    wd = wd + " ";      
             }
        }
        
        strGeometry = wd; 
        //System.out.println("[" + strGeometry + "]");


      //Compress WhiteSpaces
        wd = "";
        String prevChar = "";
        for (int i=0; i<strGeometry.length(); i++){ //i=1; i<=len(strGeometry)

            Char = strGeometry.substring(i, i+1); //mid(strGeometry,i,1);         
            if (i>0) prevChar = strGeometry.substring(i-1, i); //mid(strGeometry,i-1,1);

            if (Char.equals(" ") && prevChar.equals(" ")) {} //'do nothing 
            else{
               wd = wd + Char;
            }
        }  


        strGeometry = wd.trim();
        //System.out.println("[" + strGeometry + "]");


      //Create an Array
        String[] arr = split(strGeometry," ");
        Points points = new Points();
        double x=0;
        double y=0;
        for (int i=0; i<arr.length; i++){ //i<=ubound(pt)
            if (isEven(i)) { //'(x,y)
                //System.out.println("x=" + pt[i]);
                x = Formatter.ConvertDMStoDD(arr[i]);
            }
            else{
                //System.out.println("y=" + pt[i]);
                y = Formatter.ConvertDMStoDD(arr[i]);
                points.addPoint(x,y);
            }

        }
        
        return points;
    }
    
    
  //**************************************************************************
  //** isEven
  //**************************************************************************
  /**  Used to determine whether a integer is even or odd. */
    
    private boolean isEven(int n) {
      return (n % 2 == 0);
    }
    
    
  //**************************************************************************
  //** Case
  //**************************************************************************
    
    private boolean Case(String SearchCondition, String[] SearchParameters){
        for (int i=0; i<SearchParameters.length; i++){
             if (SearchParameters[i].equals(SearchCondition)) return true;
        }
        return false;
    }
    
    
    
  //**************************************************************************
  //** getNodeName
  //**************************************************************************
  /**  Used to retrieve the name of a given node. Removes namespace prefix. */
    
    private String getNodeName(org.w3c.dom.Node Node){
        String nodeName = Node.getNodeName();
        
      //Strip Namespace
        if (nodeName.contains((CharSequence)":")){
            nodeName = nodeName.substring(nodeName.indexOf(":")+1);
        }
        
        return nodeName;
    }
    


    
  //**************************************************************************
  //** cdbl
  //**************************************************************************
  /**  Used to convert a string to a double */
    
    private double cdbl(String str){
        return Double.valueOf(str).doubleValue(); 
    }
    
    
    
  //**************************************************************************
  //** String Utils - Artifacts from VB Port
  //**************************************************************************
    
    private String replace(String str, String find, String replacement){ 
          return str.replace((CharSequence)find,(CharSequence)replacement); 
    }
    
    private String[] split(String str, String ch){ 
        //String metaChars = "([{\^-$|])?*+.";
        
        String inValidChars = "\\.[]|(){^-$?*+";        
        for (int i=0; i < inValidChars.length(); i++){ 
             String target = inValidChars.charAt(i) + "";
             ch = replace(ch,target,"\\" + target);
        }  
        
        return str.split(ch);
    }   
 

// <editor-fold defaultstate="collapsed" desc="XML parsing methods copied from javaxt.xml.DOM">

  //**************************************************************************
  //** createDocument
  //**************************************************************************
  /**  Used to create a DOM document from a String. */

    private static Document createDocument(String xml){
        xml = xml.trim();
        String encoding = "UTF-8";

        try{
            String xmlHeader = xml.substring(xml.indexOf("<?"), xml.indexOf("?>"));
            if (xmlHeader.contains(" encoding")){
                encoding = xmlHeader.substring(xmlHeader.indexOf(" encoding")+" encoding".length());
                encoding = encoding.substring(encoding.indexOf("=")+1);
                while(encoding.substring(0, 1).equals(" ")){
                    encoding = encoding.substring(1);
                }

                if (encoding.substring(0, 1).equals("\"")){
                    encoding = encoding.substring(1);
                    encoding = encoding.substring(0, encoding.indexOf("\"")).trim();
                }
                else{
                    encoding = encoding.substring(0, encoding.indexOf(" ")).trim();
                }

            }
        }
        catch(Exception e){}


        
        java.io.InputStream is;
        try{
            is = (new java.io.ByteArrayInputStream(xml.getBytes(encoding)));
        }
        catch(Exception e){
            is = (new java.io.ByteArrayInputStream(xml.getBytes()));
        }

        try{
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = builderFactory.newDocumentBuilder();
            return builder.parse(is);
        }
        catch(Exception e){
            return null;
        }
    }


  //**************************************************************************
  //** getOuterNode
  //**************************************************************************
  /** Returns the outer node for a given xml document.
   *  @param xml A org.w3c.dom.Document
   */
    private static Node getOuterNode(Document xml){
        if (xml==null) return null;
        NodeList OuterNodes = xml.getChildNodes();
        for (int i=0; i<OuterNodes.getLength(); i++ ) {
             if (OuterNodes.item(i).getNodeType() == 1){
                 return OuterNodes.item(i);
             }
        }
        return null;
    }

  //**************************************************************************
  //** getAttributeValue
  //**************************************************************************
  /**  Used to return the value of a given node attribute. The search is case
   *   insensitive. If no match is found, returns an empty string.
   */
    private static String getAttributeValue(Node node, String attrName){

        NamedNodeMap attrCollection = node.getAttributes();
        if (attrCollection!=null){
            for (int i=0; i < attrCollection.getLength(); i++ ) {
                Node attr = attrCollection.item(i);
                if (attr.getNodeName().equalsIgnoreCase(attrName)) {
                    return attr.getNodeValue();
                }
            }
        }
        return "";
    }


  //**************************************************************************
  //** getNodeValue
  //**************************************************************************
  /** Returns the value of a given node as text.
   */
    private static String getNodeValue(Node node){

        String nodeValue = "";

        if (hasChildren(node)) {

            StringBuffer xmlTree = new StringBuffer();
            traverse(node, xmlTree);
            nodeValue = xmlTree.toString();

        }
        else{
            nodeValue = node.getTextContent();
        }

        if (nodeValue == null){
            return "";
        }
        else{
            return nodeValue;
        }
    }

    private static void traverse(Node tree, StringBuffer xmlTree){
        if (tree.getNodeType()==1){
            String Attributes = getAttributes(tree);
            xmlTree.append("<" + tree.getNodeName() + Attributes + ">");
            if (hasChildren(tree)) {

                NodeList xmlNodeList = tree.getChildNodes();
                for (int i=0; i<xmlNodeList.getLength(); i++){
                    traverse(xmlNodeList.item(i), xmlTree);
                }

            }
            else{

                String nodeValue = tree.getTextContent();
                if (nodeValue == null){
                    nodeValue = "";
                }

                xmlTree.append(nodeValue);
            }

            xmlTree.append("</" + tree.getNodeName() + ">");
        }
    }

  //**************************************************************************
  //** getAttributes
  //**************************************************************************
  /** Used to retrieve all of the attributes for a given node.   */

    private static String getAttributes(Node node){
        if (node==null) return "";
        NamedNodeMap attr = node.getAttributes();
        String Attributes = "";
        if (attr!=null){
            for (int j=0; j<attr.getLength(); j++){
                 String name = attr.item(j).getNodeName();
                 String value = attr.item(j).getTextContent();
                 if (value==null) value = attr.item(j).getNodeValue();
                 if (value==null) value = "";
                 //System.out.println(name + "=" + attr.item(j).getNodeValue());
                 Attributes += " " + name + "=\"" + value + "\"";
            }
        }
        return Attributes;
    }

  //**************************************************************************
  //** hasChildren
  //**************************************************************************
  /** Used to determine whether a given node has any children. Differs from the
   *  native DOM implementation in that this function only considers child
   *  nodes that have a node type value equal to 1.
   */
    private static boolean hasChildren(Node node){

        NodeList nodeList = node.getChildNodes();
        for (int i=0; i<nodeList.getLength(); i++ ) {
            if (nodeList.item(i).getNodeType()==1){
                return true;
            }
        }
        return false;
    }


  // </editor-fold>
}