/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
* $Id: DOMParser.java 571947 2007-09-02 10:50:40Z vgritsenko $
*/
package org.apache.xindice.xml.dom;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.data.Value;
import org.apache.xindice.util.ObjectStack;
import org.apache.xindice.util.XindiceException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
/**
* DOMParser is the Xindice compressed DOM parser.
*
* @version $Revision: 571947 $, $Date: 2007-09-02 06:50:40 -0400 (Sun, 02 Sep 2007) $
*/
public final class DOMParser extends DefaultHandler
implements DeclHandler, LexicalHandler {
private static final Log log = LogFactory.getLog(DOMParser.class);
/**
* SAX parser factory instance
*/
private static final SAXParserFactory factory = SAXParserFactory.newInstance();
private SAXParser sp;
private ErrorHandler errors;
private EntityResolver entities;
private Document doc;
private Node context;
private int state = -1;
private StringBuffer buf;
private final ObjectStack states = new ObjectStack();
public DOMParser() throws XindiceException {
try {
sp = getSAXParser();
} catch (Exception e) {
log.warn("Failed to create SAXParser", e);
throw new XindiceException("Error creating parser", e);
}
}
public void setErrorHandler(ErrorHandler errors) throws SAXException {
this.errors = errors;
if (sp != null) {
XMLReader xr = sp.getXMLReader();
xr.setErrorHandler(errors);
}
}
public void setEntityResolver(EntityResolver entities) throws SAXException {
this.entities = entities;
if (sp != null) {
XMLReader xr = sp.getXMLReader();
xr.setEntityResolver(entities);
}
}
public void parse(String xml) throws SAXException, IOException {
parse(new StringReader(xml));
}
public void parse(Reader input) throws SAXException, IOException {
parse(new InputSource(input));
}
public void parse(byte[] xml) throws SAXException, IOException {
parse(new ByteArrayInputStream(xml));
}
public void parse(Value value) throws SAXException, IOException {
parse(value.getInputStream());
}
public void parse(InputSource source) throws SAXException, IOException {
sp.parse(source, this);
}
public void parse(InputStream input) throws SAXException, IOException {
sp.parse(input, this);
}
public static Document toDocument(Reader input) throws XindiceException {
try {
DOMParser dp = new DOMParser();
dp.parse(input);
return dp.getDocument();
} catch (Exception e) {
throw new XindiceException("Error parsing Document", e);
}
}
public static Document toDocument(byte[] xml) throws XindiceException {
try {
DOMParser dp = new DOMParser();
dp.parse(xml);
return dp.getDocument();
} catch (Exception e) {
throw new XindiceException("Error parsing Document", e);
}
}
public static Document toDocument(Value value) throws XindiceException {
try {
DOMParser dp = new DOMParser();
dp.parse(value);
return dp.getDocument();
} catch (Exception e) {
throw new XindiceException("Error parsing Document", e);
}
}
public static Document toDocument(InputSource source) throws XindiceException {
try {
DOMParser dp = new DOMParser();
dp.parse(source);
return dp.getDocument();
} catch (Exception e) {
throw new XindiceException("Error parsing Document", e);
}
}
public static Document toDocument(InputStream input) throws XindiceException {
try {
DOMParser dp = new DOMParser();
dp.parse(input);
return dp.getDocument();
} catch (Exception e) {
throw new XindiceException("Error parsing Document", e);
}
}
public static Document toDocument(String xml) throws XindiceException {
try {
DOMParser dp = new DOMParser();
dp.parse(xml);
return dp.getDocument();
} catch (Exception e) {
throw new XindiceException("Error parsing Document", e);
}
}
/**
* Returns the parsed Document
* @return the parsed document
*/
public Document getDocument() {
return doc;
}
private SAXParser getSAXParser() throws Exception {
final SAXParser sp = factory.newSAXParser();
final XMLReader xr = sp.getXMLReader();
setFeature(xr, "http://xml.org/sax/features/namespaces", true);
setFeature(xr, "http://xml.org/sax/features/validation", false);
setFeature(xr, "http://xml.org/sax/features/external-general-entities", false);
setFeature(xr, "http://xml.org/sax/features/external-parameter-entities", false);
setFeature(xr, "http://xml.org/sax/features/namespace-prefixes", true);
xr.setProperty("http://xml.org/sax/properties/lexical-handler", this);
xr.setProperty("http://xml.org/sax/properties/declaration-handler", this);
if (errors != null) {
xr.setErrorHandler(errors);
}
if (entities != null) {
xr.setEntityResolver(entities);
}
return sp;
}
/**
* Tries to set SAX features. It is assumed that feature support is required
* if its value is <em>true</em> and optional if the value is <em>false</em>.
*
* @param xr XMLReader
* @param name SAX feature name
* @param value feature value
* @throws SAXNotRecognizedException is the SAX feature is not known
* @throws SAXNotSupportedException if the SAX feature is not supported and
* the value is <em>true</em>
*/
private void setFeature(XMLReader xr, String name, boolean value)
throws SAXNotRecognizedException, SAXNotSupportedException {
try {
xr.setFeature(name, value);
} catch (SAXNotSupportedException e) {
if (value) {
throw e;
} else if (log.isWarnEnabled()) {
log.warn("SAX feature " + name + " is not supported");
}
}
}
private void pushState(int newState) {
Integer i = new Integer(state);
states.push(i);
state = newState;
}
private void popState() {
Integer i = (Integer) states.pop();
state = i != null ? i.intValue() : -1;
}
// Implementation of the EntityResolver interface
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
return null;
}
// Implementation of DTDHandler interface
public void notationDecl(String name, String publicId, String systemId) throws SAXException {
}
public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException {
}
// Implementation of ContentHandler interface
public void setDocumentLocator(Locator locator) {
}
public void startDocument() throws SAXException {
doc = new DocumentImpl();
context = doc;
pushState(Node.DOCUMENT_NODE);
}
public void endDocument() throws SAXException {
context = null;
popState();
}
public void startPrefixMapping(String prefix, String uri) throws SAXException {
}
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Element e;
if (uri != null && uri.length() > 0) {
e = doc.createElementNS(uri, qName);
} else {
e = doc.createElement(localName);
}
for (int i = 0; i < attributes.getLength(); i++) {
String attrURI = attributes.getURI(i);
String attrQName = attributes.getQName(i);
String attrValue = attributes.getValue(i);
boolean isNsDef = attrQName != null && attrQName.startsWith("xmlns")
&& (attrQName.length() == 5 || attrQName.charAt(5) == ':');
if (isNsDef || (attrURI != null && attrURI.length() > 0)) {
e.setAttributeNS(attrURI, attrQName, attrValue);
} else {
e.setAttribute(attributes.getLocalName(i), attrValue);
}
}
context.appendChild(e);
pushState(Node.ELEMENT_NODE);
context = e;
}
public void endElement(String uri, String localName, String qName) throws SAXException {
context = context.getParentNode();
popState();
}
public void characters(char ch[], int start, int length) throws SAXException {
switch (state) {
case Node.CDATA_SECTION_NODE:
buf.append(ch, start, length);
break;
default:
String s = new String(ch, start, length);
context.appendChild(doc.createTextNode(s));
}
}
public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
switch (state) {
case Node.CDATA_SECTION_NODE:
buf.append(ch, start, length);
break;
default:
String s = new String(ch, start, length);
context.appendChild(doc.createTextNode(s));
}
}
public void processingInstruction(String target, String data) throws SAXException {
context.appendChild(doc.createProcessingInstruction(target, data));
}
public void skippedEntity(String name) throws SAXException {
}
// Implementation of the ErrorHandler interface
public void warning(SAXParseException e) throws SAXException {
log.info("Parsing warning (ignored)", e);
}
public void error(SAXParseException e) throws SAXException {
log.info("Parsing error (ignored)", e);
}
public void fatalError(SAXParseException e) throws SAXException {
throw e;
}
// Implementation of the DeclHandler interface
public void elementDecl(String name, String model) throws SAXException {
}
public void attributeDecl(String eName, String aName, String type, String valueDefault, String value) throws SAXException {
}
public void internalEntityDecl(String name, String value) throws SAXException {
}
public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException {
}
// Implementation of the LexicalHandler interface
public void startDTD(String name, String publicId, String systemId) throws SAXException {
}
public void endDTD() throws SAXException {
}
public void startEntity(String name) throws SAXException {
}
public void endEntity(String name) throws SAXException {
}
public void startCDATA() throws SAXException {
pushState(Node.CDATA_SECTION_NODE);
buf = new StringBuffer();
}
public void endCDATA() throws SAXException {
context.appendChild(doc.createCDATASection(buf.toString()));
buf = null;
popState();
}
public void comment(char ch[], int start, int length) throws SAXException {
String s = new String(ch, start, length);
context.appendChild(doc.createComment(s));
}
}