/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.genclass.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.jdo.listener.DetachLifecycleListener;
import org.objectweb.jorm.api.PIndexedElem;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.jorm.util.api.Loggable;
import org.objectweb.speedo.api.Debug;
import org.objectweb.speedo.genclass.GenClassAccessor;
import org.objectweb.speedo.genclass.GenClassElement;
import org.objectweb.speedo.genclass.PIndexedElemIterator;
import org.objectweb.speedo.genclass.AbstractGenClassHome;
import org.objectweb.speedo.genclass.api.SpeedoGenClassPO;
import org.objectweb.speedo.mim.api.DetachedLifeCycle;
import org.objectweb.speedo.mim.api.LifeCycle;
import org.objectweb.speedo.mim.api.StateItf;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.speedo.pm.api.POManagerItf;
import org.objectweb.util.monolog.api.BasicLevel;
/**
* @author S.Chassande-Barrioz
*/
public class CollectionAccessor
extends GenClassAccessor
implements Collection {
/**
* is the internal collection used when the gen class is not active.
* When the po is active the 'elements' field is used.
*/
protected Collection collection = null;
/**
* Instanciates and initializes a new collection with an initial size.
*/
public CollectionAccessor(SpeedoGenClassPO thepo) {
super(thepo);
collection = (Collection) gcpo.createGenClass();
}
public String toString() {
return "CollectionAccessor: id=" + gcpo.speedoGetGenClassId();
}
protected boolean duplicatAllowed() {
return true;
}
public GenClassElement speedoRemove2(Object o) {
tmpelem.setElement(o); // tmp type = etype.
Iterator it = elements.iterator();
while (it.hasNext()) {
CollectionElem e = (CollectionElem) it.next();
if (e.equals(tmpelem) && e.getElemStatus() != PIndexedElem.ELEM_DELETED) {
e.setStatus(PIndexedElem.ELEM_DELETED);
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "speedoRemove2(): removed");
}
return e;
}
}
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "speedoRemove2(): not found");
}
return null;
}
public void makePersistent(POManagerItf pm) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "makePersistent");
}
if (collection.size() > 0) {
((AbstractGenClassHome) getSpeedoPO().speedoGetHome())
.makePersistent(pm, collection.iterator(), null, null);
}
}
public void makePersistentOnAttach(POManagerItf pm, Map map) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "makePersistent");
}
if (collection.size() > 0) {
((AbstractGenClassHome) getSpeedoPO().speedoGetHome())
.makePersistent(pm, collection.iterator(), null, map);
}
}
public void deletePersistent(POManagerItf pm) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "deletePersistent");
}
clear();
}
public void setElements(Object o) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "setElements()");
}
clear();
if (o != null) {
addAll(((Collection) o));
}
}
public void loadFieldsFromAccessor(StateItf sa) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "jdoLoadFieldsFromAccessor()");
}
CollectionAccessor ca = (CollectionAccessor) sa;
collection.clear();
collection.addAll(ca.collection);
supportDelta = ca.supportDelta;
elements.clear();
elements.addAll(ca.elements);
speedoSetStatus(ca.speedoGetStatus());
}
public void detachCopy(POManagerItf pm, Map map, StateItf fieldsClone, Collection fgHints){
((CollectionAccessor)fieldsClone).loadFieldsFromAccessor(this);
//copy the elements in the collection: collection is no more available when the po is not active
((CollectionAccessor)fieldsClone).collection.clear();
//get an interator on the PIndexed elements
Iterator it = ((GenClassAccessor) fieldsClone).elements.iterator();
while(it.hasNext()){
//for each element of the collection, send the list of fields to load passed as a parameter
//indeed, the list sent will be modified (elements processed are removed)
Iterator itFgHints = fgHints.iterator();
Collection currentFgHints = new ArrayList();
while (itFgHints.hasNext()) {
currentFgHints.add((String)itFgHints.next());
}
//get the PIndexElement
CollectionElem iElem = (CollectionElem) it.next();
//get the object associated
Object elem = iElem.getElement((POManagerItf) pm);
Object clone = null;
//if it is a persistent object, detach it
if (elem instanceof PersistentObjectItf) {
//get the speedo po associated
PersistentObjectItf sp = (PersistentObjectItf) elem;
if(map != null)
clone = map.get(sp);
//detach only if not already done before
if (clone == null) {
synchronized(currentFgHints){
//get the clone of the speedo po
clone = pm.speedoDetachCopy(sp, map, currentFgHints);
}
}
} else {
//else, just copy it
clone = elem;
}
//add the clone in the collection of elements reachable when the po is no more active
((CollectionAccessor)fieldsClone).collection.add(clone);
}
((GenClassAccessor) fieldsClone).speedoSetStatus(LifeCycle.TRANSIENT);
}
public void attachCopy(POManagerItf pm, Map map, StateItf fieldsClone) {
CollectionAccessor ca = (CollectionAccessor) fieldsClone;
//get an iterator on the PIndexed elements of the collection
Iterator it = ca.collection.iterator();
while(it.hasNext()){
Object elem = it.next();
//if it is a persistent object
if (elem instanceof PersistentObjectItf) {
//get the detached speedo po
PersistentObjectItf spDetached = (PersistentObjectItf) elem;
PersistentObjectItf sp = (PersistentObjectItf) map.get(spDetached);
if(sp == null){
map.put(spDetached, sp);
sp = (PersistentObjectItf) pm.speedoAttachCopy(spDetached, map);
}
//put the element of the collection attribute in the elements attribute (the opposite of the jdoDetachCopy method)
speedoAddOnAttach(sp, false);
} else {
//just add the java object in the collection
speedoAddOnAttach(elem, false);
}
}
}
public void refresh(POManagerItf pm, Map map, Collection fgHints){
commonRefreshRetrieve(pm, map, fgHints, true);
}
public void retrieve(POManagerItf pm, Map map, Collection fgHints){
commonRefreshRetrieve(pm, map, fgHints, false);
}
/**
*
* @param pm
* @param map
* @param fgHints
* @param refresh: if true, call refresh, else call retrieve
*/
//TODO: what about refreshing Collection of String, Integer, Date????
private void commonRefreshRetrieve(POManagerItf pm, Map map, Collection fgHints, boolean refresh){
//get an interator on the PIndexed elements
Iterator it = elements.iterator();
while(it.hasNext()){
//get the PIndexElement
CollectionElem iElem = (CollectionElem) it.next();
//get the po associated
PersistentObjectItf sp = (PersistentObjectItf) iElem.getElement(pm);
if(map != null && !map.containsKey(sp.getPName())){
synchronized(fgHints){
if (refresh) { //refresh
pm.speedoRefresh(sp, map, fgHints);
} else { //retrieve
pm.speedoRetrieve(sp, map, fgHints);
}
}
}
}
}
// ------------------------------------------------------------------------
// IMPLEMENTATION OF THE Collection INTERFACE
// ------------------------------------------------------------------------
public boolean add(Object o) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "add()");
}
GenClassElement gce = speedoAdd(o, false);
//Send the event about the new element adding
if (gce != null) {
Object obj = o;
if (obj instanceof PName) {
obj = gce.getElement(((PersistentObjectItf) gcpo).speedoGetPOManager());
}
if (obj instanceof PersistentObjectItf) {
gcpo.fireSpeedoElementAdded(obj);
}
}
return gce != null;
}
public boolean addAll(Collection c) {
Iterator it = c.iterator();
boolean changed = false;
while (it.hasNext()) {
changed |= add(it.next());
}
return changed;
}
public void clear() {
if (elements.size() > 0) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "clear()");
}
Collection c = new ArrayList(this);
Iterator i = c.iterator();
while (i.hasNext()) {
remove(i.next());
}
elements.clear(); // efficient, but does not work with relations
supportDelta = false;
} else {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "clear(): empty collection");
}
}
}
public boolean contains(Object o) {
tmpelem.setElement(o);
Iterator it = elements.iterator();
while (it.hasNext()) {
PIndexedElem e = (PIndexedElem) it.next();
if (e.equals(tmpelem) && e.getElemStatus() != PIndexedElem.ELEM_DELETED) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "contains(): yes");
}
return true;
}
}
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "contains(): no");
}
return false;
}
public boolean containsAll(Collection c) {
Iterator it = c.iterator();
while (it.hasNext()) {
if (!contains(it.next())) {
return false;
}
}
return true;
}
public boolean equals(Object o) {
return (o instanceof Collection) &&
((Collection) o).size() == size() &&
containsAll((Collection) o);
}
public boolean isEmpty() {
if (elements.isEmpty()) {
return true;
}
Iterator it = elements.iterator();
// The set is empty if it contains only deleted indexed elements
while (it.hasNext()) {
if (((PIndexedElem) it.next()).getElemStatus() != PIndexedElem.ELEM_DELETED) {
return false;
}
}
return true;
}
public Iterator iterator() {
return new PIndexedElemIterator(elements, this,
((PersistentObjectItf) gcpo).speedoGetPOManager(), ((Loggable) gcpo).getLogger());
}
public boolean remove(Object o) {
GenClassElement gce = speedoRemove2(o);
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "remove: gce=" + gce);
}
if (gce != null) {
Object obj = gce.getElement(((PersistentObjectItf) gcpo).speedoGetPOManager());
if (obj != null) {
gcpo.fireSpeedoElementRemoved(obj);
}
}
return gce != null;
}
public boolean removeAll(Collection c) {
Iterator it = c.iterator();
boolean removed = true;
while (it.hasNext()) {
removed &= remove(it.next());
}
return removed;
}
public boolean retainAll(Collection c) {
throw new UnsupportedOperationException();
}
public int size() {
Iterator it = elements.iterator();
int size = 0;
while (it.hasNext()) {
if (((PIndexedElem) it.next()).getElemStatus() != PIndexedElem.ELEM_DELETED) {
size++;
}
}
return size;
}
public Object[] toArray() {
ArrayList al = new ArrayList(elements.size());
Iterator it = elements.iterator();
while (it.hasNext()) {
GenClassElement gce = (GenClassElement) it.next();
if (gce.getElemStatus() != PIndexedElem.ELEM_DELETED) {
al.add(gce.getElement());
}
}
return al.toArray();
}
public Object[] toArray(Object[] a) {
ArrayList al = new ArrayList(elements.size());
Iterator it = elements.iterator();
while (it.hasNext()) {
GenClassElement gce = (GenClassElement) it.next();
if (gce.getElemStatus() != PIndexedElem.ELEM_DELETED) {
al.add(gce.getElement());
}
}
return al.toArray(a);
}
// ------------------------------------------------------------------------
// IMPLEMENTATION OF THE PGenClassAccessor INTERFACE
// ------------------------------------------------------------------------
public PIndexedElem createPIndexedElem(GenClassAccessor gca) {
return new CollectionElem(gca);
}
// ------------------------------------------------------------------------
// IMPLEMENTATION OF THE SpeedoGenClassCoherence INTERFACE
// ------------------------------------------------------------------------
public boolean speedoAdd(Object o, Object hints) {
return speedoAdd(o, false) != null;
}
protected GenClassElement speedoAdd(Object o, boolean allowDuplicates) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "speedoAdd(, " + allowDuplicates + ")");
}
if (o instanceof PersistentObjectItf && !((PersistentObjectItf) o).speedoIsActive()) {
//if not detached, then make it persistent
if (((PersistentObjectItf) o).speedoGetReferenceState().getDetachedStatus()
== DetachedLifeCycle.DETACHED_NONE) {
((AbstractGenClassHome) getSpeedoPO().speedoGetHome())
.makePersistent(null, (PersistentObjectItf) o,
(SpeedoGenClassPO) getSpeedoPO(), null);
} else {
//attach it
//problem the map is re-instanciated on each add...
o = getSpeedoPO().speedoGetPOManager().speedoAttachCopy(o, new HashMap());
}
}
synchronized (this) {
if (!allowDuplicates && contains(o)) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "speedoAdd(, " + allowDuplicates + "): already exist");
}
return null;
}
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "speedoAdd(, " + allowDuplicates + "): adding");
}
// Put it in the "elements" list
GenClassElement element = (GenClassElement) createPIndexedElem();
element.setElement(o);
return (elements.add(element) ? element : null);
}
}
protected GenClassElement speedoAddOnAttach(Object o, boolean allowDuplicates) {
//TODO: implement the method
return speedoAdd(o, allowDuplicates);
}
public boolean speedoRemove(Object o, Object hints) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "speedoRemove()");
}
return speedoRemove2(o) != null;
}
public void forceDetachedDirty() {
}
public void restoreDetachedNone() {
}
}