blob: 925f8652a42b89e5061e9394d7180b618d351b67 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.configuration2;
import java.util.Iterator;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
/**
* <p>A subset of another configuration. The new Configuration object contains
* every key from the parent Configuration that starts with prefix. The prefix
* is removed from the keys in the subset.</p>
* <p>It is usually not necessary to use this class directly. Instead the
* {@link Configuration#subset(String)} method should be used,
* which will return a correctly initialized instance.</p>
*
*/
public class SubsetConfiguration extends AbstractConfiguration
{
/** The parent configuration. */
protected Configuration parent;
/** The prefix used to select the properties. */
protected String prefix;
/** The prefix delimiter */
protected String delimiter;
/**
* Create a subset of the specified configuration
*
* @param parent The parent configuration (must not be <b>null</b>)
* @param prefix The prefix used to select the properties
* @throws IllegalArgumentException if the parent configuration is <b>null</b>
*/
public SubsetConfiguration(final Configuration parent, final String prefix)
{
this(parent, prefix, null);
}
/**
* Create a subset of the specified configuration
*
* @param parent The parent configuration (must not be <b>null</b>)
* @param prefix The prefix used to select the properties
* @param delimiter The prefix delimiter
* @throws IllegalArgumentException if the parent configuration is <b>null</b>
*/
public SubsetConfiguration(final Configuration parent, final String prefix, final String delimiter)
{
if (parent == null)
{
throw new IllegalArgumentException(
"Parent configuration must not be null!");
}
this.parent = parent;
this.prefix = prefix;
this.delimiter = delimiter;
initInterpolator();
}
/**
* Return the key in the parent configuration associated to the specified
* key in this subset.
*
* @param key The key in the subset.
* @return the key as to be used by the parent
*/
protected String getParentKey(final String key)
{
if ("".equals(key) || key == null)
{
return prefix;
}
return delimiter == null ? prefix + key : prefix + delimiter + key;
}
/**
* Return the key in the subset configuration associated to the specified
* key in the parent configuration.
*
* @param key The key in the parent configuration.
* @return the key in the context of this subset configuration
*/
protected String getChildKey(final String key)
{
if (!key.startsWith(prefix))
{
throw new IllegalArgumentException("The parent key '" + key + "' is not in the subset.");
}
String modifiedKey = null;
if (key.length() == prefix.length())
{
modifiedKey = "";
}
else
{
final int i = prefix.length() + (delimiter != null ? delimiter.length() : 0);
modifiedKey = key.substring(i);
}
return modifiedKey;
}
/**
* Return the parent configuration for this subset.
*
* @return the parent configuration
*/
public Configuration getParent()
{
return parent;
}
/**
* Return the prefix used to select the properties in the parent configuration.
*
* @return the prefix used by this subset
*/
public String getPrefix()
{
return prefix;
}
/**
* Set the prefix used to select the properties in the parent configuration.
*
* @param prefix the prefix
*/
public void setPrefix(final String prefix)
{
this.prefix = prefix;
}
@Override
public Configuration subset(final String prefix)
{
return parent.subset(getParentKey(prefix));
}
@Override
protected boolean isEmptyInternal()
{
return !getKeysInternal().hasNext();
}
@Override
protected boolean containsKeyInternal(final String key)
{
return parent.containsKey(getParentKey(key));
}
@Override
public void addPropertyDirect(final String key, final Object value)
{
parent.addProperty(getParentKey(key), value);
}
@Override
protected void clearPropertyDirect(final String key)
{
parent.clearProperty(getParentKey(key));
}
@Override
protected Object getPropertyInternal(final String key)
{
return parent.getProperty(getParentKey(key));
}
@Override
protected Iterator<String> getKeysInternal(final String prefix)
{
return new SubsetIterator(parent.getKeys(getParentKey(prefix)));
}
@Override
protected Iterator<String> getKeysInternal()
{
return new SubsetIterator(parent.getKeys(prefix));
}
/**
* {@inheritDoc}
*
* Change the behavior of the parent configuration if it supports this feature.
*/
@Override
public void setThrowExceptionOnMissing(final boolean throwExceptionOnMissing)
{
if (parent instanceof AbstractConfiguration)
{
((AbstractConfiguration) parent).setThrowExceptionOnMissing(throwExceptionOnMissing);
}
else
{
super.setThrowExceptionOnMissing(throwExceptionOnMissing);
}
}
/**
* {@inheritDoc}
*
* The subset inherits this feature from its parent if it supports this feature.
*/
@Override
public boolean isThrowExceptionOnMissing()
{
if (parent instanceof AbstractConfiguration)
{
return ((AbstractConfiguration) parent).isThrowExceptionOnMissing();
}
return super.isThrowExceptionOnMissing();
}
/**
* {@inheritDoc} If the parent configuration extends
* {@link AbstractConfiguration}, the list delimiter handler is obtained
* from there.
*/
@Override
public ListDelimiterHandler getListDelimiterHandler()
{
return parent instanceof AbstractConfiguration ? ((AbstractConfiguration) parent)
.getListDelimiterHandler() : super.getListDelimiterHandler();
}
/**
* {@inheritDoc} If the parent configuration extends
* {@link AbstractConfiguration}, the list delimiter handler is passed to
* the parent.
*/
@Override
public void setListDelimiterHandler(
final ListDelimiterHandler listDelimiterHandler)
{
if (parent instanceof AbstractConfiguration)
{
((AbstractConfiguration) parent)
.setListDelimiterHandler(listDelimiterHandler);
}
else
{
super.setListDelimiterHandler(listDelimiterHandler);
}
}
/**
* Initializes the {@code ConfigurationInterpolator} for this sub configuration.
* This is a standard {@code ConfigurationInterpolator} which also references
* the {@code ConfigurationInterpolator} of the parent configuration.
*/
private void initInterpolator()
{
getInterpolator().setParentInterpolator(getParent().getInterpolator());
}
/**
* A specialized iterator to be returned by the {@code getKeys()}
* methods. This implementation wraps an iterator from the parent
* configuration. The keys returned by this iterator are correspondingly
* transformed.
*/
private class SubsetIterator implements Iterator<String>
{
/** Stores the wrapped iterator. */
private final Iterator<String> parentIterator;
/**
* Creates a new instance of {@code SubsetIterator} and
* initializes it with the parent iterator.
*
* @param it the iterator of the parent configuration
*/
public SubsetIterator(final Iterator<String> it)
{
parentIterator = it;
}
/**
* Checks whether there are more elements. Delegates to the parent
* iterator.
*
* @return a flag whether there are more elements
*/
@Override
public boolean hasNext()
{
return parentIterator.hasNext();
}
/**
* Returns the next element in the iteration. This is the next key from
* the parent configuration, transformed to correspond to the point of
* view of this subset configuration.
*
* @return the next element
*/
@Override
public String next()
{
return getChildKey(parentIterator.next());
}
/**
* Removes the current element from the iteration. Delegates to the
* parent iterator.
*/
@Override
public void remove()
{
parentIterator.remove();
}
}
}