blob: 0a2b12c33d2747fb796c131d4256b24b42555d2a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2018 CEA LIST 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:
* E.D.Willink(CEA LIST) - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.codegen.utilities;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.codegen.cgmodel.CGCallExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGCollectionExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGCollectionPart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGIfExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGIterationCallExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGIterator;
import org.eclipse.ocl.examples.codegen.cgmodel.CGLetExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGMapExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGMapPart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGNavigationCallExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGNumber;
import org.eclipse.ocl.examples.codegen.cgmodel.CGOperationCallExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGShadowExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGShadowPart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGTupleExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGTuplePart;
import org.eclipse.ocl.examples.codegen.cgmodel.CGValuedElement;
import org.eclipse.ocl.pivot.CollectionLiteralExp;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.LoopExp;
import org.eclipse.ocl.pivot.MapLiteralExp;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.PropertyCallExp;
import org.eclipse.ocl.pivot.ShadowExp;
import org.eclipse.ocl.pivot.ShadowPart;
import org.eclipse.ocl.pivot.TupleLiteralExp;
import org.eclipse.ocl.pivot.TupleLiteralPart;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.IntegerValue;
import org.eclipse.ocl.pivot.values.RealValue;
/**
* EquivalenceUtil provides the bodies for many of the isEquivalentToInternal operations.
*
* These return:
* <br>
* true if two values are definitely the same.
* <br>
* false if two values are definitely different.
* <br>
* null if the values could be the same or different.
*/
public class EquivalenceUtil
{
public static @Nullable Boolean isEquivalent(@NonNull CGCallExp thisValue, @NonNull CGCallExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
CGValuedElement thisSource = thisValue.getSource();
CGValuedElement thatSource = thatValue.getSource();
if ((thisSource != null) || (thatSource != null)) {
if ((thisSource == null) || (thatSource == null)) {
return null; // Inconsistent sources should never happen
}
Boolean equivalence = thisSource.isEquivalentTo(thatSource);
if (equivalence != Boolean.TRUE) {
return null; // Different sources do not guarantee different results
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGCollectionExp thisValue, @NonNull CGCollectionExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof CollectionLiteralExp) || !(thatAST instanceof CollectionLiteralExp)) {
return null; // Null ASTs should never happen
}
if (((CollectionLiteralExp)thisAST).getKind() != ((CollectionLiteralExp)thatAST).getKind()) {
return Boolean.FALSE; // Distinct kinds are not necessarily not equal
}
List<CGCollectionPart> theseParts = thisValue.getParts();
List<CGCollectionPart> thoseParts = thatValue.getParts();
int iSize = theseParts.size();
if (iSize != thoseParts.size()) {
return null; //Boolean.FALSE; -- FIXME support range/items comparison
}
for (int i = 0; i < iSize; i++) {
CGCollectionPart thisPart = theseParts.get(i);
CGCollectionPart thatPart = thoseParts.get(i);
if ((thisPart == null) || (thatPart == null)) {
return null; // Null parts should never happen
}
Boolean equivalence = thisPart.isEquivalentTo(thatPart);
if (equivalence != Boolean.TRUE) {
return null; // equivalence; -- FIXME support differently ordered comparison
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGCollectionPart thisValue, @NonNull CGCollectionPart thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
CGValuedElement thisLast = thisValue.getLast();
CGValuedElement thatLast = thatValue.getLast();
if ((thisLast != null) || (thatLast != null)) {
if ((thisLast == null) || (thatLast == null)) {
return null; // FIXME could support range/items comparison
}
Boolean equivalence = thisLast.isEquivalentTo(thatLast);
if (equivalence != Boolean.TRUE) {
return equivalence;
}
}
CGValuedElement thisFirst = thisValue.getFirst();
CGValuedElement thatFirst = thatValue.getFirst();
if ((thisFirst == null) || (thatFirst == null)) {
return null; // FIXME could support range/items comparison
}
return thisFirst.isEquivalentTo(thatFirst);
}
public static @Nullable Boolean isEquivalent(@NonNull CGIfExp thisValue, @NonNull CGIfExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
if (thisValue.isConstant() && thatValue.isConstant()) { // FIXME Move to caller
CGValuedElement thisConstant = thisValue.getNamedValue();
CGValuedElement thatConstant = thatValue.getNamedValue();
return thisConstant.isEquivalentTo(thatConstant);
}
CGValuedElement thisCondition = thisValue.getCondition();
CGValuedElement thatCondition = thatValue.getCondition();
if ((thisCondition != null) || (thatCondition != null)) {
if ((thisCondition == null) || (thatCondition == null)) {
return null; // Inconsistent conditions should never happen
}
Boolean equivalence = thisCondition.isEquivalentTo(thatCondition);
if (equivalence != Boolean.TRUE) {
return null; // Different conditions do not guarantee different results
}
}
CGValuedElement thisThen = thisValue.getThenExpression();
CGValuedElement thatThen = thatValue.getThenExpression();
if ((thisThen != null) || (thatThen != null)) {
if ((thisThen == null) || (thatThen == null)) {
return null; // Inconsistent expressions should never happen
}
Boolean equivalence = thisThen.isEquivalentTo(thatThen);
if (equivalence != Boolean.TRUE) {
return null; // Different expressions do not guarantee different results
}
}
CGValuedElement thisElse = thisValue.getElseExpression();
CGValuedElement thatElse = thatValue.getElseExpression();
if ((thisElse != null) || (thatElse != null)) {
if ((thisElse == null) || (thatElse == null)) {
return null; // Inconsistent expressions should never happen
}
Boolean equivalence = thisElse.isEquivalentTo(thatElse);
if (equivalence != Boolean.TRUE) {
return null; // Different expressions do not guarantee different results
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGIterationCallExp thisValue, @NonNull CGIterationCallExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof LoopExp) || !(thatAST instanceof LoopExp)) {
return null; // Null ASTs should never happen
}
if (((LoopExp)thisAST).getReferredIteration() != ((LoopExp)thatAST).getReferredIteration()) {
return null; // Different iterators do not guarantee different results
}
CGValuedElement thisSource = thisValue.getSource();
CGValuedElement thatSource = thatValue.getSource();
if ((thisSource != null) || (thatSource != null)) {
if ((thisSource == null) || (thatSource == null)) {
return null; // Inconsistent sources should never happen
}
Boolean equivalence = thisSource.isEquivalentTo(thatSource);
if (equivalence != Boolean.TRUE) {
return null; // Different sources do not guarantee different results
}
}
CGValuedElement thisBody = thisValue.getBody();
CGValuedElement thatBody = thatValue.getBody();
if ((thisBody != null) || (thatBody != null)) {
if ((thisBody == null) || (thatBody == null)) {
return null; // Null bodies should never happen
}
Boolean equivalence = thisBody.isEquivalentTo(thatBody);
if (equivalence != Boolean.TRUE) {
return null; // Different bodies do not guarantee different results
}
}
List<CGIterator> theseIterators = thisValue.getIterators();
List<CGIterator> thoseIterators = thatValue.getIterators();
int iSize = theseIterators.size();
if (iSize != thoseIterators.size()) {
return null; // Different iterator lists do not guarantee different results
}
for (int i = 0; i < iSize; i++) {
CGIterator thisIterator = theseIterators.get(i);
CGIterator thatIterator = thoseIterators.get(i);
if ((thisIterator != null) || (thatIterator != null)) {
if ((thisIterator == null) || (thatIterator == null)) {
return null; // Null iterators should never happen
}
Boolean equivalence = thisIterator.isEquivalentTo(thatIterator);
if (equivalence != Boolean.TRUE) {
return null; // Different iterators do not guarantee different results
}
}
}
List<CGIterator> theseCoIterators = thisValue.getCoIterators();
List<CGIterator> thoseCoIterators = thatValue.getCoIterators();
int iCoSize = theseCoIterators.size();
if (iCoSize != thoseCoIterators.size()) {
return null; // Different iterator lists do not guarantee different results
}
for (int i = 0; i < iCoSize; i++) {
CGIterator thisCoIterator = theseCoIterators.get(i);
CGIterator thatCoIterator = thoseCoIterators.get(i);
if ((thisCoIterator != null) || (thatCoIterator != null)) {
if ((thisCoIterator == null) || (thatCoIterator == null)) {
return null; // Null co-iterators should never happen
}
Boolean equivalence = thisCoIterator.isEquivalentTo(thatCoIterator);
if (equivalence != Boolean.TRUE) {
return null; // Different iterators do not guarantee different results
}
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGLetExp thisValue, @NonNull CGLetExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
if (thisValue.isConstant() && thatValue.isConstant()) { // FIXME Move to caller
CGValuedElement thisConstant = thisValue.getNamedValue();
CGValuedElement thatConstant = thatValue.getNamedValue();
return thisConstant.isEquivalentTo(thatConstant);
}
CGValuedElement thisInit = thisValue.getInit();
CGValuedElement thatInit = thatValue.getInit();
if ((thisInit != null) || (thatInit != null)) {
if ((thisInit == null) || (thatInit == null)) {
return null; // Inconsistent inits should never happen
}
Boolean equivalence = thisInit.isEquivalentTo(thatInit);
if (equivalence != Boolean.TRUE) {
return null; // Different inits do not guarantee different results
}
}
CGValuedElement thisIn = thisValue.getIn();
CGValuedElement thatIn = thatValue.getIn();
if ((thisIn != null) || (thatIn != null)) {
if ((thisIn == null) || (thatIn == null)) {
return null; // Inconsistent expressions should never happen
}
Boolean equivalence = thisIn.isEquivalentTo(thatIn);
if (equivalence != Boolean.TRUE) {
return null; // Different expressions do not guarantee different results
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGMapExp thisValue, @NonNull CGMapExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof MapLiteralExp) || !(thatAST instanceof MapLiteralExp)) {
return null; // Null ASTs should never happen
}
List<CGMapPart> theseParts = thisValue.getParts();
List<CGMapPart> thoseParts = thatValue.getParts();
int iSize = theseParts.size();
if (iSize != thoseParts.size()) {
return null; //Boolean.FALSE; -- FIXME support range/items comparison
}
for (int i = 0; i < iSize; i++) {
CGMapPart thisPart = theseParts.get(i);
CGMapPart thatPart = thoseParts.get(i);
if ((thisPart == null) || (thatPart == null)) {
return null; // Null parts should never happen
}
Boolean equivalence = thisPart.isEquivalentTo(thatPart);
if (equivalence != Boolean.TRUE) {
return null; // equivalence; -- FIXME support differently ordered comparison
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGMapPart thisPart, @NonNull CGMapPart thatPart) {
if (thisPart == thatPart) {
return Boolean.TRUE;
}
CGValuedElement thisKey = thisPart.getKey();
CGValuedElement thatKey = thatPart.getKey();
if ((thisKey != null) || (thatKey != null)) {
if ((thisKey == null) || (thatKey == null)) {
return null;
}
Boolean equivalence = thisKey.isEquivalentTo(thatKey);
if (equivalence != Boolean.TRUE) {
return equivalence;
}
}
CGValuedElement thisValue = thisPart.getValue();
CGValuedElement thatValue = thatPart.getValue();
if ((thisValue == null) || (thatValue == null)) {
return null; // FIXME could support range/items comparison
}
return thisValue.isEquivalentTo(thatValue);
}
public static @Nullable Boolean isEquivalent(@NonNull CGNumber thisValue, @NonNull CGNumber thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Number thisNumber = thisValue.getNumericValue();
Number thatNumber = thatValue.getNumericValue();
if ((thisNumber == null) || (thatNumber == null)) {
return null; // Null numbers should never happen
}
else if (thisNumber.getClass() == thatNumber.getClass()) {
return thisNumber.equals(thatNumber);
}
else if ((thisNumber instanceof RealValue) && (thatNumber instanceof RealValue)) {
return thisNumber.equals(thatNumber);
}
else if (ValueUtil.isRealNumber(thisNumber) || ValueUtil.isRealNumber(thatNumber)) {
RealValue thisReal = ValueUtil.realValueOf(thisNumber);
RealValue thatReal = ValueUtil.realValueOf(thatNumber);
return thisReal.equals(thatReal);
}
else if (ValueUtil.isIntegerNumber(thisNumber) && ValueUtil.isIntegerNumber(thatNumber)) {
IntegerValue thisInteger = ValueUtil.integerValueOf(thisNumber);
IntegerValue thatInterger = ValueUtil.integerValueOf(thatNumber);
return thisInteger.equals(thatInterger);
}
else { // This should never happen
double thisDouble = thisNumber.doubleValue();
double thatDouble = thatNumber.doubleValue();
return thisDouble == thatDouble;
}
}
public static @Nullable Boolean isEquivalent(@NonNull CGOperationCallExp thisValue, @NonNull CGOperationCallExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof OperationCallExp) || !(thatAST instanceof OperationCallExp)) {
return null; // Null ASTs should never happen
}
if (((OperationCallExp)thisAST).getReferredOperation() != ((OperationCallExp)thatAST).getReferredOperation()) {
return null; // Different operations do not guarantee different results
}
// FIXME non-conformant return types can be guaranteed to be different
CGValuedElement thisSource = thisValue.getSource();
CGValuedElement thatSource = thatValue.getSource();
if ((thisSource != null) || (thatSource != null)) {
if ((thisSource == null) || (thatSource == null)) {
return null; // Inconsistently null sources should never happen
}
Boolean equivalence = thisSource.isEquivalentTo(thatSource);
if (equivalence != Boolean.TRUE) {
return null; // Different sources do not guarantee different results
}
}
List<CGValuedElement> theseArguments = thisValue.getArguments();
List<CGValuedElement> thoseArguments = thatValue.getArguments();
int iSize = theseArguments.size();
if (iSize != thoseArguments.size()) {
return null; // Different argument lists do not guarantee different results
}
for (int i = 0; i < iSize; i++) {
CGValuedElement thisArgument = theseArguments.get(i);
CGValuedElement thatArgument = thoseArguments.get(i);
if ((thisArgument != null) || (thatArgument != null)) {
if ((thisArgument == null) || (thatArgument == null)) {
return null; // Null arguments should never happen
}
Boolean equivalence = thisArgument.isEquivalentTo(thatArgument);
if (equivalence != Boolean.TRUE) {
return null; // Different arguments do not guarantee different results
}
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGNavigationCallExp thisValue, @NonNull CGNavigationCallExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof NavigationCallExp) || !(thatAST instanceof NavigationCallExp)) {
return null; // Null ASTs should never happen
}
if (thisAST.eClass() != thatAST.eClass()) {
return null; // Different directions do not guarantee different results
}
Property thisProperty = thisAST instanceof PropertyCallExp ? ((PropertyCallExp)thisAST).getReferredProperty() : ((OppositePropertyCallExp)thisAST).getReferredProperty();
Property thatProperty = thatAST instanceof PropertyCallExp ? ((PropertyCallExp)thatAST).getReferredProperty() : ((OppositePropertyCallExp)thatAST).getReferredProperty();
if (thisProperty != thatProperty) {
return null; // Different properties do not guarantee different results
}
// FIXME non-conformant return types can be guaranteed to be different
CGValuedElement thisSource = thisValue.getSource();
CGValuedElement thatSource = thatValue.getSource();
if ((thisSource != null) || (thatSource != null)) {
if ((thisSource == null) || (thatSource == null)) {
return null; // Inconsistently null sources should never happen
}
Boolean equivalence = thisSource.isEquivalentTo(thatSource);
if (equivalence != Boolean.TRUE) {
return null; // Different sources do not guarantee different results
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGShadowExp thisValue, @NonNull CGShadowExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof ShadowExp) || !(thatAST instanceof ShadowExp)) {
return null; // Null ASTs should never happen
}
if (((ShadowExp)thisAST).getTypeId() != ((ShadowExp)thatAST).getTypeId()) {
return Boolean.FALSE; // Distinct typeids are necessarily not equal
}
List<CGShadowPart> theseParts = thisValue.getParts();
List<CGShadowPart> thoseParts = thatValue.getParts();
int iSize = theseParts.size();
if (iSize != thoseParts.size()) {
return Boolean.FALSE; // Distinct part lists are necessarily not equal
}
for (int i = 0; i < iSize; i++) {
CGShadowPart thisPart = theseParts.get(i);
CGShadowPart thatPart = thoseParts.get(i);
if ((thisPart == null) || (thatPart == null)) {
return null; // Null parts should never happen
}
Boolean equivalence = thisPart.isEquivalentTo(thatPart);
if (equivalence != Boolean.TRUE) {
return equivalence; // Distinct parts are necessarily not equal
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGShadowPart thisValue, @NonNull CGShadowPart thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof ShadowPart) || !(thatAST instanceof ShadowPart)) {
return null; // Null ASTs should never happen
}
if (((ShadowPart)thisAST).getTypeId() != ((ShadowPart)thatAST).getTypeId()) {
return Boolean.FALSE; // Distinct typeids are necessarily not equal
}
CGValuedElement thisPartInit = thisValue.getInit();
CGValuedElement thatPartInit = thatValue.getInit();
if ((thisPartInit == null) || (thatPartInit == null)) {
return null; // Null inits should never happen
}
return thisPartInit.isEquivalentTo(thatPartInit);
}
public static @Nullable Boolean isEquivalent(@NonNull CGTupleExp thisValue, @NonNull CGTupleExp thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof TupleLiteralExp) || !(thatAST instanceof TupleLiteralExp)) {
return null; // Null ASTs should never happen
}
if (((TupleLiteralExp)thisAST).getTypeId() != ((TupleLiteralExp)thatAST).getTypeId()) {
return Boolean.FALSE; // Distinct typeids are necessarily not equal
}
List<CGTuplePart> theseParts = thisValue.getParts();
List<CGTuplePart> thoseParts = thatValue.getParts();
int iSize = theseParts.size();
if (iSize != thoseParts.size()) {
return Boolean.FALSE; // Distinct part lists are necessarily not equal
}
for (int i = 0; i < iSize; i++) {
CGTuplePart thisPart = theseParts.get(i);
CGTuplePart thatPart = thoseParts.get(i);
if ((thisPart == null) || (thatPart == null)) {
return null; // Null parts should never happen
}
Boolean equivalence = thisPart.isEquivalentTo(thatPart);
if (equivalence != Boolean.TRUE) {
return equivalence; // Distinct parts are necessarily not equal
}
}
return Boolean.TRUE;
}
public static @Nullable Boolean isEquivalent(@NonNull CGTuplePart thisValue, @NonNull CGTuplePart thatValue) {
if (thisValue == thatValue) {
return Boolean.TRUE;
}
Element thisAST = thisValue.getAst();
Element thatAST = thatValue.getAst();
if (!(thisAST instanceof TupleLiteralPart) || !(thatAST instanceof TupleLiteralPart)) {
return null; // Null ASTs should never happen
}
if (((TupleLiteralPart)thisAST).getTypeId() != ((TupleLiteralPart)thatAST).getTypeId()) {
return Boolean.FALSE; // Distinct typeids are necessarily not equal
}
CGValuedElement thisPartInit = thisValue.getInit();
CGValuedElement thatPartInit = thatValue.getInit();
if ((thisPartInit == null) || (thatPartInit == null)) {
return null; // Null inits should never happen
}
return thisPartInit.isEquivalentTo(thatPartInit);
}
}