AIR.Common.xml.XmlReader.java Source code

Java tutorial

Introduction

Here is the source code for AIR.Common.xml.XmlReader.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System Copyright (c) 2014 American
 * Institutes for Research
 * 
 * Distributed under the AIR Open Source License, Version 1.0 See accompanying
 * file AIR-License-1_0.txt or at http://www.smarterapp.org/documents/
 * American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
/**
* 
 */
package AIR.Common.xml;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.util.Stack;

import org.jdom2.Content;
import org.jdom2.Content.CType;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;

/**
 * @author Shiva BEHERA [sbehera@air.org]
 * 
 */
public class XmlReader {
    private static final Logger _logger = LoggerFactory.getLogger(XmlReader.class);
    private Document _doc = null;

    private Stack<MutablePair<Content, Integer>> _stack = new Stack<MutablePair<Content, Integer>>();
    @SuppressWarnings("unused")
    private Closeable _file = null;
    @SuppressWarnings("unused")
    private Namespace _rootNameSpace = null;

    public XmlReader(InputStream file) throws IOException, JDOMException {
        this(new InputStreamReader(file));
    }

    public XmlReader(Reader file) throws IOException, JDOMException {
        // Start hack! Shiva: I am not sure why this is happening. but this happened
        // with the earlier .NET code as well.
        // why am I seeing a special character if I directly pass a
        // InputStreamReader to SAXBuilder. I had to do the same hack in
        // ItemScoringHandler.ProcessRequest.
        // I see this message as well
        // "warning: line 6050: incompatible stripping characters and condition"
        // Further information: Elena found two additional links:
        // http://mark.koli.ch/resolving-orgxmlsaxsaxparseexception-content-is-not-allowed-in-prolog
        // and
        // http://stackoverflow.com/questions/5138696/org-xml-sax-saxparseexception-content-is-not-allowed-in-prolog
        BufferedReader bfr = new BufferedReader(file);
        StringBuilder strinBuilder = new StringBuilder();
        String line = "";
        while ((line = bfr.readLine()) != null)
            strinBuilder.append(line);
        file.close();
        String fileContent = strinBuilder.toString();
        int indexOfBegin = fileContent.indexOf('<');
        if (indexOfBegin > 0)
            fileContent = fileContent.substring(indexOfBegin);
        file = new StringReader(fileContent);
        // End hack!
        buildDocument(file);
    }

    public static XmlReader create(URI uri) throws XmlReaderException {
        try {
            return new XmlReader(uri.toURL().openStream());
        } catch (Exception exp) {
            throw new XmlReaderException(exp);
        }
    }

    public static XmlReader create(String filepath) throws XmlReaderException {
        try {
            return new XmlReader(new FileInputStream(filepath));
        } catch (Exception exp) {
            throw new XmlReaderException(exp);
        }
    }

    public Document getDocument() {
        return _doc;
    }

    public String getAttribute(String attributeName) throws XmlReaderException {
        Element e = getNodeAsElement();
        if (e != null)
            return e.getAttributeValue(attributeName);
        return null;
    }

    public long getAttributeAsLong(String attributeName) throws XmlReaderException {
        return Long.parseLong(getAttribute(attributeName));
    }

    public boolean getAttributeAsBoolean(String attributeName) {
        return Boolean.parseBoolean(getAttribute(attributeName));
    }

    public Content.CType getNodeType() throws XmlReaderException {
        isEmpty();
        return _stack.peek().getLeft().getCType();
    }

    public String getName() throws XmlReaderException {
        Element e = getNodeAsElement();
        if (e != null)
            return e.getName();
        return null;
    }

    public String readString() throws XmlReaderException {
        isEmpty();
        return _stack.peek().getLeft().getValue();
    }

    public String readElementContentAsString() throws XmlReaderException {
        return readString();
    }

    public boolean readToDescendant() throws XmlReaderException {
        return getNextElement(TRAVERSE_TYPE.IMMEDIATE_CHILD_ONLY);
    }

    // TODO simplify and see if we can use another method rather than have all
    // this logic in here
    private boolean readToDescendant(String item) throws XmlReaderException, IOException {
        MutablePair<Content, Integer> alwaysTop = null;
        if (_stack.size() > 0) {
            alwaysTop = _stack.peek();
        }
        while (_stack.size() != 0) {
            MutablePair<Content, Integer> topElement = _stack.peek();

            if (topElement.getLeft().getCType() == CType.Element) {
                Element topElementNode = (Element) topElement.getLeft();
                if (StringUtils.equals(item, topElementNode.getName()))
                    return true;

                int nextChild = topElement.getRight() + 1;
                if (topElementNode.getChildren().size() > nextChild) {
                    topElement.setRight(nextChild);
                    Element nextTraversalNode = topElementNode.getChildren().get(nextChild);
                    _stack.push(new MutablePair<Content, Integer>(nextTraversalNode, -1));
                } else {
                    // we do not want to pop the original top node (alwaysTop) as we are
                    // only doing descendant.
                    if (!alwaysTop.equals(topElement))
                        _stack.pop();
                    else
                        break;
                }
            }
        }

        return false;
    }

    public boolean readToNextSibling() throws XmlReaderException {
        return getNextElement(TRAVERSE_TYPE.NEXT_SIBLING);
    }

    public boolean readToNextSibling(String item) throws XmlReaderException, IOException {
        while (this.traverseNextSibling()) {
            if (StringUtils.equals(item, getName()))
                return true;
        }
        return false;
    }

    public boolean read() throws XmlReaderException {
        // if either the InputStream is empty or our stack is empty, we do not have
        // any more elements.
        return getNextElement(TRAVERSE_TYPE.ANY);
    }

    public boolean readToFollowing(String item) throws XmlReaderException, IOException {
        if (StringUtils.equals(item, getName()))
            return true;
        while (read()) {
            if (StringUtils.equals(item, getName()))
                return true;
        }
        return false;
    }

    public void moveToElement() {
        // Shiva: No need to do anything here. we are already in the node unlike in
        // .NET when
        // the pointer shifts to an attribute.
    }

    public void close() {
        try {
            if (_file != null) {
                _file.close();
                _file = null;
            }
        } catch (IOException exp) {
            _logger.error(exp.getMessage());
        }
    }

    @Override
    public void finalize() {
        close();
    }

    public boolean isEmptyElement() {
        Content thisNode = _stack.peek().getLeft();
        switch (thisNode.getCType()) {
        case CDATA:
        case Text:
        case Comment:
            return true;
        case Element:
            Element thisElement = getNodeAsElement();
            return thisElement.getChildren().size() > 0;
        }
        return true;
    }

    private boolean getNextElement(TRAVERSE_TYPE type) {
        try {
            switch (type) {
            case ANY:
                return traverseAny();
            case IMMEDIATE_CHILD_ONLY:
                return traverseImmediateChild(true);
            case NEXT_SIBLING:
                return traverseNextSibling();
            }
        } catch (IOException exp) {
            throw new XmlReaderException(exp);
        }
        return true;
    }

    private boolean traverseNextSibling() throws IOException {
        /*
         * if (_file.available () == 0) { return false; }
         */
        if (_stack.size() == 0)
            return false;

        MutablePair<Content, Integer> currentTop = _stack.pop();
        if (_stack.size() != 0) {

            MutablePair<Content, Integer> topElement = _stack.peek();
            // We already went into topElement's children and so there is no
            // need to check if it of type element.
            Element topElementNode = (Element) topElement.getLeft();

            int nextChild = topElement.getRight() + 1;
            if (topElementNode.getChildren().size() > nextChild) {
                topElement.setRight(nextChild);
                Element nextTraversalNode = topElementNode.getChildren().get(nextChild);
                _stack.push(new MutablePair<Content, Integer>(nextTraversalNode, -1));
                return true;
            }
        }
        // else put the previous top back on the top.
        _stack.push(currentTop);
        return false;
    }

    private boolean traverseAny() throws IOException {
        /*
         * if (_file.available () == 0) { return false; }
         */
        if (_stack.size() == 0)
            return false;

        // if we have a next child then we will go to that.
        // else we will back up to the parent and find its next child.
        if (traverseImmediateChild(false))
            return true;
        else {
            // so, the current top of stack has no/more children. so we will pop it
            // and back up the stack until we find one
            while (_stack.size() > 0) {
                _stack.pop();
                if (traverseImmediateChild(false))
                    return true;
            }
        }
        // no more nodes;
        return false;
    }

    private boolean traverseImmediateChild(boolean firstOnly) throws IOException {
        /*
         * if (_file.available () == 0) { return false; }
         */
        if (_stack.size() == 0)
            return false;

        MutablePair<Content, Integer> topElement = _stack.peek();
        Content node = topElement.getLeft();
        if (node.getCType() == CType.Element) {
            Element topElementNode = (Element) node;
            int nextChild = 0;
            if (!firstOnly)
                nextChild = topElement.getRight() + 1;
            // if we have a next child then we will go to that.
            if (topElementNode.getChildren().size() > nextChild) {
                topElement.setRight(nextChild);
                Element nextTraversalNode = topElementNode.getChildren().get(nextChild);
                _stack.push(new MutablePair<Content, Integer>(nextTraversalNode, -1));
                return true;
            }
        }
        return false;
    }

    private boolean isEmpty() {
        if (_stack.size() == 0)
            throw new XmlReaderException("No more content");
        return true;
    }

    private enum TRAVERSE_TYPE {
        ANY, IMMEDIATE_CHILD_ONLY, NEXT_SIBLING
    };

    private Element getNodeAsElement() {
        isEmpty();
        Content node = _stack.peek().getLeft();
        if (node.getCType() == CType.Element)
            return (Element) node;
        return null;
    }

    public static void main(String[] args) {
        try {

            InputStream input = new FileInputStream(
                    "C:/WorkSpace/TDSCore/AppsCurrent/Student/XmlReaderApplication/books3.xml");
            XmlReader reader = new XmlReader(input);
            reader.read();
            reader.read();
            reader.readToDescendant();
            System.err.println(reader.getName());
            System.err.println(reader.readString());
            reader.readToDescendant("y");
            System.err.println(reader.getName());
            System.err.println(reader.readString());
        } catch (Exception exp) {
            exp.printStackTrace();
        }

        System.err.println("");
    }

    private void buildDocument(Closeable file) throws JDOMException, IOException {
        SAXBuilder builder = new SAXBuilder();
        if (file instanceof InputStream)
            _doc = builder.build((InputStream) file);
        else if (file instanceof Reader)
            _doc = builder.build((Reader) file);
        _stack.push(new MutablePair<Content, Integer>(_doc.getRootElement(), -1));
        _rootNameSpace = _doc.getRootElement().getNamespace();
        _file = file;
    }
}