/** * <copyright> Copyright (c) 2008-2009 Jonas Helming, Maximilian Koegel modified by Olivier Barais. 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 </copyright> */ package fr.inria.diverse.k3.benchVM.synthesis.generic.modelchanger; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; /** * Class for changing Ecore models automatically, methods only. * Changing a model includes:<br> * - deleting random EObjects and all their children<br> * - replace attributes by new random values<br> * - replace references (no containment references) by new random values * * @see #generateChanges(EObject) * @see #generateChanges(EObject, long, boolean) */ public final class ModelChanger { /** * Private constructor. */ private ModelChanger() { // all methods should be accessed in a way } /** * Generates changes for the specified root and all its direct and indirect contents * using default values for <code>seed</code> and <code>ignoreAndLog</code>. * Changing includes deleting EObjects and their children and replacing all set * attributes and references (no containment-references) with new values. * * @param rootObject the root EObject * @see #generateChanges(EObject, long, boolean) * @see #generateChanges(EObject, long, boolean, IProgressMonitor) */ public void generateChanges(EObject rootObject) { generateChanges(rootObject, System.currentTimeMillis(), false); } /** * Generates changes for the specified root and all its direct and indirect contents * using <code>seed</code> to determine random values and <code>ignoreAndLog</code> * to decide whether occurring RuntimeExceptions shall be thrown or logged only. * Changing includes deleting EObjects and their children and replacing all set * attributes and references (no containment-references) with new values. * * @param rootObject the root EObject * @param seed value to determine randomness * @param ignoreAndLog should exceptions be ignored and added to the log? * @see #generateChanges(EObject, long, boolean, IProgressMonitor) */ ModelChangerHelper modelChangerHelper = new ModelChangerHelper(); public void generateChanges(EObject rootObject, long seed, boolean ignoreAndLog) { modelChangerHelper.init(seed, ignoreAndLog); Set<EObject> allChildren = modelChangerHelper.getAllChildren(rootObject); deleteRandomEObjects(allChildren,rootObject); // changeAttributesAndReferences(rootObject); } /** * Randomly deletes EObjects and their children from a set of EObjects. * <code>allChildren</code> shouldn't contain the rootObject, so the * root (and therefore ALL contained EObjects) doesn't get deleted. * * @param allChildren set of EObjects from which EObjects are selected to be deleted * @see #deleteAllChildren(Set) * @see #deleteRandomEObjects(Set, IProgressMonitor) */ private void deleteRandomEObjects(Set<EObject> allChildren,EObject parent) { Set<EObject> deletedChildren = new LinkedHashSet<EObject>(); for(EObject eObject : allChildren) { // deleteRandomEObjects(ModelChangerHelper.getAllChildren(eObject), eObject); // was the EObject already deleted? if(deletedChildren.contains(eObject)) { continue; } if(modelChangerHelper.randomDelete()) {// // copy all children first to prevent concurrent modifications when deleting Set<EObject> contentCopy = new LinkedHashSet<EObject>(eObject.eContents()); deletedChildren.addAll(deleteAllChildren(contentCopy)); // if(deleteUsingCommand) System.out.println(eObject.toString()); modelChangerHelper.delete(eObject); // else // EcoreUtil.delete(eObject,true); // } } } /** * Deletes a list of EObjects and all their direct and indirect contents * recursively. * * @param children all children that should be deleted * @return all deleted EObjects and all their direct and indirect contents * @see #deleteRandomEObjects(Set) */ private Set<EObject> deleteAllChildren(Set<EObject> children) { Set<EObject> allDeletedChildren = new LinkedHashSet<EObject>(); for(EObject child : children) { // copy all children first to prevent concurrent modifications when deleting Set<EObject> contentCopy = new LinkedHashSet<EObject>(child.eContents()); allDeletedChildren.addAll(deleteAllChildren(contentCopy)); modelChangerHelper.delete(child); allDeletedChildren.add(child); } return allDeletedChildren; } /** * Changes all attributes and references for every child (direct and indirect) * of <code>root</code>. * * @param root the root EObject of the hierarchy that shall be changed * @see #changeAttributesAndReferences(EObject, IProgressMonitor) * @see #changeEObjectAttributes(EObject) * @see #changeEObjectReferences(EObject, Map) */ private void changeAttributesAndReferences(EObject root) { Map<EClass, List<EObject>> allObjectsByEClass = modelChangerHelper.modelGeneratorUtil.getAllClassesAndObjects(root); for(EClass eClass : allObjectsByEClass.keySet()) { for(EObject eObject : allObjectsByEClass.get(eClass)) { changeEObjectAttributes(eObject); changeEObjectReferences(eObject, allObjectsByEClass); } } } /** * Sets all of <code>eObject</code>'s attributes to new values, discarding old ones. * Therefore all values from many-valued attributes are removed using a RemoveCommand. * Single-valued attributes are simply replaced. * * @param eObject the EObject to change attributes for * @see ModelChangerHelper#clear(EObject, org.eclipse.emf.ecore.EStructuralFeature) * @see ModelChangerHelper#setEObjectAttributes(EObject) */ private void changeEObjectAttributes(EObject eObject) { // remove all values from currently set attributes for(EAttribute attribute : eObject.eClass().getEAllAttributes()) { if(modelChangerHelper.isValid(eObject, attribute)) { modelChangerHelper.clear(eObject, attribute); } } modelChangerHelper.setEObjectAttributes(eObject); } /** * Sets all of <code>eObject</code>'s references to new values, discarding old ones. * Therefore all values from many-valued references are removed using a RemoveCommand. * Single-valued references are simply replaced. * * @param eObject the EObject to change references for * @see ModelChangerHelper#clear(EObject, org.eclipse.emf.ecore.EStructuralFeature) * @see ModelChangerHelper#setReference(EObject, EClass, EReference, Map) */ private void changeEObjectReferences(EObject eObject, Map<EClass, List<EObject>> allObjectsByEClass) { for(EReference reference : modelChangerHelper.getValidReferences(eObject)) { if(modelChangerHelper.isValid(eObject, reference)) { modelChangerHelper.clear(eObject, reference); } for(EClass nextReferenceClass : modelChangerHelper.modelGeneratorUtil.getReferenceClasses(reference, allObjectsByEClass.keySet())) { modelChangerHelper.setReference(eObject, nextReferenceClass, reference, allObjectsByEClass); } } } /** * Returns the Exception-Log for the last {@link #generateChanges()}-call. * The log is empty if no RuntimeException occurred or <code>ignoreAndLog</code> * was set to <code>false</code>. * * @return a set of RuntimeExceptions that occurred during the last changing process */ public Set<RuntimeException> getLog() { return modelChangerHelper.getExceptionLog(); } }