blob: 4f4d604f584a50898288188cc435e861e29a2e52 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013 Cedric Dumoulin.
*
*
* 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:
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.layers.stackmodel.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.ForwardingList;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
/**
* An observable list notifying of events on the delegated list.
* This view allows to observe a provided list.
* The following events are notified:
* <ul>
* <li>elements added</li>
* <li>elements removed</li>
* <li></li>
* <li></li>
* </ul>
*
* @author cedric dumoulin
*
*/
public class ObservableListView<E> extends ForwardingList<E> {
protected List<E> delegate;
protected EventBus eventBus = new EventBus(ObservableListView.class.getName());
/**
* Construct an observable list with a {@link ArrayList} as delegate.
* Constructor.
*
*/
public ObservableListView() {
this(new ArrayList<E>());
}
/**
* Constructor.
* Build an observable list based on the provided list.
*
* @param delegate
*/
public ObservableListView(List<E> delegate) {
this.delegate = delegate;
}
@Override
protected List<E> delegate() {
return delegate;
}
/**
* @return the eventBus
*/
public EventBus getEventBus() {
return eventBus;
}
@Override
public boolean add(E element) {
boolean isModified = delegate().add(element);
if (isModified) {
eventBus.post(new ObservableListEvent(element));
}
return isModified;
}
@Override
public boolean addAll(Collection<? extends E> collection) {
boolean isModified = delegate().addAll(collection);
if (isModified) {
eventBus.post(new ObservableListEvent(collection));
}
return isModified;
}
@Override
public void add(int index, E element) {
delegate().add(index, element);
eventBus.post(new ObservableListEvent(element));
}
@Override
public boolean addAll(int index, Collection<? extends E> elements) {
// TODO Auto-generated method stub
boolean isModified = delegate().addAll(index, elements);
if (isModified) {
eventBus.post(new ObservableListEvent(elements));
}
return isModified;
}
@Override
public E remove(int index) {
E removed = super.remove(index);
getEventBus().post(new ObservableListEvent(null, removed));
return removed;
}
@SuppressWarnings("unchecked")
@Override
public boolean remove(Object object) {
boolean isRemoved = super.remove(object);
if (isRemoved) {
getEventBus().post(new ObservableListEvent(null, (E) object));
}
return isRemoved;
}
/**
*
* @see com.google.common.collect.ForwardingCollection#removeAll(java.util.Collection)
*
* @param collection
* @return
*/
@Override
public boolean removeAll(Collection<?> collection) {
// Compute the removed elements (for the events)
List<E> removedElements = Lists.newArrayList(delegate());
removedElements.retainAll(collection);
// Do remove
boolean isRemoved = super.removeAll(collection);
if (isRemoved) {
getEventBus().post(new ObservableListEvent(null, removedElements));
}
return isRemoved;
}
/**
* Reset the collection to the specified collection.
* Throw events containing the added and removed elements.
*
* @param collection
* @return
*/
public boolean resetTo(Collection<? extends E> collection) {
// Compute removed and added
Collection<E> elementsToRemove = new ArrayList<E>();
Collection<E> elementsToAdd = new ArrayList<E>();
// Cached list of attached elements
Collection<? extends E> attachedElements = delegate();
// Compute added and removed elements. Walk both list 2 times.
// This could certainly be improved.
// TODO improve the algorithm
// Compute added elements
for (E o : collection) {
if (!attachedElements.contains(o)) {
elementsToAdd.add(o);
continue;
}
}
// Compute removed elements
for (E o : attachedElements) {
if (!collection.contains(o)) {
elementsToRemove.add(o);
continue;
}
}
// Change the list
delegate().clear();
delegate().addAll(collection);
// Fire event
boolean isModified = !(elementsToAdd.isEmpty() && elementsToRemove.isEmpty());
if (isModified) {
getEventBus().post(new ObservableListEvent(elementsToAdd, elementsToRemove));
}
return isModified;
}
/**
* Event used to specify that the list is changed
*
* @author cedric dumoulin
*
*/
public class ObservableListEvent {
Collection<? extends E> addedElements = Collections.emptyList();
Collection<? extends E> removedElements = Collections.emptyList();
/**
*
* Constructor.
*
*/
public ObservableListEvent() {
this.addedElements = Collections.emptyList();
this.removedElements = Collections.emptyList();
}
/**
*
* Constructor.
*
*/
public ObservableListEvent(Collection<? extends E> addedElements) {
if (addedElements != null) {
this.addedElements = addedElements;
}
else {
this.addedElements = Collections.emptyList();
}
this.removedElements = Collections.emptyList();
}
/**
* Constructor.
*
* @param addedElements
* added elements or null
* @param removedElements
* removed elements or null
*/
public ObservableListEvent(Collection<? extends E> addedElements, Collection<? extends E> removedElements) {
if (addedElements != null) {
this.addedElements = addedElements;
}
else {
this.addedElements = Collections.emptyList();
}
if (removedElements != null) {
this.removedElements = removedElements;
}
else {
this.removedElements = Collections.emptyList();
}
}
/**
*
* Constructor.
*
* @param addedElement
* An element added or null;
* @param removedElement
* An element added or null
*/
public ObservableListEvent(E addedElement, E removedElement) {
if (addedElement != null) {
addedElements = Collections.singletonList(addedElement);
}
else {
addedElements = Collections.emptyList();
}
if (removedElement != null) {
removedElements = Collections.singletonList(removedElement);
}
else {
removedElements = Collections.emptyList();
}
}
/**
*
* Constructor.
*
* @param addedElement
* An element added or null;
*/
public ObservableListEvent(E addedElement) {
if (addedElement != null) {
addedElements = Collections.singletonList(addedElement);
}
else {
addedElements = Collections.emptyList();
}
removedElements = Collections.emptyList();
}
/**
* @return the addedElements
*/
public Collection<? extends E> getAddedElements() {
return addedElements;
}
// /**
// * @param addedElements the addedElements to set
// */
// public void setAddedElements(List<E> addedElements) {
// this.addedElements = addedElements;
// }
//
// /**
// * @param addedElements the addedElements to set
// */
// public void setAddedElements(E addedElement) {
// this.addedElements = Collections.singletonList(addedElement);
// }
/**
* @return the removedElements
*/
public Collection<? extends E> getRemovedElements() {
return removedElements;
}
// /**
// * @param removedElements the removedElements to set
// */
// public void setRemovedElements(List<E> removedElements) {
// this.removedElements = removedElements;
// }
//
// /**
// * @param removedElements the removedElements to set
// */
// public void setRemovedElements(E removedElement) {
// this.removedElements = Collections.singletonList(removedElement);
// }
}
/**
* Return the underlying list. This can be used as an unnotifying version of the list.
*
* @return
*/
public List<E> getUnnotifyingList() {
return delegate();
}
}