001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.xml;
017:
018: import java.io.IOException;
019: import java.io.StringReader;
020: import java.net.URI;
021: import java.net.URISyntaxException;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Stack;
026: import java.util.logging.Level;
027: import java.util.logging.Logger;
028:
029: import org.geotools.xml.gml.FCBuffer.StopException;
030: import org.geotools.xml.handlers.ComplexElementHandler;
031: import org.geotools.xml.handlers.DocumentHandler;
032: import org.geotools.xml.handlers.ElementHandlerFactory;
033: import org.geotools.xml.handlers.IgnoreHandler;
034: import org.xml.sax.Attributes;
035: import org.xml.sax.InputSource;
036: import org.xml.sax.Locator;
037: import org.xml.sax.SAXException;
038: import org.xml.sax.SAXParseException;
039: import org.xml.sax.helpers.DefaultHandler;
040:
041: /**
042: * XMLSAXHandler purpose.
043: *
044: * <p>
045: * This is a schema content handler. Code here has been modified from code
046: * written by Ian Schneider.
047: * </p>
048: *
049: * <p>
050: * This class contains one stack used to store part of the parse tree. The
051: * ElementHandlers found on the stack have direct next handlers placed on the
052: * stack. So here's the warning, be careful to read how you may be affecting
053: * (or forgetting to affect) the stack.
054: * </p>
055: *
056: * <p>
057: * If a FlowHandler implementation is available in the hints, the handler will
058: * periodically check it to see if it should stop parsing. See the FlowHandler
059: * interface.
060: * </p>
061: *
062: * @author dzwiers, Refractions Research, Inc. http://www.refractions.net
063: * @author $Author:$ (last modification)
064: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/xml/src/main/java/org/geotools/xml/XMLSAXHandler.java $
065: * @version $Id: XMLSAXHandler.java 27862 2007-11-12 19:51:19Z desruisseaux $
066: *
067: * @see XMLElementHandler
068: */
069: public class XMLSAXHandler extends DefaultHandler {
070: /**
071: * the logger -- should be used for debugging (assuming there are bugs LOL)
072: */
073: protected final static Logger logger = org.geotools.util.logging.Logging
074: .getLogger("net.refractions.xml.sax");
075: protected static Level level = Level.FINE;
076:
077: // the stack of handlers
078: private Stack handlers = new Stack();
079:
080: /** Collects string chunks in {@link #characters(char[], int, int)}
081: * callback to be handled at the beggining of {@link #endElement(String, String, String)}
082: */
083: private StringBuffer characters = new StringBuffer();
084:
085: /**
086: *
087: * TODO summary sentence for resolveEntity ...
088: *
089: * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
090: * @param pubId
091: * @param sysId
092: * @return InputSource
093: * @throws SAXException
094: */
095: public InputSource resolveEntity(String pubId, String sysId)
096: throws SAXException {
097: // avoid dtd files
098: if (sysId != null && sysId.endsWith("dtd")) {
099: return new InputSource(new StringReader(""));
100: }
101: try {
102: if (false) {
103: /*
104: * HACK: This dead code exists only in order to make J2SE 1.4 compiler happy.
105: * This hack is needed because there is a slight API change between J2SE 1.4
106: * and 1.5: the 'resolveEntity()' method didn't declared IOException in its
107: * throw clause in J2SE 1.4. Compare the two following links:
108: *
109: * http://java.sun.com/j2se/1.5/docs/api/org/xml/sax/helpers/DefaultHandler.html#resolveEntity(java.lang.String,%20java.lang.String)
110: * http://java.sun.com/j2se/1.4/docs/api/org/xml/sax/helpers/DefaultHandler.html#resolveEntity(java.lang.String,%20java.lang.String)
111: */
112: throw new IOException();
113: }
114: return super .resolveEntity(pubId, sysId);
115: } catch (IOException e) {
116: SAXException se = new SAXException(e.getLocalizedMessage());
117: se.initCause(e);
118: throw se;
119: }
120: }
121:
122: // hints
123: private Map hints;
124: private ElementHandlerFactory ehf = new ElementHandlerFactory(
125: logger);
126:
127: // used to store prefix -> targetNamespace mapping until which time as the
128: // schema uri is availiable (on the next startElement Call).
129: private Map schemaProxy = new HashMap();
130:
131: // the base handler for the document
132: private DocumentHandler document = null;
133:
134: // the Locator stores the current position in the parse
135: // for end-user debug information
136: private Locator locator;
137:
138: // the uri of the instance ducment, used to resolve relative URIs
139: private URI instanceDocument;
140:
141: /**
142: * <p>
143: * This contructor is intended to create an XMLSAXHandler to be used when
144: * parsing an XML instance document. The instance document's uri is also
145: * be provided, as this will allow the parser to resolve relative uri's.
146: * </p>
147: *
148: * @param intendedDocument
149: * @param hints DOCUMENT ME!
150: */
151: public XMLSAXHandler(URI intendedDocument, Map hints) {
152: instanceDocument = intendedDocument;
153: this .hints = hints;
154: logger.setLevel(level);
155: }
156:
157: /**
158: * <p>
159: * This contructor is intended to create an XMLSAXHandler to be used when
160: * parsing an XML instance document. The instance document's uri is also
161: * be provided, as this will allow the parser to resolve relative uri's.
162: * </p>
163: *
164: * @param hints DOCUMENT ME!
165: */
166: public XMLSAXHandler(Map hints) {
167: this .hints = hints;
168: logger.setLevel(level);
169: }
170:
171: /**
172: * Implementation of endDocument.
173: *
174: * @see org.xml.sax.ContentHandler#endDocument()
175: */
176: public void endDocument() {
177: document = ((DocumentHandler) handlers.pop());
178: }
179:
180: /**
181: * Implementation of startDocument.
182: *
183: * @see org.xml.sax.ContentHandler#startDocument()
184: */
185: public void startDocument() {
186: try {
187: document = new DocumentHandler(ehf);
188: handlers.push(document);
189: } catch (RuntimeException e) {
190: logger.warning(e.toString());
191: throw e;
192: }
193: }
194:
195: /**
196: * Implementation of characters.
197: *
198: * @param ch
199: * @param start
200: * @param length
201: *
202: * @throws SAXException
203: *
204: * @see org.xml.sax.ContentHandler#characters(char[], int, int)
205: */
206: public void characters(char[] ch, int start, int length)
207: throws SAXException {
208: characters.append(ch, start, length);
209: }
210:
211: /**
212: * Handles the string chunks collected in {@link #characters}.
213: */
214: private void handleCharacters() throws SAXException {
215: if (characters.length() == 0) {
216: return;
217: }
218: try {
219: checkStatus();
220:
221: String text = characters.toString();
222: characters.setLength(0);
223:
224: if ((text != null) && !"".equals(text)) {
225: ((XMLElementHandler) handlers.peek()).characters(text);
226: }
227: } catch (SAXException e) {
228: logger.warning(e.toString());
229: throw e;
230: }
231: }
232:
233: private void checkStatus() throws StopException {
234: if (this .hints != null
235: && hints.get(XMLHandlerHints.FLOW_HANDLER_HINT) != null) {
236: FlowHandler handler = (FlowHandler) hints
237: .get(XMLHandlerHints.FLOW_HANDLER_HINT);
238: if (handler.shouldStop(hints)) {
239: throw new StopException();
240: }
241: }
242:
243: if (Thread.currentThread().isInterrupted()) {
244: throw new StopException();
245: }
246: }
247:
248: /**
249: * Implementation of endElement.
250: *
251: * @param namespaceURI
252: * @param localName
253: * @param qName
254: *
255: * @throws SAXException
256: *
257: * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
258: * java.lang.String, java.lang.String)
259: */
260: public void endElement(String namespaceURI, String localName,
261: String qName) throws SAXException {
262: handleCharacters();
263: logger.fine("END: " + qName);
264: XMLElementHandler handler = null;
265: try {
266:
267: handler = (XMLElementHandler) handlers.peek();
268: URI uri = new URI(namespaceURI);
269: handler.endElement(uri, localName, hints);
270: } catch (Exception e) {
271: processException(e);
272:
273: logger.warning(e.getMessage());
274: logger.warning("Line " + locator.getLineNumber() + " Col "
275: + locator.getColumnNumber());
276:
277: SAXException exception = new SAXException(
278: e.getMessage() + " at Line "
279: + locator.getLineNumber() + " Col "
280: + locator.getColumnNumber() + " tag is: \n"
281: + qName, e);
282: exception.initCause(e);
283: throw exception;
284: } finally {
285: handlers.pop(); // we must do this or leak memory
286: if (handler != null && !handlers.isEmpty()) {
287: XMLElementHandler parent = ((XMLElementHandler) handlers
288: .peek());
289: if (parent instanceof ComplexElementHandler) {
290: ComplexElementHandler complexParent = (ComplexElementHandler) parent;
291: String typename = complexParent.getType()
292: .getClass().getName();
293: // TODO: HACK The required Type is not in this Module
294: if (typename
295: .equals("org.geotools.xml.wfs.WFSBasicComplexTypes$FeatureCollectionType")) {
296: complexParent.removeElement(handler);
297: }
298: }
299: }
300: }
301: }
302:
303: private void processException(Exception e) {
304: if (e instanceof RuntimeException)
305: throw (RuntimeException) e;
306: StringBuffer msg = new StringBuffer(e.getLocalizedMessage());
307: StackTraceElement[] trace = e.getStackTrace();
308:
309: for (int i = 0; i < trace.length; i++) {
310: StackTraceElement element = trace[i];
311: msg.append(" ");
312: msg.append(element.toString());
313: msg.append("\n");
314: }
315: logger.log(Level.SEVERE, msg.toString());
316: }
317:
318: /**
319: * Implementation of startElement.
320: *
321: * @param namespaceURI
322: * @param localName
323: * @param qName
324: * @param atts
325: *
326: * @throws SAXException
327: *
328: * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
329: * java.lang.String, java.lang.String, org.xml.sax.Attributes)
330: */
331: public void startElement(String namespaceURI, String localName,
332: String qName, Attributes atts) throws SAXException {
333: characters.setLength(0);
334:
335: checkStatus();
336:
337: if (schemaProxy.size() != 0) {
338: logger.fine("ADDING NAMESPACES: " + schemaProxy.size());
339:
340: String t = atts.getValue(
341: "http://www.w3.org/2001/XMLSchema-instance",
342: "schemaLocation");
343:
344: if ((t == null) || "".equals(t)) {
345: t = atts.getValue("", "schemaLocation");
346: }
347:
348: if (!((t == null) || "".equals(t))) {
349: String[] targ2uri = t.split("\\s+");
350:
351: if (targ2uri != null) {
352: if (targ2uri.length != 0
353: && targ2uri.length % 2 != 0)
354: throw new SAXException(
355: "Bad Schema location attribute: you must have an even number of terms");
356:
357: for (int i = 0; i < (targ2uri.length / 2); i++) {
358: String uri = targ2uri[(i * 2) + 1];
359: String targ = targ2uri[i * 2];
360: String prefix = (String) schemaProxy.get(targ);
361: URI targUri = null;
362:
363: boolean set = false;
364: if (hints != null
365: && hints
366: .containsKey(XMLHandlerHints.NAMESPACE_MAPPING)) {
367:
368: Map schemas = (Map) hints
369: .get(XMLHandlerHints.NAMESPACE_MAPPING);
370:
371: if (schemas.containsKey(targ)) {
372: ehf.startPrefixMapping(prefix, targ,
373: (URI) schemas.get(targ));
374: set = true;
375: break;
376: }
377: }
378:
379: if (!set) {
380: try {
381: targUri = (instanceDocument == null) ? new URI(
382: uri)
383: : instanceDocument.resolve(uri);
384: } catch (URISyntaxException e1) {
385: logger.warning(e1.toString());
386: }
387: ehf.startPrefixMapping(prefix, targ,
388: targUri);
389: }
390: schemaProxy.remove(targ);
391: }
392: }
393: }
394:
395: if (schemaProxy.size() != 0) {
396: Iterator it = schemaProxy.keySet().iterator();
397:
398: while (it.hasNext()) {
399: String targ = (String) it.next();
400: String prefix = (String) schemaProxy.get(targ);
401: ehf.startPrefixMapping(prefix, targ);
402:
403: it.remove();
404: }
405: }
406: }
407:
408: logger.finest("Moving on to finding the element handler");
409:
410: try {
411: XMLElementHandler parent = ((XMLElementHandler) handlers
412: .peek());
413: logger.finest("Parent Node = "
414: + parent.getClass().getName() + " '"
415: + parent.getName() + "'");
416:
417: // logger.finest("Parent Node = "+parent.getClass().getName()+"
418: // '"+parent.getName()+"' "+
419: // (parent.getType()==null?"null":
420: // ((((ComplexType)parent.getType()).getChild()==null)?"null":
421: // ((((ComplexType)parent.getType()).getChild().getGrouping() ==
422: // ElementGrouping.SEQUENCE)?
423: // ((((Sequence)((ComplexType)parent.getType()).getChild()).getChildren()==null)?0:
424: // ((Sequence)((ComplexType)parent.getType()).getChild()).getChildren().length)+"":"null"))));
425: logger.finest("This Node = " + localName + " :: "
426: + namespaceURI);
427: URI uri = new URI(namespaceURI);
428: XMLElementHandler eh = parent.getHandler(uri, localName,
429: hints);
430:
431: if (eh == null) {
432: eh = new IgnoreHandler();
433: }
434:
435: logger.finest("This Node = " + eh.getClass().getName());
436:
437: handlers.push(eh);
438: eh.startElement(new URI(namespaceURI), localName, atts);
439: } catch (Exception e) {
440: processException(e);
441:
442: logger.warning(e.toString());
443: logger.warning("Line " + locator.getLineNumber() + " Col "
444: + locator.getColumnNumber());
445:
446: SAXException exception = new SAXException(
447: e.getMessage() + " at Line "
448: + locator.getLineNumber() + " Col "
449: + locator.getColumnNumber() + " tag is: \n"
450: + qName, e);
451: exception.initCause(e);
452: throw exception;
453: }
454: }
455:
456: /**
457: * <p>
458: * Used to set the logger level for all XMLSAXHandlers
459: * </p>
460: *
461: * @param l
462: */
463: public static void setLogLevel(Level l) {
464: level = l;
465: logger.setLevel(l);
466: XMLElementHandler.setLogLevel(l);
467: }
468:
469: /**
470: * getDocument purpose.
471: *
472: * <p>
473: * Completes the post-processing phase, and returns the value from the
474: * parse ...
475: * </p>
476: *
477: * @return Object parsed
478: *
479: * @throws SAXException
480: *
481: * @see DocumentHandler#getValue()
482: */
483: public Object getDocument() throws SAXException {
484: return document.getValue();
485: }
486:
487: /**
488: * Implementation of error.
489: *
490: * @param exception
491: *
492: * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
493: */
494: public void error(SAXParseException exception) {
495: logger.severe("ERROR " + exception.getMessage());
496: logger.severe("col " + locator.getColumnNumber() + ", line "
497: + locator.getLineNumber());
498: }
499:
500: /**
501: * Implementation of fatalError.
502: *
503: * @param exception
504: *
505: * @throws SAXException
506: *
507: * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
508: */
509: public void fatalError(SAXParseException exception)
510: throws SAXException {
511: logger.severe("FATAL " + exception.getMessage());
512: logger.severe("col " + locator.getColumnNumber() + ", line "
513: + locator.getLineNumber());
514: throw exception;
515: }
516:
517: /**
518: * Implementation of warning.
519: *
520: * @param exception
521: *
522: * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
523: */
524: public void warning(SAXParseException exception) {
525: logger.warning("WARN " + exception.getMessage());
526: logger.severe("col " + locator.getColumnNumber() + ", line "
527: + locator.getLineNumber());
528: }
529:
530: /**
531: * Stores the locator for future error reporting
532: *
533: * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
534: */
535: public void setDocumentLocator(Locator locator) {
536: super .setDocumentLocator(locator);
537: this .locator = locator;
538: }
539:
540: /**
541: * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
542: */
543: public void endPrefixMapping(String prefix) {
544: // hard coded schemas should not be removed. For example. GML and WFS
545: if (prefix.equals("gml") || prefix.equals("wfs"))
546: return;
547: ehf.endPrefixMapping(prefix);
548: }
549:
550: /**
551: * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
552: * java.lang.String)
553: */
554: public void startPrefixMapping(String prefix, String uri) {
555: if ("http://www.w3.org/2001/XMLSchema-instance".equals(uri)) {
556: return;
557: }
558:
559: schemaProxy.put(uri, prefix);
560: }
561: }
|