/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.binding.value.support;
import org.springframework.util.ObjectUtils;
import org.valkyriercp.binding.value.IndexAdapter;
import org.valkyriercp.binding.value.ObservableList;
import javax.swing.*;
import java.util.*;
/**
* @author Keith Donald
*/
public class ListListModel extends AbstractListModel implements ObservableList {
private List items;
private Comparator sorter;
private IndexAdapter indexAdapter;
public ListListModel() {
this(null);
}
public ListListModel(List items) {
this(items, null);
}
public ListListModel(List items, Comparator sorter) {
if (items != null) {
this.items = new ArrayList(items);
}
else {
this.items = new ArrayList();
}
setComparator(sorter);
sort();
}
public void setComparator(Comparator sorter) {
this.sorter = sorter;
}
public void sort() {
if (sorter != null) {
Collections.sort(items, sorter);
fireContentsChanged(items, -1, -1);
}
}
protected List getItems() {
return items;
}
public int getSize() {
return items.size();
}
public Object getElementAt(int index) {
return items.get(index);
}
public void add(int index, Object o) {
items.add(index, o);
fireIntervalAdded(this, index, index);
}
public IndexAdapter getIndexAdapter(int index) {
if (indexAdapter == null) {
this.indexAdapter = new ThisIndexAdapter();
}
indexAdapter.setIndex(index);
return indexAdapter;
}
private class ThisIndexAdapter extends AbstractIndexAdapter {
private static final int NULL_INDEX = -1;
public Object getValue() {
if (getIndex() == NULL_INDEX) {
return null;
}
return get(getIndex());
}
public void setValue(Object value) {
if (getIndex() == NULL_INDEX) {
throw new IllegalStateException("Attempt to set value at null index; operation not allowed");
}
Object oldValue = items.set(getIndex(), value);
if (hasValueChanged(oldValue, value)) {
fireContentsChanged(getIndex());
fireValueChange(oldValue, value);
}
}
public void fireIndexedObjectChanged() {
fireContentsChanged(getIndex());
}
}
public boolean add(Object o) {
boolean result = items.add(o);
if (result) {
int end = items.size() - 1;
fireIntervalAdded(this, end, end);
}
return result;
}
public boolean addAll(Collection c) {
int firstIndex = items.size();
boolean result = items.addAll(c);
if (result) {
int lastIndex = items.size() - 1;
fireIntervalAdded(this, firstIndex, lastIndex);
}
return result;
}
public boolean addAll(int index, Collection c) {
boolean result = items.addAll(index, c);
if (result) {
fireIntervalAdded(this, index, index + c.size() - 1);
}
return result;
}
public void clear() {
if (items.size() > 0) {
int firstIndex = 0;
int lastIndex = items.size() - 1;
items.clear();
fireIntervalRemoved(this, firstIndex, lastIndex);
}
}
public boolean contains(Object o) {
return items.contains(o);
}
public boolean containsAll(Collection c) {
return items.containsAll(c);
}
public Object get(int index) {
return items.get(index);
}
public int indexOf(Object o) {
return items.indexOf(o);
}
public boolean isEmpty() {
return items.isEmpty();
}
public Iterator iterator() {
return items.iterator();
}
public int lastIndexOf(Object o) {
return items.lastIndexOf(o);
}
public ListIterator listIterator() {
return items.listIterator();
}
public ListIterator listIterator(int index) {
return items.listIterator(index);
}
public Object remove(int index) {
Object o = items.remove(index);
fireIntervalRemoved(this, index, index);
return o;
}
public boolean remove(Object o) {
int index = indexOf(o);
if (index != -1) {
remove(index);
return true;
}
return false;
}
public boolean removeAll(Collection c) {
boolean b = items.removeAll(c);
if (b) {
fireContentsChanged(this, -1, -1);
}
return b;
}
public boolean retainAll(Collection c) {
boolean b = items.retainAll(c);
if (b) {
fireContentsChanged(this, -1, -1);
}
return b;
}
/**
* Set the value of a list element at the specified index.
* @param index of element to set
* @param element New element value
* @return old element value
*/
public Object set(int index, Object element) {
Object oldObject = items.set(index, element);
if (hasChanged(oldObject, element)) {
fireContentsChanged(index);
}
return oldObject;
}
/**
* Determine if the provided objects are different (have changed). This method essentially
* embodies the "change semantics" for elements in this list. If list elements have an
* altered "equals" implementation, it may not be sufficient to detect changes in a pair of
* objects. In that case, you can override this method and implement whatever change detection
* mechanism is appropriate.
*
* @param oldElement Old (original) value to compare
* @param newElement New (updated) value to compare
* @return true if objects are different (have changed)
*/
protected boolean hasChanged(Object oldElement, Object newElement) {
return !ObjectUtils.nullSafeEquals(oldElement, newElement);
}
public int size() {
return items.size();
}
public List subList(int fromIndex, int toIndex) {
return items.subList(fromIndex, toIndex);
}
public Object[] toArray() {
return items.toArray();
}
public Object[] toArray(Object[] a) {
return items.toArray(a);
}
/**
* Notifies the list model that one of the list elements has changed.
*/
protected void fireContentsChanged(int index) {
fireContentsChanged(index, index);
}
/**
* Notifies the list model that one or more of the list elements have
* changed. The changed elements are specified by the range startIndex to
* endIndex inclusive.
*/
protected void fireContentsChanged(int startIndex, int endIndex) {
fireContentsChanged(this, startIndex, endIndex);
}
/**
* Replace this list model's items with the contents of the provided
* collection.
*
* @param collection
* The collection to replace with
*/
public boolean replaceWith(Collection collection) {
boolean changed = false;
if (items.size() > 0) {
items.clear();
changed = true;
}
if (items.addAll(0, collection) && !changed) {
changed = true;
}
if (changed) {
fireContentsChanged(-1, -1);
}
return changed;
}
}