package com.qubling.sidekick.search;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.qubling.sidekick.instance.Instance;
public class Results<SomeInstance extends Instance<SomeInstance>>
extends AbstractCollection<SomeInstance>
implements ResultSet<SomeInstance> {
private Map<String, SomeInstance> results;
private List<SomeInstance> resultIndex;
private int totalSize = -1;
private Collection<OnChangeListener<SomeInstance>> onChangeListeners = new HashSet<OnChangeListener<SomeInstance>>();
public Results() {
results = new LinkedHashMap<String, SomeInstance>();
resultIndex = new ArrayList<SomeInstance>();
}
public Results(SomeInstance singleton) {
this();
add(singleton);
}
public Results(ArrayList<SomeInstance> loadedResults) {
this();
addAll(loadedResults);
}
@Override
public boolean add(SomeInstance instance) {
if (results.put(instance.getKey(), instance) == null) {
resultIndex.add(instance);
notifyOnAdd(instance);
return true;
}
if (resultIndex.size() > results.size())
throw new IllegalStateException("invariant violated: index is larger than base collection");
return false;
}
@Override
public <OtherInstance extends Instance<OtherInstance>> void addRemap(ResultSet<OtherInstance> others, Remap<OtherInstance, SomeInstance> map) {
for (OtherInstance otherInstance : others) {
addAll(map.map(otherInstance));
}
}
@Override
public void clear() {
for (SomeInstance instance : resultIndex) {
notifyOnRemove(instance);
}
resultIndex.clear();
results.clear();
}
@Override
public boolean retainAll(Collection<?> collection) {
boolean modified = false;
HashSet<String> keepKeys = new HashSet<String>();
for (Object o : collection) {
if (o instanceof Instance) {
Instance<?> instance = (Instance<?>) o;
if (instance.equals(results.get(instance.getKey()))) {
keepKeys.add(instance.getKey());
}
}
}
for (Map.Entry<String, SomeInstance> pair : results.entrySet()) {
if (!keepKeys.contains(pair.getKey())) {
remove(pair.getValue());
modified = true;
}
}
return modified;
}
@Override
public boolean remove(Object o) {
if (o instanceof Instance) {
Instance<?> instance = (Instance<?>) o;
// This is probably not possible... but just in case
if (!instance.equals(results.get(instance.getKey())))
return false;
SomeInstance removed = results.remove(instance.getKey());
if (removed != null) {
resultIndex.remove(o);
notifyOnRemove(removed);
return true;
}
}
return false;
}
@Override
public SomeInstance get(String key) {
return results.get(key);
}
@Override
public SomeInstance get(int index) {
if (index >= results.size()) return null;
return resultIndex.get(index);
}
@Override
public boolean contains(Object o) {
if (o instanceof Instance) {
Instance<?> instance = (Instance<?>) o;
String key = instance.getKey();
if (!results.containsKey(key) || !instance.equals(results.get(key))) {
return false;
}
}
else {
return false;
}
return true;
}
@Override
public boolean containsAll(Collection<?> collection) {
for (Object o : collection) {
if (o instanceof Instance) {
Instance<?> instance = (Instance<?>) o;
String key = instance.getKey();
if (!results.containsKey(key) || !instance.equals(results.get(key))) {
return false;
}
}
else {
return false;
}
}
return true;
}
@Override
public int size() {
return results.size();
}
@Override
public int getTotalSize() {
return totalSize > -1 ? totalSize : results.size();
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
@Override
public boolean isEmpty() {
return results.isEmpty();
}
@Override
public Object[] toArray() {
return resultIndex.toArray();
}
@Override
public <OtherInstance> OtherInstance[] toArray(OtherInstance[] array) {
return resultIndex.toArray(array);
}
@Override
public Iterator<SomeInstance> iterator() {
return resultIndex.iterator();
}
@Override
public ArrayList<SomeInstance> toArrayList() {
ArrayList<SomeInstance> arrayList = new ArrayList<SomeInstance>(resultIndex.size());
arrayList.addAll(resultIndex);
return arrayList;
}
protected List<SomeInstance> allResults() {
return Collections.unmodifiableList(resultIndex);
}
@Override
public void addOnChangeListener(OnChangeListener<SomeInstance> listener) {
onChangeListeners.add(listener);
}
@Override
public void removeOnChangeListener(OnChangeListener<SomeInstance> listener) {
onChangeListeners.remove(listener);
}
protected void notifyOnAdd(SomeInstance instance) {
for (OnChangeListener<SomeInstance> listener : onChangeListeners) {
listener.onAdd(instance);
}
}
protected void notifyOnRemove(SomeInstance instance) {
for (OnChangeListener<SomeInstance> listener : onChangeListeners) {
listener.onRemove(instance);
}
}
@Override
public void sort(Comparator<SomeInstance> comparator) {
Collections.sort(resultIndex, comparator);
}
@Override
public int indexOf(SomeInstance needle) {
int i = 0;
for (SomeInstance instance : this) {
if (instance.getKey().equals(needle.getKey()))
return i;
i++;
}
return -1;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder("Results(");
for (int i = 0; i < size() && i < 3; i++) {
result.append(get(i));
result.append(",");
}
if (size() > 3)
result.append("...");
result.append(")");
return result.toString();
}
}