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