/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.config.xml.util; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.thoughtworks.xstream.converters.ConversionException; /** * The {@link NodeIterator} is an {@link Iterator} for nodes of an XML document. * <p> * This iterator offers a simple mechanism iterating through {@code Node}* objects by considering the required or * optional occurrences of attributes, values or list of values. * * @author Michael Grammling - Initial Contribution */ public class NodeIterator implements Iterator<Object> { private List<?> nodes; private int index = 0; /** * Creates a new instance of this class with the specified argument. * * @param nodes the list of nodes to be iterated through (could be null or empty) */ public NodeIterator(List<?> nodes) { this.nodes = (nodes != null) ? nodes : new ArrayList<>(0); } @Override public boolean hasNext() { return (index < this.nodes.size()); } @Override public Object next() { if (hasNext()) { return this.nodes.get(index++); } return null; } @Override public void remove() { throw new UnsupportedOperationException(); } /** * Reverts the last {@link #next()} call. * <p> * After this method returns, the iteration counter is the same as before the last {@link #next()} call. Calling * this method multiple times decreases the iteration counter by one until the index of 0 has been reached. */ public void revert() { if (index > 0) { index--; } } /** * Ensures that the end of the node has been reached. * * @throws ConversionException if the end of the node has not reached yet */ public void assertEndOfType() throws ConversionException { if (hasNext()) { List<Object> nodes = new ArrayList<>(); while (hasNext()) { nodes.add(next()); } throw new ConversionException("The document is invalid, it contains further" + " unsupported data: " + nodes + "!"); } } /** * Returns the next object if the specified name of the node fits to the next node, * or {@code null} if the node does not exist. In the last case the iterator will * <i>not</i> increase its iteration counter. * * @param nodeName the name of the node to be read next (must neither be null, nor empty) * @param required true if the occurrence of the node has to be ensured * * @return the next object if the specified name of the node fits to the next node, * otherwise null * * @throws ConversionException if the specified node could not be found in the next node * however it was specified as required */ public Object next(String nodeName, boolean required) throws ConversionException { if (hasNext()) { Object nextNode = next(); if (nextNode instanceof NodeName) { if (nodeName.equals(((NodeName) nextNode).getNodeName())) { return nextNode; } } this.index--; } if (required) { throw new ConversionException("The node '" + nodeName + "' is missing!"); } return null; } /** * Returns the next attribute if the specified name of the node fits to the next node * and the attribute with the specified name could be found, or {@code null} if the * node or attribute does not exist. In the last case the iterator will <i>not</i> * increase its iteration counter. * <p> * The next node must be of the type {@link NodeAttributes}. * * @param nodeName the name of the node to be read next (must neither be null, nor empty) * * @param attributeName the name of the attribute of the node to be read next * (must neither be null, nor empty) * * @param required true if the occurrence of the node's attribute has to be ensured * * @return the next attribute of the specified name of the node and attribute * (could be null or empty) * * @throws ConversionException if the specified node's attribute could not be found in the * next node however it was specified as required */ public String nextAttribute(String nodeName, String attributeName, boolean required) throws ConversionException { if (hasNext()) { Object nextNode = next(); if (nextNode instanceof NodeAttributes) { if (nodeName.equals(((NodeName) nextNode).getNodeName())) { return ((NodeAttributes) nextNode).getAttribute(attributeName); } } this.index--; } if (required) { throw new ConversionException("The attribute '" + attributeName + "' in the node '" + nodeName + "' is missing!"); } return null; } /** * Returns the next value if the specified name of the node fits to the next node, * or {@code null} if the node does not exist. In the last case the iterator will * <i>not</i> increase its iteration counter. * <p> * The next node must be of the type {@link NodeValue}. * * @param nodeName the name of the node to be read next (must neither be null, nor empty) * @param required true if the occurrence of the node's value has to be ensured * * @return the next value of the specified name of the node (could be null or empty) * * @throws ConversionException if the specified node's value could not be found in the * next node however it was specified as required */ public Object nextValue(String nodeName, boolean required) throws ConversionException { Object value = next(nodeName, required); if (value instanceof NodeValue) { return ((NodeValue) value).getValue(); } return null; } /** * Returns the next list of values if the specified name of the node fits to the * next node, or {@code null} if the node does not exist. In the last case the * iterator will <i>not</i> increase its iteration counter. * <p> * The next node must be of the type {@link NodeList}. * * @param nodeName the name of the node to be read next (must neither be null, nor empty) * @param required true if the occurrence of the node's list of values has to be ensured * * @return the next list of values of the specified name of the node (could be null or empty) * * @throws ConversionException if the specified node's list of values could not be found * in the next node however it was specified as required */ public List<?> nextList(String nodeName, boolean required) throws ConversionException { Object list = next(nodeName, required); if (list instanceof NodeList) { return ((NodeList) list).getList(); } return null; } }