All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.xolite.sax.SaxXMLEventParser Maven / Gradle / Ivy

Go to download

This project provides a lightweight framework to serialize/deserialize (or marshall/unmarshall) java objects into XML. The implementation is based on standard SAX (Simple Api for Xml) but it follows a original approach based on the strong data encapsulation paradigm of Object Oriented (OO) programming.

The newest version!
/*-------------------------------------------------------------------------
 Copyright 2006 Olivier Berlanger

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -------------------------------------------------------------------------*/
package net.sf.xolite.sax;


import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import net.sf.xolite.XMLParseException;
import net.sf.xolite.XMLSerializable;
import net.sf.xolite.impl.BaseXMLEventParser;
import net.sf.xolite.utils.RootHolder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
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;
import org.xml.sax.helpers.DefaultHandler;


/**
 * A XMLEventParser implementation based on a SAX parser.
 * 
 * @author Olivier Berlanger
 */
public class SaxXMLEventParser extends BaseXMLEventParser {

    /** Apache Xerces property used to turn schema validation on. */
    private static final String SCHEMA_VALIDATION_FEATURE_ID = "http://apache.org/xml/features/validation/schema";
    /** Apache Xerces property used to define schema location. */
    private static final String SCHEMA_LOCATION_PROPERTY = "http://apache.org/xml/properties/schema/external-schemaLocation";

    private static Log log = LogFactory.getLog(SaxXMLEventParser.class);

    /** SAX reader used for parsing. */
    private XMLReader reader;
    /** SAX locator used when parsing. */
    private Locator saxLocator;
    /** StringBuffer used to accumulate the characters. */
    private StringBuffer charBuffer;
    /** The attributes of the currently started element, non-null only when startElement is called. */
    private Attributes currentAttributes;


    /**
     * Build a StreamXMLEventParser using the system-default SAXParser configured to be namespace aware
     * and perform no validation.
     * 
     * @see javax.xml.parsers.SAXParserFactory
     * @see javax.xml.parsers.SAXParser
     */
    public SaxXMLEventParser() {
        try {
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            log.info("Using SAX parser factory: " + parserFactory);
            parserFactory.setNamespaceAware(true);
            parserFactory.setValidating(false);
            SAXParser parser = parserFactory.newSAXParser();
            reader = parser.getXMLReader();
        } catch (Exception e) {
            throw new RuntimeException("Cannot configure parser", e);
        }
        init();
    }


    /**
     * Build a StreamXMLEventParser using the system-default SAXParser configured to be namespace aware
     * and perform validation using the provided schema locations. The 'schemaLocations' string must be a suite of pair 'namespace
     * URI' + 'schema location URL' separated by spaces (just as the value of the standard 'xsi:schemaLocation' xml attribute).
     * 

* Caution: This setup works with Sun JRE default parser (Xerces) but is not guaranteed to work if you (or a library * you're using) have changed the default XML parser implementation.
* In this case, you have to setup a validating parser yourself (following your parser documentation) and pass it to the * SaxXMLEventParser(XMLReader) constructor. *

* * @param schemaLocations * the schema location expressed as in the standard 'xsi:schemaLocation' xml attributes. You can use the X-O lite * SchemaLocation helper to build this string if your schema is in your application jar. * @see javax.xml.parsers.SAXParserFactory * @see javax.xml.parsers.SAXParser * @see net.sf.xolite.utils.SchemaLocation */ public SaxXMLEventParser(String schemaLocations) { try { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); log.info("Using SAX parser factory: " + parserFactory); parserFactory.setNamespaceAware(true); parserFactory.setValidating(true); SAXParser parser = parserFactory.newSAXParser(); reader = parser.getXMLReader(); reader.setFeature(SCHEMA_VALIDATION_FEATURE_ID, true); reader.setProperty(SCHEMA_LOCATION_PROPERTY, schemaLocations); } catch (Exception e) { throw new RuntimeException("Cannot configure parser", e); } init(); } /** * Build a SimpleSaxParser using the given XMLReader. The XMLReader must be correctly * configured, this class will not change it's configuration (namespace aware, validating, ...). */ public SaxXMLEventParser(XMLReader rdr) { reader = rdr; if (reader != null) init(); } public ContentHandler initForExternalRead(XMLSerializable rootHandler) { SAXHandlerImpl handler = new SAXHandlerImpl(); charBuffer = new StringBuffer(); setup(rootHandler); return handler; } private void init() { SAXHandlerImpl handler = new SAXHandlerImpl(); reader.setContentHandler(handler); reader.setErrorHandler(handler); charBuffer = new StringBuffer(); } /** * Parse the given file to java objects.
* This method will succeed only if a factory knowing the class of the root element is defined. * * @param f * file containing the XML to parse. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public XMLSerializable parse(File f) throws XMLParseException { return parse(getInputSource(f)); } /** * Parse a file to java objects using the given XMLSerializable as root handler (as object representing the root * element). * * @param f * file containing the XML to parse. * @param rootHandler * the root handler. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public void parse(File f, XMLSerializable rootHandler) throws XMLParseException { parse(getInputSource(f), rootHandler); } /** * Parse the given input stream to java objects.
* This method will succeed only if a factory knowing the class of the root element is defined. * * @param is * input stream containing the XML to parse. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public XMLSerializable parse(InputStream is) throws XMLParseException { return parse(new InputSource(is)); } /** * Parse an input stream to java objects using the given XMLSerializable as root handler (as object representing * the root element). * * @param is * input stream containing the XML to parse. * @param rootHandler * the root handler. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public void parse(InputStream is, XMLSerializable rootHandler) throws XMLParseException { parse(new InputSource(is), rootHandler); } /** * Parse the given URL content to java objects.
* This method will succeed only if a factory knowing the class of the root element is defined. * * @param url * URL pointing to file containing the XML to parse. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public XMLSerializable parse(URL url) throws XMLParseException { return parse(getInputSource(url)); } /** * Parse an URL content to java objects using the given XMLSerializable as root handler (as object representing the * root element). * * @param url * URL pointing to file containing the XML to parse. * @param rootHandler * the root handler. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public void parse(URL url, XMLSerializable rootHandler) throws XMLParseException { parse(getInputSource(url), rootHandler); } /** * Parse the given input source to java objects.
* This method will succeed only if a factory knowing the class of the root element is defined. * * @param src * input source for the SAX parsing. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public XMLSerializable parse(InputSource src) throws XMLParseException { RootHolder holder = new RootHolder(); parse(src, holder); return holder.getRoot(); } /** * Parse an input source to java objects using the given XMLSerializable as root handler (as object representing * the root element). * * @param src * input source for the SAX parsing. * @param rootHandler * the root handler. * @throws XMLParseException * if the source cannot be read or contains an invalid XML document. */ public void parse(InputSource src, XMLSerializable rootHandler) throws XMLParseException { try { if (src == null) throw new IllegalArgumentException("InputSource cannot be null"); if (rootHandler == null) throw new IllegalArgumentException("Root SaxHandler cannot be null"); setup(rootHandler); reader.parse(src); tearDown(); } catch (Exception e) { transformAndThrowException(e); } } private InputSource getInputSource(File f) { InputSource is = new InputSource(f.toURI().toASCIIString()); is.setPublicId(f.getAbsolutePath()); return is; } private InputSource getInputSource(URL url) { InputSource is = new InputSource(url.toExternalForm()); is.setPublicId(url.toString()); return is; } /** * Get the current SAX locator (for reporting SAXParseException). This Locator is not null only when parsing. */ public Locator getLocator() { return saxLocator; } /** * Get the current SAX locator (for reporting SAXParseException). This Locator is not null only when parsing. */ void setLocator(Locator newLocator) { saxLocator = newLocator; } // ------------------------ XMLEventParser interface implementation ----------------------------------------------- public String getElementText() { return charBuffer.toString(); } // --------------------------------- Exception management ----------------------------------- @Override protected void addLocationInfo(XMLParseException xpe) { if (saxLocator != null) { String publicId = saxLocator.getPublicId(); if ((publicId != null) && !publicId.trim().equals("")) { xpe.setSource(publicId); } xpe.setLocation(saxLocator.getLineNumber(), saxLocator.getColumnNumber()); } } @Override protected void transformAndThrowException(Exception source) throws XMLParseException { if (source instanceof InnerTransportException) { source = ((InnerTransportException) source).getTransportedException(); } super.transformAndThrowException(source); } /** * Exception used internally to temporary wrap an inner exception into a SAXException (required by SAX handler interface). */ static class InnerTransportException extends SAXException { private static final long serialVersionUID = 3386326111058301247L; private Exception transported; InnerTransportException(Exception transportedEx) { transported = transportedEx; } Exception getTransportedException() { return transported; } } // --------------------------------- Attributes management ----------------------------------- public String getAttributeValue(String attrName) throws XMLParseException { return getAttributeValueNS(null, attrName); } public String getAttributeValueNS(String attrNamespaceURI, String attrName) throws XMLParseException { if (currentAttributes == null) throw new XMLParseException( "getAttributeValue(..) can only be called from startElement(..) method"); String attrVal = (attrNamespaceURI == null) ? currentAttributes.getValue(attrName) : currentAttributes.getValue( attrNamespaceURI, attrName); return attrVal; } public Iterator getAttributeNamespaceIterator() throws XMLParseException { if (currentAttributes == null) throw new XMLParseException( "getAttributeNameIterator(..) can only be called from startElement(..) method"); List uris = new ArrayList(); int len = currentAttributes.getLength(); for (int i = 0; i < len; i++) { String uri = currentAttributes.getURI(i); if ("".equals(uri)) uri = null; if (!uris.contains(uri)) { if (log.isDebugEnabled()) { log.debug("Found attribute namespace " + uri + " for attribute: " + currentAttributes.getLocalName(i)); } uris.add(uri); } } return uris.iterator(); } public Iterator getAttributeNameIterator(String attrNamespaceURI) throws XMLParseException { if (currentAttributes == null) throw new XMLParseException( "getAttributeNameIterator(..) can only be called from startElement(..) method"); return new AttributeNameIterator(attrNamespaceURI, currentAttributes); } void startElementImpl(String uri, String localName, Attributes attributes) throws Exception { currentAttributes = attributes; startElementImpl(uri, localName); currentAttributes = null; charBuffer.setLength(0); } @Override protected void endElementImpl(String uri, String localName) throws Exception { super.endElementImpl(uri, localName); charBuffer.setLength(0); } /** * Rem: overridden just to increase visibility. */ @Override protected void pushPrefixMappingInNextLevel(String newPrefix, String newNamespaceURI) { super.pushPrefixMappingInNextLevel(newPrefix, newNamespaceURI); } void appendCharacters(char[] ch, int start, int length) { if (charBuffer != null) charBuffer.append(ch, start, length); } // ----------------------- SAX handler implementation ---------------------------------------------- /** * The SAX handler used internally by this event parser. */ class SAXHandlerImpl extends DefaultHandler { @Override public void setDocumentLocator(Locator locator) { setLocator(locator); } @Override public void endDocument() throws SAXException { setLocator(null); } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { pushPrefixMappingInNextLevel(prefix, uri); } @Override public void endPrefixMapping(String prefix) throws SAXException { // the mapping will disappear with the level } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { try { startElementImpl(uri, localName, attributes); } catch (Exception ex) { throw new InnerTransportException(ex); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { try { endElementImpl(uri, localName); } catch (Exception ex) { throw new InnerTransportException(ex); } } @Override public void error(SAXParseException e) throws SAXException { throw e; } @Override public void characters(char[] ch, int start, int length) throws SAXException { appendCharacters(ch, start, length); } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { appendCharacters(ch, start, length); } } // --------------------- Inner Attribute Name Iterator implementation ------------------------------- /** * Iterator on attribute mames of a SAX element. */ static class AttributeNameIterator implements Iterator { private String namespaceURI; private Attributes attrList; private int nbrAttr; private int index; AttributeNameIterator(String uri, Attributes attrs) { namespaceURI = uri; attrList = attrs; index = 0; nbrAttr = (attrList == null) ? 0 : attrList.getLength(); checkNextAttibuteNamespace(); } private void checkNextAttibuteNamespace() { boolean namespaceOK; String attributeURI; while (index < nbrAttr) { attributeURI = attrList.getURI(index); if (namespaceURI == null) namespaceOK = (attributeURI == null) || attributeURI.equals(""); else namespaceOK = namespaceURI.equals(attributeURI); if (namespaceOK) break; else index++; } } public boolean hasNext() { return index < nbrAttr; } public String next() { String name = attrList.getLocalName(index); index++; checkNextAttibuteNamespace(); return name; } public void remove() { throw new UnsupportedOperationException("Cannot remove attribute"); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy