package fr.lteconsulting.hexa.databinding.gwt.watchablecollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import fr.lteconsulting.hexa.client.tools.Action1;
import fr.lteconsulting.hexa.databinding.watchablecollection.Change;
import fr.lteconsulting.hexa.databinding.watchablecollection.ChangeType;
/**
* A Watchable List. Notifications are grouped and
* are deferred through Scheduler.scheduleDeferred()
* method.
*
* @author Arnaud
*
* @param <T>
*/
public class WatchableCollectionDeferred<T> implements List<T>
{
private final List<T> list;
private boolean scheduled;
private List<Change> scheduledChanges = new ArrayList<>();
private List<Action1<List<Change>>> callbacks = new ArrayList<>();
public WatchableCollectionDeferred()
{
this( new ArrayList<T>() );
}
public WatchableCollectionDeferred( List<T> list )
{
this.list = list;
}
public void addCallback( Action1<List<Change>> callback )
{
callbacks.add( callback );
}
public void addCallbackAndSendAll( Action1<List<Change>> callback )
{
callbacks.add( callback );
callback.exec( Change.ForItems( ChangeType.ADD, list, 0 ) );
}
public void removeCallback( Action1<List<Change>> callback )
{
callbacks.remove( callback );
}
private void scheduleChange( Change change )
{
scheduledChanges.add( change );
if( ! scheduled )
{
Scheduler.get().scheduleDeferred( command );
scheduled = true;
}
}
private void scheduleChanges( Collection<Change> changes )
{
scheduledChanges.addAll( changes );
if( ! scheduled )
{
Scheduler.get().scheduleDeferred( command );
scheduled = true;
}
}
private ScheduledCommand command = new ScheduledCommand() {
@Override
public void execute()
{
scheduled = false;
for( Action1<List<Change>> callback : callbacks )
callback.exec( scheduledChanges );
scheduledChanges.clear();
}
};
public void add(int arg0, T arg1)
{
list.add(arg0, arg1);
scheduleChange( new Change( ChangeType.ADD, arg1, arg0 ) );
}
public boolean add(T arg0)
{
boolean res = list.add(arg0);
scheduleChange( new Change( ChangeType.ADD, arg0, list.size()-1 ) );
return res;
}
public boolean addAll(Collection<? extends T> arg0) {
int startIndex = list.size();
boolean res = list.addAll(arg0);
scheduleChanges( Change.ForItems( ChangeType.ADD, arg0, startIndex ) );
return res;
}
public boolean addAll(int arg0, Collection<? extends T> arg1) {
boolean res = list.addAll(arg0, arg1);
scheduleChanges( Change.ForItems( ChangeType.ADD, arg1, arg0 ) );
return res;
}
public void clear() {
Collection<Change> changes = Change.ForItems( ChangeType.REMOVE, list, 0 );
list.clear();
scheduleChanges( changes );
}
public boolean contains(Object arg0) {
return list.contains(arg0);
}
public boolean containsAll(Collection<?> arg0) {
return list.containsAll(arg0);
}
public boolean equals(Object arg0) {
return list.equals(arg0);
}
public T get(int arg0) {
return list.get(arg0);
}
public int hashCode() {
return list.hashCode();
}
public int indexOf(Object arg0) {
return list.indexOf(arg0);
}
public boolean isEmpty() {
return list.isEmpty();
}
public Iterator<T> iterator() {
return list.iterator();
}
public int lastIndexOf(Object arg0) {
return list.lastIndexOf(arg0);
}
public ListIterator<T> listIterator() {
return list.listIterator();
}
public ListIterator<T> listIterator(int arg0) {
return list.listIterator(arg0);
}
public T remove(int arg0) {
T res = list.remove(arg0);
scheduleChange( new Change( ChangeType.REMOVE, res, arg0 ) );
return res;
}
public boolean remove(Object arg0) {
int index = list.indexOf( arg0 );
boolean res = list.remove(arg0);
scheduleChange( new Change( ChangeType.REMOVE, arg0, index ) );
return res;
}
public boolean removeAll(Collection<?> arg0) {
assert false : "This implementation is bugged";
boolean res = list.removeAll(arg0);
scheduleChanges( Change.ForItems( ChangeType.REMOVE, arg0, 0 ) );
return res;
}
public boolean retainAll(Collection<?> c) {
throw new RuntimeException();
}
public T set(int index, T element) {
if(list.size()>index)
scheduleChange( new Change( ChangeType.REMOVE, list.get(index), index ) );
scheduleChange( new Change( ChangeType.ADD, element, index ) );
return list.set(index, element);
}
public int size() {
return list.size();
}
public List<T> subList(int fromIndex, int toIndex) {
return list.subList(fromIndex, toIndex);
}
public Object[] toArray() {
return list.toArray();
}
@SuppressWarnings( "hiding" )
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}