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 * <p> 27 * This class is basically a stripped-down version of the {@code FilterIterator} class of Commons Collections 28 * </p> 29 */ 30 final class PrefixedKeysIterator implements Iterator<String> { 31 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, AbstractConfiguration.DELIMITER); 56 } 57 58 /** 59 * Creates a new instance of {@code PrefixedKeysIterator} and sets the wrapped iterator and the prefix as well as the delimiter for the preix for the 60 * 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 * Tests whether there are more elements in the iteration. 75 * 76 * @return whether there are more elements in the iteration. 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 * Sets 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 130 @Override 131 public String toString() { 132 // @formatter:off 133 return new StringBuilder() 134 .append("PrefixedKeysIterator [prefix=") 135 .append(prefix) 136 .append(", delimiter=") 137 .append(delimiter) 138 .append(", nextElement=") 139 .append(nextElement) 140 .append("]") 141 .toString(); 142 // @formatter:on 143 } 144 }