Class GMLHandler

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12 package org.locationtech.jts.io.gml2;
 13  
 14 import java.util.LinkedList;
 15 import java.util.List;
 16 import java.util.Stack;
 17  
 18 import org.locationtech.jts.geom.Geometry;
 19 import org.locationtech.jts.geom.GeometryFactory;
 20 import org.locationtech.jts.io.gml2.GeometryStrategies.ParseStrategy;
 21 import org.xml.sax.Attributes;
 22 import org.xml.sax.ContentHandler;
 23 import org.xml.sax.ErrorHandler;
 24 import org.xml.sax.Locator;
 25 import org.xml.sax.SAXException;
 26 import org.xml.sax.SAXParseException;
 27 import org.xml.sax.helpers.AttributesImpl;
 28 import org.xml.sax.helpers.DefaultHandler;
 29  
 30  
 31 /**
 32  * A SAX {@link DefaultHandler} which builds {@link Geometry}s 
 33  * from GML2-formatted geometries.
 34  * An XML parser can delegate SAX events to this handler
 35  * to parse and building Geometrys. 
 36  * <p>
 37  * This handler currently ignores both namespaces and prefixes. 
 38  * 
 39  * Hints: 
 40  * <ul>
 41  * <li>If your parent handler is a DefaultHandler register the parent handler to receive the errors and locator calls.
 42  * <li>Use {@link GeometryStrategies#findStrategy(String, String)} to help check for applicability
 43  * </ul>
 44  * 
 45  * @see DefaultHandler
 46  *
 47  * @author David Zwiers, Vivid Solutions. 
 48  */
 49 public class GMLHandler extends DefaultHandler {
 50  
 51     /**
 52      * This class is intended to log the SAX activity within a given element until its termination.
 53      * At this time, a new object of value is created and passed to the parent. 
 54      * An object of value is typically either java.lang.* or a JTS Geometry
 55      * This class is not intended for use outside this distribution, 
 56      * and may change in subsequent versions.
 57      *
 58      * @author David Zwiers, Vivid Solutions.
 59      */
 60     static class Handler {
 61         protected Attributes attrs = null;
 62  
 63         protected ParseStrategy strategy;
 64  
 65         /**
 66          * @param strategy 
 67          * @param attributes Nullable
 68          */
 69         public Handler(ParseStrategy strategy, Attributes attributes) {
 70             if (attributes != null)
 71                 this.attrs = new AttributesImpl(attributes);
 72             this.strategy = strategy;
 73         }
 74  
 75         protected StringBuffer text = null;
 76  
 77         /**
 78          * Caches text for the future
 79          * @param str
 80          */
 81         public void addText(String str) {
 82             if (text == null)
 83                 text = new StringBuffer();
 84             text.append(str);
 85         }
 86  
 87         protected List children = null;
 88  
 89         /**
 90          * Store param for the future
 91          * 
 92          * @param obj
 93          */
 94         public void keep(Object obj) {
 95             if (children == null)
 96                 children = new LinkedList();
 97             children.add(obj);
 98  
 99         }
100  
101         /**
102          * @param gf GeometryFactory
103          * @return Parsed Object
104          * @throws SAXException 
105          */
106         public Object create(GeometryFactory gf) throws SAXException {
107             return strategy.parse(this, gf);
108         }
109     }
110  
111     private Stack stack = new Stack();
112  
113     private ErrorHandler delegate = null;
114  
115     private GeometryFactory gf = null;
116  
117     /**
118      * Creates a new handler.
119      * Allows the user to specify a delegate object for error / warning messages. 
120      * If the delegate also implements ContentHandler then the document Locator will be passed on.
121      * 
122      * @param gf Geometry Factory
123      * @param delegate Nullable
124      * 
125      * @see ErrorHandler
126      * @see ContentHandler
127      * @see ContentHandler#setDocumentLocator(org.xml.sax.Locator)
128      * @see org.xml.sax.Locator
129      * 
130      */
131     public GMLHandler(GeometryFactory gf, ErrorHandler delegate) {
132         this.delegate = delegate;
133         this.gf = gf;
134         stack.push(new Handler(nullnull));
135     }
136  
137     /**
138      * Tests whether this handler has completed parsing 
139      * a geometry.
140      * If this is the case, {@link #getGeometry()} can be called
141      * to get the value of the parsed geometry.
142      * 
143      * @return if the parsing of the geometry is complete
144      */
145     public boolean isGeometryComplete()
146     {
147         if (stack.size() > 1)
148             return false;
149         // top level node on stack needs to have at least one child 
150         Handler h = (Handler) stack.peek();
151         if (h.children.size() < 1)
152             return false;
153         return true;
154         
155     }
156     
157     /**
158      * Gets the geometry parsed by this handler.
159      * This method should only be called AFTER the parser has completed execution
160      * 
161      * @return the parsed Geometry, or a GeometryCollection if more than one geometry was parsed
162      * @throws IllegalStateException if called before the parse is complete
163      */
164     public Geometry getGeometry() {
165         if (stack.size() == 1) {
166             Handler h = (Handler) stack.peek();
167             if (h.children.size() == 1)
168                 return (Geometry) h.children.get(0);
169             return gf.createGeometryCollection(
170                     (Geometry[]) h.children.toArray(new Geometry[stack.size()]));
171         }
172         throw new IllegalStateException(
173                 "Parse did not complete as expected, there are " + stack.size()
174                         + " elements on the Stack");
175     }
176  
177     //////////////////////////////////////////////
178     // Parsing Methods
179  
180     /**
181      * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
182      */
183     public void characters(char[] ch, int start, int length) throws SAXException {
184         if (!stack.isEmpty())
185             ((Handler) stack.peek()).addText(new String(ch, start, length));
186     }
187  
188     /**
189      * @see org.xml.sax.helpers.DefaultHandler#ignorableWhitespace(char[], int, int)
190      */
191     public void ignorableWhitespace(char[] ch, int start, int length)
192             throws SAXException {
193         if (!stack.isEmpty())
194             ((Handler) stack.peek()).addText(" ");
195     }
196  
197     /**
198      * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
199      */
200     public void endElement(String uri, String localName, String qName)
201             throws SAXException {
202         Handler thisAction = (Handler) stack.pop();
203         ((Handler) stack.peek()).keep(thisAction.create(gf));
204     }
205  
206     /**
207      * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
208      */
209     public void startElement(String uri, String localName, String qName,
210             Attributes attributes) throws SAXException {
211         // create a handler
212         ParseStrategy ps = GeometryStrategies.findStrategy(uri, localName);
213         if (ps == null) {
214             String qn = qName.substring(qName.indexOf(':') + 1, qName.length());
215             ps = GeometryStrategies.findStrategy(null, qn);
216         }
217         Handler h = new Handler(ps, attributes);
218         // and add it to the stack
219         stack.push(h);
220     }
221  
222     //////////////////////////////////////////////
223     // Logging Methods
224  
225     /**
226      * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator)
227      */
228     public void setDocumentLocator(Locator locator) {
229         this.locator = locator;
230         if (delegate != null && delegate instanceof ContentHandler)
231             ((ContentHandler) delegate).setDocumentLocator(locator);
232  
233     }
234  
235     private Locator locator = null;
236  
237     protected Locator getDocumentLocator() {
238         return locator;
239     }
240  
241     //////////////////////////////////////////////
242     // ERROR Methods
243  
244     /**
245      * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
246      */
247     public void fatalError(SAXParseException e) throws SAXException {
248         if (delegate != null)
249             delegate.fatalError(e);
250         else
251             super.fatalError(e);
252     }
253  
254     /**
255      * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
256      */
257     public void error(SAXParseException e) throws SAXException {
258         if (delegate != null)
259             delegate.error(e);
260         else
261             super.error(e);
262     }
263  
264     /**
265      * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
266      */
267     public void warning(SAXParseException e) throws SAXException {
268         if (delegate != null)
269             delegate.warning(e);
270         else
271             super.warning(e);
272     }
273  
274 }
275