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  
18  package org.apache.commons.configuration2;
19  
20  import java.util.Iterator;
21  import java.util.NoSuchElementException;
22  
23  /**
24   * * A specialized iterator implementation used by {@link AbstractConfiguration} to return an iteration over all keys
25   * starting with a specified prefix.
26   *
27   * <p>
28   * This class is basically a stripped-down version of the {@code FilterIterator} class of Commons Collections
29   * </p>
30   */
31  final class PrefixedKeysIterator implements Iterator<String> {
32      /** Stores the wrapped iterator. */
33      private final Iterator<String> iterator;
34  
35      /** Stores the prefix. */
36      private final String prefix;
37  
38      /** Stores the prefix delimiter. Default delimiter is "." */
39      private final String delimiter;
40  
41      /** Stores the next element in the iteration. */
42      private String nextElement;
43  
44      /** A flag whether the next element has been calculated. */
45      private boolean nextElementSet;
46  
47      /**
48       * Creates a new instance of {@code PrefixedKeysIterator} and sets the wrapped iterator and the prefix for the accepted
49       * keys.
50       *
51       * @param wrappedIterator the wrapped iterator
52       * @param keyPrefix the prefix of the allowed keys
53       */
54      public PrefixedKeysIterator(final Iterator<String> wrappedIterator, final String keyPrefix) {
55          this(wrappedIterator, keyPrefix, ".");
56      }
57  
58       /**
59       * Creates a new instance of {@code PrefixedKeysIterator} and sets the wrapped iterator and the prefix as well as
60       * the delimiter for the preix for the accepted keys.
61       *
62       * @param wrappedIterator the wrapped iterator
63       * @param keyPrefix the prefix of the allowed keys
64       * @param prefixDelimiter the prefix delimiter
65       * @since 2.10.0
66       */
67      public PrefixedKeysIterator(final Iterator<String> wrappedIterator, final String keyPrefix, final String prefixDelimiter) {
68          iterator = wrappedIterator;
69          prefix = keyPrefix;
70          delimiter = prefixDelimiter;
71      }
72  
73      /**
74       * Returns a flag whether there are more elements in the iteration.
75       *
76       * @return a flag if there is a next element
77       */
78      @Override
79      public boolean hasNext() {
80          return nextElementSet || setNextElement();
81      }
82  
83      /**
84       * Returns the next element in the iteration. This is the next key that matches the specified prefix.
85       *
86       * @return the next element in the iteration
87       * @throws NoSuchElementException if there is no next element
88       */
89      @Override
90      public String next() {
91          if (!nextElementSet && !setNextElement()) {
92              throw new NoSuchElementException();
93          }
94          nextElementSet = false;
95          return nextElement;
96      }
97  
98      /**
99       * Removes from the underlying collection of the base iterator the last element returned by this iterator. This method
100      * can only be called if {@code next()} was called, but not after {@code hasNext()}, because the {@code hasNext()} call
101      * changes the base iterator.
102      *
103      * @throws IllegalStateException if {@code hasNext()} has already been called.
104      */
105     @Override
106     public void remove() {
107         if (nextElementSet) {
108             throw new IllegalStateException("remove() cannot be called");
109         }
110         iterator.remove();
111     }
112 
113     /**
114      * Determines the next element in the iteration. The return value indicates whether such an element can be found.
115      *
116      * @return a flag whether a next element exists
117      */
118     private boolean setNextElement() {
119         while (iterator.hasNext()) {
120             final String key = iterator.next();
121             if (key.startsWith(prefix + delimiter) || key.equals(prefix)) {
122                 nextElement = key;
123                 nextElementSet = true;
124                 return true;
125             }
126         }
127         return false;
128     }
129 }