/** * <copyright> * * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Douglas Bitting * Martin Taal * * </copyright> * * $Id: PersistableEMap.java,v 1.11 2008/06/03 08:30:15 mtaal Exp $ */ package org.eclipse.emf.teneo.mapping.elist; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.BasicEMap; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.EMap; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.util.EcoreEMap; /** * A persistable emap which uses the PersistableEList as its delegate. Note that this implementation * is based on the implementation of the superclass. The superclass makes use of a delegate list to * store its content. This implementation puts a persistent list in this member. * * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> * @author <a href="mailto:jdboudreault@gmail.com">Jean-Denis Boudreault</a> * * @version $Revision: 1.11 $ */ public abstract class PersistableEMap<K, V> extends EcoreEMap<K, V> implements PersistableDelegateList<BasicEMap.Entry<K, V>> { private static final long serialVersionUID = 1L; /** The logger */ private static Log log = LogFactory.getLog(PersistableEMap.class); /** The logstring */ protected String logString; /** Used for assertion */ private final int featureID; /** Is loaded from backend */ private boolean isLoaded = false; /** Is being loaded from backend */ private boolean isLoading = false; /** * The owner of the objet. we must keep a copy since emap does not have one and the delegate * EList does not expose this field publicly */ private InternalEObject owner; /** * The persisted map handled by the orm layer. This delegate is the map we receive from the * database provider. It is kept all the time, any changes to the PersistableEMap are replicated * to the ormMap. * * This field will be null unless there is a map waiting to be lazy loaded */ protected Map<K, V> ormMapDelegate = null; /** Not supported constructor */ public PersistableEMap(EClass entryEClass, EList<BasicEMap.Entry<K, V>> delegateEList) { super(entryEClass, Map.Entry.class, delegateEList); throw new UnsupportedOperationException("Explicitly passing delegate list is not supported!"); } /** Constructor */ public PersistableEMap(EClass entryEClass, Class<?> entryClass, InternalEObject owner, EStructuralFeature feature) { // invoke constructor with no lazyLoadMapDelegate super(entryEClass, Map.Entry.class, owner, owner.eClass().getFeatureID(feature)); setDelegateEList(owner, feature, new ArrayList<Entry<K, V>>()); this.owner = owner; this.featureID = owner.eClass().getFeatureID(feature); log.debug("Created persistable emap for entry eclass " + entryEClass.getName()); } /** * This version will create the lsit completely, there is no lazy lading from this constructor * * @param entryEClass * @param entryClass * @param owner * @param featureID * @param ormMapDelegate * a java.util.map that is a proxy collection taht will be used when lazy load is * invoked. if it is null, then the map is considered as loaded */ public PersistableEMap(EClass entryEClass, InternalEObject owner, EStructuralFeature feature, List<BasicEMap.Entry<K, V>> list) { super(entryEClass, Map.Entry.class, owner, owner.eClass().getFeatureID(feature)); this.owner = owner; this.featureID = owner.eClass().getFeatureID(feature); // create our list setDelegateEList(owner, feature, list); // sets the size of this map // size(); log.debug("Created persistable emap for entry eclass " + entryEClass.getName()); } /** Sets the delegatelist to a persistablelist */ protected void setDelegateEList(InternalEObject owner, EStructuralFeature feature, List<BasicEMap.Entry<K, V>> delegateORMList) { assert (owner.eClass().getFeatureID(feature) == featureID); // NOTE BEWARE: the delegateEList is a member of the superclass! delegateEList = createDelegateEList(owner, feature, delegateORMList); logString = "EMap with entry eclass: " + entryEClass.getName() + " of member " + feature.getName() + " owned by " + owner.getClass().getName() + " with delegate list " + delegateORMList.getClass().getName(); log.debug("Created/reset elist " + logString); if (delegateORMList instanceof EList) { setLoaded(true); } else if (delegateORMList instanceof ArrayList) { // already loaded lists are packaged in // an elist setLoaded(delegateORMList.size() > 0); } } /** Needs to be implemented by concrete subclass */ protected abstract EList<BasicEMap.Entry<K, V>> createDelegateEList(InternalEObject owner, EStructuralFeature feature, List<BasicEMap.Entry<K, V>> delegateORMList); /** Replace the delegate */ @SuppressWarnings("unchecked") public void replaceDelegate(Object newDelegate) { setDelegateEList(owner, getEStructuralFeature(), (List<BasicEMap.Entry<K, V>>) newDelegate); setLoaded(false); } /** * Performs the load action if not yet oaded and sends out the load notification. */ protected void load() { if (isLoaded) { // reset the size size = delegateEList.size(); return; } // When we are loading we should not be reloaded! // this can happen in the jpox elist impl. when detaching if (isLoading) { return; } isLoading = true; log.debug("Loading " + getLogString()); // set the size size = this.size(); // prevent notifications to be sent out boolean eDeliver = this.getOwner().eDeliver(); boolean setDeliver = false; try { // only set to false if it was true if (eDeliver) { log.debug("Owner " + getOwner().getClass() + " set eDeliver to false"); getOwner().eSetDeliver(false); setDeliver = true; } } catch (UnsupportedOperationException e) { // in this case the eSetDeliver was not overridden from the // baseclass // ignore } try { doLoad(); // set the size size = this.size(); } finally { isLoaded = true; isLoading = false; if (setDeliver) { owner.eSetDeliver(eDeliver); } } } /** * The load method which should be overridden by the subclass to add lazyloading */ protected abstract void doLoad(); // // /** // * Overridden to prevent the super // */ // @Override // public void initializeDelegateEList() { // this.isLoaded = false; // this.size = 0; // this.delegateEList = null; // } /** Return ourselves, this class assumes that the emap is mapped as a list */ public Object getDelegate() { return this; } /** Returns true if the elist is loaded */ public boolean isLoaded() { return isLoaded; } /** * Overridden for access to size member */ @Override protected void ensureEntryDataExists() { load(); super.ensureEntryDataExists(); } /** * Overridden because of access to size attribute */ @Override public int size() { // the subclass can override the size to perform smart size determination if (!this.isLoaded()) { load(); } size = delegateEList.size(); return super.size(); } /** * Overridden because of access to size attribute */ @Override public boolean isEmpty() { size(); if (!this.isLoaded()) { return (this.size == 0); } return super.isEmpty(); } /* * Javadoc copied from interface. Overridden because of access to size attribute */ @Override public boolean containsKey(Object key) { load(); return super.containsKey(key); } /* * Javadoc copied from interface. Overridden because of access to size attribute */ @Override public Set<K> keySet() { load(); return super.keySet(); } /* * Javadoc copied from interface. Overridden because of access to size attribute */ @Override public Collection<V> values() { load(); return super.values(); } /* * Javadoc copied from interface. Overridden because of access to size attribute */ @Override public Set<Map.Entry<K, V>> entrySet() { load(); return super.entrySet(); } /* * Javadoc copied from interface. Overridden because of access to size attribute */ @Override public boolean containsValue(Object value) { load(); return super.containsValue(value); } /* * Javadoc copied from interface. Overridden because of access to size attribute */ @Override public V get(Object key) { load(); return super.get(key); } @Override public Map<K, V> map() { load(); if (view == null) { view = new View<K, V>(); } if (view.map == null) { view.map = new PersistableDelegatingMap(); } return view.map; } /** Used to tag the returned map class and give access to the owner */ public class PersistableDelegatingMap extends DelegatingMap { /** Return my owner */ public PersistableEMap<K, V> getOwner() { return PersistableEMap.this; } } /** Set the delegate again */ protected boolean isLoading() { return isLoading; } protected void setLoading(boolean isLoading) { this.isLoading = isLoading; } protected void setLoaded(boolean isLoaded) { this.isLoaded = isLoaded; log.debug("Isloaded is " + isLoaded); } protected String getLogString() { return logString; } protected InternalEObject getOwner() { return owner; } @Override public NotificationChain basicAdd(java.util.Map.Entry<K, V> object, NotificationChain notifications) { load(); return super.basicAdd(object, notifications); } @Override public org.eclipse.emf.common.util.BasicEMap.Entry<K, V> basicGet(int index) { load(); return super.basicGet(index); } @Override public Iterator<java.util.Map.Entry<K, V>> basicIterator() { load(); return super.basicIterator(); } @Override public List<java.util.Map.Entry<K, V>> basicList() { load(); return super.basicList(); } @Override public ListIterator<java.util.Map.Entry<K, V>> basicListIterator() { load(); return super.basicListIterator(); } @Override public ListIterator<java.util.Map.Entry<K, V>> basicListIterator(int index) { load(); return super.basicListIterator(index); } @Override public NotificationChain basicRemove(Object object, NotificationChain notifications) { load(); return super.basicRemove(object, notifications); } @Override public void addUnique(java.util.Map.Entry<K, V> object) { load(); super.addUnique(object); } @Override public void addUnique(int index, java.util.Map.Entry<K, V> object) { load(); super.addUnique(index, object); } @Override public java.util.Map.Entry<K, V> setUnique(int index, java.util.Map.Entry<K, V> object) { load(); return super.setUnique(index, object); } @Override public boolean add(java.util.Map.Entry<K, V> object) { load(); return super.add(object); } @Override public void add(int index, java.util.Map.Entry<K, V> object) { load(); super.add(index, object); } @Override public boolean addAll(Collection<? extends java.util.Map.Entry<K, V>> collection) { load(); return super.addAll(collection); } @Override public boolean addAll(int index, Collection<? extends java.util.Map.Entry<K, V>> collection) { load(); return super.addAll(index, collection); } @Override public void clear() { this.isLoaded = false; super.clear(); } @Override public org.eclipse.emf.common.util.BasicEMap.Entry<K, V> get(int index) { load(); return super.get(index); } @Override protected int indexOf(int hash) { load(); return super.indexOf(hash); } @Override public int indexOf(Object object) { load(); return super.indexOf(object); } @Override public int indexOfKey(Object key) { load(); return super.indexOfKey(key); } @Override public Iterator<java.util.Map.Entry<K, V>> iterator() { load(); return super.iterator(); } @Override public int lastIndexOf(Object object) { load(); return super.lastIndexOf(object); } @Override public ListIterator<java.util.Map.Entry<K, V>> listIterator() { load(); return super.listIterator(); } @Override public ListIterator<java.util.Map.Entry<K, V>> listIterator(int index) { load(); return super.listIterator(index); } @Override public void move(int index, java.util.Map.Entry<K, V> object) { load(); super.move(index, object); } @Override public java.util.Map.Entry<K, V> move(int targetIndex, int sourceIndex) { load(); return super.move(targetIndex, sourceIndex); } @Override public V put(K key, V value) { load(); return super.put(key, value); } @Override public void putAll(EMap<? extends K, ? extends V> map) { load(); super.putAll(map); } @Override public void putAll(Map<? extends K, ? extends V> map) { load(); super.putAll(map); } @Override protected V putEntry(org.eclipse.emf.common.util.BasicEMap.Entry<K, V> entry, V value) { load(); return super.putEntry(entry, value); } @Override public java.util.Map.Entry<K, V> remove(int index) { load(); return super.remove(index); } @Override public boolean remove(Object object) { load(); return super.remove(object); } @Override public boolean removeAll(Collection<?> collection) { load(); return super.removeAll(collection); } @Override protected V removeEntry(int index, int entryIndex) { load(); return super.removeEntry(index, entryIndex); } @Override public V removeKey(Object key) { load(); return super.removeKey(key); } @Override protected V resolve(K key, V value) { load(); return super.resolve(key, value); } @Override public boolean retainAll(Collection<?> collection) { load(); return super.retainAll(collection); } @Override public java.util.Map.Entry<K, V> set(int index, java.util.Map.Entry<K, V> object) { load(); return super.set(index, object); } @Override public List<java.util.Map.Entry<K, V>> subList(int start, int end) { load(); return super.subList(start, end); } @Override public Object[] toArray() { load(); return super.toArray(); } @Override public <T> T[] toArray(T[] array) { load(); return super.toArray(array); } @Override public void set(Object value) { load(); super.set(value); } }