
com.aspectran.core.util.xml.NodeletParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectran Show documentation
Show all versions of aspectran Show documentation
Aspectran is a lightweight Java framework for building Enterprise-ready Web applications.
Also, It will be able to launch as a Console-based and Embedded application.
The newest version!
/**
* Copyright 2008-2016 Juho Jeong
*
* 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 com.aspectran.core.util.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.aspectran.core.util.logging.Log;
import com.aspectran.core.util.logging.LogFactory;
/**
* The NodeletParser is a callback based parser similar to SAX. The big
* difference is that rather than having a single callback for all nodes,
* the NodeletParser has a number of callbacks mapped to
* various nodes. The callback is called a Nodelet and it is registered
* with the NodeletParser against a specific XPath.
*/
public class NodeletParser {
private final Log log = LogFactory.getLog(NodeletParser.class);
protected final static Map EMPTY_ATTRIBUTES = new HashMap();
private Map nodeletMap = new HashMap();
private boolean validating;
private EntityResolver entityResolver;
/**
* Registers a nodelet for the specified XPath. Current XPaths supported
* are:
*
* - Text Path - /rootElement/childElement/text()
*
- Attribute Path - /rootElement/childElement/@theAttribute
*
- Element Path - /rootElement/childElement/theElement
*
- All Elements Named - //theElement
*
*
* @param xpath the xpath
* @param nodelet the nodelet
*/
public void addNodelet(String xpath, Nodelet nodelet) {
nodeletMap.put(xpath, nodelet);
}
/**
* Registers a nodelet for the specified XPath. Current XPaths supported
* are:
*
* - Text Path - /rootElement/childElement/text()
*
- Attribute Path - /rootElement/childElement/@theAttribute
*
- Element Path - /rootElement/childElement/theElement
*
- All Elements Named - //theElement
*
*
* @param prefix the prefix xpath
* @param xpath the xpath
* @param nodelet the nodelet
*/
public void addNodelet(String prefix, String xpath, Nodelet nodelet) {
addNodelet(prefix + xpath, nodelet);
}
/**
* Adds the nodelet.
*
* @param xpath the xpath
* @param nodeletAdder the nodelet adder
*/
public void addNodelet(String xpath, NodeletAdder nodeletAdder) {
nodeletAdder.process(xpath, this);
}
/**
* Adds the nodelet.
*
* @param prefix the prefix
* @param xpath the xpath
* @param nodeletAdder the nodelet adder
*/
public void addNodelet(String prefix, String xpath, NodeletAdder nodeletAdder) {
addNodelet(prefix + xpath, nodeletAdder);
}
/**
* Begins parsing from the provided Reader.
*
* @param reader the reader
* @throws NodeletException the nodelet exception
*/
public void parse(Reader reader) throws NodeletException {
try {
Document doc = createDocument(reader);
parse(doc.getLastChild());
} catch(Exception e) {
throw new NodeletException("Error parsing XML.", e);
}
}
/**
* Begins parsing from the provided InputStream.
*
* @param inputStream the input stream
* @throws NodeletException the nodelet exception
*/
public void parse(InputStream inputStream) throws NodeletException {
try {
Document doc = createDocument(inputStream);
parse(doc.getLastChild());
} catch(Exception e) {
throw new NodeletException("Error parsing XML.", e);
}
}
/**
* Begins parsing from the provided Node.
*
* @param node the node
*/
public void parse(Node node) {
processNodelet(node, "/");
process(node, new Path());
}
/**
* A recursive method that walkes the DOM tree, registers XPaths and
* calls Nodelets registered under those XPaths.
*
* @param node the node
* @param path the path
*/
private void process(Node node, Path path) {
if(node instanceof Element) {
// Element
String elementName = node.getNodeName();
path.add(elementName);
processNodelet(node, path.toString());
processNodelet(node, "//" + elementName);
/*
// Attribute
NamedNodeMap attributes = node.getAttributes();
int n = attributes.getLength();
for(int i = 0; i < n; i++) {
Node att = attributes.item(i);
String attrName = att.getNodeName();
path.add("@" + attrName);
processNodelet(att, path.toString());
processNodelet(node, new StringBuilder("//@").append(attrName).toString());
path.remove();
}
*/
// Children
NodeList children = node.getChildNodes();
for(int i = 0; i < children.getLength(); i++) {
process(children.item(i), path);
}
path.add("end()");
processNodelet(node, path.toString());
path.remove();
path.remove();
} else if(node instanceof Text) {
// Text
path.add("text()");
processNodelet(node, path.toString());
/*
processNodelet(node, "//text()");
*/
path.remove();
}
}
private void processNodelet(Node node, String pathString) {
Nodelet nodelet = nodeletMap.get(pathString);
if(nodelet != null) {
try {
Map attributes;
String text;
if(!pathString.endsWith("end()")) {
attributes = parseAttributes(node);
text = getNodeValue(node);
if(log.isTraceEnabled()) {
StringBuilder sb = new StringBuilder(pathString);
if(attributes != null && attributes.size() > 0) {
sb.append(" ").append(attributes);
}
if(text != null && text.length() > 0) {
sb.append(" ").append(text);
}
log.trace(sb.toString());
}
} else {
attributes = null;
text = null;
}
nodelet.process(node, attributes, text);
} catch(Exception e) {
throw new RuntimeException("Error parsing XPath '" + pathString + "'. Cause: " + e, e);
}
}
}
private Map parseAttributes(Node node) {
NamedNodeMap attributeNodes = node.getAttributes();
if(attributeNodes == null)
return EMPTY_ATTRIBUTES;
Map attributes = new HashMap();
for(int i = 0; i < attributeNodes.getLength(); i++) {
Node attribute = attributeNodes.item(i);
String value = attribute.getNodeValue();
attributes.put(attribute.getNodeName(), value);
}
return attributes;
}
private String getNodeValue(Node node) {
NodeList children = node.getChildNodes();
int childrenLength = children.getLength();
if(childrenLength == 0) {
String value = node.getNodeValue();
if(value != null)
return value.trim();
return null;
}
StringBuilder sb = null;
for(int i = 0; i < childrenLength; i++) {
Node child = children.item(i);
if(child.getNodeType() == Node.CDATA_SECTION_NODE ||
child.getNodeType() == Node.TEXT_NODE) {
String data = ((CharacterData)child).getData();
if(data.length() > 0) {
if(sb == null)
sb = new StringBuilder(data);
else
sb.append(data);
}
}
}
return (sb == null) ? null : sb.toString();
}
/**
* Creates a JAXP Document from a reader.
*
* @param reader the reader
* @return the document
* @throws ParserConfigurationException the parser configuration exception
* @throws FactoryConfigurationError the factory configuration error
* @throws SAXException the sax exception
* @throws IOException the io exception
*/
private Document createDocument(Reader reader) throws ParserConfigurationException, FactoryConfigurationError,
SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validating);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(new InputSource(reader));
}
/**
* Creates a JAXP Document from an InputStream.
*
* @param inputStream the input stream
* @return the document
* @throws ParserConfigurationException the parser configuration exception
* @throws FactoryConfigurationError the factory configuration error
* @throws SAXException the sax exception
* @throws IOException the io exception
*/
private Document createDocument(InputStream inputStream) throws ParserConfigurationException,
FactoryConfigurationError, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validating);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(new InputSource(inputStream));
}
public void setValidating(boolean validating) {
this.validating = validating;
}
public void setEntityResolver(EntityResolver resolver) {
this.entityResolver = resolver;
}
/**
* Inner helper class that assists with building XPath paths.
*
* Note: Currently this is a bit slow and could be optimized.
*/
private static class Path {
private List nodeList = new ArrayList();
public Path() {
}
@SuppressWarnings("unused")
public Path(String path) {
StringTokenizer parser = new StringTokenizer(path, "/", false);
while(parser.hasMoreTokens()) {
nodeList.add(parser.nextToken());
}
}
public void add(String node) {
nodeList.add(node);
}
public void remove() {
nodeList.remove(nodeList.size() - 1);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
for(String name : nodeList) {
sb.append("/");
sb.append(name);
}
return sb.toString();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy