| ProtectedList.java |
/*
* $Id: ProtectedList.java,v 1.23 2010/10/25 20:36:51 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common.collections;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import org.xins.common.MandatoryArgumentChecker;
/**
* Modifiable <code>List</code> implementaton that can be protected from
* unauthorized changes.
*
* <p>A secret key must be passed when constructing a
* <code>ProtectedList</code> instance. All modification methods on
* this object then require this same secret key to be passed, otherwise they
* fail with an {@link IncorrectSecretKeyException}.
*
* <p>Note that the secret key equality is always checked before the other
* preconditions. This means that if the secret key is incorrect, then the
* other preconditions will not even be checked. For example, if <code>
* {@link #remove(Object,int) remove}(null, -1)</code> is called, then an
* {@link IncorrectSecretKeyException} is thrown for the mismatching secret
* key, and not an {@link IndexOutOfBoundsException}, for the negative index.
*
* @version $Revision: 1.23 $ $Date: 2010/10/25 20:36:51 $
* @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*
* @since XINS 1.1.0
*/
public final class ProtectedList extends AbstractList implements Cloneable {
/**
* The secret key.
*/
private final Object _secretKey;
/**
* The list containing the objects.
*/
private ArrayList _list;
/**
* Constructs an empty <code>ProtectedList</code> with the specified
* initial capacity.
*
* @param secretKey
* the secret key that must be passed to the modification methods in
* order to be authorized to modify this collection.
*
* @param initialCapacity
* the initial capacity, cannot be a negative number.
*
* @throws IllegalArgumentException
* if <code>secretKey == null || initialCapacity < 0</code>.
*
* @since XINS 1.2.0
*/
public ProtectedList(Object secretKey, int initialCapacity)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("secretKey", secretKey);
// Store secret key and construct internal list
_secretKey = secretKey;
_list = new ArrayList(initialCapacity);
}
/**
* Constructs an empty <code>ProtectedList</code>.
*
* @param secretKey
* the secret key that must be passed to the modification methods in
* order to be authorized to modify this collection, cannot be
* <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>secretKey == null</code>.
*/
public ProtectedList(Object secretKey)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("secretKey", secretKey);
// Store secret key and construct internal list
_secretKey = secretKey;
_list = new ArrayList();
}
/**
* Constructs a new <code>ProtectedList</code> containing the elements of
* the specified collection, in the order they are returned by the
* collection's iterator.
*
* @param secretKey
* the secret key that must be passed to the modification methods in
* order to be authorized to modify this collection, cannot be
* <code>null</code>.
*
* @param c
* the collection whose elements are to be placed into this list, cannot
* be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>secretKey == null || c == null</code>.
*
* @since XINS 1.2.0
*/
public ProtectedList(Object secretKey, Collection c)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("secretKey", secretKey, "c", c);
// Store secret key and construct internal list
_secretKey = secretKey;
_list = new ArrayList(c);
}
/**
* Verifies that the specified object matches the secret key. If not, an
* exception is thrown.
*
* @param secretKey
* the secret key, must be identity-equal to the secret key passed to
* the constructor, cannot be <code>null</code>.
*
* @throws IncorrectSecretKeyException
* if <code>secretKey</code> does not match the secret key passed to the
* constructor.
*/
private void checkSecretKey(Object secretKey)
throws IncorrectSecretKeyException {
if (secretKey != _secretKey) {
throw new IncorrectSecretKeyException();
}
}
/**
* Returns the element at the specified position in this list.
*
* @param index
* the index of the element to return, must be >= 0 and <
* {@link #size()}.
*
* @return
* the element at the specified position in this list, can be
* <code>null</code>.
*
* @throws IndexOutOfBoundsException
* if <code>index < 0
* || index >= {@link #size()}</code>.
*/
public Object get(int index)
throws IndexOutOfBoundsException {
return _list.get(index);
}
/**
* Returns the number of elements in this list. If this list contains more
* than {@link Integer#MAX_VALUE} elements, then {@link Integer#MAX_VALUE}
* is returned.
*
* @return
* the size of this list, maximized to {@link Integer#MAX_VALUE}.
*/
public int size() {
return _list.size();
}
/**
* Adds the specified element to the list.
*
* <p>The correct secret key must be passed. If it is incorrect, then an
* {@link IncorrectSecretKeyException} is thrown. Note that an identity
* check is done, <em>not</em> an equality check. So
* the {@link Object#equals(Object)} method is not used, but the
* <code>==</code> operator is.
*
* @param secretKey
* the secret key, must be identity-equal to the secret key passed to
* the constructor, cannot be <code>null</code>.
*
* @param element
* the element to add to the list, can be <code>null</code>.
*
* @throws IncorrectSecretKeyException
* if <code>secretKey</code> does not match the secret key passed to the
* constructor.
*/
public void add(Object secretKey, Object element)
throws IncorrectSecretKeyException {
// Check preconditions
checkSecretKey(secretKey);
// Store the value
_list.add(element);
}
/**
* Removes the specified element.
*
* <p>The correct secret key must be passed. If it is incorrect, then an
* {@link IncorrectSecretKeyException} is thrown. Note that an identity
* check is done, <em>not</em> an equality check. So
* the {@link Object#equals(Object)} method is not used, but the
* <code>==</code> operator is.
*
* @param secretKey
* the secret key, must be identity-equal to the secret key passed to
* the constructor, cannot be <code>null</code>.
*
* @param index
* the position of the element to remove, must be >= 0 and <
* {@link #size()}.
*
* @throws IncorrectSecretKeyException
* if <code>secretKey</code> does not match the secret key passed to the
* constructor.
*
* @throws IndexOutOfBoundsException
* if <code>index < 0
* || index >= {@link #size()}</code>.
*/
public void remove(Object secretKey, int index)
throws IncorrectSecretKeyException, IndexOutOfBoundsException {
// Check preconditions
checkSecretKey(secretKey);
// Remove the element
_list.remove(index);
}
/**
* Removes the specified element.
*
* <p>The correct secret key must be passed. If it is incorrect, then an
* {@link IncorrectSecretKeyException} is thrown. Note that an identity
* check is done, <em>not</em> an equality check. So
* the {@link Object#equals(Object)} method is not used, but the
* <code>==</code> operator is.
*
* @param secretKey
* the secret key, must be identity-equal to the secret key passed to
* the constructor, cannot be <code>null</code>.
*
* @param element
* the element to remove.
*
* @throws IncorrectSecretKeyException
* if <code>secretKey</code> does not match the secret key passed to the
* constructor.
*/
public void remove(Object secretKey, Object element)
throws IncorrectSecretKeyException {
// Check preconditions
checkSecretKey(secretKey);
// Remove the element
_list.remove(element);
}
/**
* Clones this object. The returned object will be a new
* <code>ProtectedList</code> instance with the same secret key and the
* same elements.
*
* @return
* a new clone of this object, never <code>null</code>.
*/
public Object clone() {
// Use Object.clone() to create a shallow clone
ProtectedList clone = null;
try {
clone = (ProtectedList) super.clone();
} catch (CloneNotSupportedException e) {
// should never happen
}
// For a deep clone, duplicate the list as well
clone._list = (ArrayList) _list.clone();
return clone;
}
}