/*
* Copyright (c) 2011-2014 Jeppetto and Jonathan Thompson
*
* 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.iternine.jeppetto.dao.persistable;
import org.iternine.jeppetto.dao.JeppettoException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
@SuppressWarnings({ "unchecked" })
public class PersistableList
implements Persistable, List {
//-------------------------------------------------------------
// Variables - Private
//-------------------------------------------------------------
private List delegate;
private boolean rewrite = false;
private Set<Integer> modifiedIndexes = new HashSet<Integer>();
private int firstAppendedIndex;
private boolean modifiableDelegate;
private String storeIdentifier;
//-------------------------------------------------------------
// Constructors
//-------------------------------------------------------------
/**
* Default constructor that uses an ArrayList as the delegate and expects no non-Jeppetto access.
*/
public PersistableList() {
this(new ArrayList(), false);
}
/**
* Constructor that uses an ArrayList with an initial capacity as the delegate and expects no non-Jeppetto access.
*/
public PersistableList(int initialCapacity) {
this(new ArrayList(initialCapacity), false);
}
/**
* Constructor that takes is passed the delegate list along w/ an indication as to whether the
* delegate is modifiable by code outside of Jeppetto.
*
* @param delegate the underlying List implementation
* @param modifiableDelegate true if access is possible to the delegate by non-Jeppetto code
*/
public PersistableList(List delegate, boolean modifiableDelegate) {
this.delegate = delegate;
this.firstAppendedIndex = delegate.size();
this.modifiableDelegate = modifiableDelegate;
}
//-------------------------------------------------------------
// Implementation - Persistable
//-------------------------------------------------------------
@Override
public boolean __isDirty() {
return rewrite
|| delegate.size() > firstAppendedIndex
|| !modifiedIndexes.isEmpty()
|| __getDirtyFields().hasNext();
}
@Override
public void __markPersisted(String storeIdentifier) {
for (Object object : delegate) {
if (!(object instanceof Persistable)) {
continue;
}
Persistable persistable = (Persistable) object;
persistable.__markPersisted(storeIdentifier);
}
this.rewrite = false;
this.modifiedIndexes.clear();
this.firstAppendedIndex = delegate.size();
this.storeIdentifier = storeIdentifier;
}
@Override
public boolean __isPersisted(String storeIdentifier) {
return storeIdentifier.equals(this.storeIdentifier);
}
@Override
public Iterator<String> __getDirtyFields() {
return new Iterator<String>() {
private Iterator delegateIterator = delegate.iterator();
private int i = -1;
@Override
public boolean hasNext() {
while (delegateIterator.hasNext()) {
Object object = delegateIterator.next();
if (++i >= firstAppendedIndex
|| modifiedIndexes.contains(i)
|| !(object instanceof Persistable)
|| ((Persistable) object).__isDirty()) {
return true;
}
}
return false;
}
@Override
public String next() {
return Integer.toString(i);
}
@Override
public void remove() {
throw new JeppettoException("Can't remove items from dirtyKeys");
}
};
}
@Override
public Object __getDelegate() {
return delegate;
}
//-------------------------------------------------------------
// Implementation - List
//-------------------------------------------------------------
@Override
public void add(int index, Object element) {
rewrite |= index < delegate.size();
delegate.add(index, element);
}
@Override
public boolean addAll(int index, Collection elements) {
rewrite |= index < delegate.size();
return delegate.addAll(index, elements);
}
@Override
public Object remove(int index) {
Object removed = delegate.remove(index);
rewrite |= index < firstAppendedIndex;
return removed;
}
@Override
public Object get(int index) {
Object element = delegate.get(index);
return element;
// // TODO: revisit whether these semantics makes sense
// if (element == null || element instanceof Persistable || DBObjectUtil.needsNoConversion(element.getClass())) {
// return element;
// }
//
// Object converted = DBObjectUtil.toDBObject(element);
// delegate.set(index, converted);
//
// return converted;
}
@Override
public Object set(int index, Object element) {
if (index < firstAppendedIndex) {
modifiedIndexes.add(index);
}
return delegate.set(index, element);
}
@Override
public boolean add(Object element) {
return delegate.add(element);
}
@Override
public boolean addAll(Collection elements) {
return delegate.addAll(elements);
}
@Override
public boolean remove(Object element) {
boolean changed = delegate.remove(element);
rewrite |= changed;
return changed;
}
@Override
public boolean removeAll(Collection elements) {
boolean changed = delegate.removeAll(elements);
rewrite |= changed;
return changed;
}
@Override
public boolean retainAll(Collection elements) {
boolean changed = delegate.retainAll(elements);
rewrite |= changed;
return changed;
}
@Override
public boolean contains(Object element) {
return delegate.contains(element);
}
@Override
public boolean containsAll(Collection elements) {
return delegate.containsAll(elements);
}
@Override
public int indexOf(Object element) {
return delegate.indexOf(element);
}
@Override
public int lastIndexOf(Object element) {
return delegate.lastIndexOf(element);
}
@Override
public Iterator iterator() {
return listIterator(0);
}
@Override
public ListIterator listIterator() {
return listIterator(0);
}
@Override
public ListIterator listIterator(final int index) {
return new ListIterator() {
private ListIterator delegateIterator = delegate.listIterator(index);
private int modifiableIndex = -1;
@Override
public boolean hasNext() {
return delegateIterator.hasNext();
}
@Override
public Object next() {
modifiableIndex = delegateIterator.nextIndex();
Object next = delegateIterator.next();
return next;
// if (next == null || next instanceof Persistable || DBObjectUtil.needsNoConversion(next.getClass())) {
// return next;
// }
//
// Object converted = DBObjectUtil.toDBObject(next);
// delegate.set(modifiableIndex, converted);
//
// return converted;
}
@Override
public boolean hasPrevious() {
return delegateIterator.hasPrevious();
}
@Override
public Object previous() {
modifiableIndex = delegateIterator.previousIndex();
Object previous = delegateIterator.previous();
return previous;
// if (previous == null || previous instanceof Persistable || DBObjectUtil.needsNoConversion(previous.getClass())) {
// return previous;
// }
//
// Object converted = DBObjectUtil.toDBObject(previous);
// delegate.set(modifiableIndex, converted);
//
// return converted;
}
@Override
public int nextIndex() {
return delegateIterator.nextIndex();
}
@Override
public int previousIndex() {
return delegateIterator.previousIndex();
}
@Override
public void remove() {
delegateIterator.remove();
rewrite |= modifiableIndex < firstAppendedIndex;
}
@Override
public void set(Object o) {
delegateIterator.set(o);
// If items >= firstAppendedIndex are removed, the modifiedIndexes value will still be correct.
// If items below it are removed, rewrite will be set to true and this will be ignored.
modifiedIndexes.add(modifiableIndex);
}
@Override
public void add(Object o) {
throw new UnsupportedOperationException();
}
};
}
@Override
public List subList(int fromIndex, int toIndex) {
return delegate.subList(fromIndex, toIndex); // TODO: Need to track changes in the sublist
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public Object[] toArray() {
// TODO: Convert to ...?
return delegate.toArray();
}
@Override
public Object[] toArray(Object[] objects) {
// TODO: Convert to ...?
return delegate.toArray(objects);
}
@Override
public void clear() {
rewrite = true;
delegate.clear();
}
//-------------------------------------------------------------
// Methods - Public
//-------------------------------------------------------------
public boolean isRewrite() {
return rewrite;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof List)) {
return false;
}
List thatList = o instanceof PersistableList ? ((PersistableList) o).delegate : (List) o;
return delegate.equals(thatList);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
}