1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.configuration2.tree.xpath; 18 19 import org.apache.commons.configuration2.tree.NodeHandler; 20 import org.apache.commons.jxpath.ri.QName; 21 import org.apache.commons.jxpath.ri.model.NodeIterator; 22 import org.apache.commons.jxpath.ri.model.NodePointer; 23 import org.apache.commons.lang3.StringUtils; 24 25 /** 26 * <p> 27 * A base class for implementing iterators over configuration nodes. 28 * </p> 29 * <p> 30 * This class already provides common functionality for implementing the iteration process. Derived classes will 31 * implement specific behavior based on the concrete node type (child node or attribute node). 32 * </p> 33 * 34 * @since 1.3 35 * @param <T> the type of the nodes this iterator deals with 36 */ 37 abstract class AbstractConfigurationNodeIterator<T> implements NodeIterator { 38 /** Constant for the prefix separator. */ 39 private static final String PREFIX_SEPARATOR = ":"; 40 41 /** A format for constructing a node name with a namespace prefix. */ 42 private static final String FMT_NAMESPACE = "%s" + PREFIX_SEPARATOR + "%s"; 43 44 /** Stores the parent node pointer. */ 45 private final ConfigurationNodePointer<T> parent; 46 47 /** Stores the current position. */ 48 private int position; 49 50 /** Stores the start offset of the iterator. */ 51 private int startOffset; 52 53 /** Stores the reverse flag. */ 54 private final boolean reverse; 55 56 /** 57 * Creates a new instance of {@code ConfigurationNodeIteratorBase} and initializes it. 58 * 59 * @param parent the parent pointer 60 * @param reverse the reverse flag 61 */ 62 protected AbstractConfigurationNodeIterator(final ConfigurationNodePointer<T> parent, final boolean reverse) { 63 this.parent = parent; 64 this.reverse = reverse; 65 } 66 67 /** 68 * Gets the position of the iteration. 69 * 70 * @return the position 71 */ 72 @Override 73 public int getPosition() { 74 return position; 75 } 76 77 /** 78 * Sets the position of the iteration. 79 * 80 * @param pos the new position 81 * @return a flag if this is a valid position 82 */ 83 @Override 84 public boolean setPosition(final int pos) { 85 position = pos; 86 return pos >= 1 && pos <= getMaxPosition(); 87 } 88 89 /** 90 * Gets the current node pointer. 91 * 92 * @return the current pointer in this iteration 93 */ 94 @Override 95 public NodePointer getNodePointer() { 96 if (getPosition() < 1 && !setPosition(1)) { 97 return null; 98 } 99 100 return createNodePointer(positionToIndex(getPosition())); 101 } 102 103 /** 104 * Gets the parent node pointer. 105 * 106 * @return the parent node pointer 107 */ 108 protected ConfigurationNodePointer<T> getParent() { 109 return parent; 110 } 111 112 /** 113 * Gets the node handler for the managed nodes. This is a convenience method. 114 * 115 * @return the node handler 116 */ 117 protected NodeHandler<T> getNodeHandler() { 118 return getParent().getNodeHandler(); 119 } 120 121 /** 122 * Gets the start offset of the iteration. 123 * 124 * @return the start offset 125 */ 126 protected int getStartOffset() { 127 return startOffset; 128 } 129 130 /** 131 * Sets the start offset of the iteration. This is used when a start element was set. 132 * 133 * @param startOffset the start offset 134 */ 135 protected void setStartOffset(final int startOffset) { 136 this.startOffset = startOffset; 137 if (reverse) { 138 this.startOffset--; 139 } else { 140 this.startOffset++; 141 } 142 } 143 144 /** 145 * Gets the maximum position for this iterator. 146 * 147 * @return the maximum allowed position 148 */ 149 protected int getMaxPosition() { 150 return reverse ? getStartOffset() + 1 : size() - getStartOffset(); 151 } 152 153 /** 154 * Returns the index in the data list for the given position. This method also checks the reverse flag. 155 * 156 * @param pos the position (1-based) 157 * @return the corresponding list index 158 */ 159 protected int positionToIndex(final int pos) { 160 return (reverse ? 1 - pos : pos - 1) + getStartOffset(); 161 } 162 163 /** 164 * Creates the configuration node pointer for the current position. This method is called by {@code getNodePointer()}. 165 * Derived classes must create the correct pointer object. 166 * 167 * @param position the current position in the iteration 168 * @return the node pointer 169 */ 170 protected abstract NodePointer createNodePointer(int position); 171 172 /** 173 * Returns the number of elements in this iteration. 174 * 175 * @return the number of elements 176 */ 177 protected abstract int size(); 178 179 /** 180 * Generates a qualified name with a namespace prefix. 181 * 182 * @param prefix the prefix 183 * @param name the name (may be <b>null</b>) 184 * @return the qualified name 185 */ 186 protected static String prefixName(final String prefix, final String name) { 187 return String.format(FMT_NAMESPACE, prefix, StringUtils.defaultString(name)); 188 } 189 190 /** 191 * Returns the qualified name from the given {@code QName}. If the name has no namespace, result is the simple name. 192 * Otherwise, the namespace prefix is added. 193 * 194 * @param name the {@code QName} 195 * @return the qualified name 196 */ 197 protected static String qualifiedName(final QName name) { 198 return name.getPrefix() == null ? name.getName() : prefixName(name.getPrefix(), name.getName()); 199 } 200 }