/*******************************************************************************
* 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:
* E.D.Willink - initial API and implementation
* E.D.Willink (CEA LIST) - Bug 388529
*******************************************************************************/
package org.eclipse.ocl.xtext.essentialocl.attributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.CompletePackage;
import org.eclipse.ocl.pivot.Iteration;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.PrimitiveType;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.internal.complete.CompleteModelInternal;
import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager;
import org.eclipse.ocl.pivot.internal.manager.TemplateParameterSubstitutionVisitor;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.values.TemplateParameterSubstitutions;
import org.eclipse.ocl.xtext.essentialocl.cs2as.EssentialOCLCSLeft2RightVisitor.Invocations;
public abstract class AbstractOperationMatcher
{
private static Comparator<Operation> operationComparator = new Comparator<Operation>()
{
@Override
public int compare(Operation o1, Operation o2) {
String n1 = o1.getName();
String n2 = o2.getName();
if (n1 == null) n1 = "";
if (n2 == null) n2 = "";
int diff = n1.compareTo(n2);
if (diff != 0) {
return diff;
}
List<Parameter> ownedParameters1 = o1.getOwnedParameters();
List<Parameter> ownedParameters2 = o2.getOwnedParameters();
int s1 = ownedParameters1.size();
int s2 = ownedParameters2.size();
diff = s1 - s2;
if (diff != 0) {
return diff;
}
for (int i = 0; i < s1; i++) {
Parameter p1 = ownedParameters1.get(i);
Parameter p2 = ownedParameters2.get(i);
n1 = p1.getName();
n2 = p2.getName();
if (n1 == null) n1 = "";
if (n2 == null) n2 = "";
diff = n1.compareTo(n2);
if (diff != 0) {
return diff;
}
}
for (EObject e1 = o1.eContainer(), e2 = o2.eContainer(); (e1 instanceof NamedElement) && (e2 instanceof NamedElement); e1 = e1.eContainer(), e2 = e2.eContainer()) {
n1 = ((NamedElement)e1).getName();
n2 = ((NamedElement)e2).getName();
if (n1 == null) n1 = "";
if (n2 == null) n2 = "";
diff = n1.compareTo(n2);
if (diff != 0) {
return diff;
}
}
return 0;
}
};
protected final @NonNull EnvironmentFactoryInternal environmentFactory;
protected final @NonNull PivotMetamodelManager metamodelManager;
protected final @Nullable Type sourceType;
protected final @Nullable Type sourceTypeValue;
private @Nullable List<Operation> ambiguities = null;
protected AbstractOperationMatcher(@NonNull EnvironmentFactoryInternal environmentFactory, @Nullable Type sourceType, @Nullable Type sourceTypeValue) {
this.environmentFactory = environmentFactory;
this.metamodelManager = environmentFactory.getMetamodelManager();
this.sourceType = sourceType != null ? PivotUtilInternal.getType(sourceType) : null; // FIXME redundant
this.sourceTypeValue = sourceTypeValue;
}
protected int compareMatches(@NonNull Object match1, @NonNull TemplateParameterSubstitutions referenceBindings,
@NonNull Object match2, @NonNull TemplateParameterSubstitutions candidateBindings, boolean useCoercions) {
CompleteModelInternal completeModel = environmentFactory.getCompleteModel();
@NonNull Operation reference = (Operation) match1;
@NonNull Operation candidate = (Operation) match2;
org.eclipse.ocl.pivot.Class referenceClass = reference.getOwningClass();
org.eclipse.ocl.pivot.Class candidateClass = candidate.getOwningClass();
Type referenceType = referenceClass != null ? PivotUtilInternal.getType(referenceClass) : null;
Type candidateType = candidateClass != null ? PivotUtilInternal.getType(candidateClass) : null;
Type specializedReferenceType = referenceType != null ? completeModel.getSpecializedType(referenceType, referenceBindings) : null;
Type specializedCandidateType = candidateType != null ? completeModel.getSpecializedType(candidateType, candidateBindings) : null;
if ((reference instanceof Iteration) && (candidate instanceof Iteration) && (specializedReferenceType != null) && (specializedCandidateType != null)) {
int iteratorCountDelta = ((Iteration)candidate).getOwnedIterators().size() - ((Iteration)reference).getOwnedIterators().size();
if (iteratorCountDelta != 0) {
return iteratorCountDelta;
}
if (referenceType != candidateType) {
if (metamodelManager.conformsTo(specializedReferenceType, TemplateParameterSubstitutions.EMPTY, specializedCandidateType, TemplateParameterSubstitutions.EMPTY)) {
return 1;
}
else if (metamodelManager.conformsTo(specializedCandidateType, TemplateParameterSubstitutions.EMPTY, specializedReferenceType, TemplateParameterSubstitutions.EMPTY)) {
return -1;
}
}
}
int referenceConversions = 0;
int candidateConversions = 0;
Type comparedSourceType = sourceType;
// if (comparedSourceType instanceof DomainMetaclass) {
// comparedSourceType = ((DomainMetaclass)comparedSourceType).getInstanceType();
// }
if (comparedSourceType != specializedReferenceType) {
referenceConversions++;
}
if (comparedSourceType != specializedCandidateType) {
candidateConversions++;
}
List<Parameter> candidateParameters = candidate.getOwnedParameters();
List<Parameter> referenceParameters = reference.getOwnedParameters();
for (int i = 0; i < candidateParameters.size(); i++) {
OCLExpression pivotArgument = getArgument(i);
if (pivotArgument == null) {
return 0;
}
Type argumentType = pivotArgument.getType();
Parameter referenceParameter = referenceParameters.get(i);
Parameter candidateParameter = candidateParameters.get(i);
if ((referenceParameter == null) || (candidateParameter == null)) { // Doesn't happen (just a supurious NPE guard)
referenceConversions = Integer.MIN_VALUE;
candidateConversions = Integer.MIN_VALUE;
}
else {
referenceType = PivotUtilInternal.getType(ClassUtil.nonNullModel(referenceParameter.getType()));
candidateType = PivotUtilInternal.getType(ClassUtil.nonNullModel(candidateParameter.getType()));
specializedReferenceType = completeModel.getSpecializedType(referenceType, referenceBindings);
specializedCandidateType = completeModel.getSpecializedType(candidateType, candidateBindings);
if (argumentType != specializedReferenceType) {
referenceConversions++;
}
if (argumentType != specializedCandidateType) {
candidateConversions++;
}
}
}
if (candidateConversions != referenceConversions) {
return candidateConversions - referenceConversions;
}
int verdict = metamodelManager.compareOperationMatches(reference, referenceBindings, candidate, candidateBindings);
if (verdict != 0) {
return verdict;
}
if (isRedefinitionOf(reference, candidate)) {
return 1; // match2 inferior
}
if (isRedefinitionOf(candidate, reference)) {
return -1; // match1 inferior
}
org.eclipse.ocl.pivot.Package p1 = PivotUtil.getContainingPackage(reference);
org.eclipse.ocl.pivot.Package p2 = PivotUtil.getContainingPackage(candidate);
if (p1 == null) {
return 0;
}
if (p2 == null) {
return 0;
}
CompletePackage s1 = completeModel.getCompletePackage(p1);
CompletePackage s2 = completeModel.getCompletePackage(p2);
if (s1 != s2) {
return 0;
}
int i1 = s1.getIndex(p1);
int i2 = s2.getIndex(p2);
int indexDiff = i2 - i1;
if (indexDiff != 0) {
return indexDiff;
}
if ((specializedReferenceType != null) && (specializedCandidateType != null)) {
if (metamodelManager.conformsTo(specializedReferenceType, referenceBindings, specializedCandidateType, candidateBindings)) {
return 1;
}
else if (metamodelManager.conformsTo(specializedCandidateType, candidateBindings, specializedReferenceType, referenceBindings)) {
return -1;
}
}
return 0;
}
public @Nullable List<Operation> getAmbiguities() {
return ambiguities;
}
protected abstract OCLExpression getArgument(int i);
protected abstract int getArgumentCount();
public @Nullable Operation getBestOperation(@NonNull Invocations invocations, boolean useCoercions) {
ambiguities = null;
Operation bestOperation = null;
TemplateParameterSubstitutions bestBindings = TemplateParameterSubstitutions.EMPTY;
List<Operation> ambiguities2 = ambiguities;
for (NamedElement namedElement : invocations) {
if (namedElement instanceof Operation) {
Operation candidateOperation = (Operation)namedElement;
TemplateParameterSubstitutions candidateBindings = matches(candidateOperation, useCoercions);
if (candidateBindings != null) {
if (bestOperation == null) {
bestOperation = candidateOperation;
bestBindings = candidateBindings;
}
else {
int comparison = compareMatches(bestOperation, bestBindings, candidateOperation, candidateBindings, useCoercions);
if (comparison < 0) {
bestOperation = candidateOperation;
bestBindings = candidateBindings;
ambiguities = null;
}
else if (comparison == 0) {
if (ambiguities2 == null) {
ambiguities = ambiguities2 = new ArrayList<Operation>();
ambiguities2.add(bestOperation);
}
ambiguities2.add(candidateOperation);
}
}
}
}
}
if (ambiguities2 != null) {
Collections.sort(ambiguities2, operationComparator);
}
return bestOperation;
}
protected boolean isRedefinitionOf(@NonNull Operation operation1, @NonNull Operation operation2) {
List<Operation> redefinedOperations = operation1.getRedefinedOperations();
for (Operation redefinedOperation : redefinedOperations) {
if (redefinedOperation != null) {
if (redefinedOperation == operation2) {
return true;
}
if (isRedefinitionOf(redefinedOperation, operation2)) {
return true;
}
}
}
return false;
}
protected @Nullable TemplateParameterSubstitutions matches(@NonNull Operation candidateOperation, boolean useCoercions) {
List<Parameter> candidateParameters = candidateOperation.getOwnedParameters();
int iSize = getArgumentCount();
if (iSize != candidateParameters.size()) {
return null;
}
TemplateParameterSubstitutions bindings = TemplateParameterSubstitutionVisitor.createBindings(environmentFactory, sourceType, sourceTypeValue, candidateOperation);
for (int i = 0; i < iSize; i++) {
Parameter candidateParameter = candidateParameters.get(i);
if (candidateParameter != null) {
OCLExpression expression = getArgument(i);
if (expression == null) {
return null;
}
Type candidateType = PivotUtilInternal.getBehavioralType(candidateParameter);
if (candidateType == null) {
return null;
}
Type expressionType = PivotUtilInternal.getBehavioralType(expression);
if (expressionType == null) {
return null;
}
if (!metamodelManager.conformsTo(expressionType, TemplateParameterSubstitutions.EMPTY, candidateType, bindings)) {
boolean coerceable = false;
if (useCoercions) {
CompleteClass completeClass = metamodelManager.getCompleteClass(expressionType);
for (org.eclipse.ocl.pivot.Class partialClass : completeClass.getPartialClasses()) {
if (partialClass instanceof PrimitiveType) {
for (Operation coercion : ((PrimitiveType)partialClass).getCoercions()) {
Type corcedSourceType = coercion.getType();
if ((corcedSourceType != null) && metamodelManager.conformsTo(corcedSourceType, TemplateParameterSubstitutions.EMPTY, candidateType, TemplateParameterSubstitutions.EMPTY)) {
coerceable = true;
break;
}
}
if (coerceable) {
break;
}
}
}
}
if (!coerceable) {
return null;
}
}
}
}
return bindings;
}
}