/*****************************************************************************
* Copyright (c) 2010 CEA LIST.
*
*
* 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:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.textedit.state.xtext.ui.contributions;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper;
import org.eclipse.papyrus.extensionpoints.editors.ui.IPopupEditorHelper;
import org.eclipse.papyrus.infra.core.utils.EditorUtils;
import org.eclipse.papyrus.uml.textedit.state.xtext.ui.contentassist.UmlStateProposalProvider;
import org.eclipse.papyrus.uml.textedit.state.xtext.ui.internal.UmlStateActivator;
import org.eclipse.papyrus.uml.textedit.state.xtext.umlState.BehaviorKind;
import org.eclipse.papyrus.uml.textedit.state.xtext.umlState.StateRule;
import org.eclipse.papyrus.uml.textedit.state.xtext.validation.SemanticValidator;
import org.eclipse.papyrus.uml.textedit.state.xtext.validation.UmlStateJavaValidator;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.OpaqueBehavior;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.StateMachine;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.xtext.gmf.glue.PopupEditorConfiguration;
import org.eclipse.xtext.gmf.glue.edit.part.IXtextEMFReconciler;
import com.google.inject.Injector;
/**
* @author CEA LIST
*
* This class is used for contribution to the Papyrus extension point DirectEditor. It is used for the integration
* of an xtext generated editor, for States of UML StateMachines.
*
*/
public class StatePopupEditorConfigurationContribution extends PopupEditorConfiguration {
private State state = null;
private StateMachine newSubmachine = null ;
private String newStateName;
private String newEntryName = null;
private BehaviorKind newEntryKind;
private String newDoName = null;
private BehaviorKind newDoKind;
private String newExitName = null;
private BehaviorKind newExitKind;
private enum BehaviorRole_Local {
ENTRY, DO, EXIT
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.xtext.gmf.glue.PopupEditorConfiguration#createPopupEditorHelper(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart)
*/
@Override
public IPopupEditorHelper createPopupEditorHelper(Object editPart) {
// resolves the edit part, and the associated semantic element
IGraphicalEditPart graphicalEditPart = null;
if(!(editPart instanceof IGraphicalEditPart)) {
return null;
}
graphicalEditPart = (IGraphicalEditPart)editPart;
if(!(graphicalEditPart.resolveSemanticElement() instanceof State)) {
return null;
}
state = (State)graphicalEditPart.resolveSemanticElement();
UmlStateJavaValidator.init(state) ;
// retrieves the XText injector
Injector injector = UmlStateActivator.getInstance().getInjector("org.eclipse.papyrus.uml.textedit.state.xtext.UmlState");
// builds the text content and extension for a temporary file, to be edited by the xtext editor
String textToEdit = "" + this.getTextToEdit(graphicalEditPart.resolveSemanticElement());
String fileExtension = "" + ".umlstate";
// builds a new IXtextEMFReconciler.
// Its purpose is to extract any relevant information from the textual specification,
// and then merge it in the context UML model if necessary
IXtextEMFReconciler reconciler = new IXtextEMFReconciler() {
public void reconcile(EObject modelObject, EObject xtextObject) {
// first: retrieves / determines if the xtextObject is a StateRule object
EObject modifiedObject = xtextObject;
if(!(modelObject instanceof State)) {
return;
}
while(xtextObject != null && !(xtextObject instanceof StateRule)) {
modifiedObject = modifiedObject.eContainer();
}
if(modifiedObject == null) {
return;
}
StateRule stateRuleObject = (StateRule)xtextObject;
// Retrieves the information to be populated in modelObject
newStateName = "" + stateRuleObject.getName();
newSubmachine = null ;
newEntryName = "";
newDoName = "";
newExitName = "";
if (stateRuleObject.getSubmachine() != null) {
newSubmachine = stateRuleObject.getSubmachine().getSubmachine() ;
}
if(stateRuleObject.getEntry() != null) {
newEntryKind = stateRuleObject.getEntry().getKind();
if(stateRuleObject.getEntry().getBehaviorName() != null) {
newEntryName = "" + stateRuleObject.getEntry().getBehaviorName();
}
}
if(stateRuleObject.getDo() != null) {
newDoKind = stateRuleObject.getDo().getKind();
if(stateRuleObject.getDo().getBehaviorName() != null) {
newDoName = stateRuleObject.getDo().getBehaviorName();
}
}
if(stateRuleObject.getExit() != null) {
newExitKind = stateRuleObject.getExit().getKind();
if(stateRuleObject.getExit().getBehaviorName() != null) {
newExitName = stateRuleObject.getExit().getBehaviorName();
}
}
// Creates and executes the update command
UpdateUMLStateCommand updateCommand = new UpdateUMLStateCommand(state);
TransactionalEditingDomain dom = EditorUtils.getTransactionalEditingDomain();
dom.getCommandStack().execute(new GMFtoEMFCommandWrapper(updateCommand));
}
};
return super.createPopupEditorHelper(graphicalEditPart,
injector,
reconciler,
textToEdit,
fileExtension,
new SemanticValidator());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.xtext.gmf.glue.PopupEditorConfiguration#getTextToEdit(java.lang.Object)
*/
@Override
public String getTextToEdit(Object editedObject) {
if(editedObject instanceof State) {
State state = (State)editedObject;
String textToEdit = "";
// name
textToEdit = textToEdit + state.getName();
if (state.isSubmachineState()) {
textToEdit += " : " + UmlStateProposalProvider.getSubmachineLabel(state.getSubmachine()) ;
}
// entryActivity
if(state.getEntry() != null) {
String kind = behaviorKindAsString(state.getEntry());
textToEdit = textToEdit + "\nentry " + kind + " " + state.getEntry().getName();
}
// doActivity
if(state.getDoActivity() != null) {
String kind = behaviorKindAsString(state.getDoActivity());
textToEdit = textToEdit + "\ndo " + kind + " " + state.getDoActivity().getName();
}
// exitActivity
if(state.getExit() != null) {
String kind = behaviorKindAsString(state.getExit());
textToEdit = textToEdit + "\nexit " + kind + " " + state.getExit().getName();
}
return textToEdit;
}
return "not a State";
}
/**
* @author CEA LIST
*
* A command for updating the context UML model
*/
protected class UpdateUMLStateCommand extends AbstractTransactionalCommand {
private State state;
/*
* (non-Javadoc)
*
* @see
* org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand#doExecuteWithResult(org.eclipse.core.runtime.IProgressMonitor
* , org.eclipse.core.runtime.IAdaptable)
*/
@Override
protected CommandResult doExecuteWithResult(IProgressMonitor arg0, IAdaptable arg1) throws ExecutionException {
state.setName(newStateName);
state.setSubmachine(newSubmachine) ;
state.setEntry(updateOrCreateBehavior(BehaviorRole_Local.ENTRY, newEntryKind, newEntryName));
state.setDoActivity(updateOrCreateBehavior(BehaviorRole_Local.DO, newDoKind, newDoName));
state.setExit(updateOrCreateBehavior(BehaviorRole_Local.EXIT, newExitKind, newExitName));
return CommandResult.newOKCommandResult(state);
}
public UpdateUMLStateCommand(State state) {
super(EditorUtils.getTransactionalEditingDomain(), "State Update", getWorkspaceFiles(state));
this.state = state;
}
}
private String behaviorKindAsString(Behavior b) {
if(b instanceof Activity) {
return "Activity";
}
if(b instanceof StateMachine) {
return "StateMachine";
}
if(b instanceof OpaqueBehavior) {
return "OpaqueBehavior";
}
return "";
}
private BehaviorKind behaviorKindAsBehaviorKind(Behavior b) {
if(b instanceof Activity) {
return BehaviorKind.ACTIVITY;
}
if(b instanceof StateMachine) {
return BehaviorKind.STATE_MACHINE;
}
if(b instanceof OpaqueBehavior) {
return BehaviorKind.OPAQUE_BEHAVIOR;
}
return BehaviorKind.OPAQUE_BEHAVIOR;
}
private Behavior updateOrCreateBehavior(BehaviorRole_Local role, BehaviorKind kind, String behaviorName) {
Behavior behavior = null;
switch(role) {
case DO:
behavior = state.getDoActivity();
if(behavior != null) {
if(behaviorName.equals("")) {
// behavior needs to be deleted
state.setDoActivity(null);
behavior.destroy();
behavior = null;
} else {
if(behaviorKindAsBehaviorKind(behavior) != kind) {
// behavior needs to deleted, and a new one needs to be created
state.setDoActivity(null);
behavior.destroy();
behavior = createBehavior(kind, behaviorName);
} else {
// Behavior simply needs to be renamed
behavior.setName("" + behaviorName);
}
}
} else {
if(behaviorName.equals("")) {
// nothing needs to be done
} else {
// behavior needs to be created
behavior = createBehavior(kind, behaviorName);
}
}
break;
case ENTRY:
behavior = state.getEntry();
if(behavior != null) {
if(behaviorName.equals("")) {
// behavior needs to be deleted
state.setEntry(null);
behavior.destroy();
behavior = null;
} else {
if(behaviorKindAsBehaviorKind(behavior) != kind) {
// behavior needs to deleted, and a new one needs to be created
state.setEntry(null);
behavior.destroy();
behavior = createBehavior(kind, behaviorName);
} else {
// Behavior simply needs to be renamed
behavior.setName("" + behaviorName);
}
}
} else {
if(behaviorName.equals("")) {
// nothing needs to be done
} else {
// behavior needs to be created
behavior = createBehavior(kind, behaviorName);
}
}
break;
case EXIT:
behavior = state.getExit();
if(behavior != null) {
if(behaviorName.equals("")) {
// behavior needs to be deleted
state.setExit(null);
behavior.destroy();
behavior = null;
} else {
if(behaviorKindAsBehaviorKind(behavior) != kind) {
// behavior needs to deleted, and a new one needs to be created
state.setExit(null);
behavior.destroy();
behavior = createBehavior(kind, behaviorName);
} else {
// Behavior simply needs to be renamed
behavior.setName("" + behaviorName);
}
}
} else {
if(behaviorName.equals("")) {
// nothing needs to be done
} else {
// behavior needs to be created
behavior = createBehavior(kind, behaviorName);
}
}
break;
default:
break;
}
return behavior;
}
private Behavior createBehavior(BehaviorKind kind, String name) {
if(kind == null) {
return null;
}
Behavior behavior = null;
switch(kind) {
case ACTIVITY:
behavior = UMLFactory.eINSTANCE.createActivity();
break;
case OPAQUE_BEHAVIOR:
behavior = UMLFactory.eINSTANCE.createOpaqueBehavior();
break;
case STATE_MACHINE:
behavior = UMLFactory.eINSTANCE.createStateMachine();
break;
default:
break;
}
behavior.setName("" + name);
return behavior;
}
}