/**
* <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;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import fr.inria.diverse.k3.benchVM.synthesis.generic.common.ModelGeneratorConfiguration;
/**
* Class for generating Ecore models, methods only.
* Generating a model includes:<br>
* - creating EObjects from a certain model package<br>
* - building up a hierarchy starting from a certain root EObject<br>
* - setting random attributes for every created EObject<br>
* - setting references between the EObjects
*
* @see #generateModel(EPackage, EObject)
* @see #generateModel(ModelGeneratorConfiguration)
* @see #generateModel(ModelGeneratorConfiguration, IProgressMonitor)
*/
public final class ModelGenerator {
/**
* The configuration containing settings for the generation process.
*
* @see ModelGeneratorConfiguration
*/
private ModelGeneratorConfiguration config;
/**
* Generates EObjects for a certain model indicated by <code>modelPackage</code> and adds them
* as children to <code>rootEObject</code>, setting attributes and references. This method uses
* default values for <code>width, depth, seed</code> and <code>ignoreAndLog</code>.<p>
* This method call is equivalent to<br>
* <code>generateModel(new ModelGeneratorConfiguration(modelPackage, rootEObject))</code>.
*
* @param modelPackage the EPackage to create new EObjects from
* @param rootEObject the EObject that shall be the root container for all generated EObjects
* @return the validated root EObject of the resulting Ecore hierarchy
*/
public EObject generateModel(EPackage modelPackage, EObject rootEObject) {
return generateModel(new ModelGeneratorConfiguration(modelPackage, rootEObject));
}
/**
* Generates EObjects using the settings specified in <code>config</code>.
*
* @param configuration the ModelGeneratorConfiguration to use for generating EObjects
* @return the validated root EObject of the resulting Ecore hierarchy
* @see ModelGeneratorConfiguration
*/
ModelGeneratorHelper modelGeneratorHelper = new ModelGeneratorHelper();
public ModelGeneratorHelper getModelGeneratorHelper() {
return modelGeneratorHelper;
}
public EObject generateModel(ModelGeneratorConfiguration configuration) {
config = configuration;
modelGeneratorHelper.init(config);
EObject rootEObject = generateModel();
Map<EClass, List<EObject>> allObjectsByEClass = modelGeneratorHelper.modelGeneratorUtil.getAllClassesAndObjects(rootEObject);
for(EClass eClass : allObjectsByEClass.keySet()) {
for(EObject generatedEObject : allObjectsByEClass.get(eClass)) {
if (generatedEObject != null)
generateReferences(generatedEObject, allObjectsByEClass);
else
System.err.println("starnge");
}
}
return rootEObject;
}
/**
* The method that actually performs the generation. This can only be
* accessed by calling {@link #generateModel(EPackage, EObject)} or
* {@link #generateModel(ModelGeneratorConfiguration)}
*
* @return the valid root EObject used for generating the model
* @see #generateModel(EPackage, EObject)
* @see #generateModel(ModelGeneratorConfiguration)
*/
private EObject generateModel() {
EObject rootEObject = modelGeneratorHelper.validateRoot(config.getRootEObject());
Map<Integer, List<EObject>> depthToParentObjects = new LinkedHashMap<Integer, List<EObject>>();
List<EObject> parentsInThisDepth = new LinkedList<EObject>();
parentsInThisDepth.add(rootEObject);
int currentDepth = 1;
depthToParentObjects.put(2, new LinkedList<EObject>());
while(currentDepth <= config.getDepth()) {
// for all parent EObjects in this depth
for(EObject nextParentEObject : parentsInThisDepth) {
List<EObject> children = generateChildren(nextParentEObject);
// will the just created EObjects have children?
if(currentDepth < config.getDepth()) {
depthToParentObjects.get(currentDepth+1).addAll(children);
}
}
// proceed to the next level
currentDepth++;
parentsInThisDepth = depthToParentObjects.get(currentDepth);
depthToParentObjects.put((currentDepth+1), new LinkedList<EObject>());
}
return rootEObject;
}
/**
* Generates children for a certain parent EObject. Generation includes
* setting containment references and attributes. All required references
* are set first, thus the specified width might be exceeded.
*
* @param parentEObject the EObject to generate children for
* @return all generated children as a list
* @see #generateContainments(EObject, EReference, int)
*/
private List<EObject> generateChildren(EObject parentEObject) {
List<EObject> result = new LinkedList<EObject>();
// set all valid required references
for(EReference reference : parentEObject.eClass().getEAllContainments()) {
if(!modelGeneratorHelper.isValid(parentEObject, reference)) {
continue;
}
// reference's lowerBound is the maximum amount -> only necessary EObjects are created
result.addAll(generateContainments(parentEObject, reference, reference.getLowerBound()));
}
List<EReference> possibleReferences = new LinkedList<EReference>(parentEObject.eClass().getEAllContainments());
// set all possible references until width EObjects are created
for(int i=result.size(); i<config.getWidth() && !possibleReferences.isEmpty(); i++) {
EReference reference = modelGeneratorHelper.getRandomReference(parentEObject, possibleReferences);
if(reference==null) {
possibleReferences.remove(reference);
i--;
continue;
}
// create only one child to guarantee variety
List<EObject> newChildren = generateContainments(parentEObject, reference, 1);
if(newChildren.isEmpty()) {
possibleReferences.remove(reference);
i--;
}
result.addAll(newChildren);
}
return result;
}
/**
* Creates <code>maxAmount</code> EObjects, if possible, and adds them as children
* to <code>parentEObject</code>.
*
* @param parentEObject the EObject to add the created children to
* @param reference the reference used to add the created children
* @param maxAmount the maxAmount of EObjects to create as children
* @return all created children as a list
*/
private List<EObject> generateContainments(EObject parentEObject,
EReference reference, int maxAmount) {
// initialize the generation process
List<EObject> result = new LinkedList<EObject>();
int index = modelGeneratorHelper.getStartingIndex(parentEObject.eClass());
List<EClass> elementsToCreate = modelGeneratorHelper.getElementsToCreate(reference);
// repeat maxAmount times
for(int i=0; i<maxAmount; i++) {
EClass currentChildClass = modelGeneratorHelper.getNextClassToCreate(elementsToCreate, index++);
if(currentChildClass==null) {
// no possible child left -> cancel the process
break;
}
// create child and add it to parentEObject
EObject newChild = modelGeneratorHelper.setContainment(parentEObject, currentChildClass, reference);
// was creating the child successful?
if(newChild!=null) {
result.add(newChild);
}
}
// save index for later use
modelGeneratorHelper.updateIndex(parentEObject.eClass(), index);
return result;
}
/**
* Returns the Exception-Log for the last {@link #generateModel()}-call.
* The log is empty if no RuntimeException occurred or <code>ignoreAndLog</code>
* was set to <code>false</code> in the last configuration used.
*
* @return a set of RuntimeExceptions that occurred during the last generation process
*/
public Set<RuntimeException> getLog() {
return modelGeneratorHelper.getExceptionLog();
}
/**
* Generates references (no containment references) for an EObject.
* All valid references are set with EObjects generated during the generation process.
*
* @param eObject the EObject to set references for
* @param allObjectsByEClass all possible EObjects that can be referenced, mapped to their EClass
* @see ModelGeneratorHelper#setReference(EObject, EClass, EReference, Map)
*/
private void generateReferences(EObject eObject, Map<EClass, List<EObject>> allObjectsByEClass) {
for(EReference reference : modelGeneratorHelper.getValidReferences(eObject)) {
for(EClass nextReferenceClass : modelGeneratorHelper.modelGeneratorUtil.getReferenceClasses(reference, allObjectsByEClass.keySet())) {
modelGeneratorHelper.setReference(eObject, nextReferenceClass, reference, allObjectsByEClass);
}
}
}
}