/*******************************************************************************
* Copyright © 2011, 2013 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.mof.egl.utils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.edt.mof.EClass;
import org.eclipse.edt.mof.EObject;
import org.eclipse.edt.mof.MofSerializable;
import org.eclipse.edt.mof.egl.AccessKind;
import org.eclipse.edt.mof.egl.Annotation;
import org.eclipse.edt.mof.egl.AnnotationType;
import org.eclipse.edt.mof.egl.Classifier;
import org.eclipse.edt.mof.egl.Constructor;
import org.eclipse.edt.mof.egl.DataItem;
import org.eclipse.edt.mof.egl.Delegate;
import org.eclipse.edt.mof.egl.EGLClass;
import org.eclipse.edt.mof.egl.Element;
import org.eclipse.edt.mof.egl.ElementKind;
import org.eclipse.edt.mof.egl.Field;
import org.eclipse.edt.mof.egl.FixedPrecisionType;
import org.eclipse.edt.mof.egl.Function;
import org.eclipse.edt.mof.egl.FunctionMember;
import org.eclipse.edt.mof.egl.FunctionParameter;
import org.eclipse.edt.mof.egl.Member;
import org.eclipse.edt.mof.egl.MemberAccess;
import org.eclipse.edt.mof.egl.MemberName;
import org.eclipse.edt.mof.egl.MofConversion;
import org.eclipse.edt.mof.egl.NamedElement;
import org.eclipse.edt.mof.egl.Operation;
import org.eclipse.edt.mof.egl.ParameterizableType;
import org.eclipse.edt.mof.egl.Part;
import org.eclipse.edt.mof.egl.PartName;
import org.eclipse.edt.mof.egl.Program;
import org.eclipse.edt.mof.egl.SequenceType;
import org.eclipse.edt.mof.egl.Stereotype;
import org.eclipse.edt.mof.egl.StereotypeType;
import org.eclipse.edt.mof.egl.StructPart;
import org.eclipse.edt.mof.egl.StructuredField;
import org.eclipse.edt.mof.egl.SubType;
import org.eclipse.edt.mof.egl.Type;
import org.eclipse.edt.mof.egl.lookup.PartEnvironment;
import org.eclipse.edt.mof.impl.DynamicEObject;
import org.eclipse.edt.mof.serialization.DeserializationException;
import org.eclipse.edt.mof.serialization.Environment;
import org.eclipse.edt.mof.serialization.MofObjectNotFoundException;
public class TypeUtils implements MofConversion {
public static final Type Type_NULLTYPE = getType(Type_EGLNullType);
public static final Type Type_ANY = getType(Type_EGLAny);
public static final Type Type_CHAR = getType(Type_EGLChar);
public static final Type Type_MBCHAR = getType(Type_EGLMBChar);
public static final Type Type_DBCHAR = getType(Type_EGLDBChar);
public static final Type Type_STRING = getType(Type_EGLString);
public static final Type Type_UNICODE = getType(Type_EGLUnicode);
public static final Type Type_HEX = getType(Type_EGLHex);
public static final Type Type_SMALLINT = getType(Type_EGLSmallint);
public static final Type Type_INT = getType(Type_EGLInt);
public static final Type Type_BIGINT = getType(Type_EGLBigint);
public static final Type Type_DECIMAL = getType(Type_EGLDecimal);
public static final Type Type_MONEY = getType(Type_EGLDecimal);
public static final Type Type_PACF = getType(Type_EGLPacf);
public static final Type Type_UNICODENUM = getType(Type_EGLUnicodeNum);
public static final Type Type_NUM = getType(Type_EGLNum);
public static final Type Type_NUMC = getType(Type_EGLNumc);
public static final Type Type_FLOAT = getType(Type_EGLFloat);
public static final Type Type_SMALLFLOAT = getType(Type_EGLSmallfloat);
public static final Type Type_BIN = getType(Type_EGLBin);
public static final Type Type_UBIN = getType(Type_EGLUBin);
public static final Type Type_DATE = getType(Type_EGLDate);
public static final Type Type_TIME = getType(Type_EGLTime);
public static final Type Type_TIMESTAMP = getType(Type_EGLTimestamp);
public static final Type Type_MONTHSPANINTERVAL = getType(Type_EGLMonthInterval);
public static final Type Type_SECONDSPANINTERAL = getType(Type_EGLSecondsInterval);
public static final Type Type_LIST = getType(Type_EGLList);
public static final Type Type_DICTIONARY = getType(Type_EGLDictionary);
public static final Type Type_ARRAYDICTIONARY = getType(Type_EGLArrayDictionary);
public static final Type Type_CLOB = getType(Type_EGLClob);
public static final Type Type_BLOB = getType(Type_EGLBlob);
public static final Type Type_BOOLEAN = getType(Type_EGLBoolean);
public static final Type Type_BYTES = getType(Type_EGLBytes);
public static final Type Type_NUMBER = getType(Type_EGLNumber);
public static final Type Type_ANYTEXT = getType(Type_AnyText);
public static final Type Type_ANYVALUE = getType(Type_AnyValue);
public static final int TypeKind_UNDEFINED = -1;
public static final int TypeKind_VOID = 0;
public static final int TypeKind_ANY = 1;
public static final int TypeKind_CHAR = 2;
public static final int TypeKind_MBCHAR = 3;
public static final int TypeKind_DBCHAR = 4;
public static final int TypeKind_STRING = 5;
public static final int TypeKind_UNICODE = 6;
public static final int TypeKind_SMALLINT = 7;
public static final int TypeKind_INT = 8;
public static final int TypeKind_BIGINT = 9;
public static final int TypeKind_DECIMAL = 10;
public static final int TypeKind_MONEY = 11;
public static final int TypeKind_PACF = 12;
public static final int TypeKind_NUM = 13;
public static final int TypeKind_NUMC = 14;
public static final int TypeKind_BIN = 15;
public static final int TypeKind_FLOAT = 16;
public static final int TypeKind_SMALLFLOAT = 17;
public static final int TypeKind_DATE = 18;
public static final int TypeKind_TIME = 19;
public static final int TypeKind_TIMESTAMP = 20;
public static final int TypeKind_MONTHSPANINTERVAL = 21;
public static final int TypeKind_SECONDSPANINTERVAL = 22;
public static final int TypeKind_LIST = 23;
public static final int TypeKind_DICTIONARY = 24;
public static final int TypeKind_ARRAYDICTIONARY = 25;
public static final int TypeKind_LIMITEDSTRING = 26;
public static final int TypeKind_BOOLEAN = 27;
public static final int TypeKind_BLOB = 28;
public static final int TypeKind_CLOB = 29;
public static final int TypeKind_NUMBER = 30;
public static final int TypeKind_HEX = 31;
public static final int TypeKind_REFLECTTYPE = 32;
public static final int TypeKind_ARRAY = 33;
public static final int TypeKind_UBIN = 34;
public static final int TypeKind_UNICODENUM = 35;
public static final int TypeKind_NULLTYPE = 36;
public static final int TypeKind_BYTES = 37;
public static Type getType(String signature) {
try {
return (Type)new PartEnvironment().find(signature);
} catch (MofObjectNotFoundException e) {
return null;
} catch (DeserializationException e) {
return null;
}
}
/**
* Use this method only to retrieve types that are guaranteed to be there.
* Typically used within Expressions to return referenced types where the
* context where the expression exists has already resolved all type references
* @param typeSignature
* @return
*/
public static Type getEGLType(String typeSignature) {
String mofKey = Type.EGL_KeyScheme + Type.KeySchemeDelimiter + typeSignature.toUpperCase().toLowerCase();
return getType(mofKey);
}
public static int getTypeKind(Type type) {
Classifier classifier = type.getClassifier();
type = type.getClassifier();
if (type == null) return TypeKind_VOID;
else if (classifier == Type_NULLTYPE) return TypeKind_NULLTYPE;
else if (classifier == Type_ANY) return TypeKind_ANY;
else if (classifier == Type_BOOLEAN) return TypeKind_BOOLEAN;
else if (classifier == Type_CHAR) return TypeKind_CHAR;
else if (classifier == Type_MBCHAR) return TypeKind_MBCHAR;
else if (classifier == Type_DBCHAR) return TypeKind_DBCHAR;
else if (classifier == Type_STRING)
return type instanceof SequenceType ? TypeKind_LIMITEDSTRING : TypeKind_STRING;
else if (classifier == Type_UNICODE) return TypeKind_UNICODE;
else if (classifier == Type_HEX) return TypeKind_HEX;
else if (classifier == Type_SMALLFLOAT) return TypeKind_SMALLFLOAT;
else if (classifier == Type_FLOAT) return TypeKind_FLOAT;
else if (classifier == Type_SMALLINT) return TypeKind_SMALLINT;
else if (classifier == Type_INT) return TypeKind_INT;
else if (classifier == Type_BIGINT) return TypeKind_BIGINT;
else if (classifier == Type_DECIMAL) return TypeKind_DECIMAL;
else if (classifier == Type_MONEY) return TypeKind_MONEY;
else if (classifier == Type_PACF) return TypeKind_PACF;
else if (classifier == Type_NUM) return TypeKind_NUM;
else if (classifier == Type_UNICODENUM) return TypeKind_UNICODENUM;
else if (classifier == Type_NUMC) return TypeKind_NUMC;
else if (classifier == Type_BIN) return TypeKind_BIN;
else if (classifier == Type_UBIN) return TypeKind_UBIN;
else if (classifier == Type_DATE) return TypeKind_DATE;
else if (classifier == Type_TIME) return TypeKind_TIME;
else if (classifier == Type_TIMESTAMP) return TypeKind_TIMESTAMP;
else if (classifier == Type_MONTHSPANINTERVAL) return TypeKind_MONTHSPANINTERVAL;
else if (classifier == Type_SECONDSPANINTERAL) return TypeKind_SECONDSPANINTERVAL;
else if (classifier == Type_LIST) return TypeKind_ARRAY;
else if (classifier == Type_DICTIONARY) return TypeKind_DICTIONARY;
else if (classifier == Type_ARRAYDICTIONARY) return TypeKind_ARRAYDICTIONARY;
else if (classifier == Type_CLOB) return TypeKind_CLOB;
else if (classifier == Type_BLOB) return TypeKind_BLOB;
else if (classifier == Type_BYTES) return TypeKind_BYTES;
else if (classifier == Type_NUMBER) return TypeKind_NUMBER;
return TypeKind_UNDEFINED;
}
public static Type getRootType(Type type) {
// this will accept either a Type or ArrayType, and give the base level type for either. if the array is multi-dimension, it will recurse down until it gets the base type
return type;
}
public static boolean isReferenceType(Type type) {
return !isValueType(type);
}
public static boolean isValueType(Type type) {
if (type.getClassifier() instanceof EGLClass) {
String key = ((EGLClass)type.getClassifier()).getMofSerializationKey();
if (key.equalsIgnoreCase(Type_EGLNumber)) {
return false;
}
if (key.equalsIgnoreCase(Type_EGLDecimal) && type instanceof ParameterizableType) {
return false;
}
if (key.equalsIgnoreCase(Type_EGLTimestamp) && type instanceof ParameterizableType) {
return false;
}
if (key.equalsIgnoreCase(Type_EGLString) && type instanceof ParameterizableType) {
return false;
}
if (key.equalsIgnoreCase(Type_EGLBytes) && type instanceof ParameterizableType) {
return false;
}
return ((EGLClass)type.getClassifier()).isSubtypeOf((EGLClass)Type_ANYVALUE);
}
else {
return false;
}
}
public static boolean isNumericType(Type type) {
if (type.getClassifier() instanceof EGLClass) {
return ((EGLClass)type.getClassifier()).isSubtypeOf((EGLClass)Type_NUMBER);
}
else {
return false;
}
}
public static boolean isNumericTypeWithNoDecimals(Type type) {
if (!isNumericType(type)) {
return false;
}
if (TypeUtils.Type_INT.equals(type) || TypeUtils.Type_SMALLINT.equals(type) || TypeUtils.Type_BIGINT.equals(type)) {
return true;
}
if (type instanceof FixedPrecisionType) {
return ((FixedPrecisionType)type).getDecimals() == null || ((FixedPrecisionType)type).getDecimals().intValue() == 0;
}
return false;
}
public static boolean isTextType(Type type) {
if (type != null && type.getClassifier() instanceof EGLClass) {
return ((EGLClass)type.getClassifier()).isSubtypeOf((EGLClass)Type_ANYTEXT);
}
else {
return false;
}
}
public static boolean isDynamicType(Type type) {
try {
return (type != null && type.getClassifier() != null && type.getClassifier().getAnnotation("egl.lang.reflect.Dynamic") != null);
} catch (Exception e) {
return false;
}
}
public static boolean isStaticType(Type type) {
// For MOF definitions
try {
EClass staticType = (EClass)Environment.getCurrentEnv().find("org.eclipse.edt.mof.egl.StaticType");
return type != null && type.getEClass() != null && type.getEClass().isSubClassOf(staticType);
} catch (MofObjectNotFoundException e) {
} catch (DeserializationException e) {
}
return false;
}
public static boolean isSubtypeOf(Classifier subtype, EGLClass superType) {
return subtype instanceof EGLClass
? ((EGLClass)subtype).isSubtypeOf(superType)
: false;
}
/**
* Returns true if the passed in type is either the type matching the target mof key, or is a subtype
* of that type. Essentially this is like doing "type instanceof target" in Java.
*/
public static boolean isTypeOrSubtypeOf(Type type, String targetTypeMofKey) {
if (type != null && type.getClassifier() instanceof EGLClass) {
EGLClass ds = (EGLClass)getType(targetTypeMofKey);
return ds != null && type.getClassifier().equals(ds) || ((EGLClass)type.getClassifier()).isSubtypeOf(ds);
}
return false;
}
/**
* Compatibility is defined between StructPart classifiers as either having
* explicit conversion operations defined between them or the rhsType being
* a subtype of the lhsType or the lhsType being a subtype of the rhsType
*
* For a function to be compatible with a delegate, the parameters and return type must match exactly.
* The same is true for comparing two delegates.
*
* @param lhsType
* @param rhsType
* @return
*/
public static boolean areCompatible(Classifier lhsType, NamedElement rhsType) {
if (lhsType.equals(rhsType)) return true;
if (lhsType instanceof Delegate && rhsType instanceof Delegate) {
return areCompatible((Delegate)lhsType, (Delegate)rhsType, new HashSet<DelegateSet>());
}
if (lhsType instanceof StructPart && rhsType instanceof SubType) {
if (((SubType)rhsType).isSubtypeOf((StructPart)lhsType)) {
return true;
}
}
if (lhsType instanceof SubType && rhsType instanceof StructPart) {
if (((SubType)lhsType).isSubtypeOf((StructPart)rhsType)) {
return true;
}
}
if (lhsType instanceof StructPart && rhsType instanceof StructPart) {
return IRUtils.getConversionOperation((StructPart)rhsType, (StructPart)lhsType) != null;
}
if (lhsType instanceof Delegate && rhsType instanceof Function) {
return areCompatible((Delegate) lhsType, (Function)rhsType);
}
else {
return false;
}
}
/**
* Compatibility is defined between a delegate and a function as both having the same parameter types and return types
*
* @param lhsType
* @param rhsType
* @return
*/
public static boolean areCompatible(Delegate lhsType, Function rhsType) {
if (lhsType.getParameters().size() != rhsType.getParameters().size()) {
return false;
}
if (lhsType.getReturnType() == null) {
if (rhsType.getReturnType() != null) {
return false;
}
}
else {
Type lhsReturnType = lhsType.getReturnType();
Type rhsReturnType = rhsType.getReturnType();
if (lhsReturnType instanceof Delegate && rhsReturnType instanceof Delegate) {
if (!areCompatible((Delegate)lhsReturnType, (Delegate)rhsReturnType, new HashSet<DelegateSet>())) {
return false;
}
}
else if (!lhsType.getReturnType().equals(rhsType.getReturnType())) {
return false;
}
}
if (rhsType.getReturnField() != null && rhsType.getReturnField().isNullable() != lhsType.isNullable().booleanValue()) {
return false;
}
for (int i = 0; i < lhsType.getParameters().size(); i++) {
FunctionParameter lhsParm = lhsType.getParameters().get(i);
FunctionParameter rhsParm = rhsType.getParameters().get(i);
if (lhsParm.isNullable() != rhsParm.isNullable()) {
return false;
}
if (lhsParm.getParameterKind() != rhsParm.getParameterKind()) {
return false;
}
if (lhsParm.isConst().booleanValue() != rhsParm.isConst().booleanValue()) {
return false;
}
if (lhsParm.isField().booleanValue() != rhsParm.isField().booleanValue()) {
return false;
}
if (lhsParm.isDefinedSqlNullable().booleanValue() != rhsParm.isDefinedSqlNullable().booleanValue()) {
return false;
}
Type lhsParmType = lhsParm.getType();
Type rhsParmType = rhsParm.getType();
if (lhsParmType instanceof Delegate && rhsParmType instanceof Delegate) {
if (!areCompatible((Delegate)lhsParmType, (Delegate)rhsParmType, new HashSet<DelegateSet>())) {
return false;
}
}
else if (!lhsParmType.equals(rhsParmType)) {
return false;
}
}
return true;
}
public static boolean areCompatible(Delegate lhsType, Delegate rhsType, Set<DelegateSet> seenComparisons) {
DelegateSet set = new DelegateSet(lhsType, rhsType);
if (seenComparisons.contains(set)) {
return true;
}
seenComparisons.add(set);
if (lhsType.getParameters().size() != rhsType.getParameters().size()) {
return false;
}
if (lhsType.getReturnType() == null) {
if (rhsType.getReturnType() != null) {
return false;
}
}
else {
Type lhsReturnType = lhsType.getReturnType();
Type rhsReturnType = rhsType.getReturnType();
if (lhsReturnType instanceof Delegate && rhsReturnType instanceof Delegate) {
if (!areCompatible((Delegate)lhsReturnType, (Delegate)rhsReturnType, seenComparisons)) {
return false;
}
}
else if (!lhsType.getReturnType().equals(rhsType.getReturnType())) {
return false;
}
}
if (rhsType.isNullable().booleanValue() != lhsType.isNullable().booleanValue()) {
return false;
}
for (int i = 0; i < lhsType.getParameters().size(); i++) {
FunctionParameter lhsParm = lhsType.getParameters().get(i);
FunctionParameter rhsParm = rhsType.getParameters().get(i);
if (lhsParm.isNullable() != rhsParm.isNullable()) {
return false;
}
if (lhsParm.getParameterKind() != rhsParm.getParameterKind()) {
return false;
}
if (lhsParm.isConst().booleanValue() != rhsParm.isConst().booleanValue()) {
return false;
}
if (lhsParm.isField().booleanValue() != rhsParm.isField().booleanValue()) {
return false;
}
if (lhsParm.isDefinedSqlNullable().booleanValue() != rhsParm.isDefinedSqlNullable().booleanValue()) {
return false;
}
Type lhsParmType = lhsParm.getType();
Type rhsParmType = rhsParm.getType();
if (lhsParmType instanceof Delegate && rhsParmType instanceof Delegate) {
if (!areCompatible((Delegate)lhsParmType, (Delegate)rhsParmType, seenComparisons)) {
return false;
}
}
else if (!lhsParmType.equals(rhsParmType)) {
return false;
}
}
return true;
}
private static class DelegateSet {
private final Delegate del1;
private final Delegate del2;
DelegateSet(Delegate del1, Delegate del2) {
this.del1 = del1;
this.del2 = del2;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof DelegateSet) {
DelegateSet other = (DelegateSet)o;
if (this.del1.equals(other.del1)) {
return this.del2.equals(other.del2);
}
else if (this.del1.equals(other.del2)) {
return this.del2.equals(other.del1);
}
}
return false;
}
@Override
public int hashCode() {
return del1.hashCode() * del2.hashCode();
}
}
/**
* This method is used to tell if two different versions of the same type is equivalent
* from the point of view of any clients that are using the given type. Any differences that
* cause the two versions to be not equivalent will signify that any dependents that use
* the given type must be updated.
*
* @param p1
* @param p2
* @return
*/
public static boolean areStructurallyEquivalent(MofSerializable p1, MofSerializable p2) {
if (p1 == p2) {
return true;
}
if (p1 == null || p2 == null) return false;
if (!((EObject)p1).getEClass().equals(((EObject)p2).getEClass()))
return false;
if (!p1.getMofSerializationKey().equalsIgnoreCase(p2.getMofSerializationKey()))
return false;
if (p1 instanceof Part) {
Stereotype s1 = ((Part) p1).getStereotype();
Stereotype s2 = ((Part) p2).getStereotype();
if (s1 == null && s2 != null)
return false;
if (s1 != null && s2 == null)
return false;
if (s1 != null && s2 != null)
if (!s1.getEClass().equals(s2.getEClass()))
return false;
if (((Part)p1).hasCompileErrors() != ((Part)p2).hasCompileErrors()) {
return false;
}
if (!elemAnnotationsAreStructurallyEquivalent((Part)p1, (Part)p2)) {
return false;
}
}
if (p1 instanceof Program) {
// Only need to check the CALL signature is the same
Program prog1 = (Program)p1;
Program prog2 = (Program)p2;
if (prog1.getParameters().size() != prog2.getParameters().size())
return false;
for (int i=0; i<prog1.getParameters().size(); i++) {
Type t1 = prog1.getParameters().get(i).getType();
Type t2 = prog2.getParameters().get(i).getType();
if (!t1.equals(t2)) {
return false;
}
}
return true;
}
if (p1 instanceof DataItem) {
return ((DataItem)p1).getBaseType().equals(((DataItem)p2).getBaseType());
}
if (p1 instanceof Delegate) {
Delegate d1 = (Delegate)p1;
Delegate d2 = (Delegate)p2;
if (d1.getParameters().size() != d2.getParameters().size()) {
return false;
}
for (int j=0; j<d1.getParameters().size(); j++) {
FunctionParameter parm1 = d1.getParameters().get(j);
FunctionParameter parm2 = d2.getParameters().get(j);
if (!parm1.getType().equals(parm2.getType()))
return false;
if (!parm1.getParameterKind().equals(parm2.getParameterKind()))
return false;
if (parm1.isNullable() != parm2.isNullable())
return false;
}
Type rt1 = d1.getReturnType();
if (rt1 == null) {
return d2.getReturnType() == null;
}
if (!rt1.equals(d2.getReturnType())) {
return false;
}
return d1.isNullable() == d2.isNullable();
}
if (p1 instanceof EGLClass && p2 instanceof EGLClass) {
EGLClass s1 = (EGLClass)p1;
EGLClass s2 = (EGLClass)p2;
if (s1.getSuperTypes().size() == s2.getSuperTypes().size()) {
for (int i=0; i<s1.getSuperTypes().size(); i++) {
if (!s1.getSuperTypes().get(i).equals(s1.getSuperTypes().get(i)))
return false;
}
}
if (s1.getStructuredFields().size() != s2.getStructuredFields().size()) {
return false;
}
else {
// Order matters for value type structures so they must match by index
for (int i=0; i<s1.getStructuredFields().size(); i++) {
StructuredField f1 = s1.getStructuredFields().get(i);
StructuredField f2 = s2.getStructuredFields().get(i);
if (!f1.getName().equalsIgnoreCase(f2.getName()))
return false;
if (!f1.getType().equals(f2.getType()))
return false;
if (!(f1.getOccurs() != f2.getOccurs()))
return false;
if (f1.getParent() == null && f2.getParent() != null)
return false;
if (f1.getParent() != null && f2.getParent() == null)
return false;
if (!f1.getParent().getName().equalsIgnoreCase(f2.getParent().getName()))
return false;
}
}
List<Field> flds1 = collectPublicMembers(s1.getFields());
List<Field> flds2 = collectPublicMembers(s2.getFields());
if (flds1.size() != flds2.size()) {
return false;
}
else {
for (int i = 0; i < flds1.size(); i++) {
Field f1 = flds1.get(i);
Field f2 = null;
if (isValueType(s1)) {
if (flds2.size() < i) {
return false;
}
f2 = flds2.get(i);
if (!f1.getName().equalsIgnoreCase(f2.getName()))
return false;
}
else {
f2 = s2.getField(f1.getName());
}
if (f2 != null && f1.getType().equals(f2.getType()) && f1.isNullable() == f2.isNullable()) {
}
else {
return false;
}
if (!elemAnnotationsAreStructurallyEquivalent(f1, f2)) {
return false;
}
}
}
if (!areStructurallyEquivalentFunctionMembers(s1.getConstructors(), s2.getConstructors()))
return false;
if (!areStructurallyEquivalentFunctionMembers(s1.getFunctions(), s2.getFunctions()))
return false;
if (!areStructurallyEquivalentFunctionMembers(s1.getOperations(), s2.getOperations()))
return false;
}
if (p1 instanceof AnnotationType) {
AnnotationType t1 = (AnnotationType)p1;
AnnotationType t2 = (AnnotationType)p2;
if (t1.getTargets().size() != t2.getTargets().size())
return false;
for (ElementKind e1 : t1.getTargets()) {
if (!t2.getTargets().contains(e1))
return false;
}
}
if (p1 instanceof StereotypeType) {
StereotypeType t1 = (StereotypeType)p1;
StereotypeType t2 = (StereotypeType)p2;
MofSerializable s1 = t1.getDefaultSuperType();
MofSerializable s2 = t2.getDefaultSuperType();
if ((s1 == null && s2 != null) || (s1 != null && s2 == null))
return false;
if (s1 != null && !s1.equals(s2))
return false;
if (t1.getMemberAnnotations().size() != t2.getMemberAnnotations().size())
return false;
for (Object type : t1.getMemberAnnotations()) {
if (!t2.getMemberAnnotations().contains(type))
return false;
}
if (t1.getPartType() == null && t2.getPartType() != null) {
return false;
}
if (t1.getPartType() != null && t2.getPartType() == null) {
return false;
}
if (t1.getPartType() != null && !t1.getPartType().equals(t2.getPartType()))
return false;
}
return true;
}
private static boolean annotationsAreStructurallyEquivalent(Annotation ann1, Annotation ann2) {
if (ann1 == null || ann2 == null) {
return (ann1 == null && ann2 == null);
}
if (ann1 instanceof DynamicEObject) {
return ann2 instanceof DynamicEObject;
}
if (ann1.getEClass().getEFields().size() != ann2.getEClass().getEFields().size()) {
return false;
}
for (int i = 0; i < ann1.getEClass().getEFields().size(); i++) {
Object val1 = ann1.eGet(ann1.getEClass().getEFields().get(i));
Object val2 = ann2.eGet(ann2.getEClass().getEFields().get(i));
if (!objectsAreStructurallyEquivalent(val1, val2)) {
return false;
}
}
return true;
}
private static boolean elemAnnotationsAreStructurallyEquivalent(Element elem1, Element elem2) {
if (elem1 == null || elem2 == null) {
return (elem1 == null && elem2 == null);
}
if (elem1.getAnnotations().size() != elem2.getAnnotations().size()) {
return false;
}
for (Annotation ann1 : elem1.getAnnotations()) {
Annotation ann2 = elem2.getAnnotation(ann1.getEClass().getETypeSignature());
if (!annotationsAreStructurallyEquivalent(ann1, ann2)) {
return false;
}
}
return true;
}
private static boolean objectsAreStructurallyEquivalent(Object obj1, Object obj2) {
if (obj1 == null || obj2 == null) {
return (obj1 == null && obj2 == null);
}
if (obj1 == obj2) {
return true;
}
if (obj1 instanceof Object[]) {
if (obj2 instanceof Object[]) {
Object[] arr1 = (Object[]) obj1;
Object[] arr2 = (Object[]) obj2;
if (arr1.length != arr2.length) {
return false;
}
for(int i = 0; i < arr1.length; i++) {
if (!objectsAreStructurallyEquivalent(arr1[i], arr2[i])) {
return false;
}
}
return true;
}
else {
return false;
}
}
if (obj1 instanceof List) {
if (obj2 instanceof List) {
List<Object> list1 = (List<Object>) obj1;
List<Object> list2 = (List<Object>) obj2;
if (list1.size() != list2.size()) {
return false;
}
for(int i = 0; i < list1.size(); i++) {
if (!objectsAreStructurallyEquivalent(list1.get(i), list2.get(i))) {
return false;
}
}
return true;
}
else {
return false;
}
}
if (obj1.getClass() != obj2.getClass()) {
return false;
}
if (obj1 instanceof Member) {
Member mbr1 = (Member) obj1;
Member mbr2 = (Member) obj2;
return (mbr1.getId().equalsIgnoreCase(mbr2.getId()) && objectsAreStructurallyEquivalent(mbr1.getContainer(), mbr2.getContainer()));
}
if (obj1 instanceof Part) {
Part part1 = (Part) obj1;
Part part2 = (Part) obj2;
return part1.getFullyQualifiedName().equals(part2.getFullyQualifiedName());
}
if (obj1 instanceof Annotation) {
return annotationsAreStructurallyEquivalent((Annotation) obj1, (Annotation) obj2);
}
if (obj1 instanceof MemberName) {
MemberName mn1 = (MemberName) obj1;
MemberName mn2 = (MemberName) obj2;
return (mn1.getId().equalsIgnoreCase(mn2.getId()));
}
if (obj1 instanceof MemberAccess) {
MemberAccess ma1 = (MemberAccess) obj1;
MemberAccess ma2 = (MemberAccess) obj2;
return (ma1.getId().equalsIgnoreCase(ma2.getId()) && objectsAreStructurallyEquivalent(ma1.getQualifier(), ma2.getQualifier()));
}
if (obj1 instanceof PartName) {
PartName pn1 = (PartName)obj1;
PartName pn2 = (PartName)obj2;
return (pn1.getFullyQualifiedName().equals(pn2.getFullyQualifiedName()));
}
if (obj1 instanceof EObject) {
if (obj2 instanceof EObject) {
//TODO add more checking here
return true;
}
else {
return false;
}
}
return obj1.equals(obj2);
}
private static <T extends FunctionMember> boolean areStructurallyEquivalentFunctionMembers(List<T> mbrs1, List<T> mbrs2) {
List<T> funcs1 = collectPublicMembers(mbrs1);
List<T> funcs2 = collectPublicMembers(mbrs2);
if (funcs1.size() != funcs2.size()) {
return false;
}
else {
for (int i=0; i<funcs1.size(); i++) {
T f1 = funcs1.get(i);
T f2 = null;
forLoop: for (T func : funcs2) {
if ((func instanceof Constructor || f1.getName().equalsIgnoreCase(func.getName())) && f1.getParameters().size() == func.getParameters().size()) {
for (int j=0; j<f1.getParameters().size(); j++) {
FunctionParameter parm1 = f1.getParameters().get(j);
FunctionParameter parm2 = func.getParameters().get(j);
if ((parm1.getType() == null && parm2.getType() != null) || (parm1.getType() != null && parm2.getType() == null))
continue forLoop;
if ((parm1.getType() != null) && !parm1.getType().equals(parm2.getType()))
continue forLoop;
if ((parm1.getParameterKind() == null && parm2.getParameterKind() != null) || (parm1.getParameterKind() != null && parm2.getParameterKind() == null))
continue forLoop;
if ((parm1.getParameterKind() != null) && !parm1.getParameterKind().equals(parm2.getParameterKind()))
continue forLoop;
if (parm1.isNullable() != parm2.isNullable())
continue forLoop;
}
if ((f1.getType() == null && func.getType() != null) || (f1.getType() != null && func.getType() == null))
break forLoop;
if ((f1.getType() != null) && !f1.getType().equals(func.getType())) {
break forLoop;
}
if (f1.isNullable() != func.isNullable()) {
break forLoop;
}
f2 = func;
break forLoop;
}
}
if (f2 == null || f1.getAccessKind() != f2.getAccessKind() || f1.isStatic() != f2.isStatic())
return false;
if (!elemAnnotationsAreStructurallyEquivalent(f1, f2)) {
return false;
}
}
}
return true;
}
/**
* Tests which type between the two passed in is the least wide.
* This is determined by which of the two types has a widening conversion
* between the two types.
* @param type1
* @param type2
* @return integer representing which of the types is the least compatible
* value 0: neither is least
* value -1: first parameter is least
* value 1: second parameter is least
*/
private static int getLeastWideType(StructPart type1, StructPart type2) {
if (type1.equals(type2)) return 0;
if (isReferenceType(type1) && type1.isSubtypeOf(type2)) {
return -1;
}
if (isReferenceType(type2) && type2.isSubtypeOf(type1)) {
return 1;
}
if (getBestFitWidenConversionOp(type1, type2) != null) return -1;
if (getBestFitWidenConversionOp(type2, type1) != null) return 1;
return 0; // neither is least wide
}
/**
* Opposite of getLeastWidtType
* @param type1
* @param type2
* @return
*/
private static int getLeastNarrowType(StructPart type1, StructPart type2) {
if (type1.equals(type2)) return 0;
if (isReferenceType(type1) && type1.isSubtypeOf(type2)) {
return 1;
}
if (isReferenceType(type2) && type2.isSubtypeOf(type1)) {
return -1;
}
if (getWidenConversionOp(type1, type2) != null) return 1;
if (getWidenConversionOp(type2, type1) != null) return -1;
return 0; // neither is least wide
}
private static boolean requiresNarrow(NamedElement srcType, Classifier type) {
if (srcType != null && srcType.equals(type)) {
return false;
}
//If we find a widen operation, then this cannot be a narrow
if (srcType instanceof StructPart && type instanceof StructPart) {
if (getBestFitWidenConversionOp((StructPart) srcType, (StructPart)type) != null) {
return false;
}
}
if (srcType instanceof StructPart && type instanceof StructPart) {
return getBestFitNarrowConversionOp((StructPart) srcType, (StructPart)type) != null;
}
return false;
}
public static int getBestFitType(NamedElement srcType, List<Classifier> types) {
List<Classifier> classifierCandidates = new ArrayList<Classifier>();
for (Classifier type : types) {
// a value of null for a type indicates it is a generic type parameter
// that is dependent on the argument being passed in - so ignore it
if (type == null || srcType.equals(type)) classifierCandidates.add(type);
}
if (classifierCandidates.size() == 1) return types.indexOf(classifierCandidates.get(0));
if (classifierCandidates.size() > 1) return -1;
List<StructPart> candidates = new ArrayList<StructPart>();
if (srcType instanceof StructPart) {
for (Classifier type : types) {
if (type instanceof StructPart) {
if (((StructPart)srcType).isSubtypeOf((StructPart)type)) {
candidates.add((StructPart)type);
}
else{
if (getBestFitWidenConversionOp((StructPart)srcType, (StructPart)type) != null) {
candidates.add((StructPart)type);
}
}
}
}
}
if (candidates.size() == 1) return types.indexOf(candidates.get(0));
if (candidates.size() > 1) {
boolean done = false;
start:
while (!done) {
int least = 0;
for (int i=0; i<candidates.size(); i++) {
for (int j=0; j<candidates.size(); j++) {
if (i!=j && candidates.get(i) != null && candidates.get(j) != null) {
least = getLeastWideType(candidates.get(i), candidates.get(j));
if (least == 1) candidates.remove(i);
if (least == -1) candidates.remove(j);
}
if (least != 0) continue start; // start over after removing something
}
}
if (candidates.size() == 1) return types.indexOf(candidates.get(0));
if (candidates.size() > 1) return -1;
}
}
// Now check for narrow conversions
if (candidates.size() == 0 && srcType instanceof StructPart) {
for (Classifier type : types) {
if (type instanceof StructPart) {
if (isReferenceType((StructPart)type) && ((StructPart)type).isSubtypeOf((StructPart)srcType)) {
candidates.add((StructPart)type);
}
else{
if (getBestFitNarrowConversionOp((StructPart)srcType, (StructPart)type) != null) {
candidates.add((StructPart)type);
}
}
}
}
if (candidates.size() == 1) return types.indexOf(candidates.get(0));
if (candidates.size() > 1) {
boolean done = false;
start:
while (!done) {
int least = 0;
for (int i=0; i<candidates.size(); i++) {
for (int j=0; j<candidates.size(); j++) {
if (i!=j && candidates.get(i) != null && candidates.get(j) != null) {
least = getLeastNarrowType(candidates.get(i), candidates.get(j));
if (least == 1) candidates.remove(i);
if (least == -1) candidates.remove(j);
}
if (least != 0) continue start; // start over after removing something
}
}
if (candidates.size() == 1) return types.indexOf(candidates.get(0));
if (candidates.size() > 1) return -1;
}
}
}
return -1;
}
public static Operation getBinaryOperation(StructPart clazz, String opSymbol, boolean searchSuperTypes ) {
for (Operation op : clazz.getOperations()) {
if (op.getOpSymbol().equals(opSymbol) && op.getParameters().size() == 2) {
Type op0Type = op.getParameters().get(0).getType();
Type op1Type = op.getParameters().get(1).getType();
if ((op0Type.equals(clazz) || (op0Type.getClassifier() != null && op0Type.getClassifier().equals(clazz)))
&& (op1Type.equals(clazz) || (op1Type.getClassifier() != null && op1Type.getClassifier().equals(clazz)))) {
return op;
}
}
}
if (searchSuperTypes && !clazz.getSuperTypes().isEmpty()) {
return getBinaryOperation(clazz.getSuperTypes().get(0), opSymbol, searchSuperTypes);
}
return null;
}
public static Operation getWidenConversionOp(StructPart src, StructPart target) {
Operation result = null;
for (Operation op : src.getOperations()) {
if (op.isWidenConversion() && op.getParameters().size() == 1 && op.getParameters().get(0) != null) {
Type parmType = (Type)op.getParameters().get(0).getType();
if ( parmType.equals(src) && op.getType().equals(target) ) {
return op;
}
}
}
if (result == null) {
for (Operation op : target.getOperations()) {
if (op.isWidenConversion() && op.getParameters().size() == 1 && op.getParameters().get(0) != null) {
Type parmType = (Type)op.getParameters().get(0).getType();
if ( parmType.equals(src) && op.getType().equals(target) ) {
return op;
}
}
}
}
return null;
}
public static Operation getNarrowConversionOp(StructPart src, StructPart target) {
Operation result = null;
for (Operation op : src.getOperations()) {
if (op.isNarrowConversion() && op.getParameters().size() == 1 && op.getParameters().get(0) != null) {
Type parmType = (Type)op.getParameters().get(0).getType();
if (parmType.equals(src) && op.getType().equals(target)) {
return op;
}
}
}
if (result == null) {
for (Operation op : target.getOperations()) {
if (op.isNarrowConversion() && op.getParameters().size() == 1 && op.getParameters().get(0) != null) {
Type parmType = (Type)op.getParameters().get(0).getType();
if ( parmType.equals(src) && op.getType().equals(target) ) {
return op;
}
}
}
}
return null;
}
public static Operation getBestFitWidenConversionOp(StructPart src, StructPart target) {
Operation op = getBestFitWidenConversionOpSearchSource(src, target);
return op;
}
public static Operation getBestFitWidenConversionOpSearchSource(StructPart src, StructPart target) {
Operation op = getWidenConversionOp(src, target);
if (op == null) {
// Look up the super type chain of source
if (!src.getSuperTypes().isEmpty()) {
StructPart superType = src.getSuperTypes().get(0);
op = getBestFitWidenConversionOpSearchSource(superType, target);
}
}
return op;
}
public static Operation getBestFitNarrowConversionOp(StructPart src, StructPart target) {
Operation op = getBestFitNarrowConversionOpSearchSource(src, target);
return op;
}
public static Operation getBestFitNarrowConversionOpSearchSource(StructPart src, StructPart target) {
Operation op = getNarrowConversionOp(src, target);
if (op == null) {
// Look up the super type chain of source
if (!src.getSuperTypes().isEmpty()) {
StructPart superType = src.getSuperTypes().get(0);
op = getBestFitNarrowConversionOpSearchSource(superType, target);
}
}
return op;
}
public static List<Operation> getBestFitOperation(StructPart container, String opSymbol, NamedElement...argumentTypes) {
List<Operation> ops = new ArrayList<Operation>();
for (Operation op : container.getOperations()) {
if (op.getOpSymbol().equals(opSymbol)) {
if (op.getParameters().size() == argumentTypes.length) {
ops.add(op);
}
}
}
if (ops.size() <= 1) return ops;
return getBestFitFunctionMember(ops, argumentTypes);
}
public static List<Function> getBestFitFunction(StructPart container, String name, NamedElement...argumentTypes) {
List<Function> ops = new ArrayList<Function>();
for (Member mbr : container.getAllMembers()) {
Function op = mbr instanceof Function ? (Function)mbr : null;
if (op != null && op.getName().equalsIgnoreCase(name)) {
if (op.getParameters().size() == argumentTypes.length) {
ops.add(op);
}
}
}
if (ops.size() <= 1) return ops;
return getBestFitFunctionMember(ops, argumentTypes);
}
public static <T extends FunctionMember> List<T> getBestFitFunctionMember(List<T> functionMembers, NamedElement...argumentTypes) {
List<T> candidates = new ArrayList<T>();
// First check for exact parameter type matches
for (T op : functionMembers) {
boolean isCandidate = true;
int i = 0;
for (FunctionParameter parm : op.getParameters()) {
// check for generic type parameter
if (!parm.isGenericTypeParameter()) {
if (!parm.getType().getClassifier().equals(argumentTypes[i])) {
isCandidate = false;
}
if (!isCandidate) break;
}
i++;
}
if (isCandidate) {
candidates.add(op);
return candidates;
}
}
// Then check for compatible types
for (T op : functionMembers) {
boolean isCandidate = true;
int i = 0;
for (FunctionParameter parm : op.getParameters()) {
// check for generic type parameter
if (!parm.isGenericTypeParameter()) {
if (!areCompatible((Classifier)parm.getType().getClassifier(), argumentTypes[i])) {
isCandidate = false;
}
if (!isCandidate) break;
}
i++;
}
if (isCandidate) candidates.add(op);
}
if (candidates.size() <= 1) return candidates;
// Now check for best fit from the remaining list
List<T> result = new ArrayList<T>();
if (candidates.size() > 1) {
int idx;
for (int i=0; i<argumentTypes.length; i++) {
List<Classifier> types = new ArrayList<Classifier>();
for (T op : candidates) {
types.add((Classifier)op.getParameters().get(i).getType().getClassifier());
}
idx = getBestFitType(argumentTypes[i], types);
if (idx != -1) // More than one fits so bail on this parameter
result.add(candidates.get(idx));
}
}
if (result.isEmpty())
result = candidates;
if (result.size() > 1) {
//check for functions that will not require a narrowing
List<T> noNarrow = new ArrayList<T>();
boolean hasNarrow = false;
for (T op : result) {
for (int i=0; (i<argumentTypes.length) && !hasNarrow; i++) {
Classifier type = op.getParameters().get(i).getType().getClassifier();
if (requiresNarrow(argumentTypes[i], type)) {
hasNarrow = true;
}
}
if (!hasNarrow) {
noNarrow.add(op);
}
else {
hasNarrow = false;
}
}
if (!noNarrow.isEmpty()) {
result = noNarrow;
}
}
if (result.size() > 1) {
//eliminate functions from supertypes
//First find the lowest level container that implements the function
StructPart lowestContainer = null;
for (T op : result) {
if (op.getContainer() instanceof StructPart) {
StructPart current = (StructPart)op.getContainer();
if (lowestContainer == null) {
lowestContainer = current;
}
else {
if (!lowestContainer.isSubtypeOf(current)) {
if (current.isSubtypeOf(lowestContainer)) {
lowestContainer = current;
}
}
}
}
}
//Now, only keep functions from the lowest level
if (lowestContainer != null) {
List<T> lowestFuncs = new ArrayList<T>();
for (T op : result) {
if(op.getContainer() == lowestContainer) {
lowestFuncs.add(op);
}
}
result = lowestFuncs;
}
}
return result;
}
public static <T extends Member> List<T> collectPublicMembers(List<T> source) {
List<T> result = new ArrayList<T>();
for (T mbr : source) {
if (mbr.getAccessKind() != AccessKind.ACC_PRIVATE) {
result.add(mbr);
}
}
return result;
}
public static boolean isPrimitive(Type type){
return isNumericType(type) ||
isTextType(type) ||
(type != null && type.getClassifier() instanceof EGLClass &&
(type.getClassifier().equals(Type_DATE) ||
type.getClassifier().equals(Type_TIME) ||
type.getClassifier().equals(Type_TIMESTAMP) ||
type.getClassifier().equals(Type_BOOLEAN) ||
type.getClassifier().equals(Type_BYTES) ||
type.getClassifier().equals(Type_ANY)));
}
}