/*
* Licensed to Laurent Broudoux (the "Author") under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Author licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.github.lbroudoux.dsl.eip.bridge.core.ui.actions;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
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.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
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.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.Window;
import org.eclipse.sirius.business.api.dialect.DialectManager;
import org.eclipse.sirius.business.api.dialect.command.CreateRepresentationCommand;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.SessionManager;
import org.eclipse.sirius.ui.business.api.dialect.DialectUIManager;
import org.eclipse.sirius.viewpoint.DRepresentation;
import org.eclipse.sirius.viewpoint.description.RepresentationDescription;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.handlers.HandlerUtil;
import com.github.lbroudoux.dsl.eip.Channel;
import com.github.lbroudoux.dsl.eip.EIPModel;
import com.github.lbroudoux.dsl.eip.EipFactory;
import com.github.lbroudoux.dsl.eip.EipPackage;
import com.github.lbroudoux.dsl.eip.Gateway;
import com.github.lbroudoux.dsl.eip.Route;
import com.github.lbroudoux.dsl.eip.ServiceActivator;
import com.github.lbroudoux.dsl.eip.ServiceInvocation;
import com.github.lbroudoux.dsl.eip.ServiceRef;
import com.github.lbroudoux.dsl.eip.bridge.core.ui.dialogs.EIPModelAndRouteSelectionDialog;
/**
* Base Handler for Designer Bridges plugins. It defines the base process of bridging designers together
* and initializing the target EIP model as a consequence. It provides 2 hook methods that should be implemented
* by concrete subclasses : <ul>
* <li><code>checkEventCurrentSelection(ISelection)</code> should check if event is correct and return
* a Route name in this case,</li>
* <li><code>extractServiceRefs()</code> should extract from the semantic elements targeted by event,
* the service references to add into target EIP Route.</li>
* </ul>
* @author laurent
*/
public abstract class AbstractCreateEIPRouteDesignActionHandler extends AbstractHandler {
/**
* The command has been executed, so extract the needed information
* from the application context.
*/
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
ISelection currentSelection = HandlerUtil.getCurrentSelection(event);
// Ask concrete subclass to check if event is eligible and extract default
// route name in this case.
String defaultRouteName = checkEventCurrentSelection(currentSelection);
if (defaultRouteName != null && defaultRouteName.length() > 0) {
// Open target selection dialog.
EIPModelAndRouteSelectionDialog dialog = new EIPModelAndRouteSelectionDialog(
HandlerUtil.getActiveShellChecked(event),
defaultRouteName);
if (dialog.open() != Window.OK) {
return null;
}
// Load and find EIPModel definition.
IResource eipModelResource = dialog.getSelectedEIPModel();
ResourceSet resourceSet = initializeResourceSet();
final Resource emfResource = resourceSet.getResource(URI.createURI(eipModelResource.getLocationURI().toString()), true);
// Ask concrete subclass to produce a list of wrapped ServiceRef.
List<ServiceRefWrapper> serviceRefW = extractServiceRefs();
// Create route with correct service references.
Route route = null;
for (EObject object : emfResource.getContents()){
if (object instanceof EIPModel) {
EIPModel eipModel = (EIPModel) object;
route = createRouteAndServiceRefs(eipModel, dialog.getRouteName(), serviceRefW);
break;
}
}
// Finally, save modified EMF resource.
WorkspaceModifyOperation modifyOp = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor) throws CoreException,
InvocationTargetException, InterruptedException {
try {
emfResource.save(null);
} catch (IOException e) {
System.err.println("IOException while saving modified EIP model");
e.printStackTrace();
}
}
};
try {
modifyOp.run(new NullProgressMonitor());
} catch (InvocationTargetException ite) {
ite.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
try {
eipModelResource.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
} catch (CoreException e) {
System.err.println("Exception while refreshing project containing target model");
e.printStackTrace();
}
// Now, retrieve Sirius session to create default diagram for Route.
IFile airdFile = eipModelResource.getProject().getFile("representations.aird");
if (!airdFile.exists()) {
System.err.println("Could not found file:" + airdFile.getLocationURI());
return null;
}
URI airdFileURI = URI.createPlatformResourceURI(airdFile.getFullPath().toOSString(), true);
Session session = SessionManager.INSTANCE.getSession(airdFileURI, new NullProgressMonitor());
// Reload route so that we're sure it's the one associated to Sirius session.
Route sessionRoute = null;
EIPModel sessionModel = (EIPModel) session.getSemanticResources().iterator().next().getContents().get(0);
for (Route r : sessionModel.getOwnedRoutes()) {
if (r.getName().equals(route.getName())) {
sessionRoute = r;
break;
}
}
// Create route details representation.
Collection<RepresentationDescription> descriptions = DialectManager.INSTANCE.getAvailableRepresentationDescriptions(
session.getSelectedViewpoints(false), sessionRoute);
if (descriptions.isEmpty()) {
System.err.println("Could not found representation description for object: " + sessionRoute);
return null;
}
RepresentationDescription description = descriptions.iterator().next();
Command createViewCommand = new CreateRepresentationCommand(session,
description, sessionRoute, sessionRoute.getName() + " Details", new NullProgressMonitor());
session.getTransactionalEditingDomain().getCommandStack().execute(createViewCommand);
SessionManager.INSTANCE.notifyRepresentationCreated(session);
// Open-up newly created representation.
Collection<DRepresentation> representations = DialectManager.INSTANCE.getRepresentations(description, session);
DRepresentation myDiagramRepresentation = representations.iterator().next();
DialectUIManager.INSTANCE.openEditor(session, myDiagramRepresentation, new NullProgressMonitor());
// Save session and refresh workspace
session.save(new NullProgressMonitor());
try {
eipModelResource.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
} catch (CoreException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Hook for concrete subclass. Called by this base handler once ExecutionEvent is received. Current selection
* is passed in order to check if event is eligible to the creation of an EIP Route. If so, subsclass should
* return a name for the target EIP Route to create. If current selection is not valid, subclass should just
* return null and current handling will stop.
* @param currentSelection The event current selection.
* @return The name of EIP Route to create if correct, null otherwise.
*/
protected abstract String checkEventCurrentSelection(ISelection currentSelection);
/**
* Hook for concrete subclass. Only called once event is valid and valid EIP Model to host target EIP Route
* has been found and loaded. Subclass should implement this method in order to specify how to retrieve the Service
* references that need to be injected into the target EIP Route to create. These references are specific to the
* semantic of designer subclass is working on and thus should be specified.
* @return A list of ServiceRefWrapper or null if no service references found for the target EIP Route.
*/
protected abstract List<ServiceRefWrapper> extractServiceRefs();
/** Simple wrapper for EIP ServiceWrapper for sub-classes not worrying about EMF initialization stuffs... */
protected class ServiceRefWrapper {
private final String name;
private final String[] operations;
private final Object reference;
public ServiceRefWrapper(String name) {
this(name, null, null);
}
public ServiceRefWrapper(String name, String[] operations) {
this(name, operations, null);
}
public ServiceRefWrapper(String name, String[] operations, Object reference) {
this.name = name;
this.operations = operations;
this.reference = reference;
}
public String getName() {
return name;
}
public String[] getOperations() {
return operations;
}
public Object getReference() {
return reference;
}
}
/** Factorize here the EMF loading model plumbing stuffs. */
private ResourceSet initializeResourceSet() {
// Initialize a ResourceSet to later load model and add Route.
ResourceSet resourceSet = new ResourceSetImpl();
// Register the appropriate resource factory to handle all file extensions.
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION,
new XMIResourceFactoryImpl());
// Register the package to ensure it is available during loading.
resourceSet.getPackageRegistry().put(EipPackage.eNS_URI, EipPackage.eINSTANCE);
return resourceSet;
}
/** Factorize here the EMF resource creation plumbing stuffs. */
private Route createRouteAndServiceRefs(EIPModel eipModel, String routeName, List<ServiceRefWrapper> serviceRefs) {
Route route = EipFactory.eINSTANCE.createRoute();
route.setName(routeName);
// Create default GatewayIn and GatewayOut endpoints.
Gateway in = EipFactory.eINSTANCE.createGateway();
Gateway out = EipFactory.eINSTANCE.createGateway();
in.setName("GatewayIn");
out.setName("GatewayOut");
Channel inToFirst = EipFactory.eINSTANCE.createChannel();
Channel lastToOut = EipFactory.eINSTANCE.createChannel();
inToFirst.setName("GatewayIn_First");
lastToOut.setName("Last_GatewayOut");
in.getToChannels().add(inToFirst);
out.getFromChannels().add(lastToOut);
route.getOwnedEndpoints().add(in);
route.getOwnedEndpoints().add(out);
route.getOwnedChannels().add(inToFirst);
route.getOwnedChannels().add(lastToOut);
Channel incomingChannel = inToFirst;
for (int i=0; i<serviceRefs.size(); i++) {
ServiceRefWrapper serviceRefW = serviceRefs.get(i);
ServiceRef serviceRef = EipFactory.eINSTANCE.createServiceRef();
serviceRef.setName(serviceRefW.getName());
// Add operation if any.
if (serviceRefW.getOperations() != null) {
for (String operation : serviceRefW.getOperations()) {
serviceRef.getOperations().add(operation);
}
}
// Add object reference if any.
if (serviceRefW.getReference() != null) {
serviceRef.setReference(serviceRefW.getReference());
}
if (!isServiceRefAlreadyInModel(serviceRef, eipModel)) {
eipModel.getOwnedServiceRefs().add(serviceRef);
}
// Create a sample ServiceActivator and invocation for each reference.
ServiceActivator activator = EipFactory.eINSTANCE.createServiceActivator();
ServiceInvocation invocation = EipFactory.eINSTANCE.createServiceInvocation();
activator.setName(serviceRef.getName()+ " Activator");
invocation.setServiceRef(serviceRef);
activator.getOwnedServiceInvocations().add(invocation);
// Complete activator with channels.
activator.getFromChannels().add(incomingChannel);
if (i < serviceRefs.size() - 1) {
incomingChannel = EipFactory.eINSTANCE.createChannel();
incomingChannel.setName(serviceRefW.getName() + "_" + serviceRefs.get(i + 1).getName());
activator.getToChannels().add(incomingChannel);
route.getOwnedChannels().add(incomingChannel);
} else {
activator.getToChannels().add(lastToOut);
}
route.getOwnedEndpoints().add(activator);
}
eipModel.getOwnedRoutes().add(route);
return route;
}
/** Check if service reference is already present into model. */
private boolean isServiceRefAlreadyInModel(ServiceRef reference, EIPModel model) {
for (ServiceRef modelRef : model.getOwnedServiceRefs()) {
if (modelRef.getName().equals(reference.getName())) {
return true;
}
}
return false;
}
}