/**
* Copyright (c) 2010-2012 Hallvard Traetteberg
* 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
*
* Contributors:
* Hallvard Traetteberg - initial API and implementation
*/
package org.eclipse.emf.ecore.xcore.ui;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.edit.command.ChangeCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.views.properties.PropertySheetPage;
public class EOperationInvocationView extends AbstractSelectionView
{
@Override
public void dispose()
{
propertySheetPage.dispose();
propertySheetPage.dispose();
super.dispose();
}
@Override
protected boolean isValidSelection(Object o)
{
return o instanceof EObject;
}
private EObject getSelectedEObject()
{
return (EObject)this.selection;
}
private final static String NO_OPERATIONS = "<No EOperations available>";
private static class EOperationInvocation extends ChangeCommand
{
private EOperation operation;
private EObject argumentsInstance;
private Collection<?> affectedObjects;
private Object result;
public EOperationInvocation(EObject operationOwner)
{
super(operationOwner);
}
@Override
public void dispose()
{
super.dispose();
operation = null;
argumentsInstance = null;
affectedObjects = null;
result = null;
}
public EObject getOperationOwner()
{
return (EObject)notifier;
}
public void setOperation(EOperation operation, EObject argumentsInstance)
{
this.operation = operation;
this.argumentsInstance = argumentsInstance;
}
private EList<Object> argumentList = new BasicEList<Object>();
@Override
protected boolean prepare()
{
if (!super.prepare() || getOperationOwner() == null || operation == null)
{
return false;
}
for (EStructuralFeature property : argumentsInstance.eClass().getEStructuralFeatures())
{
Object value = argumentsInstance.eGet(property);
argumentList.add(value);
}
return true;
}
@Override
protected void doExecute()
{
try
{
result = getOperationOwner().eInvoke(operation, argumentList);
affectedObjects = operation.isMany() ? (Collection<?>)result : Collections.singleton(result);
}
catch (Exception e)
{
result = Collections.singletonList(e);
affectedObjects = Collections.singletonList(e);
}
}
@Override
public void undo()
{
affectedObjects = notifier != null ? Collections.singleton(notifier) : notifiers;
result = Collections.emptyList();
super.undo();
}
// public boolean didChange() {
// ChangeDescription changes = getChangeDescription();
// return changes.getObjectChanges().size() == 0 && changes.getResourceChanges().size() == 0;
// }
private void execute(IEditingDomainProvider editingDomainProvider, Shell shell)
{
if (!canExecute())
{
return;
}
if (editingDomainProvider != null)
{
editingDomainProvider.getEditingDomain().getCommandStack().execute(this);
}
else
{
execute();
}
if (affectedObjects != null && shell != null)
{
MessageDialog dialog = new MessageDialog(
shell,
"Invocation result",
null,
String.valueOf(affectedObjects),
(affectedObjects instanceof Exception ? MessageDialog.ERROR : MessageDialog.INFORMATION),
new String []{ "Close" },
0);
dialog.open();
}
}
@Override
public Collection<?> getResult()
{
return result == null ? Collections.emptyList() : Collections.singleton(result);
}
@Override
public Collection<?> getAffectedObjects()
{
return affectedObjects;
}
}
private EOperationInvocation currentInvocation;
private String toString(EOperation operation)
{
String item = operation.getName() + "(";
for (EParameter param : operation.getEParameters())
{
if (param != operation.getEParameters().get(0))
{
item += ",";
}
EClassifier type = param.getEType();
item += type.getName() + (param.isMany() ? "* " : " ") + param.getName();
}
item += ")";
return item;
}
@Override
protected void updateView()
{
EObject selection = getSelectedEObject();
EClass eClass = null;
if (selection == null)
{
currentInvocation = null;
}
else
{
eClass = selection.eClass();
currentInvocation = new EOperationInvocation(selection);
}
// don't re-populate and reset selection, when the EClass is the same
if (eClass != operationsCombo.getData())
{
EList<EOperation> operations = (eClass != null ? eClass.getEAllOperations() : new BasicEList<EOperation>());
String[] items = new String [operations.size()];
for (int i = 0; i < items.length; i++)
{
items[i] = toString(operations.get(i));
}
operationsCombo.setItems(items);
if (items.length > 0)
{
operationsCombo.select(0);
}
else
{
operationsCombo.setText(NO_OPERATIONS);
}
operationsCombo.setData(eClass);
}
operationSelected(operationsCombo.getSelectionIndex());
}
private void operationSelected(int selectionIndex)
{
EClass eClass = (EClass)operationsCombo.getData();
EOperation operation = null;
if (eClass != null && selectionIndex >= 0)
{
EList<EOperation> operations = eClass.getEAllOperations();
if (selectionIndex < operations.size())
{
operation = operations.get(selectionIndex);
}
}
EObject operationArgumentsInstance = null;
if (currentInvocation != null)
{
if (operation != null)
{
operationArgumentsInstance = operationArgumentsInstances.get(operation);
if (operationArgumentsInstance == null)
{
operationArgumentsInstance = createEOperationEObject(createEOperationEClass(operation));
operationArgumentsInstances.put(operation, operationArgumentsInstance);
initEOperationOwnerContainer(operationArgumentsInstance, currentInvocation.getOperationOwner());
}
}
currentInvocation.setOperation(operation, operationArgumentsInstance);
}
updatePropertySheet(operationArgumentsInstance);
}
private EPackage operationArgumentsPackage;
private EPackage getEOperationEPackage()
{
if (operationArgumentsPackage == null)
{
operationArgumentsPackage = EcoreFactory.eINSTANCE.createEPackage();
operationArgumentsPackage.setEFactoryInstance(EcoreFactory.eINSTANCE.createEFactory());
}
return operationArgumentsPackage;
}
private Map<EOperation, EObject> operationArgumentsInstances = new HashMap<EOperation, EObject>();
private EClass createEOperationEClass(EOperation operation)
{
EClass eClass = createEOperationEClass();
for (EParameter param : operation.getEParameters())
{
EClassifier type = param.getEType();
EStructuralFeature property = (type instanceof EClass
? EcoreFactory.eINSTANCE.createEReference() : EcoreFactory.eINSTANCE.createEAttribute());
property.setName(param.getName());
property.setEType(type);
property.setLowerBound(param.getLowerBound());
property.setUpperBound(param.getUpperBound());
eClass.getEStructuralFeatures().add(property);
}
return eClass;
}
private EObject operationOwnerContainer;
private EReference operationOwnerContainerContentRef, operationOwnerContainerContextRef;
private EReference createEObjectEReference(EClass owner, String name)
{
EReference ref = EcoreFactory.eINSTANCE.createEReference();
ref.setName(name);
ref.setEType(EcorePackage.eINSTANCE.getEObject());
owner.getEStructuralFeatures().add(ref);
return ref;
}
private EClass createEOperationEClass()
{
EClass eClass = EcoreFactory.eINSTANCE.createEClass();
getEOperationEPackage().getEClassifiers().add(eClass);
return eClass;
}
private EObject createEOperationEObject(EClass eClass)
{
EObject eObject = getEOperationEPackage().getEFactoryInstance().create(eClass);
return eObject;
}
private EObject getEOperationOwnerContainer()
{
if (operationOwnerContainer == null)
{
EClass operationOwnerContainerContainerClass = createEOperationEClass();
if (operationOwnerContainerContentRef == null)
{
operationOwnerContainerContentRef = createEObjectEReference(operationOwnerContainerContainerClass, "contentRef");
operationOwnerContainerContentRef.setContainment(true);
}
if (operationOwnerContainerContextRef == null)
{
operationOwnerContainerContextRef = createEObjectEReference(operationOwnerContainerContainerClass, "contextRef");
}
operationOwnerContainer = createEOperationEObject(operationOwnerContainerContainerClass);
}
return operationOwnerContainer;
}
private void initEOperationOwnerContainer(EObject operationArgumentsInstance, EObject operationOwner)
{
EObject container = getEOperationOwnerContainer();
container.eSet(operationOwnerContainerContentRef, operationArgumentsInstance);
container.eSet(operationOwnerContainerContextRef, getSelectedEObject());
}
private Shell parentShell;
private void updatePropertySheet(EObject eObject)
{
if (editingDomainProvider != null)
{
AdapterFactoryEditingDomain editingDomain = (AdapterFactoryEditingDomain)editingDomainProvider.getEditingDomain();
propertySheetPage.setPropertySourceProvider(new AdapterFactoryContentProvider(editingDomain.getAdapterFactory()));
}
StructuredSelection selection = (eObject != null && editingDomainProvider != null
? new StructuredSelection(eObject) : StructuredSelection.EMPTY);
propertySheetPage.selectionChanged(null, selection);
}
private Combo operationsCombo;
private PropertySheetPage propertySheetPage;
@Override
public void createPartControl(Composite parent)
{
parentShell = parent.getShell();
super.createPartControl(parent);
parent.setLayout(new GridLayout(2, false));
operationsCombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
operationsCombo.setText(NO_OPERATIONS);
operationsCombo.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
operationSelected(operationsCombo.getSelectionIndex());
}
});
operationsCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
Button invokeButton = new Button(parent, SWT.PUSH);
invokeButton.setText("Invoke!");
invokeButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
invokeButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
EOperationInvocation invocation = currentInvocation;
currentInvocation = null;
if (invocation != null)
{
invocation.execute(editingDomainProvider, parentShell);
}
updateView();
}
});
Composite propertySheetParent = new Composite(parent, SWT.NONE);
propertySheetParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
propertySheetParent.setLayout(new FillLayout());
propertySheetPage = new PropertySheetPage();
// propertySheetPage.init(getSite());
propertySheetPage.createControl(propertySheetParent);
}
@Override
public void setFocus()
{
operationsCombo.setFocus();
}
}