/**
* <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.handler;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
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 org.eclipse.emf.emfstore.client.model.ProjectSpace;
import org.eclipse.emf.emfstore.client.model.WorkspaceManager;
import org.eclipse.emf.emfstore.client.model.util.EMFStoreCommand;
import org.eclipse.emf.emfstore.common.model.Project;
import org.eclipse.emf.emfstore.modelgenerator.ModelGenerator;
import org.eclipse.emf.emfstore.modelgenerator.common.ModelGeneratorConfiguration;
import org.eclipse.emf.emfstore.modelgenerator.common.ModelGeneratorUtil;
import org.eclipse.emf.emfstore.modelgenerator.ui.ModelGeneratorWizard;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.internal.keys.model.ModelElement;
/**
* Handler for the "Generate Model" context menu command for the ECP.
* The command is available if nothing or an EObject is selected.
* This handler generates a model using values from the selection made,
* the package defined in <code>modelKey</code> and width
* and depth as defined in <code>width</code> and <code>depth</code>.
*/
public class GenerateModelHandler extends AbstractHandler {
/**
* The NsURI of the EPackage to generate EObjects from.
* This is only used if a project or nothing is selected,
* otherwise the root package of the selected EObject will be used.
*/
//private final String modelKey = "http://www.eclipse.org/emf/2002/Ecore";
private String modelKey = "http://eclipse.org/emf/emfstore/client/test/model";
/**
* The maximum number of children for each EObject.
*/
private int width = 5;
/**
* The maximum hierarchy depth of the project.
*/
private int depth = 5;
/**
* {@inheritDoc}
*/
public Object execute(ExecutionEvent event) throws ExecutionException {
final ISelection selection = HandlerUtil.getCurrentSelection(event);
ModelGeneratorWizard wizard = new ModelGeneratorWizard();
WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), wizard);
dialog.create();
dialog.open();
modelKey = wizard.getNsUri();
width = wizard.getWidth();
depth = wizard.getDepth();
new EMFStoreCommand() {
@Override
protected void doRun() {
EObject rootObject = validateSelection(selection);
EPackage pckge = getRootPackage(rootObject);
generate(rootObject, pckge);
}
}.run(false);
return null;
}
/**
* Splits the actual generation process into <code>width</code> sub-processes,
* due to performance issues. Every root of the performed sub-processes is added
* to <code>rootObject</code> as a child.
*
* @param rootObject the rootObject of the model to generate
* @param pckge the EPackage to use for the generation process
* @see #addAsChild(EObject, EObject)
*/
private void generate(EObject rootObject, EPackage pckge) {
List<EClass> ignoredClasses = new LinkedList<EClass>();
// create width subroots
for(int i=0; i<width; i++) {
EClass subRootClass = getValidEClass(rootObject, pckge, ignoredClasses);
if(subRootClass == null) {
// no valid EClasses left -> cancel process
return;
}
ModelGeneratorConfiguration config = new ModelGeneratorConfiguration(pckge, subRootClass,
ignoredClasses, width, depth-1, System.currentTimeMillis(), true);
// generate sub-hierarchy and add the new subroot as a child to the actual root
addAsChild(rootObject, ModelGenerator.generateModel(config));
}
}
/**
* Adds/Sets <code>childObject</code> as a child to <code>parentEObject</code> using
* Add/SetCommands, if <code>parentEObject</code> is no project. Otherwise, uses the
* {@link Project#addModelElement(ModelElement)}-method to add a new ModelElement.
*
* @param parentEObject the EObject <code>childObject</code> shall be added to
* @param childObject the EObject that shall be added to <code>parentEObject</code>
*/
private void addAsChild(EObject parentEObject, EObject childObject) {
if(parentEObject instanceof Project) {
((Project) parentEObject).addModelElement(childObject);
return;
}
for(EReference reference : ModelGeneratorUtil.getAllPossibleContainingReferences(childObject.eClass(), parentEObject.eClass())) {
if(reference.isMany()) {
// was the adding successful?
if(ModelGeneratorUtil.addPerCommand(parentEObject, reference, childObject,
null, false) != null) {
// then we are done
return;
}
}
else {
// was setting the reference successful?
if(ModelGeneratorUtil.setPerCommand(parentEObject, reference, childObject,
null, false) != null) {
// then we are done
return;
}
}
}
}
/**
* Returns the root EPackage of an EObject or the EPackage
* specified by <code>MODEL_KEY</code> if <code>eObject</code>
* is a Project.
*
* @param eObject the EObject to get the root package for
* @return the root EPackage of <code>eObject</code>
*/
private EPackage getRootPackage(EObject eObject) {
if(eObject instanceof Project) {
return ModelGeneratorUtil.getEPackage(modelKey);
} else {
EPackage pckge;
pckge = eObject.eClass().getEPackage();
while(pckge.getESuperPackage() != null) {
pckge = pckge.getESuperPackage();
}
return pckge;
}
}
/**
* Returns the selected EObject, or a project if a ProjectSpace was selected.
* If nothing was selected, creates and returns a new Project.
* Otherwise this method shouldn't be called, and therefore an
* IllegalArgumentException is thrown.
*
* @param selection the current selection made
* @return the valid EObject made from the selection
* @throws IllegalArgumentException if selection failed or no EObject is selected
*/
private EObject validateSelection(ISelection selection) {
if(selection != null && selection instanceof IStructuredSelection) {
IStructuredSelection strucSel = (IStructuredSelection) selection;
Object selectedElement = strucSel.getFirstElement();
if(selectedElement instanceof ProjectSpace) {
return ((ProjectSpace) selectedElement).getProject();
} else if(selectedElement instanceof EObject) {
return (EObject) selectedElement;
} else if(selectedElement==null) {
return WorkspaceManager.getInstance().getCurrentWorkspace().createLocalProject("Generated Project", "Generated").getProject();
} else {
throw new IllegalArgumentException("No EObject selected!");
}
} else {
throw new IllegalArgumentException("Selection Error!");
}
}
/**
* Returns the next valid EClass, that is an EClass that is neither
* abstract, nor an interface, from an EPackage. Any EClass that is contained in
* <code>ignoredClasses</code> or a subclass of any EClass contained in
* <code>ignoredClasses</code> won't be returned.
*
* @param ignoredClasses list of all EClasses that shouldn't be instantiated. All
* subClasses will be ignored as well
* @param allEClasses all EClasses to choose from
* @return the next EClass that can be instantiated or <code>null</code>
* if there is no such EClass
*/
private EClass getValidEClass(EObject root, EPackage pckge, List<EClass> ignoredClasses) {
List<EClass> allEClasses = new LinkedList<EClass>();
for(EReference reference : root.eClass().getEAllContainments()) {
allEClasses.addAll(ModelGeneratorUtil.getAllEContainments(reference));
}
// only allow EClasses that appear in the specified EPackage
allEClasses.retainAll(ModelGeneratorUtil.getAllEClasses(pckge));
// don't allow any EClass or sub class of all EClasses specified in ignoredClasses
for(EClass eClass : ignoredClasses) {
allEClasses.remove(eClass);
allEClasses.removeAll(ModelGeneratorUtil.getAllSubEClasses(eClass));
}
if(allEClasses.isEmpty()) {
// no valid EClass left
return null;
}
Collections.shuffle(allEClasses);
return allEClasses.get(0);
}
}