/** * <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: * Martin Taal - Initial API and implementation * * </copyright> * * $Id: EContainerRepairControl.java,v 1.10 2009/03/15 23:25:09 mtaal Exp $ */ package org.eclipse.emf.teneo; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.teneo.mapping.elist.PersistableDelegateList; import org.eclipse.emf.teneo.mapping.elist.PersistableEList; import org.eclipse.emf.teneo.mapping.elist.PersistableFeatureMap; /** * Supports the repair of the eContainer and resource setting of child objects * when an object is loaded from the backing store. * * Repair of the eContainer is required in two distinct cases: 1) 1:1 relation: * in this case the repair is implemented in the caching mechanism. This was the * correct location because in jpox an object is added to the level 1 cache just * after it is retrieved from the db and before it is passed on to the * requesting application. 2) 1:n relation: in this case the EListWrapper knows * that a containment relation is being loaded and calls the equivalent methods * here. * * Note that both cases need to take into account two-way relatiofns. For * two-way relations the featureid of the opposing ereferencing is used. For * one-way relations emf apparently works with negative featureid's. * * This class also supports caching so that the system can quickly determine if * for a certain class eContainers need to be set in child objects. * * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> * @version $Revision: 1.10 $ */ public class EContainerRepairControl { /** The logger */ private static Log log = LogFactory.getLog(EContainerRepairControl.class); /** Hashmap of classes for which no repair is required */ private static final Hashtable<Class<?>, Class<?>> norepairRequired = new Hashtable<Class<?>, Class<?>>(); /** Hashmap of repair controls for a certain class */ private static final Hashtable<Class<?>, List<RepairControl>> repair = new Hashtable<Class<?>, List<RepairControl>>(); /** * Recursively sets the resource of the object and all its referenced * objects, only if the object has a resource which is not set and does not * have a container. */ public static void setEResourceToAlLContent(InternalEObject start, Resource res) { for (EStructuralFeature estruct : start.eClass() .getEAllStructuralFeatures()) { if (estruct instanceof EReference) { final EReference eref = (EReference) estruct; if (eref.isMany()) { final EList<?> list = (EList<?>) start.eGet(eref); if (list == null) { continue; } if ((list instanceof PersistableEList) && !((PersistableEList<?>) list).isLoaded()) { continue; } if ((list instanceof PersistableFeatureMap) && !((PersistableFeatureMap) list).isLoaded()) { continue; } for (int i = 0; i < list.size(); i++) { final InternalEObject child = (InternalEObject) list .get(i); if (child.eResource() == null) // no container { setResource(child, new ArrayList<EObject>(), (Resource.Internal) res); } } } else { final InternalEObject child = (InternalEObject) start .eGet(eref); if (child != null && child.eResource() == null) { setResource(child, new ArrayList<EObject>(), (Resource.Internal) res); } } } } } /** Sets the resource on an object or if it has a container on its container */ private static void setResource(InternalEObject eobj, ArrayList<EObject> objs, Resource.Internal res) { // been here go away if (objs.contains(eobj)) { return; } // set the resource here or at the container if (eobj.eResource() == null) { if (eobj.eContainer() == null) { eobj.eSetResource(res, null); } else { objs.add(eobj); setResource((InternalEObject) eobj.eContainer(), objs, res); } } } /** Method to repair the eContainer of the child object of this object */ public static void repair(Object owner) { if (log.isDebugEnabled()) { log.debug("Repairing container relations of children of: " + owner.getClass().getName()); } if (!(owner instanceof InternalEObject)) { return; } if (norepairRequired.get(owner.getClass()) != null) { return; } List<RepairControl> repairList = repair.get(owner.getClass()); if (repairList == null) { repairList = buildRepairList((InternalEObject) owner); } if (log.isDebugEnabled() && repairList.size() > 0) { log.debug("Repairing container relations of children of: " + owner.getClass().getName()); } for (int i = 0; i < repairList.size(); i++) { RepairControl repairControl = repairList.get(i); if (log.isDebugEnabled()) { log.debug("Repairing reference " + repairControl.container.getName() + " to child " + repairControl.childClass.getName()); } repairControl.repair((InternalEObject) owner); } } /** * Convenience method to just set the container directly for an object, this * method does not cascade down. The featureid is the id of the feature of * the owner which contains the child. The feature id is corrected in the * method. */ public static void setContainer(InternalEObject owner, InternalEObject child, EStructuralFeature estruct) { if (child.eContainer() == owner) { return; } final int featureID; if (estruct instanceof EReference && ((EReference) estruct).getEOpposite() != null) { featureID = child.eClass().getFeatureID( ((EReference) estruct).getEOpposite()); } else { featureID = InternalEObject.EOPPOSITE_FEATURE_BASE - owner.eClass().getFeatureID(estruct); } child.eBasicSetContainer(owner, featureID, null); } /** * Method to repair the eContainer of the child object of this object. Note * the featureid is internally translated to an econtainer id, nl. subtract * from EOPPOSITE_FEATURE_BASE */ public static void repair(Object owner, Object child, EStructuralFeature estruct) { if (!(owner instanceof InternalEObject)) { return; } if (!(child instanceof InternalEObject)) { return; } final int correctedFeatureID; if (estruct instanceof EReference && ((EReference) estruct).getEOpposite() != null) { correctedFeatureID = ((InternalEObject) child).eClass() .getFeatureID(((EReference) estruct).getEOpposite()); } else { correctedFeatureID = InternalEObject.EOPPOSITE_FEATURE_BASE - ((InternalEObject) owner).eClass().getFeatureID(estruct); } if (norepairRequired.get(owner.getClass()) != null) { return; } List<RepairControl> repairList = repair.get(owner.getClass()); if (repairList == null) { repairList = buildRepairList((InternalEObject) owner); } if (log.isDebugEnabled() && repairList.size() > 0) { log.debug("Repairing container relations of children of: " + owner.getClass().getName()); } for (int i = 0; i < repairList.size(); i++) { RepairControl repairControl = repairList.get(i); if (repairControl.getFeatureID() == correctedFeatureID && ((Class<?>) repairControl.childClass) .isAssignableFrom(child.getClass())) { repairControl.repair((InternalEObject) owner, (InternalEObject) child); return; } } } /** Builds a repair control list for an object */ private static List<RepairControl> buildRepairList(InternalEObject owner) { final ArrayList<RepairControl> result = new ArrayList<RepairControl>(); for (EStructuralFeature estruct : owner.eClass() .getEAllStructuralFeatures()) { if (estruct instanceof EReference) { final EReference eref = (EReference) estruct; if (eref.isContainment()) { // now check if we are two or not if (eref.getEOpposite() != null) { result.add(new TwoWayContainer(eref, eref .getEOpposite())); } else { result.add(new OneWayContainer(eref)); } } } } if (result.size() == 0) { norepairRequired.put(owner.getClass(), owner.getClass()); } else { repair.put(owner.getClass(), result); } return result; } /** Abstract class for repairing containers */ private static abstract class RepairControl { /** The ereference of the owner which contains the childs */ private final EReference container; /** Some shortcuts for spead */ private final Class<?> childClass; /** Featureid set as container */ private final int featureID; /** Constructor */ RepairControl(EReference containerReference, int myFeatureID) { container = containerReference; childClass = container.getEType().getInstanceClass(); featureID = myFeatureID; } /** Returns the feature id of this containment relation */ public int getFeatureID() { return featureID; } /** Repairs all containers of the owner */ void repair(InternalEObject owner) { // The container repair of a list is done through the repair(owner, // child) method, // directly in the elist.doLoadFromStore method if (container.isMany()) { return; } final Object containedObject = owner.eGet(container); if (containedObject == null) // not set { return; } // no list should be caught in the first line assert (!(containedObject instanceof PersistableDelegateList)); /* * if (containedObject instanceof JPOXEList) { if * (((JPOXEList)containedObject).getOwner() == owner) return; * * throw new StoreJPOXEmfException("Owner of containerobject is * different from passed owner, " + "this should have been solved in * the elist" + containedObject.getClass() + "/" + * owner.getClass().getName() + "/" + container.getName()); } */ if (!(containedObject instanceof InternalEObject)) { return; } final InternalEObject containedEObject = (InternalEObject) containedObject; if (containedEObject.eContainer() == owner) { return; // already set? } if (log.isDebugEnabled()) { log.debug("Set container of child " + containedObject.getClass().getName() + " containerfield " + container.getName()); } // and set it containedEObject.eBasicSetContainer(owner, featureID, null); // also repair the resource if applicable! /* * if (containedObject instanceof InternalEObject) { final * InternalEObject eobj = (InternalEObject)containedObject; if * (eobj.eResource() != owner.eResource()) { * log.debug("Set resource of eobj " + eobj.getClass().getName() + * " to resource " + owner.eResource().getURI()); * eobj.eSetResource((Resource.Internal)owner.eResource(), null); } * } */ // and also do its children EContainerRepairControl.repair(containedEObject); } /** Repairs all specific relation */ void repair(InternalEObject owner, InternalEObject child) { if (!childClass.isAssignableFrom(child.getClass())) { return; // not handled by this container } if (child.eContainer() == owner) { return; // already set? } if (log.isDebugEnabled()) { log.debug("Set container of child " + child.getClass().getName() + " containerfield " + container.getName()); } // and set it child.eBasicSetContainer(owner, featureID, null); // also repair the resource if applicable! /* * if (child instanceof InternalEObject) { final InternalEObject * eobj = (InternalEObject)child; Object ores = owner.eResource(); * Object eres = eobj.eResource(); if (eobj.eResource() != * owner.eResource()) { log.debug("Set resource of eobj * " + eobj.getClass().getName() + " to resource " + * owner.eResource().getURI()); * eobj.eSetResource((Resource.Internal)owner.eResource(), null); } * } */ EContainerRepairControl.repair(child); } } /** * Class handles a oneway container relation, in this case the * eBasicSetContainer is used. */ private static class OneWayContainer extends RepairControl { /** Constructor */ OneWayContainer(EReference containerReference) { super(containerReference, InternalEObject.EOPPOSITE_FEATURE_BASE - containerReference.getFeatureID()); } } /** * Class handles a twoway container relation, in this case the * eBasicSetContainer is used. */ private static class TwoWayContainer extends RepairControl { /** Constructor */ TwoWayContainer(EReference containerReference, EReference toContainer) { super(containerReference, toContainer.getFeatureID()); } } }