blob: 346c24dc7b0140f7905048f518656f1801ffd376 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* IBM - Initial API and implementation
* Zeligsoft - Bug 245897
* E.D.Willink Bug 298128
*******************************************************************************/
package org.eclipse.ocl.uml.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.ocl.types.BagType;
import org.eclipse.ocl.types.CollectionType;
import org.eclipse.ocl.types.OrderedSetType;
import org.eclipse.ocl.types.PrimitiveType;
import org.eclipse.ocl.types.SequenceType;
import org.eclipse.ocl.types.SetType;
import org.eclipse.ocl.uml.UMLEnvironment;
import org.eclipse.ocl.uml.options.UMLParsingOptions;
import org.eclipse.ocl.uml.util.OCLUMLUtil;
import org.eclipse.ocl.util.ObjectUtil;
import org.eclipse.ocl.utilities.ExpressionInOCL;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.UMLReflection;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.BehavioredClassifier;
import org.eclipse.uml2.uml.CallOperationAction;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.DataType;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Feature;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.MultiplicityElement;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Reception;
import org.eclipse.uml2.uml.SendSignalAction;
import org.eclipse.uml2.uml.Signal;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.TypedElement;
import org.eclipse.uml2.uml.UMLFactory;
/**
* Implementation of the UML reflection service for the UML environment.
*
* @author Christian W. Damus (cdamus)
*/
public class UMLReflectionImpl
implements
UMLReflection<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint> {
private static Set<String> ECORE_INTEGER_TYPES = new java.util.HashSet<String>(
Arrays.asList(EcorePackage.Literals.EINT.getName(),
EcorePackage.Literals.EINTEGER_OBJECT.getName(),
EcorePackage.Literals.ELONG.getName(),
EcorePackage.Literals.ELONG_OBJECT.getName(),
EcorePackage.Literals.ESHORT.getName(),
EcorePackage.Literals.ESHORT_OBJECT.getName(),
EcorePackage.Literals.EBIG_INTEGER.getName()));
private static Set<String> ECORE_REAL_TYPES = new java.util.HashSet<String>(
Arrays.asList(EcorePackage.Literals.EFLOAT.getName(),
EcorePackage.Literals.EFLOAT_OBJECT.getName(),
EcorePackage.Literals.EDOUBLE.getName(),
EcorePackage.Literals.EDOUBLE_OBJECT.getName(),
EcorePackage.Literals.EBIG_DECIMAL.getName()));
private static Set<String> ECORE_BOOLEAN_TYPES = new java.util.HashSet<String>(
Arrays.asList(EcorePackage.Literals.EBOOLEAN.getName(),
EcorePackage.Literals.EBOOLEAN_OBJECT.getName()));
private final UMLEnvironment env;
public UMLReflectionImpl(UMLEnvironment env) {
this.env = env;
}
final java.lang.Class<? extends Association> getAssociationClassType() {
return env.getValue(UMLParsingOptions.ASSOCIATION_CLASS_TYPE);
}
/**
* Default implementation just gets the name of the named element's
* {@link org.eclipse.emf.ecore.EObject#eClass() metaclass} or, if it is a
* {@link TypedElement}, the name of its type.
*/
public String getDescription(Object namedElement) {
if (namedElement instanceof TypedElement) {
Type type = ((TypedElement) namedElement).getType();
return (type == null) ? OCLStandardLibraryImpl.INSTANCE
.getOclVoid().getName()
: type.getName();
} else if (namedElement instanceof EnumerationLiteral) {
return ((EnumerationLiteral) namedElement).getEnumeration()
.getName();
} else if (namedElement == null) {
return null;
}
return ((EObject) namedElement).eClass().getName();
}
public String getName(Object namedElement) {
return (namedElement instanceof NamedElement)?
((NamedElement) namedElement).getName() : null;
}
public String getQualifiedName(Object namedElement) {
return (namedElement instanceof NamedElement)?
((NamedElement) namedElement).getQualifiedName() : null;
}
public Classifier getOCLType(Object metaElement) {
Classifier result = null;
if (metaElement instanceof TypedElement) {
TypedElement typedElement = (TypedElement) metaElement;
result = (Classifier) typedElement.getType();
if (result == null) {
// absence of a type in UML indicates void type
result = OCLStandardLibraryImpl.INSTANCE.getOclVoid();
}
if (isMany(typedElement)) {
result = getOCLCollectionType(
result,
isOrdered(typedElement),
isUnique(typedElement));
} else {
result = getOCLType(result);
}
} else if (metaElement instanceof Operation) {
// Operations are not TypedElements!
Operation operation = (Operation) metaElement;
result = (Classifier) operation.getType();
if (result == null) {
// absence of a type in UML indicates void type
result = OCLStandardLibraryImpl.INSTANCE.getOclVoid();
}
if (isMany(operation)) {
result = getOCLCollectionType(
result,
isOrdered(operation),
isUnique(operation));
} else {
result = getOCLType(result);
}
} else if (metaElement instanceof Classifier) {
result = getOCLType((Classifier) metaElement);
}
return result;
}
public boolean isMany(Object metaElement) {
if (metaElement instanceof MultiplicityElement) {
return ((MultiplicityElement) metaElement).isMultivalued();
} else if (metaElement instanceof Operation) {
int upper = ((Operation) metaElement).getUpper();
return (upper > 1) || (upper < 0);
}
return false;
}
/**
* Queries whether the specified {@link MultiplicityElement} or
* {@link Operation} is unique.
*
* @param metaElement a multiplicity-element or operation
* @return whether it is unique
*/
protected boolean isUnique(Object metaElement) {
if (metaElement instanceof MultiplicityElement) {
return ((MultiplicityElement) metaElement).isUnique();
} else if (metaElement instanceof Operation) {
return ((Operation) metaElement).isUnique();
}
return false;
}
/**
* Queries whether the specified {@link MultiplicityElement} or
* {@link Operation} is ordered.
*
* @param metaElement a multiplicity-element or operation
* @return whether it is ordered
*/
protected boolean isOrdered(Object metaElement) {
if (metaElement instanceof MultiplicityElement) {
return ((MultiplicityElement) metaElement).isOrdered();
} else if (metaElement instanceof Operation) {
return ((Operation) metaElement).isOrdered();
}
return false;
}
public Classifier asOCLType(Classifier modelType) {
return getOCLType(modelType);
}
public Package getNestingPackage(Package pkg) {
return pkg.getNestingPackage();
}
public List<Package> getNestedPackages(Package pkg) {
return pkg.getNestedPackages();
}
public Package getPackage(Classifier classifier) {
return classifier.getNearestPackage();
}
public List<Classifier> getClassifiers(Package pkg) {
EList<Type> types = pkg.getOwnedTypes();
List<Classifier> result = new java.util.ArrayList<Classifier>(types.size());
for (Type t : types) {
if (t instanceof Classifier) {
result.add((Classifier) t);
}
}
return result;
}
public Classifier getOwningClassifier(Object feature) {
Classifier result = null;
if (feature instanceof Feature) {
result = (Classifier) ((Feature) feature).getOwner();
}
if (result instanceof Class) {
Classifier shadowed = OCLStandardLibraryImpl.getRealClassifier(
(Class) result);
if (shadowed != null) {
result = shadowed;
}
}
return result;
}
public List<Parameter> getParameters(Operation operation) {
List<Parameter> result = new java.util.ArrayList<Parameter>(
operation.getOwnedParameters().size());
for (Parameter p : operation.getOwnedParameters()) {
if (p.getDirection() != ParameterDirectionKind.RETURN_LITERAL) {
result.add(p);
}
}
return result;
}
public Operation createOperation(String name, Classifier resultType,
List<String> paramNames, List<Classifier> paramTypes) {
Operation result = UMLFactory.eINSTANCE.createOperation();
result.setName(name);
// OCL can only define query operations
result.setIsQuery(true);
if (resultType != null) {
result.setType(resultType);
}
int i = 0;
for (String pname : paramNames) {
result.createOwnedParameter(pname, paramTypes.get(i++));
}
return result;
}
public List<Operation> getOperations(Classifier classifier) {
return OCLUMLUtil.getAllOperations(classifier);
}
public Property createProperty(String name, Classifier resultType) {
Property result = UMLFactory.eINSTANCE.createProperty();
result.setName(name);
result.setType(resultType);
return result;
}
public List<Property> getAttributes(Classifier classifier) {
return OCLUMLUtil.getAllAttributes(classifier);
}
public Collection<? extends Classifier> getAllSupertypes(
Classifier classifier) {
return classifier.allParents();
}
public boolean isQuery(Operation operation) {
return operation.isQuery();
}
public boolean isStatic(Object feature) {
return (feature instanceof Feature) && ((Feature) feature).isStatic();
}
public boolean setIsStatic(Object feature, boolean isStatic) {
if (!(feature instanceof Feature))
return false;
((Feature) feature).setIsStatic(isStatic);
return true;
}
public boolean isAssociationClass(Classifier classifier) {
return getAssociationClassType().isInstance(classifier);
}
public Classifier getAssociationClass(Property property) {
return getAssociationClassType().isInstance(property.getAssociation())?
property.getAssociation() : null;
}
public List<Property> getMemberEnds(Classifier associationClass) {
if (getAssociationClassType().isInstance(associationClass)) {
return ((Association) associationClass).getMemberEnds();
}
return Collections.emptyList();
}
public boolean isOperation(Object metaElement) {
return metaElement instanceof Operation;
}
public boolean isPackage(Object metaElement) {
return metaElement instanceof Package;
}
public boolean isProperty(Object metaElement) {
return metaElement instanceof Property;
}
public boolean isClassifier(Object metaElement) {
return metaElement instanceof Classifier;
}
public boolean isClass(Object metaElement) {
return metaElement instanceof Class;
}
public boolean isConstraint(Object metaElement) {
return metaElement instanceof Constraint;
}
public boolean isDataType(Object metaElement) {
return metaElement instanceof DataType;
}
public boolean isStereotype(Classifier type) {
return type instanceof Stereotype;
}
public Object getStereotypeApplication(Object baseElement,
Classifier stereotype) {
Object result = null;
if (baseElement instanceof Element) {
Element element = (Element) baseElement;
Stereotype stereo = (Stereotype) stereotype;
if (element.isStereotypeApplied(stereo)) {
result = element.getStereotypeApplication(stereo);
}
if (result == null) {
// maybe some specializing stereotype is applied?
List<Stereotype> applied = element
.getAppliedSubstereotypes(stereo);
if (!applied.isEmpty()) {
return element.getStereotypeApplication(applied.get(0));
}
}
}
return result;
}
public boolean isEnumeration(Classifier type) {
return type instanceof Enumeration;
}
public Classifier getEnumeration(EnumerationLiteral enumerationLiteral) {
return enumerationLiteral.getEnumeration();
}
public List<EnumerationLiteral> getEnumerationLiterals(Classifier enumerationType) {
return ((Enumeration) enumerationType).getOwnedLiterals();
}
public EnumerationLiteral getEnumerationLiteral(Classifier enumerationType,
String literalName) {
return ((Enumeration) enumerationType).getOwnedLiteral(literalName);
}
public boolean isComparable(Classifier type) {
if ((type instanceof PrimitiveType<?>)
|| (type.getOperation("<", null, null) != null)) { //$NON-NLS-1$
return true;
}
EClassifier eclassifier = env.getEClassifier(type, null);
return (eclassifier != null)
&& Comparable.class.isAssignableFrom(eclassifier.getInstanceClass());
}
public Operation getOperation(CallOperationAction callOperationAction) {
return callOperationAction.getOperation();
}
public Classifier getSignal(SendSignalAction sendSignalAction) {
return sendSignalAction.getSignal();
}
public String getStereotype(Constraint constraint) {
EList<String> keywords = constraint.getKeywords();
return keywords.isEmpty()? null : keywords.get(0);
}
public void setStereotype(Constraint constraint, String stereotype) {
// first, clear existing keywords (there wouldn't usually be any)
for (String keyword : constraint.getKeywords()) {
constraint.removeKeyword(keyword);
}
constraint.addKeyword(stereotype);
}
public String getConstraintName(Constraint constraint) {
return constraint.getName();
}
public void setConstraintName(Constraint constraint, String name) {
constraint.setName(name);
}
public ExpressionInOCL<Classifier, Parameter>
getSpecification(Constraint constraint) {
if (constraint.getSpecification() instanceof ExpressionInOCL<?, ?>) {
return (org.eclipse.ocl.uml.ExpressionInOCL) constraint.getSpecification();
}
return null;
}
public void setSpecification(
Constraint constraint,
org.eclipse.ocl.utilities.ExpressionInOCL<Classifier, Parameter> specification) {
constraint.setSpecification(
(org.eclipse.ocl.uml.ExpressionInOCL) specification);
}
public Constraint getConstraint(
ExpressionInOCL<Classifier, Parameter> specification) {
EObject container = specification.eContainer();
if (container instanceof Constraint) {
return (Constraint) container;
}
return null;
}
public List<Element> getConstrainedElements(Constraint constraint) {
return constraint.getConstrainedElements();
}
public void addConstrainedElement(Constraint constraint, EObject constrainedElement) {
constraint.getConstrainedElements().add((Element) constrainedElement);
}
public List<Property> getQualifiers(Property property) {
return property.getQualifiers();
}
public Classifier getCommonSuperType(Classifier type1, Classifier type2) {
if (ObjectUtil.equal(type1, type2)) {
return type2;
}
if (type1.conformsTo(type2)) {
return type2;
}
if (type2.conformsTo(type1)) {
return type1;
}
List<Classifier> type1Ancestors = new ArrayList<Classifier>(
type1.allParents());
List<Classifier> type2Ancestors = new ArrayList<Classifier>(
type2.allParents());
type1Ancestors.retainAll(type2Ancestors);
if (!type1Ancestors.isEmpty()) {
// the least common ancestor type will be the first, because UML
// computes the ancestors starting with the immediate parents
return type1Ancestors.get(0);
}
return null;
}
public int getRelationship(Classifier type1, Classifier type2) {
if (ObjectUtil.equal(type1, type2)) {
return SAME_TYPE;
}
if (type2.conformsTo(type1)) {
return STRICT_SUPERTYPE;
} else if (type1.conformsTo(type2)) {
return STRICT_SUBTYPE;
} else if ((type2 instanceof BehavioredClassifier) && (type1 instanceof Interface)) {
for (Interface i : ((BehavioredClassifier) type2).getAllImplementedInterfaces()) {
if (i.conformsTo(type1)) {
return STRICT_SUPERTYPE;
}
}
} else if ((type1 instanceof BehavioredClassifier) && (type2 instanceof Interface)) {
for (Interface i : ((BehavioredClassifier) type1).getAllImplementedInterfaces()) {
if (i.conformsTo(type2)) {
return STRICT_SUBTYPE;
}
}
}
return UNRELATED_TYPE;
}
/**
* Translator from primitive UML types to OCL types
*
* @param dataType a data type in the UML metamodel
* @return the corresponding OCL classifier
*/
private Classifier getOCLTypeFor(DataType dataType) {
// First check if it is already an OCL data type (Enumerations represent
// themselves)
if (dataType instanceof Enumeration) {
return dataType;
}
if (dataType instanceof CollectionType<?, ?>) {
return dataType;
}
if (dataType instanceof PrimitiveType<?>) {
return dataType;
}
if (dataType instanceof org.eclipse.uml2.uml.PrimitiveType) {
// Boolean -> OCL_BOOLEAN
if (PrimitiveType.BOOLEAN_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getBoolean();
} else if (PrimitiveType.REAL_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getReal();
} else if (PrimitiveType.STRING_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getString();
} else if (PrimitiveType.INTEGER_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getInteger();
} else if (PrimitiveType.UNLIMITED_NATURAL_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getUnlimitedNatural();
} else if (SequenceType.SINGLETON_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getSequence();
} else if (SetType.SINGLETON_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getSet();
} else if (BagType.SINGLETON_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getBag();
} else if (OrderedSetType.SINGLETON_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getOrderedSet();
} else if (CollectionType.SINGLETON_NAME.equals(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getCollection();
} else if ("Object".equals(dataType.getName())) { //$NON-NLS-1$
return OCLStandardLibraryImpl.INSTANCE.getOclAny();
} else if (ECORE_INTEGER_TYPES.contains(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getInteger();
} else if (ECORE_REAL_TYPES.contains(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getReal();
} else if (ECORE_BOOLEAN_TYPES.contains(dataType.getName())) {
return OCLStandardLibraryImpl.INSTANCE.getBoolean();
} else if ("EString".equals(dataType.getName())) { //$NON-NLS-1$
return OCLStandardLibraryImpl.INSTANCE.getString();
}
}
// All other data types map to themselves
return dataType;
}
/**
* Obtains the appropriate OCL type for a {@link Classifier}, if it
* corresponds to an OCL primitive type.
*
* @param type the type to convert to an OCL type
* @return the corresponding OCL type
*/
Classifier getOCLType(Classifier type) {
Classifier resultType = type;
if (resultType instanceof DataType) {
resultType = getOCLTypeFor((DataType) resultType);
}
return resultType;
}
/**
* Obtains the appropriate OCL collection type for a {@link Classifier},
* according to the collection's orderedness and uniqueness. The mapping is
* as follows:
* <ul>
* <li>ordered, unique: ordered set type</li>
* <li>ordered, non-unique: sequence type</li>
* <li>non-ordered, unique: set type</li>
* <li>non-ordered, non-unique: bag type</li>
* </ul>
* <p>
* Note that the collection's element <code>type</code> is mapped to an
* OCL primitive type where possible.
* </p>
*
* @param type the type to get the corresponding OCL collection type for
* @param isOrdered whether the OCL type should be ordered
* @param isUnique whether the OCL type should be unique
*
* @return the corresponding OCL type
*
* @see #getOCLType(Classifier)
*/
Classifier getOCLCollectionType(Classifier type,
boolean isOrdered, boolean isUnique) {
Classifier resultType = type;
if (resultType instanceof DataType) {
resultType = getOCLTypeFor((DataType) resultType);
}
OCLFactory oclFactory = OCLFactoryImpl.INSTANCE;
if (isOrdered) {
if (isUnique) {
resultType = (Classifier) oclFactory.createOrderedSetType(resultType);
} else {
resultType = (Classifier) oclFactory.createSequenceType(resultType);
}
} else {
if (isUnique) {
resultType = (Classifier) oclFactory.createSetType(resultType);
} else {
resultType = (Classifier) oclFactory.createBagType(resultType);
}
}
return resultType;
}
public List<Classifier> getSignals(Classifier owner) {
if (owner instanceof Class) {
List<Reception> receptions = OCLUMLUtil.getAllReceptions((Class) owner);
if (!receptions.isEmpty()) {
List<Classifier> result = new java.util.ArrayList<Classifier>(
receptions.size());
for (Reception r : receptions) {
if (r.getSignal() != null) {
result.add(r.getSignal());
}
}
return result;
}
}
return Collections.emptyList();
}
public CallOperationAction createCallOperationAction(Operation operation) {
CallOperationAction result = UMLFactory.eINSTANCE.createCallOperationAction();
result.setOperation(operation);
return result;
}
public SendSignalAction createSendSignalAction(Classifier signal) {
SendSignalAction result = UMLFactory.eINSTANCE.createSendSignalAction();
result.setSignal((Signal) signal);
return result;
}
public Constraint createConstraint() {
return UMLFactory.eINSTANCE.createConstraint();
}
public ExpressionInOCL<Classifier, Parameter> createExpressionInOCL() {
return org.eclipse.ocl.uml.UMLFactory.eINSTANCE.createExpressionInOCL();
}
public void setName(
org.eclipse.ocl.utilities.TypedElement<Classifier> element,
String name) {
element.setName(name);
}
public void setType(
org.eclipse.ocl.utilities.TypedElement<Classifier> element,
Classifier type) {
element.setType(type);
}
}