/*
* Copyright (c) 2013, 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.util;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sf.orcc.df.Connection;
import net.sf.orcc.df.DfFactory;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.Port;
import net.sf.orcc.graph.Vertex;
import net.sf.orcc.util.OrccLogger;
import net.sf.orcc.xdf.ui.Activator;
import net.sf.orcc.xdf.ui.patterns.InputNetworkPortPattern;
import net.sf.orcc.xdf.ui.patterns.InstancePattern;
import net.sf.orcc.xdf.ui.patterns.OutputNetworkPortPattern;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
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.graphiti.features.IDeleteFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.context.IAddConnectionContext;
import org.eclipse.graphiti.features.context.impl.AddConnectionContext;
import org.eclipse.graphiti.features.context.impl.DeleteContext;
import org.eclipse.graphiti.features.context.impl.MultiDeleteInfo;
import org.eclipse.graphiti.mm.algorithms.Text;
import org.eclipse.graphiti.mm.algorithms.styles.Font;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.AnchorContainer;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.pattern.IFeatureProviderWithPatterns;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.ILinkService;
import org.eclipse.graphiti.ui.services.GraphitiUi;
import org.eclipse.graphiti.ui.services.IUiLayoutService;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
* @author Antoine Lorence
*
*/
public class XdfUtil {
private static ResourceSet resourceSet = new ResourceSetImpl();
public static Shell getDefaultShell() {
return PlatformUI.getWorkbench().getDisplay().getActiveShell();
}
public static IProject getProject(final EObject object) {
String path = object.eResource().getURI().toPlatformString(true);
return ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path))
.getProject();
}
/**
* Create a resource from the given URI and append a new Network instance to
* its contents. The default internal resourceSet is used.
*
* @param uri
* @return
* @throws IOException
* @see {@link #createNetworkResource(ResourceSet, URI)}
*/
public static Network createNetworkResource(final URI uri) throws IOException {
return createNetworkResource(resourceSet, uri);
}
/**
* Create a resource from the given URI and append a new Network instance to
* its contents. The resourceSet used must be authorized to write on the
* disk. This means that the default EditingDomain's resourceSet must be
* used in a write transaction (for example). If it is not possible, do not
* provide a resourceSet, the default one will be used.
*
* @param resourceSet
* @param uri
* @return The created network
* @throws IOException
*/
public static Network createNetworkResource(final ResourceSet resourceSet, final URI uri) throws IOException {
final String fileName;
if (uri.isPlatform()) {
fileName = uri.toPlatformString(true);
} else {
fileName = uri.toString();
}
// Create the network
final Network network = DfFactory.eINSTANCE.createNetwork(fileName);
// Compute the new network name
final Path networkPath = new Path(uri.trimFileExtension().path());
// 3 first segments are resource/<PROJECT>/src
network.setName(networkPath.removeFirstSegments(3).toString()
.replace('/', '.'));
// Create the resource
Resource res = resourceSet.createResource(uri);
res.getContents().add(network);
res.save(Collections.EMPTY_MAP);
return network;
}
/**
* Create a resource from the given URI and append a new Diagram instance to
* its contents. The default internal resourceSet is used.
*
* @param uri
* @return
* @throws IOException
* @see {@link #createDiagramResource(ResourceSet, URI)}
*/
public static Diagram createDiagramResource(final URI uri) throws IOException {
return createDiagramResource(resourceSet, uri);
}
/**
* Create a resource from the given URI and append a new Diagram instance to
* its contents. The resourceSet used must be authorized to write on the
* disk. This means that the default EditingDomain's resourceSet must be
* used in a write transaction (for example). If it is not possible, do not
* provide a resourceSet, the default one will be used.
*
* @param resourceSet
* @param uri
* @return The Diagram created
* @throws IOException
*/
public static Diagram createDiagramResource(final ResourceSet resourceSet, final URI uri) throws IOException {
// Compute the new diagram name
final String name = uri.trimFileExtension().lastSegment();
// Create the diagram
final Diagram diagram = Graphiti.getPeCreateService().createDiagram(Activator.DIAGRAM_TYPE, name, true);
// Create the resource
Resource res = resourceSet.createResource(uri);
res.getContents().add(diagram);
res.setTrackingModification(true);
res.save(Collections.EMPTY_MAP);
return diagram;
}
/**
* Returns the minimal width needed to display the value of given Text with
* its associated Font.
*
* @param text
* @return
*/
public static int getTextMinWidth(Text text) {
final IUiLayoutService uiLayoutService = GraphitiUi.getUiLayoutService();
final Font currentFont = Graphiti.getGaService().getFont(text, true);
return uiLayoutService.calculateTextSize(text.getValue(), currentFont).getWidth();
}
/**
* Returns the minimal height needed to display the value of given Text with
* its associated Font.
*
* @param text
* @return
*/
public static int getTextMinHeight(Text text) {
final IUiLayoutService uiLayoutService = GraphitiUi.getUiLayoutService();
final Font currentFont = Graphiti.getGaService().getFont(text, true);
return uiLayoutService.calculateTextSize(text.getValue(), currentFont).getHeight();
}
/**
* Check if the given port is contained in a Network in its inputs.
*
* @param port
* @return
*/
public static boolean isInputNetworkPort(final Port port) {
if(port.getGraph() != null) {
return ((Network) port.getGraph()).getInputs().contains(port);
}
return false;
}
/**
* Check if the given port is contained in a Network in its outputs.
*
* @param port
* @return
*/
public static boolean isOutputNetworkPort(final Port port) {
if(port.getGraph() != null) {
return ((Network) port.getGraph()).getOutputs().contains(port);
}
return false;
}
/**
* <p>
* Initialize a ready to use AddConnectionContext object from a network
* connection. This method does not add the given connection to the network.
* </p>
*
* <p>
* The returned IAddConnectionContext is ready to use. The business object
* has been set to the given connection and the target container has been
* set to the given diagram.
* </p>
*
* @param fp
* The IFeatureProviderWithPatterns instance
* @param diagram
* The diagram where the connection will be added
* @param connection
* The connection
* @return
*/
public static IAddConnectionContext getAddConnectionContext(
final IFeatureProviderWithPatterns fp,
final Diagram diagram, final Connection connection) {
final ILinkService linkServ = Graphiti.getLinkService();
// retrieve the source and target PictogramElements
final List<PictogramElement> sourcesPE = linkServ.getPictogramElements(
diagram, connection.getSource());
if (sourcesPE == null || sourcesPE.isEmpty()) {
OrccLogger
.warnln("[getAddConnectionContext] Unable to "
+ "retrieve the PictogramElement corresponding to the source "
+ connection.getSource() + ".");
return null;
}
final List<PictogramElement> targetsPE = linkServ.getPictogramElements(
diagram, connection.getTarget());
if (targetsPE == null || targetsPE.isEmpty()) {
OrccLogger
.warnln("[getAddConnectionContext] Unable to "
+ "retrieve the PictogramElement corresponding to the target "
+ connection.getTarget() + ".");
return null;
}
// source/target PictogramElement
final PictogramElement sourcePe = sourcesPE.get(0);
final PictogramElement targetPe = targetsPE.get(0);
final Anchor sourceAnchor, targetAnchor;
if (PropsUtil.isInputPort(sourcePe)) {
// Connection from a network port
final InputNetworkPortPattern spattern = (InputNetworkPortPattern) fp
.getPatternForPictogramElement(sourcePe);
sourceAnchor = spattern.getAnchor((AnchorContainer) sourcePe);
} else {
// Connection from an instance port
final InstancePattern spattern = (InstancePattern) fp
.getPatternForPictogramElement(sourcePe);
sourceAnchor = spattern.getAnchorForPort(sourcePe,
connection.getSourcePort());
}
if (PropsUtil.isOutputPort(targetPe)) {
// Connection to a network port
final OutputNetworkPortPattern tpattern = (OutputNetworkPortPattern) fp
.getPatternForPictogramElement(targetPe);
targetAnchor = tpattern.getAnchor((AnchorContainer) targetPe);
} else {
// Connection to an instance port
final InstancePattern tpattern = (InstancePattern) fp
.getPatternForPictogramElement(targetPe);
targetAnchor = tpattern.getAnchorForPort(targetPe,
connection.getTargetPort());
}
final AddConnectionContext result = new AddConnectionContext(
sourceAnchor, targetAnchor);
result.setTargetContainer(diagram);
result.setNewObject(connection);
return result;
}
/**
* If the given AnchorContainer has graphiti connections from / to itself,
* this method delete all these connections as well the business objects
* (net.sf.orcc.df.Connection) linked
*
* @param fp
* The current FeatureProvider instance
* @param ac
* An anchor container, typically an Instance or a NetworkPort
* shape
*/
public static void deleteConnections(final IFeatureProvider fp,
final AnchorContainer ac) {
// Make a copy of all connections, because we will delete them entirely
final List<org.eclipse.graphiti.mm.pictograms.Connection> connections = Graphiti
.getPeService().getAllConnections(ac);
for (final org.eclipse.graphiti.mm.pictograms.Connection connection : connections) {
deleteConnection(fp, connection);
}
}
/**
* Delete the given connection as well its net.sf.orcc.df.Connection linked
* (its business object)
*
* @param fp
* The current FeatureProvider instance
* @param c
* A connection pictogram (FreeFormConnection or any other class
* derived from Connection)
*/
public static boolean deleteConnection(final IFeatureProvider fp,
final org.eclipse.graphiti.mm.pictograms.Connection c) {
// Initialize the delete context
final DeleteContext delCtxt = new DeleteContext(c);
delCtxt.setMultiDeleteInfo(new MultiDeleteInfo(false, false, 1));
// Silently execute deletion (user will not be asked before deletion)
final IDeleteFeature delFeature = fp.getDeleteFeature(delCtxt);
if (delFeature.canDelete(delCtxt)) {
delFeature.delete(delCtxt);
return true;
}
return false;
}
/**
* Check if the given network contains a vertex (instance or port) with the
* given base name. If yes, return a new unique name formed from the given
* base and a numeric suffix. If not, returns the given base.
*
* @param network
* The network to check for existing vertex with the given name
* @param base
* The base name to assign to a new vertex
* @return A unique name to assign to a new Instance / Port in the given
* network
*/
public static String uniqueVertexName(final Network network,
final String base) {
final Set<String> existingNames = new HashSet<String>();
for (Vertex vertex : network.getVertices()) {
existingNames.add(vertex.getLabel());
}
if (!existingNames.contains(base)) {
return base;
} else {
int index = 0;
while (existingNames.contains(base + '_' + index)) {
++index;
}
return base + '_' + index;
}
}
}