/** * <copyright> Copyright (c) 2008-2009 Jonas Helming, Maximilian Koegel. 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 org.eclipse.emf.emfstore.modelgenerator.modelchanger; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecp.common.commands.DeleteModelElementCommand; import org.eclipse.emf.ecp.common.model.ECPWorkspaceManager; import org.eclipse.emf.ecp.emfstorebridge.EMFStoreECPProject; import org.eclipse.emf.emfstore.client.model.ProjectSpace; import org.eclipse.emf.emfstore.common.model.Project; import org.eclipse.emf.emfstore.modelgenerator.common.ModelGeneratorUtil; /** * Class for changing Ecore models automatically, static 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 static 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 static 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) */ public static void generateChanges(EObject rootObject, long seed, boolean ignoreAndLog) { ModelChangerHelper.init(seed, ignoreAndLog); Set<EObject> allChildren = ModelChangerHelper.getAllChildren(rootObject); deleteRandomEObjects(allChildren,rootObject); // changeAttributesAndReferences(rootObject); } /** * 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. * Using the <code>monitor</code>, a progress bar is shown during the changing process, * also making it possible to cancel it. * * @param rootObject the root EObject * @param seed value to determine randomness * @param ignoreAndLog should exceptions be ignored and added to the log? * @param monitor the monitor displaying the progress bar * @see #generateChanges(EObject, long, boolean) */ public static void generateChanges(EObject rootObject, long seed, boolean ignoreAndLog, IProgressMonitor monitor) { ModelChangerHelper.init(seed, ignoreAndLog); monitor.beginTask("Changing process", ModelChangerHelper.getAmountOfWork()); Set<EObject> allChildren=null; if(rootObject instanceof Project){ allChildren=((Project)rootObject).getAllModelElements(); } else{ allChildren = ModelChangerHelper.getAllChildren(rootObject); } deleteRandomEObjects(allChildren, monitor); changeAttributesAndReferences(rootObject, monitor); } /** * 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 static 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 static 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 static void changeAttributesAndReferences(EObject root) { Map<EClass, List<EObject>> allObjectsByEClass = ModelGeneratorUtil.getAllClassesAndObjects(root); for(EClass eClass : allObjectsByEClass.keySet()) { for(EObject eObject : allObjectsByEClass.get(eClass)) { changeEObjectAttributes(eObject); changeEObjectReferences(eObject, allObjectsByEClass); } } } /** * 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. * The <code>monitor</code> shows the progress of the whole changing process, * also making it possible to cancel it. * * @param allChildren set of EObjects from which EObjects are selected to be deleted * @param monitor the monitor displaying the progress bar * @see #deleteAllChildren(Set) */ private static void deleteRandomEObjects(Set<EObject> allChildren, IProgressMonitor monitor) { Set<EObject> deletedChildren = new LinkedHashSet<EObject>(); for(EObject eObject : allChildren) { if(monitor.isCanceled()) { return; } // was the EObject already deleted? if(deletedChildren.contains(eObject)) { monitor.worked(1); 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)); ModelChangerHelper.delete(eObject); } monitor.worked(1); } } /** * Changes all attributes and references for every child (direct and indirect) * of <code>root</code>. The <code>monitor</code> shows the progress of the * whole changing process, also making it possible to cancel it. * * @param root the root EObject of the hierarchy that shall be changed * @param monitor the monitor displaying the progress bar * @see #changeAttributesAndReferences(EObject) * @see #changeEObjectAttributes(EObject) * @see #changeEObjectReferences(EObject, Map) */ private static void changeAttributesAndReferences(EObject root, IProgressMonitor monitor) { Map<EClass, List<EObject>> allObjectsByEClass = ModelGeneratorUtil.getAllClassesAndObjects(root); for(EClass eClass : allObjectsByEClass.keySet()) { if(monitor.isCanceled()) { return; } for(EObject eObject : allObjectsByEClass.get(eClass)) { if(monitor.isCanceled()) { return; } changeEObjectAttributes(eObject); monitor.worked(1); changeEObjectReferences(eObject, allObjectsByEClass); monitor.worked(1); } } } /** * 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 static 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 static 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 : 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 static Set<RuntimeException> getLog() { return ModelChangerHelper.getExceptionLog(); } }