package org.cdlib.xtf.util; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.xml.transform.stream.StreamSource; import net.sf.saxon.Configuration; import net.sf.saxon.om.AllElementStripper; import net.sf.saxon.om.Axis; import net.sf.saxon.om.AxisIterator; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.tinytree.TinyBuilder; import net.sf.saxon.trans.XPathException; import net.sf.saxon.type.Type; /* * Copyright (c) 2005, Regents of the University of California * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the University of California nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * Provides an easy-to-use wrapper around a Saxon NodeInfo. * * @author Martin Haye */ public class EasyNode { /** The node we are wrapping */ private NodeInfo wrapped; /** Attribute names for this node */ private ArrayList<String> attrNames; /** Attribute values for this node */ private ArrayList<String> attrValues; /** Child elements for this node */ private ArrayList<EasyNode> children; /** Configuration used for parsing XML files */ private static Configuration config = new Configuration(); /** * Convenience method to read an XML file and return the root node. */ public static EasyNode readXMLFile(String path) { return readXMLFile(new File(path)); } /** * Convenience method to read an XML file and return the root node. */ public static EasyNode readXMLFile(File path) { // Read in the document (it's in XML format) StreamSource src = new StreamSource(path); NodeInfo doc = null; try { doc = TinyBuilder.build(src, new AllElementStripper(), config); return new EasyNode(doc); } catch (XPathException e) { throw new RuntimeException(e); } } /** Wrap a NodeInfo */ public EasyNode(NodeInfo toWrap) { wrapped = toWrap; } /** * Iterate the attributes and fill the 'attrNames' and 'attrValues' vectors. * This is a memo function and only has to do actual work once. */ private void getAttrs() { if (attrNames != null) return; attrNames = new ArrayList(); attrValues = new ArrayList(); NodeInfo attr; AxisIterator iter = wrapped.iterateAxis(Axis.ATTRIBUTE); while ((attr = (NodeInfo)iter.next()) != null) { String name = attr.getLocalPart(); String val = attr.getStringValue(); attrNames.add(name); attrValues.add(val); } } // getAttribs() /** * Iterate the children and fill the 'children' vector. This is a memo * function and only has to do actual work once. */ private void getChildren() { if (children != null) return; children = new ArrayList(); NodeInfo child; AxisIterator iter = wrapped.iterateAxis(Axis.CHILD); while ((child = (NodeInfo)iter.next()) != null) { if (child.getNodeKind() == Type.ELEMENT || child.getNodeKind() == Type.TEXT) { children.add(new EasyNode(child)); } } } // getChildren() /** Get the number of attributes this node has */ public int nAttrs() { getAttrs(); return attrNames.size(); } // nAttrs() /** Get a specific numbered attribute's name */ public String attrName(int index) { getAttrs(); return (String)attrNames.get(index); } // attrName() /** Get a list of all attribute names */ public List<String> attrNames() { getAttrs(); return attrNames; } /** Get a specific numbered attribute's value */ public String attrValue(int index) { getAttrs(); return (String)attrValues.get(index); } // attrName() /** Check if this node has the given attribute */ public boolean hasAttr(String name) { return attrValue(name) != null; } // hasAttr() /** Get a named attribute's value, or null if no such name found. The * name matching is performed case-insensitive. */ public String attrValue(String name) { getAttrs(); for (int i = 0; i < attrNames.size(); i++) { if (((String)attrNames.get(i)).equalsIgnoreCase(name)) return (String)attrValues.get(i); } return null; } // attrValue() /** Get a count of the number of children this node has */ public int nChildren() { getChildren(); return children.size(); } // nChildren() /** Get a specific numbered child of this node */ public EasyNode child(int index) { getChildren(); return (EasyNode)children.get(index); } /** Get the first child node with the specified name, case insensitive. * If no such child is found, returns null. */ public EasyNode child(String name) { getChildren(); for (int i = 0; i < children.size(); i++) { if (((EasyNode)children.get(i)).name().equalsIgnoreCase(name)) return (EasyNode)children.get(i); } return null; } // child() /** Get all the children */ public List<EasyNode> children() { getChildren(); return children; } /** Get the parent of this node (if any) */ public EasyNode parent() { NodeInfo parent = wrapped.getParent(); if (parent == null) return null; return new EasyNode(parent); } // parent() /** Get the name of this node */ public String name() { return wrapped.getLocalPart(); } // getName() /** Checks if this is an element node */ public boolean isElement() { return wrapped.getNodeKind() == Type.ELEMENT; } /** Checks if this is a text node */ public boolean isText() { return wrapped.getNodeKind() == Type.TEXT; } /** Get the string value of this node */ public String toString() { return wrapped.getStringValue(); } /** Get the actual node we're wrapping */ public NodeInfo getWrappedNode() { return wrapped; } // getWrappedNode() } // class EasyNode