/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * 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 net.ontopia.persistence.proxy; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.HashSet; import java.util.Set; import net.ontopia.utils.OntopiaRuntimeException; /** * INTERNAL: A set implementation that track the changes performed on * it. It keeps track of the objects that have been added and the ones * that has been removed. What makes this implementation different * from TrackableSet is that the field value is only loaded from the * storage when it is actually needed. */ public class TrackableLazySet<E> extends HashSet<E> implements TrackableCollectionIF<E> { protected TransactionIF txn; protected IdentityIF identity; protected int field; protected boolean loaded; protected Set<E> added; protected Set<E> removed; public void dump() { System.out.println("(TS: " + this + ")"); System.out.println(" (+: " + added + ")"); System.out.println(" (-: " + removed + ")"); } public TrackableLazySet(TransactionIF txn, IdentityIF identity, int field) { this.txn = txn; this.identity = identity; this.field = field; } public void resetTracking() { // Clears the lists of added and removed objects. // FIXME: Figure out if clearing collection or resetting to null is faster added = null; removed = null; // if (added != null) added.clear(); // if (removed != null) removed.clear(); } public void selfAdded() { if (!isEmpty()) { if (added == null) added = new HashSet<E>(this); else added.addAll(this); } } public Collection<E> getAdded() { return added; } public Collection<E> getRemoved() { return removed; } public boolean addWithTracking(E _o) { // Make sure persistent values are represented by their identity E o; if (_o instanceof PersistentIF) { o = (E) ((PersistentIF)_o)._p_getIdentity(); if (o == null) throw new OntopiaRuntimeException("Attempting to add PersistentIF without identity to TrackableSet"); } else o = _o; boolean result = (!loaded || super.add(o)); // Do not track if object wasn't really added. if (result) { // Register added object and remove object from removed objects if (removed == null || !removed.remove(o)) { // Initialize added set if (added == null) added = new HashSet<E>(4); added.add(o); } } return result; } public boolean removeWithTracking(E _o) { // Make sure persistent values are represented by their identity E o; if (_o instanceof PersistentIF) { o = (E) ((PersistentIF)_o)._p_getIdentity(); if (o == null) throw new OntopiaRuntimeException("Attempting to add PersistentIF without identity to TrackableSet"); } else o = _o; boolean result = (!loaded || super.remove(o)); // Do not track if object wasn't really removed. if (result) { // Register removed object and remove object from added objects if (added == null || !added.remove(o)) { // Initialize removed set if (removed == null) removed = new HashSet(4); removed.add(o); } } return result; } public void clearWithTracking() { Iterator<E> iter = new ArrayList<E>(this).iterator(); while (iter.hasNext()) { removeWithTracking(iter.next()); } } // -- immutable collection public void clear() { throw new UnsupportedOperationException(); } public boolean add(E o) { throw new UnsupportedOperationException(); } public boolean addAll(Collection<? extends E> c) { throw new UnsupportedOperationException(); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } // -- iterator public Iterator<E> iterator() { loadField(); // materialize return new PersistentIterator<E>(txn, true, super.iterator()); } // -- other public boolean contains(Object o) { loadField(); // materialize return super.contains((o instanceof PersistentIF ? ((PersistentIF)o)._p_getIdentity() : o)); } public boolean containsAll(Collection<?> c) { Iterator e = c.iterator(); while (e.hasNext()) if(!contains(e.next())) return false; return true; } public boolean equals(Object o) { loadField(); // materialize return super.equals(o); } public int hashCode() { loadField(); // materialize return super.hashCode(); } public boolean isEmpty() { loadField(); // materialize return super.isEmpty(); } public int size() { loadField(); // materialize return super.size(); } public Object[] toArray() { // materialized in size() Object[] result = new Object[size()]; Iterator it = iterator(); int i = 0; for (; it.hasNext(); i++) { result[i] = it.next(); } if (i+1 < result.length) { Object[] r = new Object[i+1]; System.arraycopy(result, 0, r, 0, i+1); return r; } else { return result; } } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { // materialized in size() int size = size(); if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); int i = 0; Iterator<E> it = iterator(); for (; it.hasNext(); i++) { a[i] = (T) it.next(); } if (a.length > i+1) a[i+1] = null; return a; } // -- load field value from storage public boolean isLoaded() { return loaded; } protected void loadField() { if (!loaded) { synchronized (this) { if (!loaded) { Collection<E> _coll = null; try { _coll = (Collection<E>)txn.loadField(identity, field); } catch (IdentityNotFoundException e) { // let coll be null } if (_coll != null && !_coll.isEmpty()) { // add all loaded elements to self Iterator<E> iter = _coll.iterator(); while (iter.hasNext()) { super.add(iter.next()); } // only remove if there's something there if (removed != null && !removed.isEmpty()) { Iterator<E> i = removed.iterator(); while (i.hasNext()) { if (!super.remove(i.next())) // should not be part of removed list if not removable i.remove(); } } } if (added != null && !added.isEmpty()) { Iterator<E> i = added.iterator(); while (i.hasNext()) { if (!super.add(i.next())) // should not be part of added list if not addable i.remove(); } } loaded = true; } } } } }