/*
* 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.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import net.sf.orcc.df.Argument;
import net.sf.orcc.df.Instance;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.Port;
import net.sf.orcc.ir.Use;
import net.sf.orcc.ir.Var;
import net.sf.orcc.util.util.EcoreHelper;
import net.sf.orcc.xdf.ui.util.PropsUtil;
import net.sf.orcc.xdf.ui.util.XdfUtil;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IPasteContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.ui.features.AbstractPasteFeature;
import org.eclipse.jface.dialogs.MessageDialog;
/**
* Implements the ability to paste previously copied/cut objects into diagram.
*
* @author Antoine Lorence
*
*/
public class PasteFeature extends AbstractPasteFeature {
public PasteFeature(IFeatureProvider fp) {
super(fp);
}
@Override
public boolean canPaste(IPasteContext context) {
// Can paste, if all objects on the clipboard are Instance or Port
final Object[] fromClipboard = getFromClipboard();
if (fromClipboard == null || fromClipboard.length == 0) {
return false;
}
for (final Object object : fromClipboard) {
if (!(object instanceof Instance || object instanceof Port)) {
return false;
}
}
return true;
}
@Override
public void paste(IPasteContext context) {
final Network network = (Network) getBusinessObjectForPictogramElement(getDiagram());
final Object[] objects = getFromClipboard();
for (final Object object : objects) {
final Set<Argument> argumentsToDelete = new HashSet<Argument>();
if (object instanceof Port) {
final Port origPort = (Port) object;
final Port port = EcoreUtil.copy(origPort);
port.setName(XdfUtil.uniqueVertexName(network, port.getName()));
addToDiagram(context, getDiagram(), port);
} else if (object instanceof Instance) {
pasteInstance((Instance) object, network, context,
argumentsToDelete);
}
if (!argumentsToDelete.isEmpty()) {
for (Argument arg : argumentsToDelete) {
EcoreUtil.delete(arg);
}
MessageDialog.openInformation(XdfUtil.getDefaultShell(),
"Arguments deleted", argumentsToDelete.size()
+ " instances' arguments were referencing "
+ "variables unknown in the target context. "
+ "They have been deleted.");
argumentsToDelete.clear();
}
}
}
private void addToDiagram(final IPasteContext context,
final Diagram diagram, final EObject object) {
final AddContext ac = new AddContext();
// Set the location for the element to add in diagram
configureAddLocation(context, ac);
ac.setTargetContainer(diagram);
addGraphicalRepresentation(ac, object);
}
/**
* Calculate the best position for the next added element. If nothing is
* selected (or the diagram is the current selection), the position for the
* next added element will be the mouse coordinates. If a port or an
* instance is selected, the next position will be this element with 10px
* added in both x and y coordinates.
*
* @param pasteContext
* The paste context
* @param addContext
* [out] The add context to update with a new location
*/
private void configureAddLocation(final IPasteContext pasteContext,
final AddContext addContext) {
final PictogramElement[] selected = pasteContext.getPictogramElements();
if (selected != null && selected.length != 0) {
final PictogramElement element = selected[0];
if (PropsUtil.isInstance(element) || PropsUtil.isPort(element)) {
final int x = element.getGraphicsAlgorithm().getX() + 10;
final int y = element.getGraphicsAlgorithm().getY() + 10;
addContext.setLocation(x, y);
return;
}
}
// Fallback
addContext.setLocation(pasteContext.getX(), pasteContext.getY());
}
/**
* Specific method to paste an instance copied from the same or another
* network to the given targetNetwork
*
* @param origInstance
* @param targetNetwork
* @param context
* @param argumentsToDelete
*/
private void pasteInstance(final Instance origInstance,
final Network targetNetwork, final IPasteContext context,
final Set<Argument> argumentsToDelete) {
// We use a Copier instance here, to keep track of mapping
// between original object and its copy
final Copier copier = new Copier();
// Get a copy of the instance
final Instance copyInstance = (Instance) copier.copy(origInstance);
// Also copy references
copier.copyReferences();
// Update the name of the new instance, ensuring it is unique
copyInstance.setName(XdfUtil.uniqueVertexName(targetNetwork,
origInstance.getName()));
// Add the new instance to the diagram (really paste the object)
addToDiagram(context, getDiagram(), copyInstance);
// Update each argument, according to variables declared in the
// target diagram
// Firstly, we loop over arguments in the original instance
for (final Argument arg : origInstance.getArguments()) {
// Get the uses objects in each argument value
for (final Iterator<EObject> it = arg.eAllContents(); it.hasNext();) {
final EObject content = it.next();
if (content instanceof Use) {
// The original Use (valid)
final Use use = (Use) content;
// The copied one (probably invalid, referencing a
// variable not visible in the new context)
final Use copyUse = (Use) copier.get(use);
if (copyUse.getVariable() == null) {
final String varName = use.getVariable().getName();
// Try to find an existing variable / parameter
// in the target network
Var theVar = targetNetwork.getVariable(varName);
if (theVar == null) {
theVar = targetNetwork.getParameter(varName);
}
// Not found, the arg must be deleted from
// pasted instance
if (theVar == null) {
argumentsToDelete
.add(EcoreHelper.getContainerOfType(
copyUse, Argument.class));
}
// Found, pasted instance argument is updated
else {
copyUse.setVariable(theVar);
}
}
}
}
}
}
}