blob: 4421c396da1fdeda438057805ec488906a6273ea [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2016 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - initial API and implementation
* E.D.Willink (Obeo) - Bug 416287 - tuple-valued constraints
*******************************************************************************/
package org.eclipse.ocl.xtext.base.utilities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.Constraint;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.LanguageExpression;
import org.eclipse.ocl.pivot.Model;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.TemplateBinding;
import org.eclipse.ocl.pivot.TemplateParameter;
import org.eclipse.ocl.pivot.TemplateSignature;
import org.eclipse.ocl.pivot.TemplateableElement;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager;
import org.eclipse.ocl.pivot.internal.resource.ICSI2ASMapping;
import org.eclipse.ocl.pivot.internal.scoping.Attribution;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.resource.ASResource;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.xtext.base.attributes.RootCSAttribution;
import org.eclipse.ocl.xtext.base.cs2as.CS2AS;
import org.eclipse.ocl.xtext.base.cs2as.ImportDiagnostic;
import org.eclipse.ocl.xtext.base.cs2as.LibraryDiagnostic;
import org.eclipse.ocl.xtext.basecs.ElementCS;
import org.eclipse.ocl.xtext.basecs.ModelElementCS;
import org.eclipse.ocl.xtext.basecs.MultiplicityCS;
import org.eclipse.ocl.xtext.basecs.NamedElementCS;
import org.eclipse.ocl.xtext.basecs.OperationCS;
import org.eclipse.ocl.xtext.basecs.PathElementCS;
import org.eclipse.ocl.xtext.basecs.PathNameCS;
import org.eclipse.ocl.xtext.basecs.StructuredClassCS;
import org.eclipse.ocl.xtext.basecs.TemplateBindingCS;
import org.eclipse.ocl.xtext.basecs.TemplateParameterSubstitutionCS;
import org.eclipse.ocl.xtext.basecs.TypeRefCS;
import org.eclipse.ocl.xtext.basecs.TypedElementCS;
import org.eclipse.ocl.xtext.basecs.TypedRefCS;
import org.eclipse.ocl.xtext.basecs.TypedTypeRefCS;
import org.eclipse.ocl.xtext.basecs.WildcardTypeRefCS;
import org.eclipse.xtext.nodemodel.BidiIterator;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.impl.AbstractNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextSyntaxDiagnostic;
import org.eclipse.xtext.util.ITextRegion;
public class ElementUtil
{
private static final String delegateExtensionPoints[] = {
// EcorePlugin.CONVERSION_DELEGATE_PPID, -- not available in EMF 2.7
EcorePlugin.INVOCATION_DELEGATE_PPID,
EcorePlugin.QUERY_DELEGATE_PPID,
EcorePlugin.SETTING_DELEGATE_PPID,
EcorePlugin.VALIDATION_DELEGATE_PPID
};
private static String[][] delegationModes = null;
public static void appendTextRegion(@NonNull StringBuilder s, @Nullable ITextRegion textRegion, boolean isSignificant) {
s.append(isSignificant ? "[" : "(");
if (textRegion != null) {
s.append(textRegion.getOffset() + "," + textRegion.getLength());
}
else {
s.append("null");
}
s.append(isSignificant ? "]" : ")");
}
public static @Nullable String getCollectionTypeName(@NonNull TypedElementCS csTypedElement) {
TypedRefCS csTypeRef = csTypedElement.getOwnedType();
if (csTypeRef == null) {
return null;
}
// if (csTypeRef instanceof CollectionTypeRefCS) {
// Type csType = ((CollectionTypeRefCS)csTypeRef).getType();
// if (csType instanceof CollectionType) {
// return ((CollectionType)csType).getName();
// }
// }
//FIXME Obsolete compatibility
MultiplicityCS csMultiplicity = csTypeRef.getOwnedMultiplicity();
if (csMultiplicity == null) {
return null;
}
int upper = csMultiplicity.getUpper();
if (upper == 1) {
return null;
}
List<String> qualifiers = csTypedElement.getQualifiers();
boolean isOrdered = true;
boolean isUnique = true;
if (qualifiers.contains("!ordered")) { //$NON-NLS-1$
isOrdered = false;
}
else if (qualifiers.contains("ordered")) { //$NON-NLS-1$
isOrdered = true;
}
if (qualifiers.contains("!unique")) { //$NON-NLS-1$
isUnique = false;
}
else if (qualifiers.contains("unique")) { //$NON-NLS-1$
isUnique = true;
}
return getCollectionName(isOrdered, isUnique);
}
public static @NonNull String getCollectionName(boolean ordered, boolean unique) {
if (ordered) {
return unique ? TypeId.ORDERED_SET_NAME : TypeId.SEQUENCE_NAME;
}
else {
return unique ? TypeId.SET_NAME : TypeId.BAG_NAME;
}
}
public static @Nullable ModelElementCS getCsElement(@NonNull Element asElement) {
Resource asResource = asElement.eResource();
if (asResource == null) {
return null;
}
ResourceSet asResourceSet = asResource.getResourceSet();
if (asResourceSet == null) {
return null;
}
PivotMetamodelManager metamodelManager = PivotMetamodelManager.findAdapter(asResourceSet);
if (metamodelManager == null) {
return null;
}
EnvironmentFactoryInternal environmentFactory = metamodelManager.getEnvironmentFactory();
ICSI2ASMapping csi2asMapping = environmentFactory.getCSI2ASMapping();
if (csi2asMapping == null) {
return null;
}
return ((CSI2ASMapping)csi2asMapping).getCSElement(asElement);
}
// FIXME share with common.ui once promoted from examples
public static String[][] getDelegateURIs() {
if (delegationModes == null) {
Set<String> uris = new HashSet<String>();
IExtensionRegistry pluginRegistry = Platform.getExtensionRegistry();
String pluginID = EcorePlugin.getPlugin().getBundle().getSymbolicName();
for (String extensionPointID : delegateExtensionPoints) {
IExtensionPoint point = pluginRegistry.getExtensionPoint(pluginID, extensionPointID);
if (point != null) {
IConfigurationElement[] elements = point.getConfigurationElements();
for (int i = 0; i < elements.length; i++) {
String uri = elements[i].getAttribute("uri"); //$NON-NLS-1$
if (uri != null) {
uris.add(uri);
}
}
}
}
List<String> uriList = new ArrayList<String>(uris);
Collections.sort(uriList);
delegationModes = new String[uriList.size()][2];
for (int i = 0; i < uris.size(); i++) {
delegationModes[i][0] = uriList.get(i);
delegationModes[i][1] = uriList.get(i);
}
}
return delegationModes;
}
public static @Nullable RootCSAttribution getDocumentAttribution(@NonNull ElementCS context) {
for (ElementCS target = context, parent; (parent = target.getParent()) != null; target = parent) {
Attribution attribution = PivotUtilInternal.getAttribution(parent);
if (attribution instanceof RootCSAttribution) {
return (RootCSAttribution) attribution;
}
}
return null;
}
/**
* Return the user text for csElement preserving all surrounding whitespace.
* <br>
* Except that Carriage Returns are removed.
* <br>
* Except that a first space is removed since it originates from the auto-formatter.
* <br>
* The leading whitespace of the next element is included since the folloowing token
* is expected to be a semicolon.
*/
public static @NonNull String getExpressionText(@NonNull ElementCS csElement) {
ICompositeNode parserNode = NodeModelUtils.getNode(csElement);
if (parserNode != null) {
String text = parserNode.getText().replace("\r", "");
if ((text.length() > 0) && text.charAt(0) == ' ') {
text = text.substring(1); // Step over the leading separator.
}
INode nextNode = parserNode.getNextSibling();
for (INode parent = parserNode.getParent(); parent != null; parent = parent.getParent()) {
nextNode = parent.getNextSibling();
if (nextNode != null) {
String nextText = nextNode.getText().replace("\r", "");
int i = 0;
int iMax = nextText.length();
for ( ; i < iMax; i++) { // Step up to the leading separator.
if (!Character.isWhitespace(nextText.charAt(i))) {
break;
}
}
return text + nextText.substring(0, i);
}
}
assert text != null;
return text;
}
return "null";
}
/**
* Extract the first embedded ExpressionInOCL.
* @throws ParserException
*/
public static @Nullable ExpressionInOCL getFirstQuery(@NonNull PivotMetamodelManager metamodelManager, BaseCSResource csResource) throws ParserException {
CS2AS cs2as = csResource.findCS2AS();
if (cs2as != null) {
ASResource asResource = cs2as.getASResource();
for (EObject eRoot: asResource.getContents()) {
if (eRoot instanceof Model) {
for (org.eclipse.ocl.pivot.Package asPackage: ((Model)eRoot).getOwnedPackages()) {
for (org.eclipse.ocl.pivot.Class asType: asPackage.getOwnedClasses()) {
for (Constraint asConstraint : asType.getOwnedInvariants()) {
LanguageExpression specification = asConstraint.getOwnedSpecification();
if (specification != null) {
return metamodelManager.parseSpecification(specification);
}
}
for (Operation asOperation : asType.getOwnedOperations()) {
LanguageExpression specification = asOperation.getBodyExpression();
if (specification != null) {
return metamodelManager.parseSpecification(specification);
}
}
}
}
}
}
}
return null;
}
public static @Nullable TemplateParameter getFormalTemplateParameter(@NonNull TemplateParameterSubstitutionCS csTemplateParameterSubstitution) {
TemplateBindingCS csTemplateBinding = csTemplateParameterSubstitution.getOwningBinding();
int index = csTemplateBinding.getOwnedSubstitutions().indexOf(csTemplateParameterSubstitution);
if (index < 0) {
return null;
}
TemplateBinding templateBinding = (TemplateBinding) csTemplateBinding.getPivot();
TemplateSignature templateSignature = templateBinding.getTemplateSignature();
List<TemplateParameter> templateParameters = templateSignature.getOwnedParameters();
if (templateParameters.size() <= index) {
return null;
}
return templateParameters.get(index);
}
public static @Nullable ILeafNode getLeafNode(@NonNull INode node) {
ILeafNode leafNode = null;
if (node instanceof ILeafNode) {
return (ILeafNode) node;
}
else {
for (ILeafNode lNode : node.getLeafNodes()) {
if (!lNode.isHidden()) {
leafNode = lNode;
return leafNode;
}
}
}
return null;
}
public static int getLower(@NonNull TypedElementCS csTypedElement) {
TypedRefCS csTypeRef = csTypedElement.getOwnedType();
if (csTypeRef == null) {
return 0; // e.g. missing Operation return type
}
MultiplicityCS csMultiplicity = csTypeRef.getOwnedMultiplicity();
if (csMultiplicity == null) {
Type type = PivotUtil.getPivot(Type.class, csTypeRef);
if (type instanceof CollectionType) {
return 1;
}
if (type != null) {
EObject esObject = type.getESObject();
if ((esObject instanceof EDataType) && isPrimitiveInstanceClass((EDataType) esObject)) {
return 1;
}
}
return 0; // OCL legacy allows null even though UML lowerBound() default is 1.
}
return csMultiplicity.getLower();
}
public static @Nullable <T extends NamedElementCS> T getNamedElementCS(@NonNull Collection<T> namedElements, @NonNull String name) {
for (T namedElement : namedElements) {
if (name.equals(namedElement.getName())) {
return namedElement;
}
}
return null;
}
/** This makes INode.getEndOffset from Xtext 2.5 available on 2.3 */
public static int getEndOffset(@NonNull INode iNode) {
if (!(iNode instanceof AbstractNode)) {
return -1;
}
AbstractNode node = (AbstractNode) iNode;
BidiIterator<AbstractNode> iter = node .basicIterator();
while(iter.hasPrevious()) {
INode prev = iter.previous();
if (prev instanceof ILeafNode && !((ILeafNode) prev).isHidden()) {
return prev.getTotalEndOffset();
}
}
return node.getTotalEndOffset();
}
public static boolean getQualifier(@NonNull List<String> qualifiers, @NonNull String trueString, @NonNull String falseString, boolean defaultValue) {
if (qualifiers.contains(trueString)) {
return true;
}
else if (qualifiers.contains(falseString)) {
return false;
}
else {
return defaultValue;
}
}
/**
* Return the raw text associated with a csElement.
*/
public static @Nullable String getText(@NonNull ElementCS csElement) {
ICompositeNode node = NodeModelUtils.getNode(csElement);
return node != null ? NodeModelUtils.getTokenText(node) : null;
}
public static @Nullable String getText(@NonNull ElementCS csElement, /*@NonNull*/ EReference feature) {
@SuppressWarnings("null")@NonNull List<INode> nodes = NodeModelUtils.findNodesForFeature(csElement, feature);
// assert (nodes.size() == 1;
if (nodes.isEmpty()) {
return null;
}
else if (nodes.size() == 1) {
return NodeModelUtils.getTokenText(nodes.get(0));
}
else {
StringBuilder s = new StringBuilder();
for (INode node : nodes) {
s.append(NodeModelUtils.getTokenText(node));
}
return s.toString();
}
}
/**
* Return the logical text associated with a csElement. (EScaped identifers are unescaped.)
*/
public static @Nullable String getTextName(@NonNull ElementCS csElement) {
String text = getText(csElement);
if (text == null) {
return null;
}
int length = text.length();
if ((length >= 3) && text.startsWith("_'") && text.endsWith("'")) {
return text.substring(2, length-1);
}
else {
return text;
}
}
public static int getUpper(@NonNull TypedElementCS csTypedElement) {
TypedRefCS csTypeRef = csTypedElement.getOwnedType();
if (csTypeRef == null) {
return 1;
}
MultiplicityCS csMultiplicity = csTypeRef.getOwnedMultiplicity();
if (csMultiplicity == null) {
return 1;
}
return csMultiplicity.getUpper();
}
public static boolean hasSyntaxError(@NonNull List<Diagnostic> diagnostics) {
for (Diagnostic diagnostic : diagnostics) {
if (diagnostic instanceof LibraryDiagnostic) {
return true;
}
else if (diagnostic instanceof XtextSyntaxDiagnostic) {
return true;
}
else if (diagnostic instanceof ImportDiagnostic) {
return true;
}
else if (diagnostic.getClass().getName().equals("org.eclipse.ocl.xtext.essentialocl.utilities.EssentialOCLCSResource$RenamedDiagnostic")) { // FIXME Intyroduce an interface
return true;
}
}
return false;
}
public static boolean isInOperation(@NonNull ElementCS csElement) {
for (EObject eObject = csElement; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof OperationCS) {
return true;
}
else if (eObject instanceof StructuredClassCS) {
return false;
}
}
return false;
}
public static boolean isOrdered(@NonNull TypedElementCS csTypedElement) {
List<String> qualifiers = csTypedElement.getQualifiers();
assert qualifiers != null;
return getQualifier(qualifiers, "ordered", "!ordered", false);
}
public static boolean isPrimitiveInstanceClass(@NonNull EDataType esObject) {
Class<?> instanceClass = esObject.getInstanceClass();
return (instanceClass == byte.class) || (instanceClass == char.class) || (instanceClass == double.class) || (instanceClass == float.class)
|| (instanceClass == int.class) || (instanceClass == long.class) || (instanceClass == short.class);
}
public static boolean isRequired(@Nullable TypedRefCS csTypeRef) {
if (csTypeRef != null) {
MultiplicityCS csMultiplicity = csTypeRef.getOwnedMultiplicity();
if (csMultiplicity != null) {
int lower = csMultiplicity.getLower();
if (lower > 0) {
return true;
}
}
}
return false;
}
public static boolean isSpecialization(@NonNull TemplateBindingCS csTemplateBinding) {
TypedTypeRefCS csTypedTypeRef = csTemplateBinding.getOwningElement();
Element type = csTypedTypeRef.getPivot();
for (TemplateParameterSubstitutionCS csTemplateParameterSubstitution : csTemplateBinding.getOwnedSubstitutions()) {
TypeRefCS ownedActualParameter = csTemplateParameterSubstitution.getOwnedActualParameter();
if (ownedActualParameter instanceof WildcardTypeRefCS) {
return true;
}
Type actualParameterClass = (Type) ownedActualParameter.getPivot();
TemplateParameter templateParameter = actualParameterClass.isTemplateParameter();
if (templateParameter == null) {
return true;
}
TemplateSignature signature = templateParameter.getOwningSignature();
TemplateableElement template = signature.getOwningElement();
if (template != type) {
return true;
}
}
return false;
}
public static boolean isUnique(@NonNull TypedElementCS csTypedElement) {
List<String> qualifiers = csTypedElement.getQualifiers();
assert qualifiers != null;
return getQualifier(qualifiers, "unique", "!unique", true);
}
public static void setLastPathElement(@NonNull PathNameCS ownedPathName, @NonNull Element asElement) {
List<PathElementCS> ownedPathElements = ownedPathName.getOwnedPathElements();
int size = ownedPathElements.size();
if (size > 0) {
PathElementCS pathElementCS = ownedPathElements.get(size-1);
pathElementCS.setReferredElement(asElement);
}
}
}