/*
* Rapid Beans Framework: XmlNode.java
*
* Copyright (C) 2009 Martin Bluemel
*
* Creation Date: 10/21/2007
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copies of the GNU Lesser General Public License and the
* GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
package org.rapidbeans.core.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.rapidbeans.core.exception.RapidBeansRuntimeException;
import org.rapidbeans.core.exception.UtilException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* @author Martin Bluemel
*
* Utility class for primitive XML parsing using good old JAXP DOM
* parser technology.
*/
public class XmlNode {
/**
* the node encapsulated.
*/
private Node node = null;
/**
* @return the XML node's name.
*/
public String getName() {
return this.node.getNodeName();
}
/**
* @return the XML node's type.
*/
public short getType() {
return this.node.getNodeType();
}
/**
* constructor.
*
* @param argNode
* the node to encupsulate
*/
public XmlNode(final Node argNode) {
this.node = argNode;
}
/**
* retrieve the first subnode of a node according to the given pattern.<br/>
*
* @param pattern
* the pattern: <entity 1>[<entity 2><entity
* 3>...]
*
* @return the found node or null if no node was found
*/
public XmlNode getFirstSubnode(final String pattern) {
Collection<XmlNode> subnodes = this.getSubnodes(pattern);
if (subnodes.size() == 0) {
throw new UtilException("XML Util Error: could not find subnode \"" + pattern + "\" of node \""
+ this.node.getNodeName() + "\"");
}
return subnodes.iterator().next();
}
/**
* determine all sunbnodes of a node.
*
* @return a collection of all subnodes (empty collection if no subnode was
* found).
*/
public Collection<XmlNode> getSubnodes() {
final NodeList subnodes = this.node.getChildNodes();
final int subnodesCount = subnodes.getLength();
Collection<XmlNode> foundNodes = new ArrayList<XmlNode>();
for (int i = 0; i < subnodesCount; i++) {
foundNodes.add(new XmlNode(subnodes.item(i)));
}
return foundNodes;
}
/**
* determine all sub nodes of a node according to the given pattern.
*
* @param pattern
* the pattern: <entity 1>[<entity 2><entity
* 3>...]
* @return a collection of found nodes (empty collection if no node was
* found).
*/
public List<XmlNode> getSubnodes(final String pattern) {
String firstPatternToken = null;
if (pattern != null) {
firstPatternToken = new StringTokenizer(pattern, "/").nextToken();
}
final NodeList subnodes = this.node.getChildNodes();
final int subnodesCount = subnodes.getLength();
final List<XmlNode> foundNodes = new ArrayList<XmlNode>();
for (int i = 0; i < subnodesCount; i++) {
if (firstPatternToken == null || subnodes.item(i).getNodeName().equals(firstPatternToken)) {
foundNodes.add(new XmlNode(subnodes.item(i)));
}
}
return foundNodes;
}
/**
* find all attributes of the node.
*
* @return collection with all attributes
*/
public Collection<XmlAttribute> getAttributes() {
Collection<XmlAttribute> attrs = new ArrayList<XmlAttribute>();
NamedNodeMap nodes = this.node.getAttributes();
if (nodes != null) {
int nodesLen = nodes.getLength();
for (int i = 0; i < nodesLen; i++) {
attrs.add(new XmlAttribute(nodes.item(i)));
}
}
return attrs;
}
/**
* @return the node's namespace URI "xmlns:ns"
*/
public String getNamespaceURI() {
return this.node.getNamespaceURI();
}
/**
* retrieve an attribute out of a node according to the given pattern and
* return it's value.
*
* @param pattern
* the attribute pattern: [<entity 1><entity
* 2><entity 3>.../]@<attribute>
* @return the found attribute's value or null if not found
*/
public String getAttributeValue(final String pattern) {
return getAttributeValue(pattern, null);
}
/**
* retrieve an attribute out of a node according to the given pattern and
* return it's value (with default value).
*
* @param pattern
* the attribute pattern: [<entity 1><entity
* 2><entity 3>.../]@<attribute>
* @param defaultValue
* the default value in case the attribute is not found
* @return the found attribute's value or a default value if not found
*/
public String getAttributeValue(final String pattern, final String defaultValue) {
String ret = defaultValue;
final String firstPatternToken = new StringTokenizer(pattern, "/").nextToken();
if (firstPatternToken.startsWith("@")) {
String attrName = firstPatternToken.substring(1);
final NamedNodeMap attrs = this.node.getAttributes();
if (attrs != null) {
final Node attrNode = attrs.getNamedItem(attrName);
if (attrNode != null) {
ret = attrNode.getNodeValue();
}
}
}
return ret;
}
/**
* Parse an XML document from a URL using a DOM parser and get the top level
* node.
*
* @param url
* the URL
*
* @return the top level node
*/
public static XmlNodeTopLevel getDocumentTopLevel(final URL url) {
if (url.getProtocol().equals("file")) {
return getDocumentTopLevel(new File(url.getFile().replaceAll("%20", " ")));
} else if (url.getProtocol().equals("ftp") || url.getProtocol().equals("http")
|| url.toString().startsWith("jar:http:")) {
return getDocumentTopLevelConnection(url);
} else {
throw new RapidBeansRuntimeException("Unsupported protocol \"" + url.getProtocol());
}
}
/**
* load a DOM document from a file and get the top level node.
*
* @param xmlResourceFile
* the resource file
*
* @return the top level node
*/
public static XmlNodeTopLevel getDocumentTopLevel(final File xmlResourceFile) {
InputStream is = null;
try {
is = new FileInputStream(xmlResourceFile);
return getDocumentTopLevel(is);
} catch (FileNotFoundException e) {
throw new UtilException(e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* load a DOM document via HTTP or FTP and get the top level node.
*
* @param url
* the URL
*
* @return the top level node
*/
private static XmlNodeTopLevel getDocumentTopLevelConnection(final URL url) {
InputStream is = null;
try {
URLConnection urlc = url.openConnection();
is = urlc.getInputStream();
if (is == null) {
throw new UtilException("URL \"" + url.toString() + "\" not found");
}
return getDocumentTopLevel(is);
} catch (IOException e) {
throw new UtilException("Problems opening a connection for URL \"" + url.toString() + "\"", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* load a DOM document from a string and get the top level node.
*
* @param descr
* the XML description string
*
* @return the top level node
*/
public static XmlNode getDocumentTopLevel(final String descr) {
return getDocumentTopLevel(new ByteArrayInputStream(descr.getBytes()));
}
/**
* load a DOM document and get the top level node.
*
* @param inputStream
* the stream.
*
* @return the top level node
*/
public static XmlNodeTopLevel getDocumentTopLevel(final InputStream inputStream) {
try {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
// dbf.setValidating(true);
dbf.setValidating(false);
final DocumentBuilder db = dbf.newDocumentBuilder();
// db.setErrorHandler(new ErrorHandler() {
// @Override
// public void error(SAXParseException e) throws SAXException {
// System.out.println("PARSER ERROR: " + e.getMessage());
// }
// @Override
// public void fatalError(SAXParseException e) throws SAXException {
// System.out.println("PARSER FATAL ERROR: " + e.getMessage());
// }
// @Override
// public void warning(SAXParseException e) throws SAXException {
// System.out.println("PARSER WARNING: " + e.getMessage());
// }
// });
final Document doc = db.parse(inputStream);
final NodeList topLevelNodes = doc.getChildNodes();
int i = 0;
Node topLevelNode = null;
Node node;
while ((node = topLevelNodes.item(i++)) != null) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
topLevelNode = node;
}
}
if (topLevelNode == null) {
throw new RapidBeansRuntimeException("No top level element found.");
}
return new XmlNodeTopLevel(topLevelNode, doc.getXmlEncoding());
} catch (ParserConfigurationException e) {
throw new UtilException(e);
} catch (SAXException e) {
throw new UtilException(e);
} catch (IOException e) {
throw new UtilException(e);
}
}
/**
* @return the XML node value.
*/
public String getValue() {
if (this.node.getFirstChild() != null) {
return this.node.getFirstChild().getNodeValue();
} else {
return "";
}
}
}