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 * https://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 * @param <T> the type of the nodes this iterator deals with
35 * @since 1.3
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 /**
45 * Generates a qualified name with a namespace prefix.
46 *
47 * @param prefix the prefix
48 * @param name the name (may be <strong>null</strong>)
49 * @return the qualified name
50 */
51 protected static String prefixName(final String prefix, final String name) {
52 return String.format(FMT_NAMESPACE, prefix, StringUtils.defaultString(name));
53 }
54
55 /**
56 * Returns the qualified name from the given {@code QName}. If the name has no namespace, result is the simple name.
57 * Otherwise, the namespace prefix is added.
58 *
59 * @param qName the {@code QName}
60 * @return the qualified name
61 */
62 protected static String qualifiedName(final QName qName) {
63 return qName.getPrefix() == null ? qName.getName() : prefixName(qName.getPrefix(), qName.getName());
64 }
65
66 /** Stores the parent node pointer. */
67 private final ConfigurationNodePointer<T> parent;
68
69 /** Stores the current position. */
70 private int position;
71
72 /** Stores the start offset of the iterator. */
73 private int startOffset;
74
75 /** Stores the reverse flag. */
76 private final boolean reverse;
77
78 /**
79 * Creates a new instance of {@code ConfigurationNodeIteratorBase} and initializes it.
80 *
81 * @param parent the parent pointer
82 * @param reverse the reverse flag
83 */
84 protected AbstractConfigurationNodeIterator(final ConfigurationNodePointer<T> parent, final boolean reverse) {
85 this.parent = parent;
86 this.reverse = reverse;
87 }
88
89 /**
90 * Creates the configuration node pointer for the current position. This method is called by {@code getNodePointer()}.
91 * Derived classes must create the correct pointer object.
92 *
93 * @param position the current position in the iteration
94 * @return the node pointer
95 */
96 protected abstract NodePointer createNodePointer(int position);
97
98 /**
99 * Gets the maximum position for this iterator.
100 *
101 * @return the maximum allowed position
102 */
103 protected int getMaxPosition() {
104 return reverse ? getStartOffset() + 1 : size() - getStartOffset();
105 }
106
107 /**
108 * Gets the node handler for the managed nodes. This is a convenience method.
109 *
110 * @return the node handler
111 */
112 protected NodeHandler<T> getNodeHandler() {
113 return getParent().getNodeHandler();
114 }
115
116 /**
117 * Gets the current node pointer.
118 *
119 * @return the current pointer in this iteration
120 */
121 @Override
122 public NodePointer getNodePointer() {
123 if (getPosition() < 1 && !setPosition(1)) {
124 return null;
125 }
126
127 return createNodePointer(positionToIndex(getPosition()));
128 }
129
130 /**
131 * Gets the parent node pointer.
132 *
133 * @return the parent node pointer
134 */
135 protected ConfigurationNodePointer<T> getParent() {
136 return parent;
137 }
138
139 /**
140 * Gets the position of the iteration.
141 *
142 * @return the position
143 */
144 @Override
145 public int getPosition() {
146 return position;
147 }
148
149 /**
150 * Gets the start offset of the iteration.
151 *
152 * @return the start offset
153 */
154 protected int getStartOffset() {
155 return startOffset;
156 }
157
158 /**
159 * Returns the index in the data list for the given position. This method also checks the reverse flag.
160 *
161 * @param pos the position (1-based)
162 * @return the corresponding list index
163 */
164 protected int positionToIndex(final int pos) {
165 return (reverse ? 1 - pos : pos - 1) + getStartOffset();
166 }
167
168 /**
169 * Sets the position of the iteration.
170 *
171 * @param pos the new position
172 * @return a flag if this is a valid position
173 */
174 @Override
175 public boolean setPosition(final int pos) {
176 position = pos;
177 return pos >= 1 && pos <= getMaxPosition();
178 }
179
180 /**
181 * Sets the start offset of the iteration. This is used when a start element was set.
182 *
183 * @param startOffset the start offset
184 */
185 protected void setStartOffset(final int startOffset) {
186 this.startOffset = startOffset;
187 if (reverse) {
188 this.startOffset--;
189 } else {
190 this.startOffset++;
191 }
192 }
193
194 /**
195 * Returns the number of elements in this iteration.
196 *
197 * @return the number of elements
198 */
199 protected abstract int size();
200 }