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