/*******************************************************************************
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 java.util.ArrayList;
import org.gocha.collection.ObjectChanged;
import org.gocha.collection.ObjectChangedListener;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
/**
* Класс создающий уведомления об изменении списка.
* @author Камнев Георгий Павлович
*/
public class BasicEventList<E> extends ListWrapper<E> implements ObjectChanged, EventList<E>
{
public BasicEventList()
{
this(new ArrayList<E>());
}
/**
* Конструктор.
*
* @param list
* Обвараичваемый список
* @throws NullPointerException
* если list==null
*/
public BasicEventList(List<E> list) throws NullPointerException
{
super(list);
if (list == null)
{
throw new NullPointerException();
}
for (E it : list)
{
attach(it);
}
addEventListListener(new EventListAdapter<E>()
{
@Override
public void inserted(EventListArgs<E> evnt)
{
if (evnt == null || evnt.getItem() == null)
return;
attach(evnt.getItem());
}
@Override
protected void deleted(EventListArgs<E> evnt)
{
if (evnt == null || evnt.getItem() == null)
return;
detach(evnt.getItem());
}
});
}
private Vector<ObjectChangedListener> changed = new Vector<ObjectChangedListener>();
/**
* Добавляет слушателя на реакцию изменения списка.<br>
* @param listener
* слушатель
*/
@Override
public void addObjectChangedListener(ObjectChangedListener listener)
{
if (listener == null)
{
return;
}
changed.add(listener);
}
/**
* Удаляет слушателья с реакции изменения списка.
*
* @param listener
* слушатель
*/
@Override
public void removeObjectChangedListener(ObjectChangedListener listener)
{
changed.remove(listener);
}
/**
* Вывзваеться для отправки сообщения слушателям о измении списка
*/
protected void onSelfChanged()
{
for (ObjectChangedListener listener : changed)
{
if (listener == null)
{
continue;
}
listener.changed(this);
}
}
private ObjectChangedListener itemListener = null;
private ObjectChangedListener getItemListener()
{
if (itemListener == null)
{
itemListener = new ObjectChangedListener()
{
@Override
public void changed(Object sender)
{
onItemChanged(sender);
}
};
}
return itemListener;
}
/**
* Вызывается при добавлении объекта. Доавляет своего слушателя изменения
* (ObjectChangedListener) объекта item
*
* @param item
* Объект коллекции
*/
protected void attach(E item)
{
//onSelfChanged();
if (item == null)
{
return;
}
if (item instanceof ObjectChanged)
{
ObjectChanged iOC = (ObjectChanged) item;
iOC.addObjectChangedListener(getItemListener());
}
}
/**
* Вызывается при удаления объекта. Удаляет своего слушателя изменения
* (ObjectChangedListener) объекта item
*
* @param item
* Объект коллекции
*/
protected void detach(E item)
{
try
{
//onSelfChanged();
}
finally
{
if (item == null)
{
return;
}
if (item instanceof ObjectChanged)
{
ObjectChanged iOC = (ObjectChanged) item;
iOC.removeObjectChangedListener(getItemListener());
}
}
}
/**
* Вызываается при изменении объекта коллекции. Вызывает реакции update для
* коллекции и свою реакцию изменения ObjectChanged
*
* @param item
* Объект который изменился в коллекции
*/
@SuppressWarnings("unchecked")
protected void onItemChanged(Object item)
{
try
{
E eItem = (E) item;
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Update,eItem,indexOf(item)));
}
catch (ClassCastException ex)
{
}
}
private Collection<EventListListener<E>> listEventListeners = new HashSet<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);
}
}
/**
* Добавление объекта в список
* @param e Объект
* @return Факт добавления
*/
@Override
public boolean add(E e)
{
boolean res = super.add(e);
if (res)
{
int index = indexOf(e);
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Insert,e,index));
}
return res;
}
/**
* Добавление объекта в список, в заданную позицию
* @param index Позиция
* @param element Объект
*/
@Override
public void add(int index, E element)
{
super.add(index, element);
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Insert,element,indexOf(element)));
}
/**
* Добавление объектов в список
* @param c Объекты
* @return Факт добавления
*/
@Override
public boolean addAll(Collection<? extends E> c)
{
boolean res = super.addAll(c);
if (res)
{
for (E e : c)
{
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Insert,e,indexOf(e)));
}
}
return res;
}
/**
* Добавление объектов в список, в заданную позицию
* @param index Позиция
* @param c Объекты
* @return Факт добавления
*/
@Override
public boolean addAll(int index, Collection<? extends E> c)
{
boolean res = super.addAll(index, c);
if (res)
{
for (E e : c)
{
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Insert,e,indexOf(e)));
}
}
return res;
}
/**
* Удаление объектов из списка
*/
@SuppressWarnings("unchecked")
@Override
public void clear()
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object o : this )
{
indexes.put(o, indexOf(o));
}
Object[] items = super.toArray();
super.clear();
for (Object o : items)
{
try
{
E e = (E) o;
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,e,indexes.get(o)));
}
catch (ClassCastException ex)
{
continue;
}
}
}
/**
* Удаление объекта из списка
* @param o Объект
* @return Факт удаления
*/
@SuppressWarnings("unchecked")
@Override
public boolean remove(Object o)
{
int index = indexOf(o);
@SuppressWarnings("element-type-mismatch")
boolean res = super.remove(o);
if (res)
{
try
{
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,(E)o,index));
}
catch (ClassCastException ex)
{
}
}
return res;
}
/**
* Удаление объекта из списка
* @param index Индекс элемента
* @return Удаленный элемент
*/
@Override
public E remove(int index)
{
E item = super.remove(index);
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,item,index));
return item;
}
/**
* Удаляет группу объектов из списка
* @param c Группа объектов
* @return Факт удаления
*/
@SuppressWarnings("unchecked")
@Override
public boolean removeAll(Collection<?> c)
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object o : c )
{
indexes.put(o, indexOf(o));
}
boolean res = super.removeAll(c);
if (res)
{
for (Object o : c)
{
try
{
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,(E)o,indexes.get(o)));
}
catch (ClassCastException ex)
{
}
}
}
return res;
}
/**
* Удаляет объекты из списка, не входящиие в указанную группу объектов
* @param c Группа объектов
* @return Факт удаления
*/
@SuppressWarnings("unchecked")
@Override
public boolean retainAll(Collection<?> c)
{
Map<Object,Integer> indexes = new HashMap<Object, Integer>();
for( Object o : this )
{
indexes.put(o, indexOf(o));
}
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
{
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,(E) delObj,indexes.get(delObj)));
}
catch (ClassCastException ex)
{
}
}
}
return res;
}
/**
* Заменяет объект в указанной позиции
* Вызывает реакции delete, insert и ObjectChanged
* @param index Позиция элемента
* @param element Новый элемент
* @return Старый элемент
*/
@Override
public E set(int index, E element)
{
E deleted = super.set(index, element);
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Delete,(E) deleted,index));
fireEventListArgs(
new BasicEventListArgs<E>(
this,EventListAction.Insert,(E) element,index));
return deleted;
}
}