/******************************************************************************
* Copyright (c) 2016 Oracle
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Konstantin Komissarchik - initial implementation and ongoing maintenance
******************************************************************************/
package org.eclipse.sapphire;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* An implementation of {@link List} that is {@link Observable} using listeners.
*
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public final class ObservableList<E> extends AbstractList<E> implements Observable
{
private final List<E> base;
private ListenerContext listeners;
public ObservableList()
{
this( null );
}
public ObservableList( final List<E> base )
{
this.base = ( base == null ? new ArrayList<E>() : base );
}
@Override
public void attach( final Listener listener )
{
if( listener == null )
{
throw new IllegalArgumentException();
}
synchronized( this )
{
if( this.listeners == null )
{
this.listeners = new ListenerContext();
}
this.listeners.attach( listener );
}
}
@Override
public void detach( final Listener listener )
{
if( listener == null )
{
throw new IllegalArgumentException();
}
synchronized( this )
{
if( this.listeners != null )
{
this.listeners.detach( listener );
}
}
}
private void broadcast()
{
final ListenerContext listeners;
synchronized( this )
{
listeners = this.listeners;
}
if( listeners != null )
{
listeners.broadcast( new Event() );
}
}
@Override
public int size()
{
synchronized( this )
{
return this.base.size();
}
}
@Override
public boolean isEmpty()
{
synchronized( this )
{
return this.base.isEmpty();
}
}
@Override
public boolean contains( final Object obj )
{
synchronized( this )
{
return this.base.contains( obj );
}
}
@Override
public boolean containsAll( final Collection<?> collection )
{
synchronized( this )
{
return this.base.containsAll( collection );
}
}
@Override
public int indexOf( final Object obj )
{
synchronized( this )
{
return this.base.indexOf( obj );
}
}
@Override
public int lastIndexOf( final Object obj )
{
synchronized( this )
{
return this.base.lastIndexOf( obj );
}
}
@Override
public Iterator<E> iterator()
{
synchronized( this )
{
return new Iterator<E>()
{
private final Iterator<E> base = ObservableList.this.base.iterator();
public boolean hasNext()
{
synchronized( ObservableList.this )
{
return this.base.hasNext();
}
}
public E next()
{
synchronized( ObservableList.this )
{
return this.base.next();
}
}
public void remove()
{
synchronized( ObservableList.this )
{
this.base.remove();
broadcast();
}
}
};
}
}
@Override
public Object[] toArray()
{
synchronized( this )
{
return this.base.toArray();
}
}
@Override
public <T> T[] toArray( final T[] array )
{
synchronized( this )
{
return this.base.toArray( array );
}
}
@Override
public E get( final int index )
{
synchronized( this )
{
return this.base.get( index );
}
}
@Override
public E set( final int index, final E entry )
{
final E previous;
final boolean modified;
synchronized( this )
{
previous = this.base.set( index, entry );
modified = ( previous != entry );
}
if( modified )
{
broadcast();
}
return previous;
}
@Override
public boolean add( final E entry )
{
final boolean modified;
synchronized( this )
{
modified = this.base.add( entry );
}
if( modified )
{
broadcast();
}
return modified;
}
@Override
public void add( final int index, final E entry )
{
synchronized( this )
{
this.base.add( index, entry );
}
broadcast();
}
@Override
public boolean addAll( Collection<? extends E> collection )
{
final boolean modified;
synchronized( this )
{
modified = this.base.addAll( collection );
}
if( modified )
{
broadcast();
}
return modified;
}
@Override
public boolean addAll( final int index, final Collection<? extends E> collection )
{
final boolean modified;
synchronized( this )
{
modified = this.base.addAll( index, collection );
}
if( modified )
{
broadcast();
}
return modified;
}
@Override
public E remove( final int index )
{
final E removed;
synchronized( this )
{
removed = this.base.remove( index );
}
broadcast();
return removed;
}
@Override
public boolean remove( final Object obj )
{
final boolean modified;
synchronized( this )
{
modified = this.base.remove( obj );
}
if( modified )
{
broadcast();
}
return modified;
}
@Override
public boolean removeAll( final Collection<?> collection )
{
final boolean modified;
synchronized( this )
{
modified = this.base.removeAll( collection );
}
if( modified )
{
broadcast();
}
return modified;
}
@Override
public boolean retainAll( final Collection<?> collection )
{
final boolean modified;
synchronized( this )
{
modified = this.base.retainAll( collection );
}
if( modified )
{
broadcast();
}
return modified;
}
@Override
public void clear()
{
boolean broadcast = false;
synchronized( this )
{
if( ! this.base.isEmpty() )
{
broadcast = true;
}
this.base.clear();
}
if( broadcast )
{
broadcast();
}
}
}