/**
* 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.map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.objectweb.jorm.api.PExceptionIO;
import org.objectweb.jorm.api.PIndexedElem;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.speedo.api.Debug;
import org.objectweb.speedo.genclass.GenClassAccessor;
import org.objectweb.speedo.genclass.GenClassElement;
import org.objectweb.speedo.genclass.AbstractGenClassHome;
import org.objectweb.speedo.genclass.api.SpeedoGenClassPO;
import org.objectweb.speedo.metadata.SpeedoFetchGroup;
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;
public class MapAccessor
extends GenClassAccessor
implements Map {
/**
* A map containing the indexed elements of the genclass. It associates each
* element to its index. This map does NOT contain the deleted elements.
*/
protected Map map = null;
/**
* Instanciates and initializes a new map with an initial size.
*/
public MapAccessor(SpeedoGenClassPO apo) {
super(apo);
this.elements = new ArrayList();
this.map = (Map) gcpo.createGenClass();
}
public void makePersistent(POManagerItf pm) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "makePersistent");
}
if (elements.size() > 0) {
((AbstractGenClassHome) getSpeedoPO().speedoGetHome())
.makePersistent(pm, values().iterator(), null, null);
}
}
public void deletePersistent(POManagerItf pm) {
clear();
}
public void setElements(Object o) {
clear();
if (o != null) {
putAll(((Map) o));
}
}
public void loadFieldsFromAccessor(StateItf sa) {
MapAccessor ma = (MapAccessor) sa;
map.clear();
map.putAll(ma.map);
supportDelta = ma.supportDelta;
elements.clear();
elements.addAll(ma.elements);
speedoSetStatus(ma.speedoGetStatus());
}
public boolean speedoAdd(Object elemToAdd, Object hints) {
put(hints, elemToAdd, false);
return true;
}
public boolean speedoRemove(Object elemToRemove, Object hints) {
return remove(hints, false) != null;
}
public void detachCopy(POManagerItf pm, Map detachCtx, StateItf fieldsClone, Collection fgHints){
((MapAccessor)fieldsClone).map = new HashMap();
((MapAccessor)fieldsClone).loadFieldsFromAccessor(this);
//copy the elements in the map: collection is no more available when the po is not active
((MapAccessor)fieldsClone).map.clear();
boolean valueToFetch = false;
Collection fgHintsToSend = new ArrayList();
synchronized(fgHints){
Collection copyFgHints = new ArrayList(fgHints);
Iterator it2 = copyFgHints.iterator();
while(it2.hasNext() && !valueToFetch){
String s = (String) it2.next();
int idxValue = s.indexOf(SpeedoFetchGroup.FG_VALUE);
if(s.indexOf(SpeedoFetchGroup.FG_KEY) != -1)
fgHints.remove(s);
else if(idxValue != -1){
valueToFetch = true;
fgHints.remove(s);
String add = s.substring(idxValue+SpeedoFetchGroup.FG_VALUE.length());
if(add.length() > 0){
fgHintsToSend.add(add);
}
}
}
}
//get an interator on the PIndexed elements
Iterator it = ((GenClassAccessor) fieldsClone).elements.iterator();
while(it.hasNext()){
//get the PIndexElement
MapElem iElem = (MapElem) it.next();
Object elem = iElem.getElement(pm);
Object clone = null;
//if the element is a Persistent Object
if (elem instanceof PersistentObjectItf) {
//get the speedo po associated
PersistentObjectItf sp = (PersistentObjectItf) elem;
if(detachCtx != null)
clone = detachCtx.get(sp);
if(clone == null){
synchronized(fgHints){
if(valueToFetch){
// get the clone of the speedo po
clone = pm.speedoDetachCopy(sp, detachCtx, fgHintsToSend);
}
}
}
} else {
//just copy the java object
clone = elem;
}
Object key = iElem.getIndex();
//add it in the map of elements reachable when the po is no more active
((MapAccessor)fieldsClone).map.put(key, clone);
}
((GenClassAccessor) fieldsClone).speedoSetStatus(LifeCycle.TRANSIENT);
}
public void attachCopy(POManagerItf pm, Map attachCtx, StateItf fieldsClone) {
MapAccessor ma = (MapAccessor) fieldsClone;
//get an iterator on the PIndexed elements of the collection
Iterator it = ma.map.entrySet().iterator();
while(it.hasNext()){
Map.Entry mapEntry = (Map.Entry) it.next();
Object elem = mapEntry.getValue();
Object key = mapEntry.getKey();
boolean put = true;
if (elem instanceof MapElem) {
//if the element has not been touched, do not put it in the map again
put = false;
elem = ((MapElem) elem).getElement();
}
//if the element is a Persistent Object, attach it
if (elem instanceof PersistentObjectItf) {
//get the detached speedo po
PersistentObjectItf spDetached = (PersistentObjectItf) elem;
PersistentObjectItf sp = (PersistentObjectItf) attachCtx.get(spDetached);
if(sp == null){
attachCtx.put(spDetached, sp);
sp = (PersistentObjectItf) pm.speedoAttachCopy(spDetached, attachCtx);
}
//get the pname
elem = sp.getPName();
}
if (put) {
//put the elem in the element list
put(key, elem, false);
}
}
}
public void refresh(POManagerItf pm, Map refreshCtx, Collection fgHints){
commonRefreshRetrieve(pm, refreshCtx, fgHints, true);
}
public void retrieve(POManagerItf pm, Map retrieveCtx, Collection fgHints){
commonRefreshRetrieve(pm, retrieveCtx, fgHints, false);
}
/**
*
* @param pm
* @param ctx
* @param fgHints
* @param refresh: if true, call refresh, else call retrieve
*/
//TODO: what about refreshing a map of String, Double, Integer???
private void commonRefreshRetrieve(POManagerItf pm, Map ctx, Collection fgHints, boolean refresh){
boolean valueToFetch = false;
Collection fgHintsToSend = new ArrayList();
synchronized(fgHints){
Collection copyFgHints = new ArrayList(fgHints);
Iterator it2 = copyFgHints.iterator();
while(it2.hasNext() && !valueToFetch){
String s = (String) it2.next();
int idxValue = s.indexOf(SpeedoFetchGroup.FG_VALUE);
if(s.indexOf(SpeedoFetchGroup.FG_KEY) != -1)
fgHints.remove(s);
else if(idxValue != -1){
valueToFetch = true;
fgHints.remove(s);
String add = s.substring(idxValue+SpeedoFetchGroup.FG_VALUE.length());
if(add.length() > 0){
fgHintsToSend.add(add);
}
}
}
}
//get an interator on the PIndexed elements
Iterator it = this.elements.iterator();
while(it.hasNext()){
//get the PIndexElement
MapElem iElem = (MapElem) it.next();
Object elem = iElem.getElement(pm);
//if the element is a PO
if (elem instanceof PersistentObjectItf) {
//get the speedo po associated
PersistentObjectItf sp = (PersistentObjectItf) elem;
if(ctx != null && !ctx.containsKey(sp.getPName())){
synchronized(fgHints){
if(valueToFetch){
if (refresh) {
//refresh
pm.speedoRefresh(sp, ctx, fgHintsToSend);
} else {
//retrieve
pm.speedoRetrieve(sp, ctx, fgHintsToSend);
}
}
}
}
}
}
}
public Object put(Object key, Object value, boolean withCoherence) {
MapElem element = null;
Object res = map.get(key);
//if detached, just put the value in the map
if(detachedStatus != DetachedLifeCycle.DETACHED_NONE) {
map.put(key, value);
} else {
if (res == null) {
// Put a new entry in the map
element = (MapElem) createPIndexedElem();
element.setIndex(key);
elements.add(element);
map.put(key, element);
} else {
// Mark as modify
element = (MapElem) res;
element.setStatus(PIndexedElem.ELEM_MODIFIED);
res = element.getElement(gcpo.speedoGetPOManager());
}
element.setElement(value);
if (value instanceof PersistentObjectItf) {
((AbstractGenClassHome) getSpeedoPO().speedoGetHome())
.makePersistent(null, (PersistentObjectItf) value,
(SpeedoGenClassPO) getSpeedoPO(), null);
}
}
if (withCoherence) {
Object v = value;
if (v instanceof PName) {
v = element.getElement(gcpo.speedoGetPOManager());
}
if (v instanceof PersistentObjectItf) {
gcpo.fireSpeedoElementAdded(v);
}
}
return res;
}
public Object remove(Object key, boolean withCoherence) {
//if detached, just remove the key entry from the map, it returns the value
if(detachedStatus != DetachedLifeCycle.DETACHED_NONE) {
return map.remove(key);
} else {
GenClassElement gcelem = (GenClassElement) map.get(key);
if (gcelem == null) {
return null;
}
gcelem.setStatus(PIndexedElem.ELEM_DELETED);
Object value = get(key);
map.remove(key);
if (withCoherence) {
Object el = gcelem.getElement(gcpo.speedoGetPOManager());
if (el != null) {
gcpo.fireSpeedoElementRemoved(el);
}
}
return value;
}
}
// ------------------------------------------------------------------------
// IMPLEMENTATION OF THE Map INTERFACE
// ------------------------------------------------------------------------
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
// TODO: support the containsValue method on Map
return false;
}
public Object get(Object key) {
Object value = map.get(key);
if (value == null) {
return null;
} else {
//if the state is detached
if(detachedStatus != DetachedLifeCycle.DETACHED_NONE)
return value;
else
return ((MapElem) value).getElement(gcpo.speedoGetPOManager());
}
}
public Object put(Object key, Object value) {
return put(key, value, true);
}
public Object remove(Object key) {
return remove(key, true);
}
public void putAll(Map t) {
Iterator i = t.entrySet().iterator();
while (i.hasNext()) {
Map.Entry entry = (Map.Entry) i.next();
put(entry.getKey(), entry.getValue());
}
}
public void clear() {
if (detachedStatus == DetachedLifeCycle.DETACHED_NONE) {
Iterator i = elements.iterator();
POManagerItf pm = null;
while (i.hasNext()) {
GenClassElement gcelem = (GenClassElement) i.next();
gcelem.setStatus(PIndexedElem.ELEM_DELETED);
if (pm == null) {
pm = gcpo.speedoGetPOManager();
}
Object el = gcelem.getElement(pm);
if (el != null) {
gcpo.fireSpeedoElementRemoved(el);
}
}
}
map.clear();
supportDelta = false;
}
public Set keySet() {
return Collections.unmodifiableSet(map.keySet());
}
public Collection values() {
ArrayList res = new ArrayList(elements.size());
//if detached, look into map
if (detachedStatus != DetachedLifeCycle.DETACHED_NONE) {
Iterator it = map.values().iterator();
while(it.hasNext()) {
res.add(it.next());
}
} else {
//else, look into elements
for(int i=(elements.size()-1); i>=0; i--) {
MapElem me = (MapElem) elements.get(i);
if (me.getElemStatus() != PIndexedElem.ELEM_DELETED) {
res.add(me.getElement());
}
}
}
return Collections.unmodifiableCollection(res);
}
public Set entrySet() {
if (detachedStatus != DetachedLifeCycle.DETACHED_NONE) {
return map.entrySet();
} else {
Set res = new HashSet(elements.size());
for(int i=(elements.size()-1); i>=0; i--) {
MapElem me = (MapElem) elements.get(i);
if (me.getElemStatus() != PIndexedElem.ELEM_DELETED) {
res.add(new MyMapEntry(me));
}
}
return Collections.unmodifiableSet(res);
}
}
private class MyMapEntry implements Map.Entry {
MapElem me;
public MyMapEntry(MapElem _me) {
this.me = _me;
}
public Object getKey() {
return me.getIndex();
}
public Object getValue() {
return me.getElement();
}
public Object setValue(Object value) {
Object res = me.getElement();
me.setStatus(PIndexedElem.ELEM_MODIFIED);
me.setElement(value);
return res;
}
}
// ------------------------------------------------------------------------
// IMPLEMENTATION OF THE PGenClassAccessor INTERFACE
// ------------------------------------------------------------------------
public PIndexedElem createPIndexedElem(GenClassAccessor gca) {
return new MapElem(gca);
}
public void paAdd(PIndexedElem elem, Object conn) throws PExceptionIO {
GenClassElement gcelem = (GenClassElement) elem;
// the elem is read from the DS, set it to unmodified.
gcelem.setStatus(PIndexedElem.ELEM_UNMODIFIED);
elements.add(gcelem);
map.put(gcelem.getIndex(), gcelem);
}
public int paGetNbElem() {
return size();
}
public Iterator paIterator() {
return elements.iterator();
}
public void paSetNbElem(int nbelem) {
if (nbelem == -1) {
elements = new ArrayList();
} else {
elements = new ArrayList(nbelem);
}
map = (Map) gcpo.createGenClass();
}
public void forceDetachedDirty() {
}
public void restoreDetachedNone() {
}
public void makePersistentOnAttach(POManagerItf pm, Map map) {
if (Debug.ON && getLogger() != null) {
logger.log(BasicLevel.DEBUG, "makePersistent");
}
if (elements.size() > 0) {
((AbstractGenClassHome) getSpeedoPO().speedoGetHome())
.makePersistent(pm, values().iterator(), null, map);
}
}
}