blob: 2e2e36845d0e1acad2e2c601e8bc19ccb4b20638 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 AIRBUS FRANCE.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License_Identifier: EPL-2.0
*
* Contributors:
* Pierre-Charles David (Obeo) - initial API and implementation, pierre-charles.david@obeo.fr
* Vincent Hemery (Atos Origin) - removing modeler dependencies, laurent.redor@obeo.fr
* Arthur Daussy (Atos) - Update to be use in EASE, arthur.daussy@atos.net
*******************************************************************************/
package org.eclipse.ease.modules.modeling;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.internal.runtime.AdapterManager;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.ease.ICodeFactory;
import org.eclipse.ease.Logger;
import org.eclipse.ease.modules.AbstractScriptModule;
import org.eclipse.ease.modules.ScriptParameter;
import org.eclipse.ease.modules.WrapToScript;
import org.eclipse.ease.modules.modeling.selector.GMFSemanticSeletor;
import org.eclipse.ease.modules.modeling.ui.UriSelectionDialog;
import org.eclipse.ease.modules.platform.ResourcesModule;
import org.eclipse.ease.modules.platform.UIModule;
import org.eclipse.ease.service.ScriptService;
import org.eclipse.ease.tools.ResourceTools;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.change.util.ChangeRecorder;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.emf.edit.command.ChangeCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IItemLabelProvider;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
/**
* This offer to user means to handle EMF models (Create EObject, Select element, Handle resources etc..). This module need to be initialized with an nsURI
* referencing a metamodel usin {@link EcoreModule#initEPackage(String)}.Once this module has been initialized all creation method of the factory is injected in
* the script.
*/
public class EcoreModule extends AbstractScriptModule {
private String fUri;
/**
* Returns the currently selected model element, either in the editor or the outline view. If several elements are selected, only the first is returned.
*
* @return the currently selected model element.
*/
public EObject getSelection() {
final Object selection = getSelectionModule().getCustomSelectionFromSelector(GMFSemanticSeletor.SELECTOR_ID);
if (selection instanceof EObject) {
return (EObject) selection;
} else {
final String message = "Unable to retreive a EObject from the selection";
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", message);
return null;
}
}
private SelectionModule getSelectionModule() {
return getEnvironment().getModule(SelectionModule.class);
}
/**
* Return if the current instance is a instance of an EClass define by its name.
*
* @param eObject
* The {@link EObject} you want to test.
* @param type
* The name of the EClass defined in the metamodel
* @return <code>true</code> if the {@link EObject} is instance of typeName
*/
@WrapToScript
public boolean eInstanceOf(final EObject eObject, final String type) {
final EClassifier classifier = getEPackage().getEClassifier(type);
if (classifier == null) {
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", "Unable to find EClass named :" + type);
}
return classifier.isInstance(eObject);
}
protected String getUri() {
return fUri;
}
/**
* Returns the currently selected model element in the current editor if it is an instance of the named meta-class (or a sub-class).
*
* @param type
* the name of a meta-class (e.g. "Property" or "Package")
* @return the first element selected in the current editor if there is one and it is an instance of the named meta-class or a sub-class of it.
*/
@WrapToScript
public EObject getSelection(@ScriptParameter(defaultValue = ScriptParameter.NULL) final String type) {
final EObject selection = getSelection();
if (type != null) {
if (eInstanceOf(selection, type)) {
return selection;
} else {
return null;
}
}
return selection;
}
protected String getFactoryVariableName() {
return "__" + getEPackage().getName().toUpperCase() + "__FACTORY";
}
/**
* Filter used to match all create method from the factory.
*/
protected static final Predicate<Method> CREATE_METHOD_FILTER = arg0 -> {
if (arg0 != null) {
return arg0.getName().startsWith("create");
}
return false;
};
/**
* Initialized the module with the correct metamodèle. If this method is not called the module will at runtime ask with metamodel shall be used.
*
* @param nsURI
* of the metamodel
*/
@WrapToScript
public void initEPackage(final String nsURI) {
if (nsURI == null)
initEPackageFromDialog();
else
fUri = nsURI;
final EPackage ePack = getEPackage();
if (ePack == null) {
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", "Unable to find metamodel with URI : " + fUri);
return;
}
final EFactory factory = getFactory();
if (factory != null) {
try {
getScriptEngine().setVariable(getFactoryVariableName(), factory);
getEnvironment().wrap(factory.getClass(), false);
} catch (final java.util.concurrent.ExecutionException e) {
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", "Failed to wrap factory class");
}
} else {
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", "Unable to find metamodel with URI : " + fUri);
}
}
/**
* Create a new resource to hold model elements.
*
* @param name
* Name of the resource (Optional parameter ask dynamically to the user)
* @param uri
* URI locating the container of the resource (Optional parameter ask dynamically to the user)
* @return the created resource
*/
@WrapToScript
public Resource createResource(@ScriptParameter(defaultValue = ScriptParameter.NULL) final String name,
@ScriptParameter(defaultValue = ScriptParameter.NULL) final String uri) {
ResourceSet resourceSet = getResourceSet();
if (resourceSet == null) {
Logger.warning(Activator.PLUGIN_ID, "Unable to get the current resourceSet. Creating a new one...");
resourceSet = new ResourceSetImpl();
}
final URI resourceURI = createURI(uri, name);
Resource resource = null;
try {
resource = resourceSet.getResource(resourceURI, true);
} catch (final Exception e) {
resource = resourceSet.createResource(resourceURI);
}
return resource;
}
/**
* Create a new URI. This URI is use to locate a resource.
*
* @param containerURI
* path of the container of the new resource. (Optional Ask dynamically to the user)
* @param fileName
* name of the new resource. (Optional Ask dynamically to the user)
* @return the created URI
*/
@WrapToScript
public URI createURI(@ScriptParameter(defaultValue = ScriptParameter.NULL) final String containerURI,
@ScriptParameter(defaultValue = ScriptParameter.NULL) String fileName) {
URI container = null;
if (containerURI == null) {
// Launch dialog to get an URI
final String location = getEnvironment().getModule(ResourcesModule.class).showFolderSelectionDialog(null,
"Select where you want to add your resource", null);
if ((location == null) || (location.isEmpty())) {
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", "Unable to retreive a container for the new resource from your selestion");
return null;
}
final IPath containerPath = ((IResource) ResourceTools.resolve(location)).getFullPath();
container = URI.createPlatformResourceURI(containerPath.toString(), true);
} else {
container = URI.createFileURI(containerURI);
}
if (fileName == null) {
// Launch input dialog
fileName = getEnvironment().getModule(UIModule.class).showInputDialog("", "Give the resource name (With it's extension)", "");
}
container = container.appendSegment(fileName);
return container;
}
/**
* Get the factory of selected meta model.
*
* @return the found factory
*/
@WrapToScript
public EFactory getFactory() {
if (fUri == null) {
initEPackageFromDialog();
}
final EPackage ePackage = getEPackage();
if (ePackage == null) {
throw new RuntimeException("Unable to retreive EPackage with URI " + fUri);
}
return ePackage.getEFactoryInstance();
}
/**
* Get the {@link EPackage} of the selected meta model
*
* @return the EPackage for the meta model
*/
@WrapToScript
public EPackage getEPackage() {
if (fUri == null) {
initEPackageFromDialog();
}
final EPackage ePack = EPackage.Registry.INSTANCE.getEPackage(fUri);
return ePack;
}
private void initEPackageFromDialog() {
getEnvironment().getModule(UIModule.class);
final UriSelectionDialog dialog = new UriSelectionDialog(UIModule.getShell());
final int returnCode = DialogHelper.openDialog(dialog);
if (returnCode == Window.OK) {
final Object[] result = dialog.getResult();
if ((result != null) && (result.length == 1)) {
fUri = (String) result[0];
}
}
}
/**
* Add an error marker on a EObject
*
* @param eObject
* The Object you want to add a error marker
* @param message
* Message of the marker
* @throws CoreException
* If the marker cannot be added because of a null resource.
*/
@WrapToScript
public void addErrorMarker(final EObject eObject, final String message) throws CoreException {
EMFMarkerUtil.addMarkerFor(eObject, message, IMarker.SEVERITY_ERROR);
}
/**
* Add an Information marker on a EObject
*
* @param eObject
* The Object you want to add a error marker
* @param message
* Message of the marker
* @throws CoreException
* If the marker cannot be added because of a null resource.
*/
@WrapToScript
public void addInfoMarker(final EObject eObject, final String message) throws CoreException {
EMFMarkerUtil.addMarkerFor(eObject, message, IMarker.SEVERITY_INFO);
}
/**
* Add a Warning marker on a EObject
*
* @param eObject
* The Object you want to add a error marker
* @param message
* Message of the marker
* @throws CoreException
* If the marker cannot be added because of a null resource.
*/
@WrapToScript
public void addWarningMarker(final EObject eObject, final String message) throws CoreException {
EMFMarkerUtil.addMarkerFor(eObject, message, IMarker.SEVERITY_WARNING);
}
/**
* The current editor part is return or null if there is any active editor. In the case of there is any active editor a message is display to inform the
* user.
*
* @return IEditorPart The current editor part or null
*/
protected IEditorPart getCurrentEditorPart() {
/**
* ActiveEditorRef
*/
class ActiveEditorRef {
public IEditorPart activeEditorPart = null;
}
final IWorkbench workbench = PlatformUI.getWorkbench();
final ActiveEditorRef activeEditorRef = new ActiveEditorRef();
final Display display = workbench.getDisplay();
display.syncExec(() -> {
final IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
// this can be null if you close all perspectives
if ((window != null) && (window.getActivePage() != null) && (window.getActivePage().getActiveEditor() != null))
activeEditorRef.activeEditorPart = window.getActivePage().getActiveEditor();
});
return activeEditorRef.activeEditorPart;
}
/**
* Save the current editor (if no eObject is passed in argument) or the resource containing the eObject passed in argument.
*
* @param target
* Help to locate the resource to save (Optional save the current editor)
*/
@WrapToScript
public void save(@ScriptParameter(defaultValue = ScriptParameter.NULL) final Object target) {
Resource toSave = null;
if (target instanceof EObject) {
final EObject eObject = (EObject) target;
toSave = eObject.eResource();
} else if (target instanceof Resource) {
toSave = (Resource) target;
}
if (toSave != null) {
try {
toSave.save(null);
} catch (final IOException e) {
e.printStackTrace();
getEnvironment().getModule(UIModule.class).showErrorDialog("Error", e.getMessage());
return;
}
} else {
save();
}
}
public void save() {
getCurrentEditorPart().doSave(new NullProgressMonitor());
}
protected EditingDomain getEditingDomain() {
final IEditorPart currentEditorPart = getCurrentEditorPart();
if (currentEditorPart != null) {
Object domain = currentEditorPart.getAdapter(EditingDomain.class);
if (domain instanceof EditingDomain) {
return (EditingDomain) domain;
}
domain = AdapterManager.getDefault().getAdapter(currentEditorPart, EditingDomain.class);
if (domain instanceof EditingDomain) {
return (EditingDomain) domain;
}
if (currentEditorPart instanceof IEditingDomainProvider) {
return ((IEditingDomainProvider) currentEditorPart).getEditingDomain();
}
} else {
Logger.warning(Activator.PLUGIN_ID, "Unable to retreive editing domain. There is not opened editor");
}
return null;
}
/**
* Return all object referencing this EObject. The return value is a collection of Array of size 2. Result[0] = EStructual feature linking the two object
* Result[1] = The referencing object
*
* @param eObject
* object to look for
* @return usages of the object
*/
@WrapToScript
public static Collection<Object[]> getUsages(final EObject eObject) {
if (eObject == null) {
return Collections.emptyList();
}
ECrossReferenceAdapter crossReferencer = ECrossReferenceAdapter.getCrossReferenceAdapter(eObject);
if (crossReferencer == null) {
// try to register a cross referencer at the highest level
crossReferencer = new ECrossReferenceAdapter();
if (eObject.eResource() != null) {
if (eObject.eResource().getResourceSet() != null) {
crossReferencer.setTarget(eObject.eResource().getResourceSet());
} else {
crossReferencer.setTarget(eObject.eResource());
}
} else {
crossReferencer.setTarget(eObject);
}
}
final Collection<Setting> result = crossReferencer.getInverseReferences(eObject, true);
return Collections2.transform(result, arg0 -> {
final Object[] setting = new Object[2];
setting[1] = arg0.getEStructuralFeature();
setting[0] = arg0.getEObject();
return setting;
});
}
protected ResourceSet getResourceSet() {
final EditingDomain editingDomain = getEditingDomain();
if (editingDomain != null) {
return editingDomain.getResourceSet();
}
return null;
}
public void runOperation(final Runnable operation) {
runOperation(operation, "Script Operation");
}
/**
* Run an operation in the current editor's command stack. This is really helpful to manipulate a model using transaction
*
* @param operation
* the operation to run
* @param operationName
* the name to give to the operation execution
*/
@WrapToScript
public void runOperation(final Runnable operation, @ScriptParameter(defaultValue = "Script Operation") final String operationName) {
final EditingDomain domain = getEditingDomain();
if (domain instanceof TransactionalEditingDomain) {
((TransactionalEditingDomain) domain).getCommandStack().execute(
new GMFtoEMFCommandWrapper(new RunnableTransactionalCommandWrapper((TransactionalEditingDomain) domain, operationName, null, operation)));
// ((EditingDomain)domain).getCommandStack().undo();
} else if (domain != null) {
// execute the operation in a command
final ChangeRecorder recorder = new ChangeRecorder();
domain.getCommandStack().execute(new RunnableCommandWrapper(operation, domain.getResourceSet(), recorder));
recorder.dispose();
} else {
// try simply running the operation
operation.run();
}
}
protected static class RunnableCommandWrapper extends ChangeCommand {
private final Runnable operation;
public RunnableCommandWrapper(final Runnable operation, final ResourceSet set, final ChangeRecorder recorder) {
super(recorder, set);
this.operation = operation;
}
@Override
protected void doExecute() {
operation.run();
}
}
/**
* Display a dialog which asks the user to select between a list of Objects.
*
* @param inputs
* List of choice for the user
* @return The selected object
*/
@WrapToScript
public Object[] selectFromList(final List<? extends Object> inputs) {
return DialogHelper.selectFromList(inputs.toArray(), new LabelProvider() {
@Override
public String getText(final Object element) {
if (element instanceof EObject) {
return getLabelProvider((EObject) element).getText(element);
}
return element.toString();
}
});
}
private static ComposedAdapterFactory adapter = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
/**
* Print an {@link EObject} using label providers.
*
* @param target
* the object to print
* @return text provided from label provider
*/
@WrapToScript
public String ePrint(final EObject target) {
final IItemLabelProvider labelProvider = getLabelProvider(target);
if (labelProvider != null) {
return labelProvider.getText(target);
}
return "[ERRO] Unable to print this EObject";
}
private IItemLabelProvider getLabelProvider(final EObject target) {
return (IItemLabelProvider) adapter.adapt(target, IItemLabelProvider.class);
}
protected static class RunnableTransactionalCommandWrapper extends AbstractTransactionalCommand {
public RunnableTransactionalCommandWrapper(final TransactionalEditingDomain domain, final String label, final List affectedFiles,
final Runnable operation) {
super(domain, label, affectedFiles);
fOperation = operation;
}
private final Runnable fOperation;
@Override
protected CommandResult doExecuteWithResult(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException {
fOperation.run();
return CommandResult.newOKCommandResult();
}
}
public ICodeFactory getCodeFactory() {
return ScriptService.getCodeFactory(getScriptEngine());
}
}