blob: 3c9994ecfe13ea09c2f4b5cdf4ad06efa631d8ca [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Christian W. Damus 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:
* Christian W. Damus - initial API and implementation
*
*******************************************************************************/
package org.eclipse.mdht.uml.validation.ocl;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.uml.UMLEnvironment;
import org.eclipse.ocl.utilities.TypedElement;
import org.eclipse.ocl.utilities.UMLReflection;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.CallOperationAction;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.EnumerationLiteral;
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.Property;
import org.eclipse.uml2.uml.SendSignalAction;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.util.UMLUtil;
import org.osgi.framework.Bundle;
/**
* An OCL environment suitable for parsing OCL constraints on UML models that have the Ecore profile applied, supporting such extended semantics as
* <ul>
* <li>wrapping Java classes in <tt>{@literal <<eDataType>>} PrimitiveType</tt>s</li>
* <li>suppression of associations and property redefinition, which Ecore doesn't support</li>
* </ul>
* and possibly more.
*/
public class EcoreProfileEnvironment extends UMLEnvironment {
private static final String QUERY = "QUERY";
private static final String EDATATYPE = UMLUtil.PROFILE__ECORE + NamedElement.SEPARATOR +
UMLUtil.STEREOTYPE__E_DATA_TYPE;
private EcoreProfileReflection reflection;
public EcoreProfileEnvironment(
Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> parent) {
super(parent);
}
public EcoreProfileEnvironment(EcoreProfileEnvironmentFactory factory, EPackage.Registry registry,
ResourceSet rset) {
super(registry, rset);
setFactory(factory);
}
public EcoreProfileEnvironment(EcoreProfileEnvironmentFactory factory, EPackage.Registry registry, ResourceSet rset,
Resource resource) {
super(registry, rset, resource);
setFactory(factory);
}
/**
* Tests whether a given {@code constraint} is an MDHT query-style constraint (rejecting violating objects).
*
* @param constraint
* a constraint
*
* @return {@code true} if it is a reject-query constraint; {@code false} if it's just an ordinary boolean condition
*/
public static boolean isQueryConstraint(Constraint constraint) {
boolean result = false;
for (Comment next : constraint.getOwnedComments()) {
if ((next.getBody() != null) && QUERY.equalsIgnoreCase(next.getBody().trim())) {
result = true;
break;
}
}
return result;
}
@Override
public UMLReflection<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint> getUMLReflection() {
if (reflection == null) {
reflection = new EcoreProfileReflection(this);
}
return reflection;
}
@Override
public Classifier lookupAssociationClassReference(Classifier owner, String name) {
return null; // Ecore doesn't support association classes
}
@Override
public Classifier lookupClassifier(List<String> names) {
Classifier result = super.lookupClassifier(names);
if (result instanceof Association) {
result = null; // Ecore doesn't support associations as classifiers
}
return result;
}
@Override
public Property lookupProperty(Classifier owner, String name) {
Property result = super.lookupProperty(owner, name);
if ((result == null) && ((owner == null) || (owner == getContextClassifier()))) {
owner = getContextClassifier(); // in case of null, which happens for implicit navigation source
// the UML-to-Ecore transformation transfers constraints from nested classes to the outermost nesting class.
// Maybe we're trying to access a feature of that class from the context of a nesting class?
Classifier ecoreContext = getNestingRoot(owner);
if (owner != ecoreContext) {
result = lookupProperty(ecoreContext, name);
}
}
return result;
}
@Override
public Operation lookupOperation(Classifier owner, String name, List<? extends TypedElement<Classifier>> args) {
Operation result = super.lookupOperation(owner, name, args);
if ((result == null) && ((owner == null) || (owner == getContextClassifier()))) {
owner = getContextClassifier(); // in case of null, which happens for implicit operation call source
// the UML-to-Ecore transformation transfers constraints from nested classes to the outermost nesting class.
// Maybe we're trying to access a feature of that class from the context of a nesting class?
Classifier ecoreContext = getNestingRoot(owner);
if (owner != ecoreContext) {
result = lookupOperation(ecoreContext, name, args);
}
}
if (result == null) {
java.lang.Class<?> javaClass = getJavaClass(owner);
if (javaClass != null) {
Classifier oclClass = getOCLClass(javaClass);
if (oclClass != null) {
// delegate to the corresponding OCL standard library type
result = super.lookupOperation(oclClass, name, args);
}
}
}
return result;
}
private java.lang.Class<?> getJavaClass(Classifier classifier) {
java.lang.Class<?> result = null;
String javaClassName = (String) UMLUtil.getTaggedValue(
classifier, EDATATYPE, UMLUtil.TAG_DEFINITION__INSTANCE_CLASS_NAME);
javaClassName = (javaClassName == null)
? javaClassName
: javaClassName.trim();
if ((javaClassName != null) && (javaClassName.length() > 0)) {
result = getJavaClass(classifier.eResource(), javaClassName);
}
return result;
}
private java.lang.Class<?> getJavaClass(Resource context, String name) {
java.lang.Class<?> result = null;
try {
result = java.lang.Class.forName(name);
} catch (Exception e) {
// if the resource is in some plug-in, then use its class loader to try again
try {
URIConverter conv = (context.getResourceSet() == null)
? URIConverter.INSTANCE
: context.getResourceSet().getURIConverter();
URI normalizedURI = conv.normalize(context.getURI());
if (normalizedURI.isPlatformPlugin()) {
String pluginID = normalizedURI.segment(1); // segment 0 is "plugin"
Bundle bundle = Platform.getBundle(pluginID);
if (bundle != null) {
result = bundle.loadClass(name);
}
}
} catch (Exception e2) {
// fine, no such class. That's normal
}
}
return result;
}
private Classifier getOCLClass(java.lang.Class<?> javaClass) {
Classifier result = null;
final OCLStandardLibrary<Classifier> stdlib = getOCLStandardLibrary();
if (javaClass == String.class) {
result = stdlib.getString();
} else if (javaClass == Object.class) {
result = stdlib.getOclAny();
} else if (javaClass == Boolean.class || javaClass == boolean.class) {
result = stdlib.getBoolean();
} else if (javaClass == Integer.class || javaClass == int.class || javaClass == Short.class ||
javaClass == short.class || javaClass == Long.class || javaClass == long.class ||
javaClass == Byte.class || javaClass == byte.class || javaClass == BigInteger.class) {
result = stdlib.getInteger();
} else if (javaClass == Double.class || javaClass == double.class || javaClass == Float.class ||
javaClass == float.class || javaClass == BigDecimal.class) {
result = stdlib.getReal();
} else if (javaClass == Void.class || javaClass == void.class) {
result = stdlib.getOclVoid();
}
return result;
}
private Classifier getNestingRoot(Classifier classifier) {
Classifier result;
for (result = classifier; result.getOwner() instanceof Classifier;) {
result = (Classifier) result.getOwner();
}
return result;
}
}