/*******************************************************************************
gocha.org-lib-java Библеотека общего назначения
(с) Камнев Георгий Павлович 2009 GPLv2
Данная программа является свободным программным обеспечением. Вы вправе
распространять ее и/или модифицировать в соответствии с условиями версии 2
либо по вашему выбору с условиями более поздней версии
Стандартной Общественной Лицензии GNU, опубликованной Free Software Foundation.
Мы распространяем данную программу в надежде на то, что она будет вам полезной,
однако НЕ ПРЕДОСТАВЛЯЕМ НА НЕЕ НИКАКИХ ГАРАНТИЙ,
в том числе ГАРАНТИИ ТОВАРНОГО СОСТОЯНИЯ ПРИ ПРОДАЖЕ
и ПРИГОДНОСТИ ДЛЯ ИСПОЛЬЗОВАНИЯ В КОНКРЕТНЫХ ЦЕЛЯХ.
Для получения более подробной информации ознакомьтесь
со Стандартной Общественной Лицензией GNU.
Вместе с данной программой вы должны были получить экземпляр
Стандартной Общественной Лицензии GNU.
Если вы его не получили, сообщите об этом в Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*******************************************************************************/
package org.gocha.collection.list;
import org.gocha.collection.ObjectChangeCancel;
import org.gocha.collection.ObjectChangeCancelEvent;
import org.gocha.collection.ObjectChanged;
import org.gocha.collection.ObjectChangeCancelListener;
import org.gocha.collection.ObjectChangedListener;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
/**
* Список поддерживающий контроль измения своего состава элементов.
* Может уведолять слушателей о операциях над элементами списка (встака, удаление элементов, ....).
* Может отменять операции над элементами списка (встака, удаление элементов, ....).
*
* Список не реализует хранеие данных, для этого он использует другой список (List).
* Все операции манипуляции с элементами, он делегирует списку
*
* @author gocha
* @param <E> Тип элементов списка
*/
public class BasicEventListCancel<E> extends ListWrapper<E> implements EventListCancel<E>
{
/**
* Конструктор
* @param list Список хранящий элементы
*/
public BasicEventListCancel(List<E> list)
{
super(list);
for (E it : list)
{
added(it);
}
}
/**
* Указывает модель отмены операций (CANCELMODEL_FOR_ALL,CANCELMODEL_FOR_EACH)
*/
public int cancelModel = CANCELMODEL_FOR_EACH;
/**
* Модель отмены для всех -
* Если в процессе операции происходит "отмена" для элемента, то операция отменяется для всех последующих элементов
*/
public static final int CANCELMODEL_FOR_ALL = 1;
/**
* Модель отмены для каждого -
* Если в процессе операции происходит "отмена" для элемента, то операция отменяется только для указанного
*/
public static final int CANCELMODEL_FOR_EACH = 2;
// /////////////////////////////////////////////////////////////////////////
/**
* Вызывается когда добавляется элемент в коллекцию (операции add, insert, ...).
* Если элемент реализует интерфейс ObjectChanged, то устанавливается слушатель, с последующей генерацией сообщений Update.
* Вызывает onInserted(), onSelfChanged()
* @param e Элемент.
*/
protected void added(E e)
{
int index = indexOf(e);
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Insert,e,index));
if (e == null)
{
return;
}
if (e instanceof ObjectChanged)
{
ObjectChanged iOC = (ObjectChanged) e;
iOC.addObjectChangedListener(getItemChangedListener());
}
if (e instanceof ObjectChangeCancel)
{
ObjectChangeCancel iOC = (ObjectChangeCancel) e;
iOC.addObjectChangeCancelListener(getItemChangeCancelListener());
}
}
/**
* Вызывается когда удаляется элемент из коллекции (операции remove, clear, ...).
* Если элемент реализует интерфейс ObjectChanged, то удаляется слушатель.
* Вызывает onDeleted(), onSelfChanged()
* @param e Элемент.
*/
protected void removed(E e, int index)
{
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,e,index));
if (e == null)
{
return;
}
if (e instanceof ObjectChanged)
{
ObjectChanged iOC = (ObjectChanged) e;
iOC.removeObjectChangedListener(getItemChangedListener());
}
if (e instanceof ObjectChangeCancel)
{
ObjectChangeCancel iOC = (ObjectChangeCancel) e;
iOC.removeObjectChangeCancelListener(getItemChangeCancelListener());
}
}
//************************************************************************
private ObjectChangedListener itemChangedListener = null;
/**
* Возвращает слушателя элементов списка
* @return Слушатель
*/
public ObjectChangedListener getItemChangedListener()
{
if (itemChangedListener == null)
{
itemChangedListener = new ObjectChangedListener()
{
@Override
public void changed(Object sender)
{
onItemChanged(sender);
}
};
}
return itemChangedListener;
}
/**
* Вызывает образотчиков сообщения Update для указанного элемента
* @param item Элемент
*/
@SuppressWarnings("unchecked")
protected void onItemChanged(Object item)
{
try
{
E eItem = (E) item;
int index = indexOf(eItem);
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Update,eItem,index));
}
catch (ClassCastException ex)
{
}
}
//************************************************************************
private ObjectChangeCancelListener itemChangeCancelListener = null;
/**
* Возвращает слушателя элементов списка
* @return Слушатель
*/
public ObjectChangeCancelListener getItemChangeCancelListener()
{
if (itemChangeCancelListener == null)
{
itemChangeCancelListener = new ObjectChangeCancelListener()
{
@Override
public void changing(ObjectChangeCancelEvent evnt)
{
onItemChanging(evnt);
}
};
}
return itemChangeCancelListener;
}
/**
* Вызывает образотчиков сообщения Update для указанного элемента
* @param evnt Элемент
*/
@SuppressWarnings("unchecked")
protected void onItemChanging(ObjectChangeCancelEvent evnt)
{
try
{
E eItem = (E) evnt.getObject();
int index = indexOf(eItem);
BasicEventListCancelChangeArgs<E> evnt2 =
(BasicEventListCancelChangeArgs<E>)
fireEventListCancelArgs(
new BasicEventListCancelChangeArgs<E>(
this,EventListAction.Update,eItem,false, evnt, index));
if( evnt2.getCancel() )
{
evnt.setCancel(true);
evnt.setException(null);
if( evnt2.getException()!=null )
{
throw evnt.getException();
}
}
}
catch (ClassCastException ex)
{
}
}
// /////////////////////////////////////////////////////////////////////////
/**
* Добавление объекта в список
* @param e Объект
* @return Факт добавления
*/
@Override
public boolean add(E e)
{
int index = size();
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
boolean res = super.add(e);
if (res)
{
added(e);
}
return res;
}
/**
* Добавление объекта в список, в заданную позицию
* @param index Позиция
* @param element Объект
*/
@Override
public void add(int index, E element)
{
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,element,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return;
}
}
super.add(index, element);
added(element);
}
/**
* Добавление объектов в список
* @param c Объекты
* @return Факт добавления
*/
@Override
public boolean addAll(Collection<? extends E> c)
{
if (this.cancelModel == CANCELMODEL_FOR_ALL)
{
int index = size()-1;
for (E e : c)
{
index++;
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
}
boolean res = super.addAll(c);
if (res)
{
for (E e : c)
{
added(e);
}
}
return res;
}
else if (this.cancelModel == CANCELMODEL_FOR_EACH)
{
int index = size()-1;
for (E e : c)
{
index++;
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
index--;
continue;
}
}
super.add(e);
added(e);
}
return true;
}
return false;
}
/**
* Добавление объектов в список
* @param index Позиция
* @param c Объекты
* @return Факт добавления
*/
@Override
public boolean addAll(int index, Collection<? extends E> c)
{
if (this.cancelModel == CANCELMODEL_FOR_ALL)
{
int cc = 0;
for (E e : c)
{
int eindex = index + cc;
cc++;
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,e,false,eindex));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
}
boolean res = super.addAll(index, c);
if (res)
{
for (E e : c)
{
added(e);
}
}
return res;
}
else if (this.cancelModel == CANCELMODEL_FOR_EACH)
{
int eindex = index - 1;
for (E e : c)
{
eindex++;
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,e,false,eindex));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
eindex--;
continue;
}
}
super.add(index, e);
added(e);
index++;
}
return true;
}
return false;
}
// ////////////////////////////////////////////////////////////////////////////
/**
* Удаление объектов из списка
*/
@Override
@SuppressWarnings("unchecked")
public void clear()
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object o : this )
{
indexes.put(o, indexOf(o));
}
if (this.cancelModel == CANCELMODEL_FOR_ALL)
{
Object[] items = super.toArray();
for (Object o : items)
{
try
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return;
}
}
}
catch (ClassCastException ex)
{
continue;
}
}
super.clear();
for (Object o : items)
{
try
{
E e = (E) o;
removed(e,indexes.get(o));
}
catch (ClassCastException ex)
{
}
}
}
else if (this.cancelModel == CANCELMODEL_FOR_EACH)
{
Object[] items = super.toArray();
for (Object o : items)
{
try
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
continue;
}
}
super.remove(e);
removed(e,indexes.get(o));
}
catch (ClassCastException ex)
{
continue;
}
}
}
}
/**
* Удаление объекта из списка
* @param o Объект
* @return Факт удаления
*/
@Override
@SuppressWarnings("unchecked")
public boolean remove(Object o)
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object ok : this )
{
indexes.put(ok, indexOf(ok));
}
try
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
}
catch (ClassCastException ex)
{
}
boolean res = super.remove(o);
if (res)
{
try
{
E e = (E) o;
removed(e,indexes.get(o));
}
catch (ClassCastException ex)
{
}
}
return res;
}
/**
* Удаление объекта из списка
* @param index Индекс элемента
* @return Удаленный элемент
*/
@Override
public E remove(int index)
{
E e = super.get(index);
// int index = indexOf(e)
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return null;
}
}
E ee = super.remove(index);
removed(ee,index);
return ee;
}
/**
* Удаляет группу объектов из списка
* @param c Группа объектов
* @return Факт удаления
*/
@Override
@SuppressWarnings("unchecked")
public boolean removeAll(Collection<?> c)
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object o : this )
{
indexes.put(o, indexOf(o));
}
if (this.cancelModel == CANCELMODEL_FOR_ALL)
{
for (Object o : c)
{
try
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
}
catch (ClassCastException ex)
{
}
}
boolean res = super.removeAll(c);
if (res)
{
for (Object o : c)
{
try
{
E e = (E) o;
removed(e,indexes.get(o));
}
catch (ClassCastException ex)
{
}
}
}
return res;
}
else if (this.cancelModel == CANCELMODEL_FOR_EACH)
{
for (Object o : c)
{
try
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
super.remove(e);
removed(e,indexes.get(o));
}
catch (ClassCastException ex)
{
}
}
}
return false;
}
/**
* Удаляет объекты из списка, не входящиие в указанную группу объектов
* @param c Группа объектов
* @return Факт удаления
*/
@Override
@SuppressWarnings("unchecked")
public boolean retainAll(Collection<?> c)
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object o : this )
{
indexes.put(o, indexOf(o));
}
if (this.cancelModel == CANCELMODEL_FOR_ALL)
{
for (Object o : this)
{
if (!c.contains(o))
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
}
}
Object[] beforeItems = super.toArray();
boolean res = super.retainAll(c);
Object[] afterItems = super.toArray();
if (res)
{
LinkedList<Object> dels = new LinkedList<Object>();
for (Object bi : beforeItems)
{
if (bi == null)
{
continue;
}
boolean found = false;
for (Object ai : afterItems)
{
if (bi == ai)
{
found = true;
}
}
if (!found)
{
dels.add(bi);
}
}
for (Object delObj : dels)
{
try
{
removed((E) delObj,indexes.get(delObj));
}
catch (ClassCastException ex)
{
}
}
}
return res;
}
else if (this.cancelModel == CANCELMODEL_FOR_EACH)
{
Object[] items = super.toArray();
for (Object o : items)
{
try
{
if (!c.contains(o))
{
E e = (E) o;
int index = indexOf(o);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,e,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return false;
}
}
super.remove(e);
removed(e,indexes.get(e));
}
}
catch (ClassCastException ex)
{
}
}
}
return false;
}
// /////////////////////////////////////////////////////////////////////////
/**
* Заменяет объект в указанной позиции
* @param index Позиция элемента
* @param element Новый элемент
* @return Старый элемент
*/
@Override
public E set(int index, E element)
{
E forDeleting = super.get(index);
EventListCancelArgs<E> evnt =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Delete,forDeleting,false,index));
if (evnt.getCancel()) // DENY
{
if (evnt.getException() != null)
{
throw evnt.getException();
}
else
{
return element;
}
}
EventListCancelArgs<E> evnt2 =
fireEventListCancelArgs(
new BasicEventListCancelArgs<E>(
this,EventListAction.Insert,element,false,index));
if (evnt2.getCancel()) // DENY
{
if (evnt2.getException() != null)
{
throw evnt2.getException();
}
else
{
return forDeleting;
}
}
E res = super.set(index, element);
removed(res,index);
added(element);
return res;
}
// /////////////////////////////////////////////////////////////////////////
// Событие changed
// /////////////////////////////////////////////////////////////////////////
private Vector<ObjectChangedListener> changed = new Vector<ObjectChangedListener>();
/**
* Добавляет слушателя на реакцию изменения списка.<br>
* @param listener
* слушатель
*/
public void addObjectChangedListener(ObjectChangedListener listener)
{
if (listener == null)
{
return;
}
changed.add(listener);
}
/**
* Удаляет слушателья с реакции изменения списка.
*
* @param listener
* слушатель
*/
public void removeObjectChangedListener(ObjectChangedListener listener)
{
changed.remove(listener);
}
/**
* Вывзваеться для отправки сообщения слушателям о измении списка
*/
protected void onSelfChanged()
{
for (ObjectChangedListener listener : changed)
{
if (listener == null)
{
continue;
}
listener.changed(this);
}
}
private Vector<EventListListener<E>> listEventListeners = new Vector<EventListListener<E>>();
/* (non-javadoc)
* @see org.gocha.collection.list.IEventListSender#addEventListListener
*/
@Override
public void addEventListListener(EventListListener<E> listener)
{
if (listener == null)
{
return;
}
listEventListeners.add(listener);
}
/* (non-javadoc)
* @see org.gocha.collection.list.IEventListSender#removeEventListListener
*/
@Override
public void removeEventListListener(EventListListener<E> listener)
{
listEventListeners.remove(listener);
}
/**
* Вызываеться для отправки сообщения измения элементов списка
* @param evnt Сообщение
*/
protected void fireEventListArgs(EventListArgs<E> evnt)
{
for (EventListListener<E> listener : listEventListeners)
{
if (listener == null)
{
continue;
}
listener.listItemsChanged(evnt);
}
}
private Vector<EventListCancelListener<E>> listEventCancelListeners
= new Vector<EventListCancelListener<E>>();
/* (non-javadoc)
* @see org.gocha.collection.list.IEventCancelListSender#addEventListCancelListener
*/
@Override
public void addEventListCancelListener(EventListCancelListener<E> listener)
{
if (listener == null)
{
return;
}
listEventCancelListeners.add(listener);
}
/* (non-javadoc)
* @see org.gocha.collection.list.IEventCancelListSender#removeEventListCancelListener
*/
@Override
public void removeEventListCancelListener(EventListCancelListener<E> listener)
{
listEventCancelListeners.remove(listener);
}
/**
* Вызываеться для отправки сообщения измения элементов списка
* @param evnt Сообщение
* @return Сообщение
*/
protected EventListCancelArgs<E> fireEventListCancelArgs(EventListCancelArgs<E> evnt)
{
for (EventListCancelListener<E> listener : listEventCancelListeners)
{
if (listener == null)
{
continue;
}
listener.listItemsChanging(evnt);
}
return evnt;
}
}