/*
Copyright (c) 2012 LinkedIn Corp.
Licensed 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 com.linkedin.data.collections;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Checked List.
* <p>
*
* It enables a {@link ListChecker} to be added to check
* keys and values being stored into the {@link CheckedList}.
* <p>
*
* The underlying list implementation is {@link ArrayList}. It delegates
* list operations to the underlying [@link ArrayList} associated
* with this {@link CheckedList}.
* <p>
*
* A {@link CheckedList} may be marked read-only to disable mutations,
* and to avoid unintentional changes. It may also be invalidated to
* release its reference and decrease the reference count on the underlying
* {@link ArrayList}.
* <P>
*
* @author slim
*/
public class CheckedList<E> extends AbstractList<E> implements CommonList<E>, Cloneable
{
/**
* Construct an empty list.
*/
public CheckedList()
{
_checker = null;
_list = new InternalList<E>();
}
/**
* Construct a new list with elements provided by the specified list.
*
* @param list provides the elements to be added to the new list.
*/
public CheckedList(List<? extends E> list)
{
_checker = null;
checkAll(list);
_list = new InternalList<E>(list);
}
/**
* Construct a new list with the specified initial capacity.
*
* @param initialCapacity provides the initial capacity.
*/
public CheckedList(int initialCapacity)
{
_checker = null;
_list = new InternalList<E>(initialCapacity);
}
/**
* Construct an empty list with the specified {@link ListChecker}.
*
* @param checker provides the {@link ListChecker}.
*/
public CheckedList(ListChecker<E> checker)
{
_checker = checker;
_list = new InternalList<E>();
}
/**
* Construct a new list with elements provided by the specified list and
* the specified {@link ListChecker}.
*
* @param list provides the elements to be added to the new list.
* @param checker provides the {@link ListChecker}.
*/
public CheckedList(List<? extends E> list, ListChecker<E> checker)
{
_checker = checker;
checkAll(list);
_list = new InternalList<E>(list);
}
/**
* Construct a new list with the specified initial capacity and
* specified {@link ListChecker}.
*
* @param initialCapacity provides the initial capacity.
* @param checker provides the {@link ListChecker}.
*/
public CheckedList(int initialCapacity, ListChecker<E> checker)
{
_checker = checker;
_list = new InternalList<E>(initialCapacity);
}
@Override
public boolean add(E e)
{
check(e);
checkMutability();
return _list.add(e);
}
@Override
public void add(int index, E element)
{
check(element);
checkMutability();
_list.add(index, element);
}
@Override
public boolean addAll(Collection<? extends E> c)
{
checkAll(c);
checkMutability();
return _list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends E> c)
{
checkAll(c);
checkMutability();
return _list.addAll(index, c);
}
@Override
public void clear()
{
checkMutability();
_list.clear();
}
@Override
@SuppressWarnings("unchecked")
public CheckedList<E> clone() throws CloneNotSupportedException
{
CheckedList<E> o = (CheckedList<E>) super.clone();
o._list = (InternalList<E>) _list.clone();
o._readOnly = false;
return o;
}
@Override
public boolean contains(Object o)
{
return _list.contains(o);
}
@Override
public boolean containsAll(Collection<?> c)
{
return _list.containsAll(c);
}
@Override
public boolean equals(Object object)
{
return _list.equals(object);
}
@Override
public E get(int index)
{
return _list.get(index);
}
@Override
public int hashCode()
{
return _list.hashCode();
}
@Override
public int indexOf(Object o)
{
return _list.indexOf(o);
}
@Override
public boolean isEmpty()
{
return _list.isEmpty();
}
@Override
public int lastIndexOf(Object o)
{
return _list.lastIndexOf(o);
}
@Override
public E remove(int index)
{
checkMutability();
return _list.remove(index);
}
@Override
public boolean remove(Object o)
{
checkMutability();
return _list.remove(o);
}
@Override
public boolean removeAll(Collection<?> c)
{
checkMutability();
return _list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c)
{
checkMutability();
return _list.retainAll(c);
}
@Override
public void removeRange(int fromIndex, int toIndex)
{
checkMutability();
_list.removeRange(fromIndex, toIndex);
}
@Override
public E set(int index, E element)
{
check(element);
checkMutability();
return _list.set(index, element);
}
@Override
public int size()
{
return _list.size();
}
@Override
public Object[] toArray()
{
return _list.toArray();
}
@Override
public <T> T[] toArray(T[] a)
{
return _list.toArray(a);
}
@Override
public String toString()
{
return _list.toString();
}
@Override
public boolean isReadOnly()
{
return _readOnly;
}
@Override
public void setReadOnly()
{
_readOnly = true;
}
@Override
public void invalidate()
{
_list = null;
}
/**
* Add that does not invoke checker but does check for read-only, use with caution.
*
* This method skips all value checks.
*
* @param element provides the element to be added to the list.
* @return true.
* @throws UnsupportedOperationException if the list is read-only.
*/
protected boolean addWithoutChecking(E element)
{
checkMutability();
return _list.add(element);
}
/**
* Set without checking, use with caution.
*
* This method skips all checks.
*
* @param index of the element to replace.
* @param element to be stored at the specified position.
* @return the element previously at the specified position.
*/
protected E setWithoutChecking(int index, E element)
{
checkMutability();
return _list.set(index, element);
}
boolean addWithAssertChecking(E element)
{
assert(assertCheck(element)) : "Check is failed";
return addWithoutChecking(element);
}
E setWithAssertChecking(int index, E element)
{
assert(assertCheck(element)) : "Check is failed";
return setWithoutChecking(index, element);
}
private final void checkMutability()
{
if (_readOnly)
{
throw new UnsupportedOperationException("Cannot mutate a read-only list");
}
}
private final void check(E e)
{
if (_checker != null)
{
_checker.check(this, e);
}
}
private final void checkAll(Collection<? extends E> c)
{
if (_checker != null)
{
for (E e : c)
{
_checker.check(this, e);
}
}
}
private boolean assertCheck(E e)
{
try
{
check(e);
return true;
}
catch (IllegalArgumentException ex)
{
return false;
}
}
/**
* Unit test use only.
*
* @return underlying map.
*/
protected final List<E> getObject()
{
return _list;
}
@SuppressWarnings("serial")
private static class InternalList<E> extends ArrayList<E>
{
public InternalList()
{
}
public InternalList(List<? extends E> l)
{
super(l);
}
public InternalList(int initialCapacity)
{
super(initialCapacity);
}
@Override
public void removeRange(int fromIndex, int toIndex)
{
super.removeRange(fromIndex, toIndex);
}
}
protected ListChecker<E> _checker;
private boolean _readOnly = false;
private InternalList<E> _list;
}