package pt.ist.fenixframework.pstm; import java.lang.ref.SoftReference; import java.util.AbstractList; import java.util.Iterator; import java.util.Set; import jvstm.PerTxBox; import pt.ist.fenixframework.DomainObject; import dml.runtime.Relation; import dml.runtime.FunctionalSet; public class RelationList<E1 extends DomainObject,E2 extends DomainObject> extends AbstractList<E2> implements VersionedSubject,Set<E2>,dml.runtime.RelationBaseSet<E2> { private E1 listHolder; private Relation<E1,E2> relation; private String attributeName; private SoftReference<VBox<FunctionalSet<E2>>> elementsRef; private PerTxBox<FunctionalSet<E2>> elementsToAdd = new PerTxBox<FunctionalSet<E2>>(DOFunctionalSet.EMPTY) { public void commit(FunctionalSet<E2> toAdd) { consolidateElementsIfLoaded(); } }; private PerTxBox<FunctionalSet<E2>> elementsToRemove = new PerTxBox<FunctionalSet<E2>>(DOFunctionalSet.EMPTY) { public void commit(FunctionalSet<E2> toRemove) { consolidateElementsIfLoaded(); } }; public RelationList(E1 listHolder, Relation<E1,E2> relation, String attributeName, boolean allocateOnly) { this.listHolder = listHolder; this.relation = relation; this.attributeName = attributeName; VBox elementsBox = null; if (allocateOnly) { elementsBox = SoftReferencedVBox.makeNew(listHolder, attributeName, allocateOnly); } else { elementsBox = new SoftReferencedVBox<FunctionalSet<E2>>(listHolder, attributeName, DOFunctionalSet.EMPTY); } this.elementsRef = new SoftReference<VBox<FunctionalSet<E2>>>(elementsBox); } // The access to the elementsRef field should be synchronized private synchronized VBox<FunctionalSet<E2>> getElementsBox() { VBox<FunctionalSet<E2>> box = elementsRef.get(); if (box == null) { box = SoftReferencedVBox.makeNew(this.listHolder, this.attributeName, true); this.elementsRef = new SoftReference<VBox<FunctionalSet<E2>>>(box); } return box; } public jvstm.VBoxBody addNewVersion(String attr, int txNumber) { return getElementsBox().addNewVersion(attr, txNumber); } public Object getCurrentValue(Object obj, String attrName) { // what's the correct value to return here? should it be the // RelationList instance or the FunctionalSet within it? I'll // go with the RelationList for now, but maybe this will // change later. Either way, this code should be considered // experimental. return this; } private FunctionalSet<E2> elementSet() { consolidateElements(); return getElementsBox().get(listHolder, attributeName); } protected void consolidateElementsIfLoaded() { if (elementsToAdd.get().size() + elementsToRemove.get().size() > 0) { VBox<FunctionalSet<E2>> box = getElementsBox(); if (box.hasValue()) { consolidateElements(); } else { // here we write the NOT_LOADED_VALUE to force the box to go to the write-set box.putNotLoadedValue(); } } } private void consolidateElements() { VBox<FunctionalSet<E2>> box = getElementsBox(); FunctionalSet<E2> origSet = box.get(listHolder, attributeName); FunctionalSet<E2> newSet = origSet; if (elementsToRemove.get().size() > 0) { Iterator<E2> iter = elementsToRemove.get().iterator(); while (iter.hasNext()) { newSet = newSet.remove(iter.next()); } elementsToRemove.put(DOFunctionalSet.EMPTY); } if (elementsToAdd.get().size() > 0) { Iterator<E2> iter = elementsToAdd.get().iterator(); while (iter.hasNext()) { newSet = newSet.add(iter.next()); } elementsToAdd.put(DOFunctionalSet.EMPTY); } if (newSet != origSet) { box.put(newSet); } } public void setFromOJB(Object obj, String attr, OJBFunctionalSetWrapper ojbList) { getElementsBox().setFromOJB(obj, attr, ojbList.getElements()); } public void justAdd(E2 obj) { Transaction.logAttrChange((DomainObject)listHolder, attributeName); elementsToAdd.put(elementsToAdd.get().add(obj)); elementsToRemove.put(elementsToRemove.get().remove(obj)); } public void justRemove(E2 obj) { Transaction.logAttrChange((DomainObject)listHolder, attributeName); elementsToRemove.put(elementsToRemove.get().add(obj)); elementsToAdd.put(elementsToAdd.get().remove(obj)); } public int size() { return elementSet().size(); } public E2 get(int index) { return elementSet().get(index); } public E2 set(int index, E2 element) { E2 oldElement = get(index); int oldModCount = modCount; if (oldElement != element) { remove(oldElement); add(index, element); } // After the remove and add the modCount would have been incremented twice modCount = oldModCount + 1; return oldElement; } public void add(int index, E2 element) { relation.add(listHolder, element); modCount++; } public E2 remove(int index) { E2 elemToRemove = get(index); remove(elemToRemove); return elemToRemove; } public boolean remove(Object o) { modCount++; relation.remove(listHolder, (E2)o); // HACK!!! What to return here? // I wouldn't like to force a load of the list to be able to return the correct boolean value return true; } public Iterator<E2> iterator() { return new RelationListIterator<E2>(this); } private static class RelationListIterator<X extends DomainObject> implements Iterator<X> { private RelationList<?,X> list; private Iterator<X> iter; private boolean canRemove = false; private X previous = null; RelationListIterator(RelationList<?,X> list) { this.list = list; this.iter = list.elementSet().iterator(); } public boolean hasNext() { return iter.hasNext(); } public X next() { X result = iter.next(); canRemove = true; previous = result; return result; } public void remove() { if (! canRemove) { throw new IllegalStateException(); } else { canRemove = false; list.remove(previous); } } } }