/*******************************************************************************
* Copyright (c) 2011, 2015 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:
* C.Damus, K.Hussey, E.D.Willink - Initial API and implementation
* E.D.Willink (Obeo) - Bug 416287 - tuple-valued constraints
*******************************************************************************/
package org.eclipse.ocl.pivot.internal.delegate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.common.OCLCommon;
import org.eclipse.ocl.common.OCLConstants;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.CompletePackage;
import org.eclipse.ocl.pivot.Constraint;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.LanguageExpression;
import org.eclipse.ocl.pivot.Namespace;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.PivotPackage;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.internal.ecore.as2es.AS2Ecore;
import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager;
import org.eclipse.ocl.pivot.internal.prettyprint.PrettyPrintOptions;
import org.eclipse.ocl.pivot.internal.prettyprint.PrettyPrinter;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.options.OCLinEcoreOptions;
import org.eclipse.ocl.pivot.util.DerivedConstants;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.PivotConstants;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
public class DelegateInstaller
{
/**
* True to apply result = () wrapper to invariant body.
*/
public static final @NonNull String OPTION_BOOLEAN_INVARIANTS = "booleanInvariants";
/**
* True to omit the setting delegates declaration. Useful for matching UML2Ecore behaviour.
*/
public static final @NonNull String OPTION_OMIT_SETTING_DELEGATES = "omitSettingDelegates";
public static @Nullable String getAnnotationKey(@NonNull Constraint pivotConstraint) {
String name = pivotConstraint.getName();
EStructuralFeature eContainingFeature = pivotConstraint.eContainingFeature();
if (eContainingFeature == PivotPackage.Literals.CLASS__OWNED_INVARIANTS) {
if (pivotConstraint.isIsCallable()) {
return "body";
}
else {
return name;
}
}
else if (eContainingFeature == PivotPackage.Literals.OPERATION__OWNED_PRECONDITIONS) {
return name != null ? "pre_" + name : "pre";
}
else if (eContainingFeature == PivotPackage.Literals.OPERATION__OWNED_POSTCONDITIONS) {
return name != null ? "post_" + name : "post";
}
else {
// error("Unsupported " + pivotConstraint);
}
return null;
}
public static @Nullable String getDelegateURI(@NonNull List<EObject> contents) {
for (EObject eObject : contents) {
if (eObject instanceof EPackage) {
String exportURI = getDelegateURI((EPackage)eObject);
if (exportURI != null) {
return exportURI;
}
}
}
return null;
}
public static @Nullable String getDelegateURI(@NonNull EPackage ePackage) {
Set<String> allURIs = new HashSet<String>();
// allURIs.addAll(EcoreUtil.getConversionDelegates(ePackage));
allURIs.addAll(ClassUtil.nonNull(EcoreUtil.getInvocationDelegates(ePackage)));
// allURIs.addAll(EcoreUtil.getQueryDelegates(ePackage));
allURIs.addAll(ClassUtil.nonNull(EcoreUtil.getSettingDelegates(ePackage)));
allURIs.addAll(ClassUtil.nonNull(EcoreUtil.getValidationDelegates(ePackage)));
String theURI = null;
for (String uri : allURIs) {
if (uri.startsWith(OCLConstants.OCL_DELEGATE_URI)) {
if (theURI != null) {
return OCLConstants.OCL_DELEGATE_URI;
}
theURI = uri;
}
}
if (theURI != null) {
return theURI;
}
for (@SuppressWarnings("null")@NonNull EPackage eSubpackage : ePackage.getESubpackages()) {
String exportURI = getDelegateURI(eSubpackage);
if (exportURI != null) {
return exportURI;
}
}
for (EClassifier eClassifier : ePackage.getEClassifiers()) {
EAnnotation classifierAnnotation = OCLCommon.getDelegateAnnotation(eClassifier);
if ((classifierAnnotation != null) && !classifierAnnotation.getDetails().isEmpty()) {
return classifierAnnotation.getSource();
}
if (eClassifier instanceof EClass) {
EClass eClass = (EClass) eClassifier;
for (EStructuralFeature eFeature : eClass.getEStructuralFeatures()) {
EAnnotation featureAnnotation = OCLCommon.getDelegateAnnotation(eFeature);
if ((featureAnnotation != null) && !featureAnnotation.getDetails().isEmpty()) {
return featureAnnotation.getSource();
}
}
for (EOperation eOperation : eClass.getEOperations()) {
EAnnotation operationAnnotation = OCLCommon.getDelegateAnnotation(eOperation);
if ((operationAnnotation != null) && !operationAnnotation.getDetails().isEmpty()) {
return operationAnnotation.getSource();
}
}
}
}
return null;
}
public static @Nullable String getExportDelegateURI(@NonNull Map<String, Object> options) {
String exportDelegateURI = (String)options.get(OCLConstants.OCL_DELEGATE_URI);
return exportDelegateURI != null ? exportDelegateURI : OCLinEcoreOptions.EXPORT_DELEGATION_URI.getPreferredValue();
}
public static boolean isBooleanInvariants(@NonNull Map<String,Object> options) {
return Boolean.valueOf(String.valueOf(options.get(OPTION_BOOLEAN_INVARIANTS)));
}
public static boolean needsDelegates(@NonNull EPackage ePackage) {
boolean needsDelegates = false;
for (EClassifier eClassifier : ePackage.getEClassifiers()) {
EAnnotation classifierAnnotation = OCLCommon.getDelegateAnnotation(eClassifier);
if ((classifierAnnotation != null) && !classifierAnnotation.getDetails().isEmpty()) {
needsDelegates = true;
break;
}
if (eClassifier instanceof EClass) {
EClass eClass = (EClass) eClassifier;
for (EStructuralFeature eFeature : eClass.getEStructuralFeatures()) {
EAnnotation featureAnnotation = OCLCommon.getDelegateAnnotation(eFeature);
if ((featureAnnotation != null) && !featureAnnotation.getDetails().isEmpty()) {
needsDelegates = true;
break;
}
}
if (needsDelegates) {
break;
}
for (EOperation eOperation : eClass.getEOperations()) {
EAnnotation operationAnnotation = OCLCommon.getDelegateAnnotation(eOperation);
if ((operationAnnotation != null) && !operationAnnotation.getDetails().isEmpty()) {
needsDelegates = true;
break;
}
}
if (needsDelegates) {
break;
}
}
}
return needsDelegates;
}
protected final @NonNull EnvironmentFactoryInternal environmentFactory;
protected final @NonNull Map<String, Object> options;
protected final @Nullable String exportDelegateURI;
public DelegateInstaller(@NonNull EnvironmentFactoryInternal environmentFactory, @Nullable Map<String, Object> options) {
this.environmentFactory = environmentFactory;
// this.metamodelManager = metamodelManager;
this.options = options != null ? options : new HashMap<String,Object>();
this.exportDelegateURI = getExportDelegateURI(this.options);
}
protected @NonNull EAnnotation createAnnotation(@NonNull EModelElement eModelElement) {
EAnnotation oclAnnotation = removeDelegateAnnotations(eModelElement, exportDelegateURI);
if (oclAnnotation == null) {
oclAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
oclAnnotation.setSource(exportDelegateURI);
eModelElement.getEAnnotations().add(oclAnnotation);
}
return oclAnnotation;
}
public @Nullable EAnnotation createConstraintDelegate(@NonNull EModelElement eModelElement, @NonNull Constraint pivotConstraint, @Nullable URI ecoreURI) {
LanguageExpression specification = pivotConstraint.getOwnedSpecification();
if (specification == null) {
return null;
}
String exprString = createExpression(specification, ecoreURI);
if (exprString == null) {
return null;
}
EAnnotation oclAnnotation = createAnnotation(eModelElement);
String key = getAnnotationKey(pivotConstraint);
oclAnnotation.getDetails().put(key, exprString);
return oclAnnotation;
}
protected @Nullable String createExpression(@NonNull LanguageExpression specification, @Nullable URI ecoreURI) {
String exprString = specification.getBody();
if ((exprString == null) && (specification instanceof ExpressionInOCL)) {
OCLExpression bodyExpression2 = ((ExpressionInOCL)specification).getOwnedBody();
if (bodyExpression2 != null) {
exprString = createExpression(bodyExpression2, ecoreURI);
}
}
return exprString;
}
protected @Nullable String createExpression(@NonNull OCLExpression bodyExpression, @Nullable URI ecoreURI) {
Namespace namespace = PivotUtil.getNamespace(bodyExpression);
PrettyPrintOptions.Global options = PrettyPrinter.createOptions(namespace);
options.setBaseURI(ecoreURI);
return PrettyPrinter.print(bodyExpression, options);
}
public @Nullable EAnnotation createOperationDelegate(@NonNull EOperation eOperation, @NonNull LanguageExpression bodyExpression, @Nullable URI ecoreURI) {
String exprString = createExpression(bodyExpression, ecoreURI);
if (exprString == null) {
return null;
}
if (isBooleanInvariants(options)) {
exprString = "result = (" + exprString + ")";
}
EAnnotation oclAnnotation = createAnnotation(eOperation);
oclAnnotation.getDetails().put(InvocationBehavior.BODY_CONSTRAINT_KEY, exprString);
return oclAnnotation;
}
public @Nullable EAnnotation createPropertyDelegate(@NonNull EStructuralFeature eStructuralFeature, @NonNull LanguageExpression defaultExpression, @Nullable URI ecoreURI) {
String exprString = createExpression(defaultExpression, ecoreURI);
if (exprString == null) {
return null;
}
EAnnotation oclAnnotation = createAnnotation(eStructuralFeature);
oclAnnotation.getDetails().put(SettingBehavior.DERIVATION_CONSTRAINT_KEY, exprString);
return oclAnnotation;
}
public @NonNull EnvironmentFactory getEnvironmentFactory() {
return environmentFactory;
}
public @Nullable String getExportDelegateURI() {
return exportDelegateURI;
}
// public @NonNull MetamodelManager getMetamodelManager() {
// return metamodelManager;
// }
/**
* Install all Constraints from pivotPackage and its nestedPackages as OCL Delegates.
*/
public void installDelegates(@NonNull CompletePackage completePackage) {
boolean hasDelegates = false;
// for (Type aType : metamodelManager.getLocalClasses(pivotPackage)) {
for (CompleteClass completeClass : completePackage.getOwnedCompleteClasses()) {
if (installDelegates(completeClass.getPrimaryClass())) {
hasDelegates = true;
}
}
// PackageServer packageServer = metamodelManager.getPackageServer(pivotPackage);
EPackage ePackage = completePackage.getEPackage();
if ((ePackage != null) && hasDelegates) {
installDelegates(ePackage);
}
for (CompletePackage nestedPackage : completePackage.getOwnedCompletePackages()) {
if (nestedPackage != null) {
installDelegates(nestedPackage);
}
}
}
/**
* Install all Constraints from pivotType and its operations as OCL Delegates. Returning true if any OCL Delegate installed.
*
* @param metamodelManager
* @param pivotPackage
*/
private boolean installDelegates(org.eclipse.ocl.pivot.@NonNull Class pivotType) {
boolean hasDelegates = false;
PivotMetamodelManager metamodelManager = environmentFactory.getMetamodelManager();
Type primaryType = metamodelManager.getPrimaryType(pivotType);
EObject eTarget = primaryType.getESObject();
if (eTarget instanceof EClassifier) {
@NonNull EClassifier eClassifier = (EClassifier)eTarget;
removeDelegateAnnotations(eClassifier, null);
for (Constraint constraint : metamodelManager.getLocalInvariants(pivotType)) {
if (constraint.isIsCallable()) {
EOperation eContext = null;
String name = constraint.getName();
for (EOperation candidate : ((EClass) eClassifier).getEOperations()) {
if (name.equals(candidate.getName()) && EcoreUtil.isInvariant(candidate)) {
eContext = candidate;
break;
}
}
if (eContext == null) {
@NonNull EOperation eOperation = AS2Ecore.createConstraintEOperation(constraint, name, null);
((EClass) eClassifier).getEOperations().add(eOperation);
eContext = eOperation;
}
EAnnotation oclAnnotation = createConstraintDelegate(eContext, constraint, null);
if (oclAnnotation == null) {
return false;
}
eContext.getEAnnotations().add(oclAnnotation);
hasDelegates = true;
}
else {
EAnnotation oclAnnotation = createConstraintDelegate(eClassifier, constraint, null);
if (oclAnnotation == null) {
return false;
}
eClassifier.getEAnnotations().add(oclAnnotation);
hasDelegates = true;
}
}
for (Operation anOperation : metamodelManager.getMemberOperations(pivotType, false)) {
EOperation eOperation = (EOperation)anOperation.getESObject();
if (eOperation != null) {
installDelegate(eOperation);
}
}
for (Operation anOperation : metamodelManager.getMemberOperations(pivotType, true)) {
EOperation eOperation = (EOperation)anOperation.getESObject();
if (eOperation != null) {
installDelegate(eOperation);
}
}
for (Property aProperty : metamodelManager.getMemberProperties(pivotType, false)) {
EStructuralFeature eFeature = (EStructuralFeature)aProperty.getESObject();
if (eFeature != null) {
installDelegate(eFeature);
}
}
for (Property aProperty : metamodelManager.getMemberProperties(pivotType, true)) {
EStructuralFeature eFeature = (EStructuralFeature)aProperty.getESObject();
if (eFeature != null) {
installDelegate(eFeature);
}
}
for (EAnnotation eAnnotation : eClassifier.getEAnnotations()) { // Fix redefines/duplicates
for (TreeIterator<EObject> tit = eAnnotation.eAllContents(); tit.hasNext(); ) {
EObject eObject = tit.next();
if (eObject instanceof EAnnotation) {
EAnnotation nestedAnnotation = (EAnnotation) eObject;
if (DerivedConstants.UML2_GEN_MODEL_PACKAGE_1_1_NS_URI.equals(nestedAnnotation.getSource())) {
nestedAnnotation.setSource(PivotConstants.OCL_DELEGATE_URI_PIVOT);
}
}
}
}
if (hasDelegates) {
installDelegates(eClassifier, pivotType);
}
}
return hasDelegates;
}
public void installDelegate(@NonNull EOperation eOperation) {
List<EAnnotation> eAnnotations = eOperation.getEAnnotations();
EAnnotation oclAnnotation = eOperation.getEAnnotation(DerivedConstants.UML2_GEN_MODEL_PACKAGE_1_1_NS_URI);
if (oclAnnotation != null) {
eAnnotations.remove(oclAnnotation);
oclAnnotation.setSource(exportDelegateURI);
eAnnotations.add(oclAnnotation);
}
}
public void installDelegate(@NonNull EStructuralFeature eFeature) {
List<EAnnotation> eAnnotations = eFeature.getEAnnotations();
EAnnotation oclAnnotation = eFeature.getEAnnotation(DerivedConstants.UML2_GEN_MODEL_PACKAGE_1_1_NS_URI);
if (oclAnnotation != null) {
eAnnotations.remove(oclAnnotation);
oclAnnotation.setSource(exportDelegateURI);
eAnnotations.add(oclAnnotation);
}
}
public void installDelegates(@NonNull EClassifier eClassifier, org.eclipse.ocl.pivot.@NonNull Class pivotType) {
StringBuilder s = null;
PivotMetamodelManager metamodelManager = environmentFactory.getMetamodelManager();
for (Constraint pivotConstraint : metamodelManager.getLocalInvariants(pivotType)) {
String constraintName = pivotConstraint.getName();
if (!pivotConstraint.isIsCallable() && (constraintName != null)) {
if (s == null) {
s = new StringBuilder();
}
else {
s.append(" ");
}
s.append(constraintName);
}
}
EAnnotation eAnnotation = eClassifier.getEAnnotation(EcorePackage.eNS_URI);
if (s != null) {
if (eAnnotation == null) {
eAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
eAnnotation.setSource(EcorePackage.eNS_URI);
eClassifier.getEAnnotations().add(/*0,*/ eAnnotation);
}
eAnnotation.getDetails().put("constraints", s.toString());
}
else {
eClassifier.getEAnnotations().remove(eAnnotation);
}
}
public void installDelegates(@NonNull EPackage ePackage) {
EAnnotation packageAnnotation = ClassUtil.getEAnnotation(ePackage, EcorePackage.eNS_URI);
EMap<String, String> details = packageAnnotation.getDetails();
details.put(InvocationBehavior.NAME, exportDelegateURI);
if (!Boolean.valueOf(String.valueOf(options.get(OPTION_OMIT_SETTING_DELEGATES)))) {
details.put(SettingBehavior.NAME, exportDelegateURI);
}
details.put(ValidationBehavior.NAME, exportDelegateURI);
}
/**
* Remove all OCL Delegate annotations except that corresponding to exportDelegateURI which is returned.
*/
protected @Nullable EAnnotation removeDelegateAnnotations(@NonNull EModelElement eModelElement, @Nullable String exportDelegateURI) {
List<EAnnotation> eAnnotations = eModelElement.getEAnnotations();
EAnnotation oclAnnotation = null;
EAnnotation annotation1 = eModelElement.getEAnnotation(OCLConstants.OCL_DELEGATE_URI);
if (annotation1 != null) {
if (OCLConstants.OCL_DELEGATE_URI.equals(exportDelegateURI)) {
oclAnnotation = annotation1;
}
else {
eAnnotations.remove(annotation1);
}
}
EAnnotation annotation2 = eModelElement.getEAnnotation(OCLConstants.OCL_DELEGATE_URI_LPG);
if (annotation2 != null) {
if (OCLConstants.OCL_DELEGATE_URI_LPG.equals(exportDelegateURI)) {
oclAnnotation = annotation2;
}
else {
eAnnotations.remove(annotation2);
}
}
EAnnotation annotation3 = eModelElement.getEAnnotation(PivotConstants.OCL_DELEGATE_URI_PIVOT);
if (annotation3 != null) {
if (PivotConstants.OCL_DELEGATE_URI_PIVOT.equals(exportDelegateURI)) {
oclAnnotation = annotation3;
}
else {
eAnnotations.remove(annotation3);
}
}
EAnnotation annotation4 = eModelElement.getEAnnotation(DerivedConstants.UML2_GEN_MODEL_PACKAGE_1_1_NS_URI);
if (annotation4 != null) {
eAnnotations.remove(annotation4);
}
return oclAnnotation;
}
}