/*
* 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.configuration;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
A simple class that supports creation of and iteration on complex
configuration keys.
For key creation the class works similar to a StringBuilder: There are several appendXXXX()
methods with which single parts of a key can be constructed. All these methods return a reference to the actual object so they can be written in a chain. When using this methods the exact syntax for keys need not be known.
This class also defines a specialized iterator for configuration keys.
With such an iterator a key can be tokenized into its single parts. For
each part it can be checked whether it has an associated index.
Author: Commons
Configuration team Version: $Id: ConfigurationKey.java 1231749 2012-01-15 20:48:56Z oheger $ Deprecated: Use DefaultConfigurationKey
instead. It is associated with a DefaultExpressionEngine
and thus can produce correct keys even if key separators have been changed.
/**
* <p>A simple class that supports creation of and iteration on complex
* configuration keys.</p>
*
* <p>For key creation the class works similar to a StringBuilder: There are
* several {@code appendXXXX()} methods with which single parts
* of a key can be constructed. All these methods return a reference to the
* actual object so they can be written in a chain. When using this methods
* the exact syntax for keys need not be known.</p>
*
* <p>This class also defines a specialized iterator for configuration keys.
* With such an iterator a key can be tokenized into its single parts. For
* each part it can be checked whether it has an associated index.</p>
*
* @author <a
* href="http://commons.apache.org/configuration/team-list.html">Commons
* Configuration team</a>
* @version $Id: ConfigurationKey.java 1231749 2012-01-15 20:48:56Z oheger $
* @deprecated Use {@link org.apache.commons.configuration.tree.DefaultConfigurationKey}
* instead. It is associated with a {@code DefaultExpressionEngine} and thus
* can produce correct keys even if key separators have been changed.
*/
@Deprecated
public class ConfigurationKey implements Serializable
{
Constant for a property delimiter./** Constant for a property delimiter.*/
public static final char PROPERTY_DELIMITER = '.';
Constant for an escaped delimiter. /** Constant for an escaped delimiter. */
public static final String ESCAPED_DELIMITER =
String.valueOf(PROPERTY_DELIMITER) + String.valueOf(PROPERTY_DELIMITER);
Constant for an attribute start marker./** Constant for an attribute start marker.*/
private static final String ATTRIBUTE_START = "[@";
Constant for an attribute end marker./** Constant for an attribute end marker.*/
private static final String ATTRIBUTE_END = "]";
Constant for an index start marker./** Constant for an index start marker.*/
private static final char INDEX_START = '(';
Constant for an index end marker./** Constant for an index end marker.*/
private static final char INDEX_END = ')';
Constant for the initial StringBuilder size./** Constant for the initial StringBuilder size.*/
private static final int INITIAL_SIZE = 32;
The serial version ID.
/**
* The serial version ID.
*/
private static final long serialVersionUID = -4299732083605277656L;
Holds a buffer with the so far created key./** Holds a buffer with the so far created key.*/
private StringBuilder keyBuffer;
Creates a new, empty instance of ConfigurationKey
. /**
* Creates a new, empty instance of {@code ConfigurationKey}.
*/
public ConfigurationKey()
{
keyBuffer = new StringBuilder(INITIAL_SIZE);
}
Creates a new instance of ConfigurationKey
and initializes it with the given key. Params: - key – the key as a string
/**
* Creates a new instance of {@code ConfigurationKey} and
* initializes it with the given key.
*
* @param key the key as a string
*/
public ConfigurationKey(String key)
{
keyBuffer = new StringBuilder(key);
removeTrailingDelimiter();
}
Appends the name of a property to this key. If necessary, a
property delimiter will be added.
Params: - property – the name of the property to be added
Returns: a reference to this object
/**
* Appends the name of a property to this key. If necessary, a
* property delimiter will be added.
*
* @param property the name of the property to be added
* @return a reference to this object
*/
public ConfigurationKey append(String property)
{
if (keyBuffer.length() > 0 && !hasDelimiter() && !isAttributeKey(property))
{
keyBuffer.append(PROPERTY_DELIMITER);
}
keyBuffer.append(property);
removeTrailingDelimiter();
return this;
}
Appends an index to this configuration key.
Params: - index – the index to be appended
Returns: a reference to this object
/**
* Appends an index to this configuration key.
*
* @param index the index to be appended
* @return a reference to this object
*/
public ConfigurationKey appendIndex(int index)
{
keyBuffer.append(INDEX_START).append(index);
keyBuffer.append(INDEX_END);
return this;
}
Appends an attribute to this configuration key.
Params: - attr – the name of the attribute to be appended
Returns: a reference to this object
/**
* Appends an attribute to this configuration key.
*
* @param attr the name of the attribute to be appended
* @return a reference to this object
*/
public ConfigurationKey appendAttribute(String attr)
{
keyBuffer.append(constructAttributeKey(attr));
return this;
}
Checks if this key is an attribute key.
Returns: a flag if this key is an attribute key
/**
* Checks if this key is an attribute key.
*
* @return a flag if this key is an attribute key
*/
public boolean isAttributeKey()
{
return isAttributeKey(keyBuffer.toString());
}
Checks if the passed in key is an attribute key. Such attribute keys
start and end with certain marker strings. In some cases they must be
treated slightly different.
Params: - key – the key (part) to be checked
Returns: a flag if this key is an attribute key
/**
* Checks if the passed in key is an attribute key. Such attribute keys
* start and end with certain marker strings. In some cases they must be
* treated slightly different.
*
* @param key the key (part) to be checked
* @return a flag if this key is an attribute key
*/
public static boolean isAttributeKey(String key)
{
return key != null
&& key.startsWith(ATTRIBUTE_START)
&& key.endsWith(ATTRIBUTE_END);
}
Decorates the given key so that it represents an attribute. Adds
special start and end markers.
Params: - key – the key to be decorated
Returns: the decorated attribute key
/**
* Decorates the given key so that it represents an attribute. Adds
* special start and end markers.
*
* @param key the key to be decorated
* @return the decorated attribute key
*/
public static String constructAttributeKey(String key)
{
StringBuilder buf = new StringBuilder();
buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END);
return buf.toString();
}
Extracts the name of the attribute from the given attribute key.
This method removes the attribute markers - if any - from the
specified key.
Params: - key – the attribute key
Returns: the name of the corresponding attribute
/**
* Extracts the name of the attribute from the given attribute key.
* This method removes the attribute markers - if any - from the
* specified key.
*
* @param key the attribute key
* @return the name of the corresponding attribute
*/
public static String attributeName(String key)
{
return isAttributeKey(key) ? removeAttributeMarkers(key) : key;
}
Helper method for removing attribute markers from a key.
Params: - key – the key
Returns: the key with removed attribute markers
/**
* Helper method for removing attribute markers from a key.
*
* @param key the key
* @return the key with removed attribute markers
*/
static String removeAttributeMarkers(String key)
{
return key.substring(ATTRIBUTE_START.length(), key.length() - ATTRIBUTE_END.length());
}
Helper method that checks if the actual buffer ends with a property
delimiter.
Returns: a flag if there is a trailing delimiter
/**
* Helper method that checks if the actual buffer ends with a property
* delimiter.
*
* @return a flag if there is a trailing delimiter
*/
private boolean hasDelimiter()
{
int count = 0;
for (int idx = keyBuffer.length() - 1; idx >= 0
&& keyBuffer.charAt(idx) == PROPERTY_DELIMITER; idx--)
{
count++;
}
return count % 2 != 0;
}
Removes a trailing delimiter if there is any.
/**
* Removes a trailing delimiter if there is any.
*/
private void removeTrailingDelimiter()
{
while (hasDelimiter())
{
keyBuffer.deleteCharAt(keyBuffer.length() - 1);
}
}
Returns a string representation of this object. This is the
configuration key as a plain string.
Returns: a string for this object
/**
* Returns a string representation of this object. This is the
* configuration key as a plain string.
*
* @return a string for this object
*/
@Override
public String toString()
{
return keyBuffer.toString();
}
Returns an iterator for iterating over the single components of
this configuration key.
Returns: an iterator for this key
/**
* Returns an iterator for iterating over the single components of
* this configuration key.
*
* @return an iterator for this key
*/
public KeyIterator iterator()
{
return new KeyIterator();
}
Returns the actual length of this configuration key.
Returns: the length of this key
/**
* Returns the actual length of this configuration key.
*
* @return the length of this key
*/
public int length()
{
return keyBuffer.length();
}
Sets the new length of this configuration key. With this method it is possible to truncate the key, e.g. to return to a state prior calling some append()
methods. The semantic is the same as the setLength()
method of StringBuilder
. Params: - len – the new length of the key
/**
* Sets the new length of this configuration key. With this method it is
* possible to truncate the key, e.g. to return to a state prior calling
* some {@code append()} methods. The semantic is the same as
* the {@code setLength()} method of {@code StringBuilder}.
*
* @param len the new length of the key
*/
public void setLength(int len)
{
keyBuffer.setLength(len);
}
Checks if two ConfigurationKey
objects are equal. The method can be called with strings or other objects, too. Params: - c – the object to compare
Returns: a flag if both objects are equal
/**
* Checks if two {@code ConfigurationKey} objects are equal. The
* method can be called with strings or other objects, too.
*
* @param c the object to compare
* @return a flag if both objects are equal
*/
@Override
public boolean equals(Object c)
{
if (c == null)
{
return false;
}
return keyBuffer.toString().equals(c.toString());
}
Returns the hash code for this object.
Returns: the hash code
/**
* Returns the hash code for this object.
*
* @return the hash code
*/
@Override
public int hashCode()
{
return String.valueOf(keyBuffer).hashCode();
}
Returns a configuration key object that is initialized with the part
of the key that is common to this key and the passed in key.
Params: - other – the other key
Returns: a key object with the common key part
/**
* Returns a configuration key object that is initialized with the part
* of the key that is common to this key and the passed in key.
*
* @param other the other key
* @return a key object with the common key part
*/
public ConfigurationKey commonKey(ConfigurationKey other)
{
if (other == null)
{
throw new IllegalArgumentException("Other key must no be null!");
}
ConfigurationKey result = new ConfigurationKey();
KeyIterator it1 = iterator();
KeyIterator it2 = other.iterator();
while (it1.hasNext() && it2.hasNext() && partsEqual(it1, it2))
{
if (it1.isAttribute())
{
result.appendAttribute(it1.currentKey());
}
else
{
result.append(it1.currentKey());
if (it1.hasIndex)
{
result.appendIndex(it1.getIndex());
}
}
}
return result;
}
Returns the "difference key" to a given key. This value is the part of the passed in key that differs from this key. There is the following relation: other = key.commonKey(other) + key.differenceKey(other)
for an arbitrary configuration key key
. Params: - other – the key for which the difference is to be calculated
Returns: the difference key
/**
* Returns the "difference key" to a given key. This value
* is the part of the passed in key that differs from this key. There is
* the following relation:
* {@code other = key.commonKey(other) + key.differenceKey(other)}
* for an arbitrary configuration key {@code key}.
*
* @param other the key for which the difference is to be calculated
* @return the difference key
*/
public ConfigurationKey differenceKey(ConfigurationKey other)
{
ConfigurationKey common = commonKey(other);
ConfigurationKey result = new ConfigurationKey();
if (common.length() < other.length())
{
String k = other.toString().substring(common.length());
// skip trailing delimiters
int i = 0;
while (i < k.length() && k.charAt(i) == PROPERTY_DELIMITER)
{
i++;
}
if (i < k.length())
{
result.append(k.substring(i));
}
}
return result;
}
Helper method for comparing two key parts.
Params: - it1 – the iterator with the first part
- it2 – the iterator with the second part
Returns: a flag if both parts are equal
/**
* Helper method for comparing two key parts.
*
* @param it1 the iterator with the first part
* @param it2 the iterator with the second part
* @return a flag if both parts are equal
*/
private static boolean partsEqual(KeyIterator it1, KeyIterator it2)
{
return it1.nextKey().equals(it2.nextKey())
&& it1.getIndex() == it2.getIndex()
&& it1.isAttribute() == it2.isAttribute();
}
A specialized iterator class for tokenizing a configuration key.
This class implements the normal iterator interface. In addition it
provides some specific methods for configuration keys.
/**
* A specialized iterator class for tokenizing a configuration key.
* This class implements the normal iterator interface. In addition it
* provides some specific methods for configuration keys.
*/
public class KeyIterator implements Iterator<Object>, Cloneable
{
Stores the current key name./** Stores the current key name.*/
private String current;
Stores the start index of the actual token./** Stores the start index of the actual token.*/
private int startIndex;
Stores the end index of the actual token./** Stores the end index of the actual token.*/
private int endIndex;
Stores the index of the actual property if there is one./** Stores the index of the actual property if there is one.*/
private int indexValue;
Stores a flag if the actual property has an index./** Stores a flag if the actual property has an index.*/
private boolean hasIndex;
Stores a flag if the actual property is an attribute./** Stores a flag if the actual property is an attribute.*/
private boolean attribute;
Helper method for determining the next indices.
Returns: the next key part
/**
* Helper method for determining the next indices.
*
* @return the next key part
*/
private String findNextIndices()
{
startIndex = endIndex;
// skip empty names
while (startIndex < keyBuffer.length()
&& keyBuffer.charAt(startIndex) == PROPERTY_DELIMITER)
{
startIndex++;
}
// Key ends with a delimiter?
if (startIndex >= keyBuffer.length())
{
endIndex = keyBuffer.length();
startIndex = endIndex - 1;
return keyBuffer.substring(startIndex, endIndex);
}
else
{
return nextKeyPart();
}
}
Helper method for extracting the next key part. Takes escaping of
delimiter characters into account.
Returns: the next key part
/**
* Helper method for extracting the next key part. Takes escaping of
* delimiter characters into account.
*
* @return the next key part
*/
private String nextKeyPart()
{
StringBuilder key = new StringBuilder(INITIAL_SIZE);
int idx = startIndex;
int endIdx = keyBuffer.toString().indexOf(ATTRIBUTE_START,
startIndex);
if (endIdx < 0 || endIdx == startIndex)
{
endIdx = keyBuffer.length();
}
boolean found = false;
while (!found && idx < endIdx)
{
char c = keyBuffer.charAt(idx);
if (c == PROPERTY_DELIMITER)
{
// a duplicated delimiter means escaping
if (idx == endIdx - 1
|| keyBuffer.charAt(idx + 1) != PROPERTY_DELIMITER)
{
found = true;
}
else
{
idx++;
}
}
if (!found)
{
key.append(c);
idx++;
}
}
endIndex = idx;
return key.toString();
}
Returns the next key part of this configuration key. This is a short form of nextKey(false)
. Returns: the next key part
/**
* Returns the next key part of this configuration key. This is a short
* form of {@code nextKey(false)}.
*
* @return the next key part
*/
public String nextKey()
{
return nextKey(false);
}
Returns the next key part of this configuration key. The boolean
parameter indicates wheter a decorated key should be returned. This
affects only attribute keys: if the parameter is false, the
attribute markers are stripped from the key; if it is true,
they remain.
Params: - decorated – a flag if the decorated key is to be returned
Returns: the next key part
/**
* Returns the next key part of this configuration key. The boolean
* parameter indicates wheter a decorated key should be returned. This
* affects only attribute keys: if the parameter is <b>false</b>, the
* attribute markers are stripped from the key; if it is <b>true</b>,
* they remain.
*
* @param decorated a flag if the decorated key is to be returned
* @return the next key part
*/
public String nextKey(boolean decorated)
{
if (!hasNext())
{
throw new NoSuchElementException("No more key parts!");
}
hasIndex = false;
indexValue = -1;
String key = findNextIndices();
current = key;
hasIndex = checkIndex(key);
attribute = checkAttribute(current);
return currentKey(decorated);
}
Helper method for checking if the passed key is an attribute.
If this is the case, the internal fields will be set.
Params: - key – the key to be checked
Returns: a flag if the key is an attribute
/**
* Helper method for checking if the passed key is an attribute.
* If this is the case, the internal fields will be set.
*
* @param key the key to be checked
* @return a flag if the key is an attribute
*/
private boolean checkAttribute(String key)
{
if (isAttributeKey(key))
{
current = removeAttributeMarkers(key);
return true;
}
else
{
return false;
}
}
Helper method for checking if the passed key contains an index.
If this is the case, internal fields will be set.
Params: - key – the key to be checked
Returns: a flag if an index is defined
/**
* Helper method for checking if the passed key contains an index.
* If this is the case, internal fields will be set.
*
* @param key the key to be checked
* @return a flag if an index is defined
*/
private boolean checkIndex(String key)
{
boolean result = false;
int idx = key.lastIndexOf(INDEX_START);
if (idx > 0)
{
int endidx = key.indexOf(INDEX_END, idx);
if (endidx > idx + 1)
{
indexValue = Integer.parseInt(key.substring(idx + 1, endidx));
current = key.substring(0, idx);
result = true;
}
}
return result;
}
Checks if there is a next element.
Returns: a flag if there is a next element
/**
* Checks if there is a next element.
*
* @return a flag if there is a next element
*/
public boolean hasNext()
{
return endIndex < keyBuffer.length();
}
Returns the next object in the iteration.
Returns: the next object
/**
* Returns the next object in the iteration.
*
* @return the next object
*/
public Object next()
{
return nextKey();
}
Removes the current object in the iteration. This method is not
supported by this iterator type, so an exception is thrown.
/**
* Removes the current object in the iteration. This method is not
* supported by this iterator type, so an exception is thrown.
*/
public void remove()
{
throw new UnsupportedOperationException("Remove not supported!");
}
Returns the current key of the iteration (without skipping to the next element). This is the same key the previous next()
call had returned. (Short form of currentKey(false)
. Returns: the current key
/**
* Returns the current key of the iteration (without skipping to the
* next element). This is the same key the previous {@code next()}
* call had returned. (Short form of {@code currentKey(false)}.
*
* @return the current key
*/
public String currentKey()
{
return currentKey(false);
}
Returns the current key of the iteration (without skipping to the
next element). The boolean parameter indicates wheter a decorated
key should be returned. This affects only attribute keys: if the
parameter is false, the attribute markers are stripped from
the key; if it is true, they remain.
Params: - decorated – a flag if the decorated key is to be returned
Returns: the current key
/**
* Returns the current key of the iteration (without skipping to the
* next element). The boolean parameter indicates wheter a decorated
* key should be returned. This affects only attribute keys: if the
* parameter is <b>false</b>, the attribute markers are stripped from
* the key; if it is <b>true</b>, they remain.
*
* @param decorated a flag if the decorated key is to be returned
* @return the current key
*/
public String currentKey(boolean decorated)
{
return (decorated && isAttribute()) ? constructAttributeKey(current) : current;
}
Returns a flag if the current key is an attribute. This method can be called after next()
. Returns: a flag if the current key is an attribute
/**
* Returns a flag if the current key is an attribute. This method can
* be called after {@code next()}.
*
* @return a flag if the current key is an attribute
*/
public boolean isAttribute()
{
return attribute;
}
Returns the index value of the current key. If the current key does not have an index, return value is -1. This method can be called after next()
. Returns: the index value of the current key
/**
* Returns the index value of the current key. If the current key does
* not have an index, return value is -1. This method can be called
* after {@code next()}.
*
* @return the index value of the current key
*/
public int getIndex()
{
return indexValue;
}
Returns a flag if the current key has an associated index. This method can be called after next()
. Returns: a flag if the current key has an index
/**
* Returns a flag if the current key has an associated index.
* This method can be called after {@code next()}.
*
* @return a flag if the current key has an index
*/
public boolean hasIndex()
{
return hasIndex;
}
Creates a clone of this object.
Returns: a clone of this object
/**
* Creates a clone of this object.
*
* @return a clone of this object
*/
@Override
public Object clone()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException cex)
{
// should not happen
return null;
}
}
}
}