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      /** 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 }