/*
* Copyright (c) 2014, IETR/INSA of Rennes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the IETR/INSA of Rennes nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package net.sf.orcc.xdf.ui.features;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.orcc.df.Argument;
import net.sf.orcc.df.Connection;
import net.sf.orcc.df.DfFactory;
import net.sf.orcc.df.Entity;
import net.sf.orcc.df.Instance;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.Port;
import net.sf.orcc.ir.ExprVar;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.IrUtil;
import net.sf.orcc.util.OrccLogger;
import net.sf.orcc.xdf.ui.diagram.XdfDiagramFeatureProvider;
import net.sf.orcc.xdf.ui.dialogs.NewNetworkWizard;
import net.sf.orcc.xdf.ui.patterns.InstancePattern;
import net.sf.orcc.xdf.ui.util.PropsUtil;
import net.sf.orcc.xdf.ui.util.XdfUtil;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.graphiti.features.IDirectEditingInfo;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IContext;
import org.eclipse.graphiti.features.context.ICustomContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.features.context.impl.CustomContext;
import org.eclipse.graphiti.features.context.impl.DeleteContext;
import org.eclipse.graphiti.features.context.impl.MultiDeleteInfo;
import org.eclipse.graphiti.features.custom.ICustomFeature;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.pattern.IFeatureProviderWithPatterns;
import org.eclipse.graphiti.pattern.IPattern;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.ui.PlatformUI;
/**
* Group selected instances into a new Network located somewhere. Replace the
* selected instances by a new one, refined on the network created.
*
* @author Antoine Lorence
*
*/
public class GroupInstancesFeature extends AbstractTimeConsumingCustomFeature {
private Network newNetwork;
public GroupInstancesFeature(IFeatureProvider fp) {
super(fp);
}
@Override
public String getName() {
return "Group selected instances into new network";
}
@Override
public String getDescription() {
return "Create a new network containing selected elements, and replace them with a new instance refined on this network.";
}
@Override
public boolean isAvailable(IContext context) {
return super.isAvailable(context);
}
/*
* This feature can be executed if user selected at least 2 instances. If
* other elements are selected (ports, connections) they will be ignored
*
* (non-Javadoc)
*
* @see
* org.eclipse.graphiti.features.custom.AbstractCustomFeature#canExecute
* (org.eclipse.graphiti.features.context.ICustomContext)
*/
@Override
public boolean canExecute(ICustomContext context) {
final PictogramElement[] selection = context.getPictogramElements();
if (selection.length < 2) {
return false;
}
int cptInstances = 0;
for (final PictogramElement pe : selection) {
if (PropsUtil.isInstance(pe)) {
cptInstances++;
}
}
return cptInstances >= 2;
}
/**
* Display a window to user, to configure location and name of the Network
* which will be created to store grouped instances.
*
* @return A valid Network instance, configured with its name, fileName and
* registered in a Resource
*/
protected Network selectNewNetworkResource() {
final Network currentNetwork = (Network) getBusinessObjectForPictogramElement(getDiagram());
// Create the wizard used to select name and location for the new
// network
final NewNetworkWizard wizard = new NewNetworkWizard(false);
// Initialize the wizard with the current package location
final StructuredSelection networkSelection = new StructuredSelection(
currentNetwork.getFile().getParent());
wizard.init(PlatformUI.getWorkbench(), networkSelection);
final WizardDialog dialog = new WizardDialog(XdfUtil.getDefaultShell(),
wizard);
dialog.open();
if (dialog.getReturnCode() != Dialog.OK) {
return null;
}
return wizard.getCreatedNetwork();
}
@Override
protected void beforeJobExecution() {
// The network to fill with selected content
newNetwork = selectNewNetworkResource();
}
@Override
public void execute(ICustomContext context, IProgressMonitor parentMonitor) {
if (newNetwork == null) {
return;
}
// The current network, where the selected content will be replaced by a
// new instance
final Network currentNetwork = (Network) getBusinessObjectForPictogramElement(getDiagram());
// Configure lists of selected Instances and PictogramElements. If user
// selected Ports or Connections, they are ignored
final Set<Instance> selectedInstances = new HashSet<Instance>();
final Set<PictogramElement> selectedPe = new HashSet<PictogramElement>();
for (final PictogramElement pe : context.getPictogramElements()) {
final Object selected = getBusinessObjectForPictogramElement(pe);
if (selected instanceof Instance) {
selectedInstances.add((Instance) selected);
selectedPe.add(pe);
} else {
continue;
}
}
final SubMonitor monitor = SubMonitor.convert(parentMonitor, 100);
monitor.newChild(5);
// Create an Instance refined on the new Network
final String instanceName = XdfUtil.uniqueVertexName(currentNetwork,
"groupedInstances");
final Instance newInstance = DfFactory.eINSTANCE.createInstance(
instanceName, newNetwork);
currentNetwork.add(newInstance);
SubMonitor loopProgress = monitor.newChild(5).setWorkRemaining(
selectedInstances.size());
monitor.setTaskName("Initialization");
// This will store the mapping between original instances and their copy
// in the new Network
final Map<Instance, Instance> copyMap = new HashMap<Instance, Instance>();
// Copy each selected Instance into the new Network
for (final Instance originalInstance : selectedInstances) {
loopProgress.newChild(1);
final Instance copyInstance = IrUtil.copy(originalInstance);
copyMap.put(originalInstance, copyInstance);
newNetwork.add(copyInstance);
// IrUtil.copy() duplicated all arguments, and set their Use objects
// to original variables, owned by the current network
for (Argument arg : copyInstance.getArguments()) {
for (Iterator<EObject> it = arg.eAllContents(); it.hasNext();) {
final EObject childEObject = it.next();
if (childEObject instanceof ExprVar) {
final ExprVar exprVar = (ExprVar) childEObject;
final Var varOrig = exprVar.getUse().getVariable();
// Create a copy of the original variable (owned by
// currentNetwork)
final Var varCopy = EcoreUtil.copy(varOrig);
// The newNetwork must contains this copy
newNetwork.getParameters().add(varCopy);
exprVar.getUse().setVariable(varCopy);
final ExprVar newExprVar = IrFactory.eINSTANCE
.createExprVar(varOrig);
final Argument newArg = DfFactory.eINSTANCE
.createArgument(varCopy, newExprVar);
newInstance.getArguments().add(newArg);
}
}
}
}
// This set will be filled with connections which needs to be
// re-added to the diagram
final Set<Connection> toUpdateInDiagram = new HashSet<Connection>();
loopProgress = monitor.newChild(5).setWorkRemaining(
currentNetwork.getConnections().size());
monitor.setTaskName("Update current network connections");
// Manage Connections in the current Network. Connections will NOT be
// deleted, only updated, so it is not mandatory to make a copy before
// looping on the list
for (final Connection connection : currentNetwork.getConnections()) {
loopProgress.newChild(1);
// 1 - Inner connection: connect 2 vertex both contained in the
// selection
if (selectedInstances.contains(connection.getSource())
&& selectedInstances.contains(connection.getTarget())) {
final Connection copy = EcoreUtil.copy(connection);
final Instance src = copyMap.get(connection.getSource());
final Instance tgt = copyMap.get(connection.getTarget());
copy.setSource(src);
copy.setTarget(tgt);
copy.setSourcePort(src.getAdapter(Entity.class).getOutput(
connection.getSourcePort().getName()));
copy.setTargetPort(tgt.getAdapter(Entity.class).getInput(
connection.getTargetPort().getName()));
newNetwork.add(copy);
}
// 2 - Cut connection: connected TO a vertex contained in the
// selection
else if (selectedInstances.contains(connection.getTarget())) {
// Create an input Port in the new Network
final Port newInputPort = DfFactory.eINSTANCE.createPort(
EcoreUtil.copy(connection.getTargetPort().getType()),
XdfUtil.uniqueVertexName(newNetwork, connection
.getTargetPort().getName()));
newNetwork.addInput(newInputPort);
// Create a Connection in the new Network
final Instance target = copyMap.get(connection.getTarget());
final Port targetPort = target.getAdapter(Entity.class)
.getInput(connection.getTargetPort().getName());
newNetwork.add(DfFactory.eINSTANCE.createConnection(
newInputPort, null, target, targetPort,
connection.getSize()));
// Update this Connection target: the new Instance, on the right
// input Port
connection.setTarget(newInstance);
connection.setTargetPort(newInputPort);
toUpdateInDiagram.add(connection);
}
// 3 - Cut connections: connected FROM a vertex contained in the
// selection
else if (selectedInstances.contains(connection.getSource())) {
// Create an output Port in the new Network
final Port newOutputPort = DfFactory.eINSTANCE.createPort(
EcoreUtil.copy(connection.getSourcePort().getType()),
XdfUtil.uniqueVertexName(newNetwork, connection
.getSourcePort().getName()));
newNetwork.addOutput(newOutputPort);
// Create a Connection in the new Network
final Instance source = copyMap.get(connection.getSource());
final Port sourcePort = source.getAdapter(Entity.class)
.getOutput(connection.getSourcePort().getName());
newNetwork.add(DfFactory.eINSTANCE.createConnection(source,
sourcePort, newOutputPort, null, connection.getSize()));
// Update this Connection source: the new Instance, on the right
// output Port
connection.setSource(newInstance);
connection.setSourcePort(newOutputPort);
toUpdateInDiagram.add(connection);
}
}
monitor.newChild(5);
monitor.setTaskName("Add the new instance");
// Now the new Network is up-to-date. In particular, it contains all its
// Ports. We can add the new Instance refined on this Network in the
// current Diagram. Doing this now allows to have Anchors for Instance
// Port available. These Anchors will be used to update existing
// FreeFormConnections
final AddContext addContext = new AddContext();
addContext.setTargetContainer(getDiagram());
addContext.setNewObject(newInstance);
// We will run the layout at the end
addContext.setLocation(10, 10);
final PictogramElement newInstancePe = getFeatureProvider()
.addIfPossible(addContext);
// Get the instancePattern
final IFeatureProviderWithPatterns fp = (IFeatureProviderWithPatterns) getFeatureProvider();
final InstancePattern instancePattern = (InstancePattern) fp
.getPatternForPictogramElement(newInstancePe);
loopProgress = monitor.newChild(5).setWorkRemaining(
toUpdateInDiagram.size());
monitor.setTaskName("Update FreeFormConnections");
// We can update graphiti Connections to start or end from/to the newly
// added Instance
for (final Connection connection : toUpdateInDiagram) {
loopProgress.newChild(1);
final List<PictogramElement> pictogramElements = Graphiti
.getLinkService().getPictogramElements(getDiagram(),
connection);
if (newInstance.equals(connection.getTarget())) {
// Update the PE connection(s) target
// (org.eclipse.graphiti.mm.pictograms.Connection#setEnd())
for (final PictogramElement pe : pictogramElements) {
if (pe instanceof org.eclipse.graphiti.mm.pictograms.Connection) {
org.eclipse.graphiti.mm.pictograms.Connection peConnection = (org.eclipse.graphiti.mm.pictograms.Connection) pe;
peConnection.setEnd(instancePattern.getAnchorForPort(
newInstancePe, connection.getTargetPort()));
}
}
} else if (newInstance.equals(connection.getSource())) {
// Update the PE connection(s) source
// (org.eclipse.graphiti.mm.pictograms.Connection#setStart())
for (final PictogramElement pe : pictogramElements) {
if (pe instanceof org.eclipse.graphiti.mm.pictograms.Connection) {
org.eclipse.graphiti.mm.pictograms.Connection peConnection = (org.eclipse.graphiti.mm.pictograms.Connection) pe;
peConnection.setStart(instancePattern.getAnchorForPort(
newInstancePe, connection.getSourcePort()));
}
}
} else {
OrccLogger.severeln("Some connections will not be updated. "
+ "This is an error in the code. Please report a bug.");
}
}
loopProgress = monitor.newChild(60).setWorkRemaining(selectedPe.size());
monitor.setTaskName("Delete selected instances");
// Finally remove from diagram useless elements. Inner connections
// are also deleted, since deleting an instance or a port from a
// diagram also clean related connections
for (final PictogramElement pe : selectedPe) {
loopProgress.newChild(1);
final IPattern pattern = fp.getPatternForPictogramElement(pe);
final DeleteContext delContext = new DeleteContext(pe);
delContext.setMultiDeleteInfo(new MultiDeleteInfo(false, false, 0));
pattern.delete(delContext);
}
monitor.newChild(10);
monitor.setTaskName("Lay out the diagram");
// Layout the resulting diagram
final IContext layoutContext = new CustomContext();
final ICustomFeature layoutFeature = ((XdfDiagramFeatureProvider) getFeatureProvider())
.getDefaultLayoutFeature();
if (layoutFeature.canExecute(layoutContext)) {
layoutFeature.execute(layoutContext);
}
monitor.newChild(10);
monitor.setTaskName("Save all");
// Save the new Network on the disk
try {
newNetwork.eResource().save(Collections.EMPTY_MAP);
} catch (IOException e) {
e.printStackTrace();
}
monitor.done();
// Finally, active direct editing on the newly created instance
final IDirectEditingInfo dei = getFeatureProvider()
.getDirectEditingInfo();
dei.setMainPictogramElement(newInstancePe);
dei.setActive(true);
}
}