blob: f1b295eaffbbae2bb61c2b5a33e856bf5bc46ac8 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019 CEA LIST, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.sysml16.service.types.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.uml2.uml.ConnectableElement;
import org.eclipse.uml2.uml.Connector;
import org.eclipse.uml2.uml.ConnectorEnd;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.TypedElement;
/**
* Utility class for Connector edit helpers.
*/
// TODO : this class should be move to uml core
public class UMLConnectorUtils extends org.eclipse.papyrus.uml.service.types.utils.ConnectorUtils {
/**
* the key used to store the nested path as parameter of the request. The value will be a List of {@link Property}
*/
public static final String NESTED_CONNECTOR_END_PATH = "connectorEndPath"; //$NON-NLS-1$
/** the separator used in the role path to distinguish the part. */
public static final String PART_SEPARATOR = "."; //$NON-NLS-1$
/** allow to know if a string contains others characters than a-z A-Z 0-9 and _. */
public static final String HAS_NO_WORD_CHAR_REGEX = "\\W+"; //$NON-NLS-1$
/** String used to delimit a name with contains special chars. */
public static final String STRING_DELIMITER = "\'"; //$NON-NLS-1$
/**
* Get the path of structure views crossed by the checked end.
*
* @param checkedEnd
* the checked end view.
* @param oppositeEnd
* the opposite end view.
* @return the list of crossed structure views.
*/
public List<View> getNestedPath(View checkedEnd, View oppositeEnd) {
List<View> isNestedConnectableElement = new ArrayList<>();
View nearestContainer = new UMLConnectorUtils().getNearestStructureContainer(checkedEnd);
if (nearestContainer != null) {
View containerView = new UMLConnectorUtils().deduceViewContainer(checkedEnd, oppositeEnd);
List<View> containers = new UMLConnectorUtils().getStructureContainers(checkedEnd);
if (containers.indexOf(nearestContainer) < containers.indexOf(containerView)) {
isNestedConnectableElement = containers.subList(containers.indexOf(nearestContainer), containers.indexOf(containerView));
}
}
// nested path is taken from the top block to the deepest property ==> collection must be reverted
Collections.reverse(isNestedConnectableElement);
return isNestedConnectableElement;
}
/**
* Get the path of structure views crossed by the checked end.
*
* @param checkedEnd
* the checked end view.
* @param oppositeEnd
* the opposite end view.
* @return the list of crossed structure views.
*/
public List<Property> getNestedPropertyPath(View checkedEnd, View oppositeEnd) {
List<Property> nestedPropertyPath = new ArrayList<>();
List<View> nestedPath = getNestedPath(checkedEnd, oppositeEnd);
for (View view : nestedPath) {
if ((view.getElement() != null) && (view.getElement() instanceof Property)) {
nestedPropertyPath.add((Property) view.getElement());
}
}
// if end is a port, and the list is not empty, add the property from the check view in the list
if (!nestedPropertyPath.isEmpty() && checkedEnd.getElement() instanceof Port) {
Property partWithPort = getPartWithPort(checkedEnd, oppositeEnd);
if (partWithPort != null) {
nestedPropertyPath.add(partWithPort);
}
}
return nestedPropertyPath;
}
/**
* Test if the checked end is nested (means it cross StructuredClassifier borders).
*
* @param checkedEnd
* the checked end view.
* @param oppositeEnd
* the opposite end view.
* @return true if the checked end is nested.
*/
public boolean isNestedConnectableElement(View checkedEnd, View oppositeEnd) {
return !(getNestedPath(checkedEnd, oppositeEnd).isEmpty());
}
/**
* Gets the nearest structure container.
*
* @param view
* the view
* @return the nearest structure container or null
*/
public View getNearestStructureContainer(View view) {
View nearestStructureContainer = null;
for (View containerView : getStructureContainers(view)) {
if (view == containerView) {
continue;
}
nearestStructureContainer = containerView;
break;
}
return nearestStructureContainer;
}
/**
* Gets the name with quotes.
*
* @param property
* a property
* @return the name with quotes
* the property name with name delimiter if it is required
*/
public static final String getNameWithQuotes(final NamedElement property) {
final String partName = property.getName();
final StringBuilder partNameBuffer = new StringBuilder();
final Pattern pattern = Pattern.compile(UMLConnectorUtils.HAS_NO_WORD_CHAR_REGEX);
final Matcher matcher = pattern.matcher(partName);
boolean mustHaveQuote = false;
while (matcher.find() && !mustHaveQuote) {
mustHaveQuote = true;
}
if (mustHaveQuote) {
partNameBuffer.append(UMLConnectorUtils.STRING_DELIMITER);
partNameBuffer.append(partName);
partNameBuffer.append(UMLConnectorUtils.STRING_DELIMITER);
} else {
partNameBuffer.append(partName);
}
return partNameBuffer.toString();
}
/**
* Can display existing connector between views according to partWithPort.
*
* @param connector
* the connector
* @param sourceView
* the source view
* @param targetView
* the target view
* @return true, if successful
*/
public boolean canDisplayExistingConnectorBetweenViewsAccordingToPartWithPort(final Connector connector, final View sourceView, final View targetView) {
Property partWithPort = getPartWithPortFromConnector(connector);
if (partWithPort != null) {
String partWithPortName = partWithPort.getName();
Type partWithPortType = partWithPort.getType();
EObject sourceContainer = ((View) sourceView.eContainer()).getElement();
EObject targetContainer = ((View) targetView.eContainer()).getElement();
boolean sameSourceType = false;
if (sourceContainer instanceof TypedElement) {
sameSourceType = partWithPortType.conformsTo(((TypedElement) sourceContainer).getType());
}
boolean sameSourceName = partWithPortName.equals(((NamedElement) sourceContainer).getName());
boolean sameTargetType = false;
if (targetContainer instanceof TypedElement) {
sameTargetType = partWithPortType.conformsTo(((TypedElement) targetContainer).getType());
}
boolean sametargetName = partWithPortName.equals(((NamedElement) targetContainer).getName());
return (sameSourceType && sameSourceName) || (sameTargetType && sametargetName);
}
return true;
}
/**
* Gets the partWithPort from the connector.
*
* @param connector
* the connector
* @return the part with port from connector
*/
public Property getPartWithPortFromConnector(final Connector connector) {
if (connector != null && connector.getEnds() != null) {
for (final ConnectorEnd end : connector.getEnds()) {
Property partWithPort = end.getPartWithPort();
if (partWithPort != null) {
return partWithPort;
}
}
}
return null;
}
/**
* Test if the relationship creation is allowed.
*
* @deprecated in 0.10.1 (will be removed in 0.11.0)
*
* @param source
* the relationship source can be null
* @param target
* the relationship target can be null
* @param sourceView
* the relationship graphical source can be null
* @param targetView
* the relationship graphical target can be null
* @return true if the creation is allowed
*/
@Deprecated // test creation should be done in approve request
public static boolean canCreate(EObject source, EObject target, View sourceView, View targetView) {
if ((source != null) && !(source instanceof ConnectableElement)) {
return false;
}
if ((target != null) && !(target instanceof ConnectableElement)) {
return false;
}
if ((sourceView != null) && (targetView != null)) {
// Cannot create a connector from a view representing a Part to its own Port (or the opposite)
if ((sourceView.getChildren().contains(targetView)) || (targetView.getChildren().contains(sourceView))) {
return false;
}
// Cannot connect a Part to one of its (possibly indirect) containment, must connect to one of its Port.
if (new UMLConnectorUtils().getStructureContainers(sourceView).contains(targetView) || new UMLConnectorUtils().getStructureContainers(targetView).contains(sourceView)) {
return false;
}
}
return true;
}
}