/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.resource; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EObject; import org.teiid.core.designer.id.ObjectID; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.ModelerCore; /** * EObjectCacheImpl - manager of maps containing EObject instances for resources * in a resource set. The maps are keyed on the UUID associated with the EObject. * @since 8.0 */ public class EObjectCacheImpl implements EObjectCache { private static final int MAX_MAP_SIZE = 1500; private static final int MAP_CONSOLIDATION_SIZE = 300; private final Collection mapOfMaps; private Map currentMap; // ================================================================================== // C O N S T R U C T O R S // ================================================================================== public EObjectCacheImpl() { this.currentMap = createMap(); this.mapOfMaps = new ArrayList(); this.mapOfMaps.add(this.currentMap); } //================================================================================== // I N T E R F A C E M E T H O D S //================================================================================== /** * @see org.teiid.designer.core.resource.EObjectCache#add(org.eclipse.emf.ecore.EObject[], boolean) * @since 4.3 */ @Override public void add(final EObject[] values, final boolean recurse) { for (int i = 0; i != values.length; ++i) { add(values[i],recurse); } } /** * @see org.teiid.designer.core.resource.EObjectCache#add(org.eclipse.emf.ecore.EObject, boolean) * @since 4.3 */ @Override public void add(final EObject value, final boolean recurse) { CoreArgCheck.isNotNull(value); final Object key = getCacheKey(value); // If the manager already has an EObject for this key then remove it. // A new EObject instance may have been instantiated due to reloading // a resource so we want this instance in the maps now. final Map eObjMap = findMapForKey(key); if (eObjMap != null) { eObjMap.remove(key); } // Add the new value to the current map if (this.currentMap.size() < MAX_MAP_SIZE) { this.currentMap.put(key, value); } else { boolean newMap = true; // Add to existing map if one has room for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { final Map next = (Map)iter.next(); if (next != this.currentMap && next.size() < MAX_MAP_SIZE) { this.currentMap = next; this.currentMap.put(key, value); newMap = false; break; } } // need to create a new map if (newMap) { this.currentMap = createMap(); mapOfMaps.add(this.currentMap); this.currentMap.put(key, value); } } // Continue the add operation which will add this EObject // instance to the cache along with the contents of this // EObject by recursively calling add(EObject,boolean) on // the owned EObject instances. if (recurse) { for (final Iterator iter = value.eContents().iterator(); iter.hasNext();) { final Object obj = iter.next(); if (obj instanceof EObject) { add((EObject)obj, recurse); } } } } /** * @see org.teiid.designer.core.resource.EObjectCache#clear() * @since 4.3 */ @Override public void clear() { try { for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { Map next = (Map)iter.next(); next.clear(); iter.remove(); } // Reset the state back to when it was first constructed this.currentMap = createMap(); this.mapOfMaps.add(this.currentMap); } finally { runGC(); } } /** * @see org.teiid.designer.core.resource.EObjectCache#containsKey(ObjectID) * @since 4.3 */ @Override public boolean containsKey(final ObjectID key) { if (this.currentMap.containsKey(key)) { return true; } for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { Map next = (Map)iter.next(); if (next != this.currentMap && next.containsKey(key)) { return true; } } return false; } /** * @see org.teiid.designer.core.resource.EObjectCache#containsValue(org.eclipse.emf.ecore.EObject) * @since 4.3 */ @Override public boolean containsValue(final EObject value) { if (this.currentMap.containsValue(value)) { return true; } for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { Map next = (Map)iter.next(); if (next != this.currentMap && next.containsValue(value)) { return true; } } return false; } /** * @see org.teiid.designer.core.resource.EObjectCache#get(org.teiid.core.designer.id.ObjectID) * @since 4.3 */ @Override public EObject get(final ObjectID key) { if (key == null) { return null; } final Map map = findMapForKey(key); if (map != null) { return (EObject)map.get(key); } return null; } /** * @see org.teiid.designer.core.resource.EObjectCache#remove(org.eclipse.emf.ecore.EObject, boolean) * @since 4.3 */ @Override public void remove(final EObject value, final boolean recurse) { CoreArgCheck.isNotNull(value); final Object key = getCacheKey(value); final Map map = findMapForValue(value); if (map != null) { map.remove(key); // See if we should consolidate this map if(map != this.currentMap && map.size() < MAP_CONSOLIDATION_SIZE) { consolidateMap(map); } } // Remove the entire tree if (recurse) { for (final Iterator iter = value.eContents().iterator(); iter.hasNext();) { final Object obj = iter.next(); if (obj instanceof EObject) { remove((EObject)obj, recurse); } } } } /** * @see org.teiid.designer.core.resource.EObjectCache#remove(org.eclipse.emf.ecore.EObject[], boolean) * @since 4.3 */ @Override public void remove(final EObject[] values, final boolean recurse) { for (int i = 0; i != values.length; ++i) { remove(values[i],recurse); } } /** * @see org.teiid.designer.core.resource.EObjectCache#remove(ObjectID, boolean) * @since 4.3 */ @Override public void remove(final ObjectID key, final boolean recurse) { CoreArgCheck.isNotNull(key); EObject value = null; final Map map = findMapForKey(key); if (map != null) { value = (EObject)map.remove(key); // See if we should consolidate this map if(map != this.currentMap && map.size() < MAP_CONSOLIDATION_SIZE) { consolidateMap(map); } } // Remove the entire tree if (recurse && value != null) { for (final Iterator iter = value.eContents().iterator(); iter.hasNext();) { final Object obj = iter.next(); if (obj instanceof EObject) { remove((EObject)obj, recurse); } } } } /** * @see org.teiid.designer.core.resource.EObjectCache#remove(EObject[], boolean) * @since 4.3 */ @Override public void remove(final ObjectID[] keys, final boolean recurse) { for (int i = 0; i != keys.length; ++i) { remove(keys[i],recurse); } } /** * @see org.teiid.designer.core.resource.EObjectCache#size() * @since 4.3 */ @Override public int size() { int size = 0; for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { Map next = (Map)iter.next(); size += next.size(); } return size; } /** * @see org.teiid.designer.core.resource.EObjectCache#values() * @since 4.3 */ @Override public EObject[] values() { final List values = new ArrayList(size()); for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { Map next = (Map)iter.next(); values.addAll(next.values()); } return (EObject[])values.toArray(new EObject[values.size()]); } // ================================================================================== // P R O T E C T E D M E T H O D S // ================================================================================== protected Object getCacheKey(final EObject value) { CoreArgCheck.isNotNull(value); return ModelerCore.getObjectId(value); } protected void consolidateMap(final Map map) { if(map == this.currentMap) { return; } try { // Try currentMap first final int size = map.size(); if (this.currentMap.size() + size < MAX_MAP_SIZE) { this.currentMap.putAll(map); this.mapOfMaps.remove(map); return; } // Check other maps in the list for (final Iterator iter = mapOfMaps.iterator(); iter.hasNext();) { final Map next = (Map)iter.next(); if ((next != this.currentMap) && (next.size() + size < MAX_MAP_SIZE)) { next.putAll(map); this.mapOfMaps.remove(map); return; } } } finally { runGC(); } } protected Map findMapForValue(final EObject eObject){ final Object id = ModelerCore.getObjectId(eObject); return findMapForKey(id); } protected Map findMapForKey(final Object id) { if(id == null) { return null; } //check current map first; if(this.currentMap.containsKey(id) ) { return this.currentMap; } final Iterator maps = mapOfMaps.iterator(); while(maps.hasNext() ) { final Map next = (Map)maps.next(); if(next != this.currentMap && next.containsKey(id) ) { return next; } } return null; } /** * @return Returns the mapOfMaps. * @since 4.3 */ protected Collection getMapOfMaps() { return this.mapOfMaps; } protected Map createMap() { return new HashMap(); } protected void runGC() { // System.gc(); // Thread.yield(); } }