/* * 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.patterns; import net.sf.orcc.df.DfFactory; import net.sf.orcc.df.Instance; import net.sf.orcc.df.Network; import net.sf.orcc.df.Port; import net.sf.orcc.graph.Vertex; import net.sf.orcc.ir.IrFactory; import net.sf.orcc.ir.Type; import net.sf.orcc.xdf.ui.styles.StyleUtil; 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.graphiti.features.IDirectEditingInfo; import org.eclipse.graphiti.features.IReason; import org.eclipse.graphiti.features.context.IAddContext; import org.eclipse.graphiti.features.context.ICreateContext; import org.eclipse.graphiti.features.context.IDeleteContext; import org.eclipse.graphiti.features.context.IDirectEditingContext; import org.eclipse.graphiti.features.context.ILayoutContext; import org.eclipse.graphiti.features.context.IResizeShapeContext; import org.eclipse.graphiti.features.context.IUpdateContext; import org.eclipse.graphiti.features.impl.Reason; import org.eclipse.graphiti.func.IDirectEditing; import org.eclipse.graphiti.mm.GraphicsAlgorithmContainer; import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm; import org.eclipse.graphiti.mm.algorithms.Polygon; import org.eclipse.graphiti.mm.algorithms.Rectangle; import org.eclipse.graphiti.mm.algorithms.Text; import org.eclipse.graphiti.mm.pictograms.Anchor; import org.eclipse.graphiti.mm.pictograms.AnchorContainer; import org.eclipse.graphiti.mm.pictograms.ContainerShape; import org.eclipse.graphiti.mm.pictograms.Diagram; import org.eclipse.graphiti.mm.pictograms.PictogramElement; import org.eclipse.graphiti.pattern.AbstractPattern; import org.eclipse.graphiti.pattern.IPattern; import org.eclipse.graphiti.services.Graphiti; import org.eclipse.graphiti.services.IGaLayoutService; import org.eclipse.graphiti.services.IGaService; import org.eclipse.graphiti.services.IPeCreateService; /** * This class is the common part for Network port management. Concrete * implementations handle specific cases for input and output ports. * * @see InputNetworkPortPattern * @see OutputNetworkPortPattern * * @author Antoine Lorence * */ abstract public class NetworkPortPattern extends AbstractPattern implements IPattern { public final static int SHAPE_HEIGHT = 34; public final static int SHAPE_WIDTH = (int) (SHAPE_HEIGHT * 0.866); private final static int TEXT_PORT_SPACE = 4; private final static int TEXT_DEFAULT_WIDTH = 50; private final static int TEXT_DEFAULT_HEIGHT = 12; public static final String SHAPE_ID = "PORT_SHAPE"; private static final String LABEL_ID = "PORT_LABEL"; public NetworkPortPattern() { super(null); } @Override abstract public String getCreateName(); abstract protected void setIdentifier(ContainerShape topLevelShape); abstract protected boolean checkIdentifier(PictogramElement pe); abstract protected Polygon getPortPolygon(final GraphicsAlgorithmContainer shape, final IGaService gaService); abstract protected void addPortToNetwork(Port port, Network network); @Override protected boolean isPatternRoot(PictogramElement pe) { return checkIdentifier(pe); } @Override protected boolean isPatternControlled(PictogramElement pe) { if (isPatternRoot(pe)) { return true; } final String identifier = PropsUtil.getIdentifier(pe); final String[] otherIds = { SHAPE_ID, LABEL_ID }; for (final String e : otherIds) { if (e.equals(identifier)) { return true; } } return false; } @Override public boolean canDirectEdit(IDirectEditingContext context) { boolean isText = context.getGraphicsAlgorithm() instanceof Text; boolean isLabel = PropsUtil.isExpectedPc(context.getGraphicsAlgorithm(), LABEL_ID); return isText && isLabel; } @Override public int getEditingType() { return IDirectEditing.TYPE_TEXT; } @Override public String getInitialValue(IDirectEditingContext context) { Port obj = (Port) getBusinessObjectForPictogramElement(context.getPictogramElement()); return obj.getName(); } @Override public String checkValueValid(String value, IDirectEditingContext context) { final Port port = (Port) getBusinessObjectForPictogramElement(context .getPictogramElement()); return checkValueValid(value, port); } public String checkValueValid(final String value, final Port port) { if (value.length() < 1) { return "Please enter a text to name the Port."; } if (!value.matches("[a-zA-Z][a-zA-Z0-9_]*")) { return "Port name must start with a letter, and contains only alphanumeric characters"; } final Network network = (Network) getBusinessObjectForPictogramElement(getDiagram()); for (final Vertex vertex : network.getVertices()) { if (!vertex.equals(port) && vertex.getLabel().equals(value)) { final String vertexType = vertex instanceof Instance ? "an instance" : "a port"; return "The network already contains a vertex of the same name (" + vertexType + ")"; } } // null -> value is valid return null; } @Override public void setValue(String value, IDirectEditingContext context) { PictogramElement pe = context.getPictogramElement(); final Port port = (Port) getBusinessObjectForPictogramElement(pe); port.setName(value); updatePictogramElement(pe); } @Override public boolean stretchFieldToFitText() { return true; } @Override public void preDelete(IDeleteContext context) { final PictogramElement pe = context.getPictogramElement(); if (pe instanceof AnchorContainer) { XdfUtil.deleteConnections(getFeatureProvider(), (AnchorContainer) pe); } } @Override public boolean canCreate(ICreateContext context) { // We create the instance in a diagram if (context.getTargetContainer() instanceof Diagram) { // A network is associated to this diagram final Object bo = getBusinessObjectForPictogramElement(context.getTargetContainer()); if (bo instanceof Network) { return true; } } return false; } @Override public Object[] create(ICreateContext context) { final Network network = (Network) getBusinessObjectForPictogramElement(getDiagram()); // Create the Port instance final Port newPort = DfFactory.eINSTANCE.createPort(); newPort.setName(XdfUtil.uniqueVertexName(network, "port")); // Set default type to i32 for the port newPort.setType(IrFactory.eINSTANCE.createTypeInt()); // Add the newly created port to the network addPortToNetwork(newPort, network); addGraphicalRepresentation(context, newPort); // activate direct editing after object creation getFeatureProvider().getDirectEditingInfo().setActive(true); return new Object[] { newPort }; } @Override public boolean canAdd(IAddContext context) { if (context.getTargetContainer() instanceof Diagram) { return isMainBusinessObjectApplicable(context.getNewObject()); } return false; } @Override public PictogramElement add(IAddContext context) { final Diagram targetDiagram = (Diagram) context.getTargetContainer(); final IPeCreateService peCreateService = Graphiti.getPeCreateService(); final IGaService gaService = Graphiti.getGaService(); final Port addedDomainObject = (Port) context.getNewObject(); // Create the container final ContainerShape topLevelShape = peCreateService.createContainerShape(targetDiagram, true); setIdentifier(topLevelShape); peCreateService.createChopboxAnchor(topLevelShape); // The main container is an invisible rectangle final Rectangle topLevelInvisibleRect = gaService.createInvisibleRectangle(topLevelShape); // Draw the port according to its direction final Polygon polygon = getPortPolygon(topLevelInvisibleRect, gaService); gaService.setSize(polygon, SHAPE_WIDTH, SHAPE_HEIGHT); PropsUtil.setIdentifier(polygon, SHAPE_ID); // Add the label of the port final Text text = gaService.createPlainText(topLevelInvisibleRect); PropsUtil.setIdentifier(text, LABEL_ID); // Configure text properties text.setStyle(StyleUtil.portText(getDiagram())); // We define an arbitrary width to text, allowing user to see chars // when first direct editing port name gaService.setLocationAndSize(text, 0, SHAPE_HEIGHT + TEXT_PORT_SPACE, TEXT_DEFAULT_WIDTH, TEXT_DEFAULT_HEIGHT); // Initialize the port if domain object already exists if (addedDomainObject.getName() != null) { text.setValue(addedDomainObject.getName()); } // Configure direct editing // 1- Get the IDirectEditingInfo object final IDirectEditingInfo directEditingInfo = getFeatureProvider().getDirectEditingInfo(); // 2- These 2 members will be used to retrieve the pattern to call for // direct editing directEditingInfo.setPictogramElement(topLevelShape); directEditingInfo.setGraphicsAlgorithm(text); // 3- This PictogramElement is used to locate input on the diagram directEditingInfo.setMainPictogramElement(topLevelShape); link(topLevelShape, addedDomainObject); gaService.setLocation(topLevelInvisibleRect, context.getX(), context.getY()); layoutPictogramElement(topLevelShape); return topLevelShape; } /** * @see NetworkPortPattern#layout(ILayoutContext) */ @Override public boolean canResizeShape(IResizeShapeContext context) { return false; } @Override public boolean canLayout(ILayoutContext context) { return isPatternRoot(context.getPictogramElement()); } /** * Port can't be resized. In that particular case, layout() does everything * needed for a nice display. It adapts the size of text to its value and * ensures everything is always centered * * @param context * @return */ @Override public boolean layout(ILayoutContext context) { final IGaLayoutService gaService = Graphiti.getGaLayoutService(); final PictogramElement topLevelPe = context.getPictogramElement(); final GraphicsAlgorithm topLevelGa = topLevelPe.getGraphicsAlgorithm(); final Text txt = (Text) PropsUtil.findPcFromIdentifier(topLevelPe, LABEL_ID); final Polygon poly = (Polygon) PropsUtil.findPcFromIdentifier(topLevelPe, SHAPE_ID); final int minTxtWidth = XdfUtil.getTextMinWidth(txt); final int newTextWidth = Math.max(minTxtWidth, SHAPE_WIDTH); // Update text width gaService.setSize(txt, newTextWidth, XdfUtil.getTextMinHeight(txt)); // Update top level invisible rectangle size & position gaService.setSize(topLevelGa, newTextWidth, SHAPE_HEIGHT + TEXT_PORT_SPACE + TEXT_DEFAULT_HEIGHT); // Update position of polygon in the top level rectangle gaService.setLocation(poly, (newTextWidth - SHAPE_WIDTH) / 2, 0); return true; } @Override public IReason updateNeeded(IUpdateContext context) { final PictogramElement pe = context.getPictogramElement(); if (isPatternRoot(pe)) { final Text text = (Text) PropsUtil.findPcFromIdentifier(pe, LABEL_ID); if (text == null) { return Reason.createFalseReason("Label Not found !!"); } final Port port = (Port) getBusinessObjectForPictogramElement(pe); if (!text.getValue().equals(port.getName())) { return Reason.createTrueReason("The port name has been updated from outside of the diagram"); } } return super.updateNeeded(context); } @Override public boolean update(IUpdateContext context) { final PictogramElement pe = context.getPictogramElement(); if (!isPatternRoot(pe)) { return false; } final Text txt = (Text) PropsUtil.findPcFromIdentifier(pe, LABEL_ID); final Port port = (Port) getBusinessObjectForPictogramElement(pe); txt.setValue(port.getName()); layoutPictogramElement(pe); return true; } /** * Returns the PictogramElement to use as main element for displaying a * selection around a port. * * @param pe * @return */ public GraphicsAlgorithm getSelectionBorder(PictogramElement pe) { if (isPatternRoot(pe)) { return (GraphicsAlgorithm) PropsUtil.findPcFromIdentifier(pe, SHAPE_ID); } return null; } /** * Find and return the type associated with the Network port. This * information is stored in the linked objects. * * @param pe * @return * @deprecated This method will not work until the port type is stored as * string in the shape and a type parser can be used to crete * the Type object from this String */ @Deprecated public Type getTypeFromShape(PictogramElement pe) { if (isPatternRoot(pe)) { for (EObject businessObject : pe.getLink().getBusinessObjects()) { if (businessObject instanceof Type) { return (Type) businessObject; } } } return null; } /** * Return the name of the port, from information contained in graphical * representation only. If this port has no name, returns an empty String. * * @param pe * @return */ public String getNameFromShape(final PictogramElement pe) { if (isPatternRoot(pe)) { final Text label = (Text) PropsUtil.findPcFromIdentifier(pe, LABEL_ID); return label.getValue(); } return ""; } /** * Retrieve the ChopboxAnchor for the port. * * @param container * @return */ public Anchor getAnchor(final AnchorContainer container) { return Graphiti.getPeService().getChopboxAnchor(container); } }