/*
* Rapid Beans Framework: ReadonlyListCollectionCached.java
*
* Copyright (C) 2009 Martin Bluemel
*
* Creation Date: 03/31/2006
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copies of the GNU Lesser General Public License and the
* GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
package org.rapidbeans.core.common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeSet;
import org.rapidbeans.core.basic.PropertyCollection;
import org.rapidbeans.core.event.PropertyChangeEvent;
import org.rapidbeans.core.event.PropertyChangeListener;
import org.rapidbeans.core.type.TypePropertyCollection;
/**
* Encapsulates any collection to make it 1) immutable (read only). Hence if you
* try to use a changing method like add you get a RuntimeException 2) a List.
* If the collection is a List the reading List methods use the List directly.
* 3) If the collection is not a List the reading List methods work on an array
* (internally made out of it) in addition caches of the array list or array if
* they are used The object listens to any changes of this property and
* invalidates the caches if the collection has changed.
*
* Please note that you must release this collection explicitly in order to make
* it possible that they are garbage collected.
*
* @author Martin Bluemel
*/
@SuppressWarnings("unchecked")
public class ReadonlyListCollectionCached<T> extends ReadonlyListCollection<T> implements PropertyChangeListener {
/**
* cached array.
*/
private Object[] arrayCache = null;
/**
* cached array list.
*/
private ArrayList<T> arrayListCache = null;
/**
* the observed property.
*/
private PropertyCollection property = null;
/**
* if the property is sorted.
*/
private boolean propertyIsSorted = false;
/**
* @param prop
* the property
* @param col
* the collection to encapsulate.
*/
public ReadonlyListCollectionCached(final PropertyCollection prop, final Collection<T> col) {
super(col, prop.getType());
if (col instanceof ReadonlyListCollectionCached) {
this.setCollection(((ReadonlyListCollectionCached<T>) col).getCollection());
}
this.property = prop;
Class<?> colClass = ((TypePropertyCollection) prop.getType()).getCollectionClass();
if (colClass != null) {
if (colClass == TreeSet.class) {
this.propertyIsSorted = true;
}
}
this.property.getBean().addPropertyChangeListener(this);
}
/**
* It's important to release the object in order to dispose it for garbage
* collection.
*/
public void release() {
this.property.getBean().removePropertyChangeListener(this);
}
/**
* returns the collection converted to an array. Since an array is immutable
* this is not a problem. All users of to array must be aware that they will
* get a snapshot of the collection's current state.
*
* @return the collection converted to an array
*/
public Object[] toArray() {
if (this.arrayCache == null) {
this.arrayCache = this.getCollection().toArray();
}
return this.arrayCache;
}
/**
* returns the collection converted to an array list. Since an arry list is
* not immutable this method is private.
*
* @return the collection converted to an array
*/
private ArrayList<T> toArrayList() {
if (this.arrayListCache == null) {
this.arrayListCache = new ArrayList<T>(this.getCollection());
}
return this.arrayListCache;
}
/**
* returns the element at the specified position in the list. Caution:
* Collections that are no Lists will be converted to arrays which will
* cause performance problems in case a big collections.
*
* @param index
* the position
* @return the element at the specified position in the list.
*/
public T get(final int index) {
if (this.getCollection() instanceof List) {
return ((List<T>) this.getCollection()).get(index);
} else {
return (T) this.toArrayList().get(index);
}
}
/**
* returns the last index of the element with the given reference.
*
* @param o
* the object reference tha specifies the element to search for
* @return the last index of the element with the given reference or -1 if
* not found
*/
public int lastIndexOf(final Object o) {
if (this.getCollection() instanceof List) {
return ((List<Integer>) this.getCollection()).lastIndexOf(o);
} else {
return this.toArrayList().lastIndexOf(o);
}
}
/**
* The before property change event handle method to implement by every
* listener.
*
* @param e
* the property change event
*/
public void propertyChangePre(final PropertyChangeEvent e) {
// do nothing
}
/**
* Implementation of the PropertyChangeListener interface.
*
* @param e
* the event
*/
public void propertyChanged(final PropertyChangeEvent e) {
// invalidate the caches
if (e.getProperty() == this.property) {
switch (e.getType()) {
case addlink:
this.arrayCache = null;
if (this.arrayListCache != null) {
if (e.getLink() != null && (this.propertyIsSorted)) {
this.arrayListCache.add((T) e.getLink());
} else {
this.arrayListCache = null;
}
}
break;
case removelink:
this.arrayCache = null;
if (this.arrayListCache != null && (!this.propertyIsSorted)) {
if (e.getLink() != null) {
this.arrayListCache.remove(e.getLink());
} else {
this.arrayListCache = null;
}
}
break;
default:
this.arrayCache = null;
this.arrayListCache = null;
break;
}
}
}
}