blob: d5d7e8094a3061bb8f20833a2c918930bb809089 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 Eclipse.org
*
* 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
*******************************************************************************/
package org.eclipse.gmf.internal.xpand;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.gmf.internal.xpand.expression.PolymorphicResolver;
import org.eclipse.gmf.internal.xpand.migration.Activator;
import org.eclipse.gmf.internal.xpand.model.XpandDefinitionWrap;
import org.eclipse.gmf.internal.xpand.model.XpandIterator;
/**
* XXX Guess, will need special support to recognize the fact
* EJavaObject.isSupertypeOf(EObject)
*
* @author artem
*/
@SuppressWarnings("unchecked")
public class BuiltinMetaModel {
public final static String SET = "Set";
public final static String LIST = "List";
protected static EPackage XECORE = EcoreFactory.eINSTANCE.createEPackage();
static {
XECORE.setName("xecore");
XECORE.setNsPrefix("xecore");
XECORE.setNsURI("uri:org.eclipse.modeling/m2t/xpand/xecore/1.0");
}
private static EClass PARAMETERIZED_TYPE = EcoreFactory.eINSTANCE.createEClass();
private static EReference PT_INNER_TYPE_REF = EcoreFactory.eINSTANCE.createEReference();
private static EAttribute PT_INNER_TYPE_ATTR = EcoreFactory.eINSTANCE.createEAttribute();
static {
PARAMETERIZED_TYPE.setName("ParameterizedType");
PARAMETERIZED_TYPE.getESuperTypes().add(EcorePackage.eINSTANCE.getEClass());
PARAMETERIZED_TYPE.setAbstract(true);
PT_INNER_TYPE_REF.setName("innerType");
PT_INNER_TYPE_REF.setContainment(false);
PT_INNER_TYPE_REF.setEType(EcorePackage.eINSTANCE.getEClass());
PARAMETERIZED_TYPE.getEStructuralFeatures().add(PT_INNER_TYPE_REF);
PT_INNER_TYPE_ATTR.setName("innerDataType");
PT_INNER_TYPE_ATTR.setEType(EcorePackage.eINSTANCE.getEDataType());
PARAMETERIZED_TYPE.getEStructuralFeatures().add(PT_INNER_TYPE_ATTR);
XECORE.getEClassifiers().add(PARAMETERIZED_TYPE);
}
/**
* Checks whether classifier is one of user's model extension classes
* (conforming to ParameterizedType from our extended ECore meta-model).
* EClassifier instances available in analyze() methods are param
* candidates.
*
* @param parameterizedTypeM1 -
* e.g. EClass "Order", or XEClass "OrderList"
*/
public static boolean isParameterizedType(EClassifier parameterizedTypeM1) {
return PARAMETERIZED_TYPE.isSuperTypeOf(parameterizedTypeM1.eClass());
}
// XXX revisit invocations, this check is doubled with isParameterizedType, perhaps, can refactor it
public static boolean isCollectionType(EClassifier parameterizedTypeM1) {
// XXX this implementation is not really 'isCollectionType', it's just a copy of what was in the original code
return isAssignableFrom(CollectionTypesSupport.COLLECTION_OF_OBJECT, parameterizedTypeM1);
}
public static EClassifier getInnerType(EClassifier parameterizedTypeM1) {
assert isParameterizedType(parameterizedTypeM1);
if (parameterizedTypeM1.eIsSet(PT_INNER_TYPE_REF)) {
return (EClass) parameterizedTypeM1.eGet(PT_INNER_TYPE_REF);
} else {
return (EDataType) parameterizedTypeM1.eGet(PT_INNER_TYPE_ATTR);
}
}
/**
* NOTE, parameterizedTypeM1 is M1 instance, you can't pass {@link BuiltinMetaModel#COLLECTION_TYPE} (or {@link BuiltinMetaModel#LIST_TYPE}) here,
* because COLLECTION_TYPE just extends PARAMETERIZED_TYPE, but still instance of EClass. We could, however, have COLLECTION_TYPE to be an
* instance of PARAMETERIZED_TYPE, and then we could use this method. The reasons not to do so (at least, now) are
* (a) didn't think it over yet (b) looks like extending M2 (sic!) dynamically, though I don't like even M1 polluting with type
* that happens.
* @param parameterizedTypeM1
* @param innerTypeM1
* @return
*/
public static EClass cloneParametrizedType(EClassifier parameterizedTypeM1, EClassifier innerTypeM1) {
assert isParameterizedType(parameterizedTypeM1);
return collectionTypes.getCollectionType(parameterizedTypeM1.eClass(), innerTypeM1);
}
/*package*/ static EClass internalNewParameterizedType(EClass parameterizedTypeM2, EClassifier inner) {
assert PARAMETERIZED_TYPE.isSuperTypeOf(parameterizedTypeM2);
EObject anInstance = XECORE.getEFactoryInstance().create(parameterizedTypeM2);
assert anInstance instanceof EClass : "EClass is first supertype with instanceClass set";
// e.g. "OrderCollection" or "IntegerList"
((EClass) anInstance).setName(inner.getName() + parameterizedTypeM2.getName());
anInstance.eSet(inner instanceof EClass ? PT_INNER_TYPE_REF : PT_INNER_TYPE_ATTR, inner);
return (EClass) anInstance;
}
public static final EClass VOID = EcoreFactory.eINSTANCE.createEClass();
static {
VOID.setName("void");
XECORE.getEClassifiers().add(VOID);
}
protected static CollectionTypesSupport collectionTypes = new CollectionTypesSupport();
static {
collectionTypes.init(XECORE, PARAMETERIZED_TYPE);
}
/**
* @param name
* @return true if name is one of M2 collection meta-types (either Collection, List, Set)
*/
public static boolean isCollectionMetaType(String name) {
return collectionTypes.isCollectionMetaType(name);
}
public static EClass getCollectionType(String metaTypeName, EClassifier innerType) {
return collectionTypes.getCollectionType(metaTypeName, innerType);
}
// XXX actually, it's odd to use abstract and vague 'collection'
public static EClass getCollectionType(EClassifier innerType) {
return collectionTypes.getCollectionType(innerType);
}
public static EClass getListType(EClassifier innerType) {
return collectionTypes.getListType(innerType);
}
public static EClass getSetType(EClassifier innerType) {
return collectionTypes.getSetType(innerType);
}
public static final EClass DEFINITION_TYPE = EcoreFactory.eINSTANCE.createEClass();
static {
DEFINITION_TYPE.setName("xpand2::Definition");
DEFINITION_TYPE.getESuperTypes().add(EcorePackage.eINSTANCE.getEClass());
XECORE.getEClassifiers().add(DEFINITION_TYPE);
}
public static final EClass ITERATOR_TYPE = EcoreFactory.eINSTANCE.createEClass();
static {
ITERATOR_TYPE.setName("xpand2::Iterator");
ITERATOR_TYPE.getESuperTypes().add(EcorePackage.eINSTANCE.getEClass());
XECORE.getEClassifiers().add(ITERATOR_TYPE);
}
/**
* ECore doesn't support 'return types' for Enums, to my best knowledge,
* they are all integers. original EEnumType returns itself as static
* property's return type BTW, what if we'd like to get string value, name,
* instead
*/
public static EClassifier getReturnType(EEnumLiteral sp) {
return sp.getEEnum();
}
// TODO obj.getClass lookup tree?
public static EClassifier getType(Object obj) {
if (obj == null) {
return VOID;
}
if (obj instanceof Enumerator) {
// unlike original impl, we don't return Enum as type
// mostly because it's just too hard to look for actual enum
// XXX perhaps, EEnumLiteral.getEEnum could help?
return EcorePackage.eINSTANCE.getEEnumerator();
}
if (obj instanceof EObject) {
return ((EObject) obj).eClass();
}
if (obj instanceof Collection) {
EClassifier type = null;
if (!((Collection) obj).isEmpty()) {
// FIXME respect all! elements in the collection, not only the first one
type = getType(((Collection) obj).iterator().next());
}
if (obj instanceof Set) {
return collectionTypes.getSetType(type);
}
if (obj instanceof List) {
return collectionTypes.getListType(type);
}
return collectionTypes.getCollectionType(type);
}
if (obj instanceof Boolean) {
return EcorePackage.eINSTANCE.getEBoolean();
}
if ((obj instanceof Byte) || (obj instanceof Integer) || (obj instanceof Long) || obj instanceof Short) {
return EcorePackage.eINSTANCE.getEInt();
}
if ((obj instanceof Float) || (obj instanceof Double)) {
return EcorePackage.eINSTANCE.getEDouble();
}
if (obj instanceof String) {
return EcorePackage.eINSTANCE.getEString();
}
if (obj instanceof XpandDefinitionWrap) {
return DEFINITION_TYPE;
}
if (obj instanceof XpandIterator) {
return ITERATOR_TYPE;
}
return EcorePackage.eINSTANCE.getEJavaObject();
}
/**
* FIXME HACK!!!
*/
public static Object newInstance(EClassifier t) {
if (isCollectionType(t)) {
return collectionTypes.newInstance(t);
}
if (t.getInstanceClass() != null) {
try {
return t.getInstanceClass().newInstance();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
/**
* FIXME what if args has null or getType() returns null - we can't find an
* op then, and it's caller who knows how to handle this.
*
* @param name
* @param args
* @param instance
* @return
*/
public static Operation executableOperation(String name, Object[] args, Object instance) {
EClassifier[] argTypes = new EClassifier[args.length];
for (int i = 0; i < args.length; i++) {
argTypes[i] = getType(args[i]);
}
EOperation metaOp = findOperation(getType(instance), name, argTypes);
if (metaOp == null) {
return null;
}
if (InternalOperation.isInternalOp(metaOp)) {
for (List<InternalOperation> ops : internalOperationsMap.values()) {
for (InternalOperation internalOp : ops) {
if (internalOp.metaOp == metaOp) {
return new OperationEx(instance, args, internalOp);
}
}
}
throw new IllegalStateException("Can't find implementation of built-in operation" + metaOp);
}
return new Operation(instance, args, metaOp);
}
public static EOperation findOperation(EClassifier targetType, String name, EClassifier[] args) {
List<EOperation> allOp;
if (hasBuiltinSupport(targetType)) {
// this one is to cover m2 types that are *propagated* to user's model, m1. Those like
// boolean, integer, string, etc.
allOp = findInternalOp(targetType);
} else if (hasBuiltinSupport(targetType.eClass())){
// this one is to cover collection types, because we register their operations
// against meta-model (m2) instance, rather than against M1
allOp = findInternalOp(targetType.eClass());
} else {
if (false == (targetType instanceof EClass)) {
return null;
} else {
allOp = new LinkedList<EOperation>(((EClass) targetType).getEAllOperations());
allOp.addAll(findInternalOp(EcorePackage.eINSTANCE.getEJavaObject()));
}
}
return PolymorphicResolver.filterOperation(allOp, name, targetType, Arrays.asList(args));
}
private static Map<String, String> attrNameSubsts = new TreeMap<String, String>();
public static EOperation Collection_IsEmpty;
public static EOperation Collection_Add;
public static EOperation Collection_AddAll;
public static EOperation Collection_Clear;
public static EOperation Collection_Flatten;
public static EOperation Collection_Size;
public static EOperation Collection_Union;
public static EOperation Collection_Intersect;
public static EOperation Collection_Without;
public static EOperation Collection_ToSet;
public static EOperation Collection_ToList;
public static EOperation Collection_Contains;
public static EOperation Collection_ContainsAll;
public static EOperation List_Get;
public static EOperation List_First;
public static EOperation List_Last;
public static EOperation List_WithoutFirst;
public static EOperation List_WithoutLast;
public static EOperation List_PurgeDups;
public static EOperation List_IndexOf;
public static EOperation Boolean_NE;
public static EOperation Int_Plus_Int;
public static EOperation Int_Plus_Double;
public static EOperation Int_Minus_Int;
public static EOperation Int_Minus_Double;
public static EOperation Int_Mult_Int;
public static EOperation Int_Mult_Double;
public static EOperation Int_Div_Int;
public static EOperation Int_Div_Double;
public static EOperation Int_Unary_Minus;
public static EOperation Int_GreatOrEqual;
public static EOperation Int_LessOrEqual;
public static EOperation Int_Less;
public static EOperation Int_Greater;
public static EOperation Int_UpTo;
public static EOperation Double_Plus_Double;
public static EOperation Double_Plus_Int;
public static EOperation Double_Minus_Double;
public static EOperation Double_Minus_Int;
public static EOperation Double_Mult_Double;
public static EOperation Double_Mult_Int;
public static EOperation Double_Div_Double;
public static EOperation Double_Div_Int;
public static EOperation Double_Unary_Minus;
public static EOperation Object_EQ;
public static EOperation EString_ToFirstUpper;
public static EOperation EString_Plus_EJavaObject;
public static EOperation EString_ToFirstLower;
public static EOperation EString_ToCharList;
public static EOperation EString_StartsWith;
public static EOperation EString_EndsWith;
public static EOperation EString_SubString_StartEnd;
public static EOperation EString_SubString;
public static EOperation EString_ToUpperCase;
public static EOperation EString_ToLowerCase;
public static EOperation EString_ReplaceAll;
public static EOperation EString_ReplaceFirst;
public static EOperation EString_Split;
public static EOperation EString_Matches;
public static EOperation EString_Trim;
public static EOperation EString_Length;
public static EOperation Object_CompareTo;
public static EOperation Object_ToString;
public static EOperation Object_NotEQ;
static {
attrNameSubsts.put("default_", "default");
}
public static EStructuralFeature getAttribute(EClassifier type, String name) {
if (hasBuiltinSupport(type)) {
return findInternalAttr(type, name);
}
if (type instanceof EClass) {
return ((EClass) type).getEStructuralFeature(attrNameSubsts.containsKey(name) ? attrNameSubsts.get(name): name);
}
if (type instanceof EEnum || type == EcorePackage.eINSTANCE.getEEnumLiteral() || type == EcorePackage.eINSTANCE.getEEnumerator()) {
return EcorePackage.eINSTANCE.getEEnumLiteral().getEStructuralFeature(name);
}
return null;
}
public static Object getValue(EStructuralFeature prop, Object instance) {
if (instance instanceof Enumerator) {
if (prop == EcorePackage.eINSTANCE.getEEnumLiteral_Literal()) {
return ((Enumerator) instance).getLiteral();
}
if (prop == EcorePackage.eINSTANCE.getENamedElement_Name()) {
return ((Enumerator) instance).getName();
}
if (prop == EcorePackage.eINSTANCE.getEEnumLiteral_Value()) {
return ((Enumerator) instance).getValue();
}
}
if (instance instanceof EObject) {
return ((EObject) instance).eGet(prop);
}
// handle collection/set/list properties?
return "HeyHo!";
}
private static boolean hasBuiltinSupport(EClassifier type) {
return internalOperationsMap.containsKey(type);
}
private static EStructuralFeature findInternalAttr(EClassifier type, String name) {
if (type instanceof EClass) {
return ((EClass) type).getEStructuralFeature(name);
}
if (type instanceof EDataType && type == EcorePackage.eINSTANCE.getEEnumerator()) {
// I do not know where EMF uses EEnumLiteralImpl and where Enumerator for EEnum value.
return EcorePackage.eINSTANCE.getEEnumLiteral().getEStructuralFeature(name);
}
// TODO Auto-generated method stub
return null;
}
private static final Map<EClassifier,List<InternalOperation>> internalOperationsMap = new HashMap<EClassifier, List<InternalOperation>>();
static {
final EcorePackage ecorePkg = EcorePackage.eINSTANCE;
final OperationFactory opf = new OperationFactory();
final List<InternalOperation> objectOps = new LinkedList<InternalOperation>();
objectOps.add(new InternalOperation<Object>(Object_CompareTo = opf.create("compareTo", ecorePkg.getEBoolean(), ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(Object target, Object[] params) {
if (target == null) {
return params[0] == null ? 0 : -1;
}
if (params[0] == null) {
return 1;
}
if (target instanceof Comparable) {
return ((Comparable) target).compareTo(params[0]);
}
// note, unlike ObjectTypeImpl we don't invoke toString registered against metatype here
return String.valueOf(target).compareTo(String.valueOf(params[0]));
}
});
objectOps.add(new InternalOperation<Object>(Object_ToString = opf.create("toString", ecorePkg.getEString())) {
@Override
public Object evaluate(Object target, Object[] params) {
return String.valueOf(target);
}
});
objectOps.add(new InternalOperation<Object>(Object_EQ = opf.create("==", boolean.class, Object.class)) {
@Override
public Object evaluate(Object target, Object[] params) {
return target == null ? params[0] == null : target.equals(params[0]);
}
});
objectOps.add(new InternalOperation<Object>(Object_NotEQ = opf.create("!=", boolean.class, Object.class)) {
@Override
public Object evaluate(Object target, Object[] params) {
return target == null ? params[0] != null : !target.equals(params[0]);
}
});
List<InternalOperation> unmodifiableObjectOps = Collections.unmodifiableList(objectOps);
internalOperationsMap.put(ecorePkg.getEJavaObject(), unmodifiableObjectOps);
// EEnumerator are enum literal instances at runtime (#evaluate),
// while EEnum are their types during #analyze phase
internalOperationsMap.put(ecorePkg.getEEnumerator(), unmodifiableObjectOps);
internalOperationsMap.put(ecorePkg.getEEnum(), unmodifiableObjectOps);
final List<InternalOperation> stringOps = new LinkedList<InternalOperation>();
stringOps.add(new InternalOperation<String>(EString_Plus_EJavaObject = opf.create("+",ecorePkg.getEString(),ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(String target, Object[] params) {
return target + String.valueOf(params[0]);
}
});
stringOps.add(new InternalOperation<String>(EString_ToFirstUpper = opf.create("toFirstUpper",ecorePkg.getEString())) {
@Override
public Object evaluate(String target, Object[] params) {
return StringHelper.firstUpper(target);
}
});
stringOps.add(new InternalOperation<String>(EString_ToFirstLower = opf.create("toFirstLower",ecorePkg.getEString())) {
@Override
public Object evaluate(String target, Object[] params) {
return StringHelper.firstLower(target);
}
});
stringOps.add(new InternalOperation<String>(EString_ToCharList = opf.create("toCharList",collectionTypes.getListType(ecorePkg.getEString()))) {
@Override
public Object evaluate(String target, Object[] params) {
ArrayList<String> rv = new ArrayList<String>(target.length());
for (int i = 0; i < target.length(); i++) {
rv.add(target.substring(i, i+1));
}
return rv;
}
});
InternalOperation internalOp;
stringOps.add(internalOp = opf.createReflective(String.class, "startsWith", String.class));
EString_StartsWith = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "endsWith", String.class));
EString_EndsWith = internalOp.metaOp;
InternalOperation subStringOp = opf.createReflective(String.class, "substring", int.class, int.class);
subStringOp.metaOp.setName("subString");
EString_SubString_StartEnd = subStringOp.metaOp;
stringOps.add(subStringOp);
subStringOp = opf.createReflective(String.class, "substring", int.class);
subStringOp.metaOp.setName("subString");
EString_SubString = subStringOp.metaOp;
stringOps.add(subStringOp);
stringOps.add(internalOp = opf.createReflective(String.class, "toUpperCase"));
EString_ToUpperCase = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "toLowerCase"));
EString_ToLowerCase = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "replaceAll", String.class, String.class));
EString_ReplaceAll = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "replaceFirst", String.class, String.class));
EString_ReplaceFirst = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "split", String.class));
EString_Split = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "matches", String.class));
EString_Matches = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "trim"));
EString_Trim = internalOp.metaOp;
stringOps.add(internalOp = opf.createReflective(String.class, "length"));
EString_Length = internalOp.metaOp;
stringOps.addAll(unmodifiableObjectOps);
internalOperationsMap.put(ecorePkg.getEString(), Collections.unmodifiableList(stringOps));
final List<InternalOperation> booleanOps = new LinkedList<InternalOperation>();
booleanOps.add(new InternalOperation<Boolean>(Boolean_NE = opf.create("!", boolean.class)) {
@Override
public Object evaluate(Boolean target, Object[] params) {
return Boolean.valueOf(!target.booleanValue());
}
});
booleanOps.addAll(unmodifiableObjectOps);
internalOperationsMap.put(ecorePkg.getEBoolean(), Collections.unmodifiableList(booleanOps));
final List<InternalOperation> voidOps = new LinkedList<InternalOperation>();
voidOps.addAll(unmodifiableObjectOps);
internalOperationsMap.put(VOID, Collections.unmodifiableList(voidOps));
//---------------------------------------------------------------------------------
class InternalSumOp extends InternalOperation<Number> {
InternalSumOp(EOperation metaOp) {
super(metaOp);
}
@Override
public Object evaluate(Number target, Object[] params) {
if (target instanceof Double || params[0] instanceof Double) {
return new Double(target.doubleValue() + ((Number) params[0]).doubleValue());
} else {
return new Integer(target.intValue() + ((Number) params[0]).intValue());
}
}
};
class InternalSubOp extends InternalOperation<Number> {
InternalSubOp(EOperation metaOp) {
super(metaOp);
}
@Override
public Object evaluate(Number target, Object[] params) {
if (target instanceof Double || params[0] instanceof Double) {
return new Double(target.doubleValue() - ((Number) params[0]).doubleValue());
} else {
return new Integer(target.intValue() - ((Number) params[0]).intValue());
}
}
};
class InternalMulOp extends InternalOperation<Number> {
InternalMulOp(EOperation metaOp) {
super(metaOp);
}
@Override
public Object evaluate(Number target, Object[] params) {
if (target instanceof Double || params[0] instanceof Double) {
return new Double(target.doubleValue() * ((Number) params[0]).doubleValue());
} else {
return new Integer(target.intValue() * ((Number) params[0]).intValue());
}
}
};
class InternalDivOp extends InternalOperation<Number> {
InternalDivOp(EOperation metaOp) {
super(metaOp);
}
@Override
public Object evaluate(Number target, Object[] params) {
if (target instanceof Double || params[0] instanceof Double) {
return new Double(target.doubleValue() / ((Number) params[0]).doubleValue());
} else {
return new Integer(target.intValue() / ((Number) params[0]).intValue());
}
}
};
class InternalNegateOp extends InternalOperation<Number> {
InternalNegateOp(EOperation metaOp) {
super(metaOp);
}
@Override
public Object evaluate(Number target, Object[] params) {
if (target instanceof Double) {
return -target.doubleValue();
}
return -target.intValue();
}
}
//---------------------------------------------------------------------------------
final List<InternalOperation> intOps = new LinkedList<InternalOperation>();
intOps.add(new InternalSumOp(Int_Plus_Int = opf.create("+", int.class, int.class)));
intOps.add(new InternalSumOp(Int_Plus_Double = opf.create("+", int.class, double.class)));
intOps.add(new InternalSubOp(Int_Minus_Int = opf.create("-", int.class, int.class)));
intOps.add(new InternalSubOp(Int_Minus_Double = opf.create("-", double.class, double.class)));
intOps.add(new InternalMulOp(Int_Mult_Int = opf.create("*", int.class, int.class)));
intOps.add(new InternalMulOp(Int_Mult_Double = opf.create("*", int.class, double.class)));
intOps.add(new InternalDivOp(Int_Div_Int = opf.create("/", int.class, int.class)));
intOps.add(new InternalDivOp(Int_Div_Double = opf.create("/", int.class, double.class)));
intOps.add(new InternalNegateOp(Int_Unary_Minus = opf.create("-", int.class)));
// intOps.add(new InternalOperation<Number>(opf.create("==", boolean.class, int.class)) {
// @Override
// public Object evaluate(Number target, Object[] params) {
// //we may need this to handle cases like {Long(5), Long(4)}.exists(a | a == 5)
// return Boolean.valueOf(target.intValue() == ((Integer) params[0]).intValue());
// }
//
// });
intOps.add(new InternalOperation<Number>(Int_GreatOrEqual = opf.create(">=", boolean.class, int.class)) {
@Override
public Object evaluate(Number target, Object[] params) {
return Boolean.valueOf(target.intValue() >= ((Number) params[0]).intValue());
}
});
intOps.add(new InternalOperation<Number>(Int_LessOrEqual = opf.create("<=", boolean.class, int.class)) {
@Override
public Object evaluate(Number target, Object[] params) {
return Boolean.valueOf(target.intValue() <= ((Number) params[0]).intValue());
}
});
intOps.add(new InternalOperation<Number>(Int_Less = opf.create("<", boolean.class, int.class)) {
@Override
public Object evaluate(Number target, Object[] params) {
return Boolean.valueOf(target.intValue() < ((Number) params[0]).intValue());
}
});
intOps.add(new InternalOperation<Number>(Int_Greater = opf.create(">", boolean.class, int.class)) {
@Override
public Object evaluate(Number target, Object[] params) {
return Boolean.valueOf(target.intValue() > ((Number) params[0]).intValue());
}
});
intOps.add(new InternalOperation<Number>(Int_UpTo = opf.create("upTo", collectionTypes.getListType(ecorePkg.getEInt()), ecorePkg.getEInt())) {
@Override
public Object evaluate(Number target, Object[] params) {
final ArrayList<Integer> result = new ArrayList<Integer>();
for (int l1 = target.intValue(), l2 = ((Number) params[0]).intValue(); l1 <= l2; l1++) {
result.add(new Integer(l1));
}
return result;
}
});
intOps.addAll(unmodifiableObjectOps);
List<InternalOperation> unmodifiableListIntOps = Collections.unmodifiableList(intOps);
internalOperationsMap.put(ecorePkg.getEIntegerObject(), unmodifiableListIntOps);
internalOperationsMap.put(ecorePkg.getEInt(), unmodifiableListIntOps);
final List<InternalOperation> doubleOps = new LinkedList<InternalOperation>();
doubleOps.add(new InternalSumOp(Double_Plus_Double = opf.create("+", double.class, double.class)));
doubleOps.add(new InternalSumOp(Double_Plus_Int = opf.create("+", double.class, int.class)));
doubleOps.add(new InternalSubOp(Double_Minus_Double = opf.create("-", double.class, double.class)));
doubleOps.add(new InternalSubOp(Double_Minus_Int = opf.create("-", double.class, int.class)));
doubleOps.add(new InternalMulOp(Double_Mult_Double = opf.create("*", double.class, double.class)));
doubleOps.add(new InternalMulOp(Double_Mult_Int = opf.create("*", double.class, int.class)));
doubleOps.add(new InternalDivOp(Double_Div_Double = opf.create("/", double.class, double.class)));
doubleOps.add(new InternalDivOp(Double_Div_Int = opf.create("/", double.class, int.class)));
doubleOps.add(new InternalNegateOp(Double_Unary_Minus = opf.create("-", double.class)));
doubleOps.addAll(unmodifiableObjectOps);
internalOperationsMap.put(ecorePkg.getEDouble(), doubleOps);
final List<InternalOperation> collectionOps = new LinkedList<InternalOperation>();
collectionOps.add(new InternalOperation<Collection>(Collection_IsEmpty = opf.create("isEmpty", ecorePkg.getEBoolean())) {
@Override
public Object evaluate(Collection target, Object[] params) {
// TODO isEmpty is rather attribute
return target.isEmpty();
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Add = opf.create("add", CollectionTypesSupport.COLLECTION_OF_OBJECT, ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(Collection target, Object[] params) {
target.add(params[0]);
return target;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_AddAll = opf.create("addAll", CollectionTypesSupport.COLLECTION_OF_OBJECT, CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
target.addAll((Collection) params[0]);
return target;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Clear = opf.create("clear", CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
target.clear();
return target;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Flatten = opf.create("flatten", CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
LinkedList rv = new LinkedList();
for (Object o : target) {
if (o instanceof Collection) {
// XXX unlike original xpand, we do not flatten recursively
rv.addAll((Collection) o);
} else {
rv.add(o);
}
}
return rv;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Size = opf.create("size", ecorePkg.getEInt())) {
@Override
public Object evaluate(Collection target, Object[] params) {
return target.size();
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Union = opf.create("union", CollectionTypesSupport.COLLECTION_OF_OBJECT, CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
LinkedHashSet<Object> rv = new LinkedHashSet<Object>(target);
rv.addAll((Collection) params[0]);
return rv;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Intersect = opf.create("intersect", CollectionTypesSupport.COLLECTION_OF_OBJECT, CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
LinkedHashSet<Object> rv = new LinkedHashSet<Object>(target);
rv.retainAll((Collection) params[0]);
return rv;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Without = opf.create("without", CollectionTypesSupport.COLLECTION_OF_OBJECT, CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
LinkedHashSet<Object> rv = new LinkedHashSet<Object>(target);
rv.removeAll((Collection) params[0]);
return rv;
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_ToSet = opf.create("toSet", CollectionTypesSupport.SET_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
return new LinkedHashSet<Object>(target);
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_ToList = opf.create("toList", CollectionTypesSupport.LIST_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
return new LinkedList<Object>(target);
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_Contains = opf.create("contains", ecorePkg.getEBoolean(), ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(Collection target, Object[] params) {
return target.contains(params[0]);
}
});
collectionOps.add(new InternalOperation<Collection>(Collection_ContainsAll = opf.create("containsAll", ecorePkg.getEBoolean(), CollectionTypesSupport.COLLECTION_OF_OBJECT)) {
@Override
public Object evaluate(Collection target, Object[] params) {
return target.containsAll((Collection) params[0]);
}
});
List<InternalOperation> unmodifiableListCollectionOps = Collections.unmodifiableList(collectionOps);
internalOperationsMap.put(CollectionTypesSupport.COLLECTION_TYPE, unmodifiableListCollectionOps);
internalOperationsMap.put(CollectionTypesSupport.SET_TYPE, unmodifiableListCollectionOps);
final List<InternalOperation> listOps = new LinkedList<InternalOperation>(unmodifiableListCollectionOps);
listOps.add(new InternalOperation<List>(List_Get = opf.create("get", ecorePkg.getEJavaObject(), ecorePkg.getEInt())) {
@Override
public Object evaluate(List target, Object[] params) {
int index = ((Number) params[0]).intValue();
return index < target.size() ? target.get(index) : null;
}
});
listOps.add(new InternalOperation<List>(List_First = opf.create("first", ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(List target, Object[] params) {
return target.isEmpty() ? null : target.get(0);
}
});
listOps.add(new InternalOperation<List>(List_Last = opf.create("last", ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(List target, Object[] params) {
return target.isEmpty() ? null : target.get(target.size() - 1);
}
});
listOps.add(new InternalOperation<List>(List_WithoutFirst = opf.create("withoutFirst", CollectionTypesSupport.LIST_OF_OBJECT)) {
@Override
public Object evaluate(List target, Object[] params) {
if (!target.isEmpty()) {
LinkedList rv = new LinkedList(target);
rv.removeFirst();
return rv;
}
return target;
}
});
listOps.add(new InternalOperation<List>(List_WithoutLast = opf.create("withoutLast", CollectionTypesSupport.LIST_OF_OBJECT)) {
@Override
public Object evaluate(List target, Object[] params) {
if (!target.isEmpty()) {
LinkedList rv = new LinkedList(target);
rv.removeLast();
return rv;
}
return target;
}
});
listOps.add(new InternalOperation<List>(List_PurgeDups = opf.create("purgeDups", CollectionTypesSupport.LIST_OF_OBJECT)) {
@Override
public Object evaluate(List target, Object[] params) {
if (target.isEmpty()) {
return target;
}
return new LinkedList<Object>(new LinkedHashSet<Object>(target));
}
});
listOps.add(new InternalOperation<List>(List_IndexOf = opf.create("indexOf", ecorePkg.getEInt(), ecorePkg.getEJavaObject())) {
@Override
public Object evaluate(List target, Object[] params) {
return target.indexOf(params[0]);
}
});
internalOperationsMap.put(CollectionTypesSupport.LIST_TYPE, Collections.unmodifiableList(listOps));
final List<InternalOperation> definitionOps = new LinkedList<InternalOperation>();
definitionOps.add(new InternalOperation<XpandDefinitionWrap>(opf.create("proceed", VOID)) {
@Override
public Object evaluate(XpandDefinitionWrap target, Object[] params) {
target.proceed();
return null;
}
});
internalOperationsMap.put(DEFINITION_TYPE, Collections.unmodifiableList(definitionOps));
final List<InternalOperation> iteratorOps = new LinkedList<InternalOperation>();
iteratorOps.add(new InternalOperation<XpandIterator>(opf.create("isFirstIteration", ecorePkg.getEBoolean())) {
@Override
public Object evaluate(XpandIterator target, Object[] params) {
return target.isFirstIteration();
}
});
internalOperationsMap.put(ITERATOR_TYPE, Collections.unmodifiableList(iteratorOps));
}
private static List<EOperation> findInternalOp(EClassifier targetType) {
List<InternalOperation> ops = internalOperationsMap.get(targetType);
if (ops != null) {
List<EOperation> rv = new ArrayList<EOperation>(ops.size());
for (InternalOperation iop : ops) {
rv.add(iop.metaOp);
}
return rv;
}
if (targetType.eClass() == EcorePackage.eINSTANCE.getEClass()) {
// not instanceof because parametric type and all other XECore
// classes have different operations, at least
// meanwhile, when they are dynamic
return((EClass) targetType).getEAllOperations();
}
// TODO Auto-generated method stub
// String length, +, startsWith, endsWith, subString, toUpperCase, toFirstUpper, toFirstLower, toCharList, replaceAll, replaceFirst, split, matches, trim
// Int/Real +-*/ > >= < <= != ==
// Collection toList, toSet, toString, add, addAll, contains, containsAll, remove, removeAll, without, intersect, flatten ;
// properties - size, isEmpty
// List get, indexOf, last, first, withoutFirst, withoutLast
// Set - none
return null;
}
public static EClassifier getTypedElementType(ETypedElement p) {
if (p.isMany()) {
return p.isOrdered() ? getListType(p.getEType()) : p.isUnique() ? getSetType(p.getEType()) : getCollectionType(p.getEType());
}
return p.getEType();
}
private static class OperationFactory {
EOperation create(String name, Class returnType, Class ... params) {
EClassifier[] paramsNew = new EClassifier[params.length];
for (int i = 0; i < paramsNew.length; i++) {
paramsNew[i] = toEClassifier(params[i]);
assert params[i] != null;
}
EClassifier rt = toEClassifier(returnType);
assert rt != null : "Unrecognized return type:" + returnType;
return create(name, rt, paramsNew);
}
/**
* NOTE, targetType is NOT operation's return type, but method owning class
*/
InternalOperation createReflective(Class targetType, String methodName, Class ... params) {
try {
final Method m = targetType.getMethod(methodName, params);
assert m != null;
return new InternalOperation<Object>(create(methodName, m.getReturnType(), params)) {
public Object evaluate(Object target, Object[] params) {
try {
Object rv = m.invoke(target, params);
if (rv != null && rv.getClass().isArray()) {
return new LinkedList(Arrays.asList((Object[]) rv));
}
return rv;
} catch (Exception e) {
Activator.logError(e);
return null;
}
}
};
} catch (NoSuchMethodException ex) {
assert false : ex.getMessage();
}
return null;
}
private EClassifier toEClassifier(Class targetType) {
if (targetType == void.class) {
return VOID;
}
for (EClassifier c : EcorePackage.eINSTANCE.getEClassifiers()) {
if (c.getInstanceClass() == targetType) {
return c;
}
}
if (targetType.isArray()) {
EClassifier t = toEClassifier(targetType.getComponentType());
assert t != null : "Unrecognized array component type:" + targetType;
return getListType(t);
}
// TODO other packages
return null;
}
EOperation create(String name, EClassifier returnType, EClassifier ... params) {
EOperation op = EcoreFactory.eINSTANCE.createEOperation();
op.setName(name);
op.setEType(returnType);
for (EClassifier c : params) {
EParameter p1 = EcoreFactory.eINSTANCE.createEParameter();
p1.setName("arg" + c.getName());
p1.setEType(c);
op.getEParameters().add(p1);
}
return op;
}
}
private static abstract class InternalOperation<T> {
private final EOperation metaOp;
private static String INTERNAL_OP_ANNOTATION = "::internalop::";
private InternalOperation(EOperation metaOp) {
assert metaOp != null;
this.metaOp = metaOp;
EAnnotation internalOpAnn = EcoreFactory.eINSTANCE.createEAnnotation();
internalOpAnn.setSource(INTERNAL_OP_ANNOTATION);
metaOp.getEAnnotations().add(internalOpAnn);
}
public abstract Object evaluate(T target, Object[] params);
static boolean isInternalOp(EOperation op) {
return op.getEAnnotation(INTERNAL_OP_ANNOTATION) != null;
}
}
public static class OperationEx extends Operation {
private final InternalOperation<Object> internalOp;
private OperationEx(Object targetObject, Object[] args, InternalOperation<Object> internalOp) {
super(targetObject, args, internalOp.metaOp);
this.internalOp = internalOp;
}
@Override
public Object evaluate() {
return internalOp.evaluate(targetObject, args);
}
}
private static final Map<EOperation, Method> externalOpImplementations = new HashMap<EOperation, Method>();
public static void registerOperationImpl(EOperation metaOp, Method implementation) {
assert metaOp != null;
assert implementation != null;
assert Modifier.isStatic(implementation.getModifiers());
externalOpImplementations.put(metaOp, implementation);
}
public static class Operation {
protected final EOperation metaOp;
protected final Object[] args;
protected final Object targetObject;
private Operation(Object targetObject, Object[] args, EOperation metaOp) {
this.targetObject = targetObject;
this.args = args;
this.metaOp = metaOp;
}
public Object evaluate() {
try {
final Method m;
if (externalOpImplementations.containsKey(metaOp)) {
m = externalOpImplementations.get(metaOp);
} else {
m = targetObject.getClass().getMethod(metaOp.getName(), getParameterClasses());
}
return m.invoke(targetObject, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Class[] getParameterClasses() {
List<EParameter> emfParams = metaOp.getEParameters();
final Class[] paramClasses = new Class[emfParams.size()];
for (int i = 0, x = emfParams.size(); i < x; i++) {
final EParameter param = emfParams.get(i);
// XXX works only for generated classes, or those with instance
// class set,
// and doesn't work with dynamic models, right?
paramClasses[i] = param.getEType().getInstanceClass();
}
return paramClasses;
}
}
/**
* @return true if first argument is more general and second is more
* specific, think Object and String
* @see AbstractTypeImpl.isAssignableFrom(this, t)
*/
public static boolean isAssignableFrom(EClassifier c1, EClassifier t) {
if ((t == null) || (c1 == null)) {
return false;
}
if (BuiltinMetaModel.primEquals(c1, t)) {
return true;
}
if (t.equals(VOID)) {
return true;
}
if (false == (t instanceof EClass)) {
if (c1 instanceof EEnum && t == EcorePackage.eINSTANCE.getEEnumerator()) {
return true; // HACK - any enumerator instance can be assigned to any enum attribute.
}
if (c1 instanceof EDataType && t instanceof EDataType) {
return isCompatibleDataTypes((EDataType) c1, (EDataType) t);
}
return false;
}
if (c1 instanceof EDataType) {
Class c1Class = ((EDataType) c1).getInstanceClass();
return c1Class.isAssignableFrom(t.getInstanceClass() == null ? Object.class : t.getInstanceClass());
}
if (isParameterizedType(c1) && isParameterizedType(t)) {
return c1.eClass().isSuperTypeOf(t.eClass()) && isAssignableFrom(getInnerType(c1), getInnerType(t));
}
// == c1.isSuperTypeOf(t);
for (EClass superType : ((EClass) t).getEAllSuperTypes()) {
if (BuiltinMetaModel.primEquals(superType, c1)) {
return true;
}
}
return false;
}
private static boolean primEquals(EClassifier c1, EClassifier obj) {
if (obj == null) {
return false;
}
if (c1 == obj) {
return true;
}
final boolean namesEqual = c1.getName().equals(obj.getName());
if (!namesEqual) {
return false;
}
if (c1.getEPackage() == null) {
return obj.getEPackage() == null;
}
if (obj.getEPackage() == null) {
return false;
}
if (c1.getEPackage().getNsURI() == null) {
return obj.getEPackage().getNsURI() == null;
}
return c1.getEPackage().getNsURI().equals(obj.getEPackage().getNsURI());
}
private static boolean isCompatibleDataTypes(EDataType dt1, EDataType dt2) {
try {
final Class dt1Class = dt1.getInstanceClass();
final Class dt2Class = dt2.getInstanceClass();
if (dt1Class != null && dt2Class != null) {
if (dt1Class == Object.class) {
// anything (with or without wrapping) can be assigned to object
return true;
}
if (dt1Class.isPrimitive() && !dt2Class.isPrimitive()) {
Field f = dt2Class.getField("TYPE");
return dt1Class.equals(f.get(null));
} else if (!dt1Class.isPrimitive() && dt2Class.isPrimitive()) {
Field f = dt1Class.getField("TYPE");
return dt2Class.equals(f.get(null));
}
return dt1Class.isAssignableFrom(dt2Class);
}
if (dt1Class != null && dt2Class == null) {
// special case for dt2 datatype from dynamic model instance
// (e.g. model file in same workspace as template)
// hence no instance classes yet, but anything is assignable to Object
// Fixed while resolving #analyze of enum literals compare (==)
return dt1Class == Object.class;
}
} catch (NoSuchFieldException ex) {
// IGNORE
} catch (IllegalAccessException ex) {
// IGNORE
}
return false;
}
public static List<EStructuralFeature> getAllFeatures(EClassifier targetType) {
// FIXME @see getAllOperations
if (targetType instanceof EClass) {
return ((EClass) targetType).getEAllStructuralFeatures();
}
return Collections.emptyList();
}
public static List<EOperation> getAllOperation(EClassifier targetType) {
// FIXME - either have datatypes like int/real as 'honest' EClasses, or
// provide their operations here
if (hasBuiltinSupport(targetType)) {
return findInternalOp(targetType);
}
if (targetType instanceof EClass) {
return ((EClass) targetType).getEAllOperations();
}
/*
* XXX might be not bad idea to use java reflection to provide
* datatype's possible operations if (targetType instanceof EDataType &&
* false == targetType instanceof EEnum) { EDataType dt = (EDataType)
* targetType; dt.getInstanceClass().getMethods() }
*/
return Collections.emptyList();
}
}