blob: c22c57643345644def712a58c5fdb614c9a98ca5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2020 Borland Software Corporation 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:
* Borland Software Corporation - initial API and implementation
* Christopher Gerking - bugs 326871, 400233, 427237, 566216, 566230
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.blackbox.java;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.m2m.internal.qvt.oml.NLS;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalModuleEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalStdLibrary;
import org.eclipse.m2m.qvt.oml.blackbox.java.JavaModelInstance;
import org.eclipse.m2m.qvt.oml.util.Dictionary;
import org.eclipse.m2m.qvt.oml.util.MutableList;
import org.eclipse.ocl.TypeResolver;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.util.Bag;
class Java2QVTTypeResolver {
/**
* Flag indicates that strict type equality is using.
*/
static final int STRICT_TYPE = 1;
/**
* Flag indicates that subtypes of given type will be considered in case strict equality fails.
*/
static final int ALLOW_SUBTYPE = 2;
/**
* Flag indicates that supertypes of given type will be considered in case strict equality fails.
*/
static final int ALLOW_SUPERTYPE = 4;
private QvtOperationalModuleEnv fEnv;
private Collection<String> fPackageURIs;
// used to delegate the OCL type determination to MDT OCL UMLReflection
private EClassifier fHelperEClassiferAdapter;
private BasicDiagnostic fDiagnostics;
Java2QVTTypeResolver(QvtOperationalModuleEnv env, Collection<String> packageURIs, BasicDiagnostic diagnostics) {
fEnv = env;
fPackageURIs = packageURIs;
fDiagnostics = diagnostics;
}
QvtOperationalModuleEnv getEnvironment() {
return fEnv;
}
EClassifier toEClassifier(Type type, int relationship) {
EClassifier result = type2EClassifier(type, relationship);
if(result == null) {
if(type instanceof Class<?>) {
EClassifier eWrapper = asEClassifier((Class<?>) type);
EClassifier asOCLType = fEnv.getUMLReflection().asOCLType(eWrapper);
if(asOCLType != eWrapper) {
return asOCLType;
}
}
} else {
return fEnv.getUMLReflection().asOCLType(result);
}
return result;
}
private EClassifier type2EClassifier(Type type, int relationship) {
if(type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
return handleParameterizedType(parameterizedType, relationship);
}
else if(type instanceof Class<?>) {
return handleType((Class<?>)type, relationship);
} else if(type instanceof TypeVariable<?>) {
TypeVariable<?> typeVariable = (TypeVariable<?>) type;
OCLStandardLibrary<EClassifier> stdLib = fEnv.getOCLStandardLibrary();
if(stdLib.getT().getName().equals(typeVariable.getName())) {
return getEnvironment().getOCLStandardLibrary().getT();
//return paramType;
} else if(stdLib.getT2().getName().equals(typeVariable.getName())) {
return getEnvironment().getOCLStandardLibrary().getT();
} else if(QvtOperationalStdLibrary.INSTANCE.getKeyT().getName().equals(typeVariable.getName())) {
return QvtOperationalStdLibrary.INSTANCE.getKeyT();
}
}
return null;
}
private EClassifier handleParameterizedType(ParameterizedType parameterizedType, int relationship) {
Type rawType = parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if(actualTypeArguments.length == 0) {
return null;
}
Type actualElementType = actualTypeArguments[0];
if(rawType instanceof Class<?> == false) {
return null;
}
Class<?> rawClass = (Class<?>) rawType;
if(rawClass == Dictionary.class) {
EClassifier keyType = toEClassifier(actualElementType, relationship);
Type actualElementType2 = actualTypeArguments.length > 1 ? actualTypeArguments[1] : null;
if(keyType != null && actualElementType2 != null) {
EClassifier elementType = toEClassifier(actualElementType2, relationship);
if(elementType != null) {
return fEnv.getTypeResolver().resolveDictionaryType(keyType, elementType);
}
}
} else if(rawClass == MutableList.class) {
EClassifier listElementType = toEClassifier(actualElementType, relationship);
if(listElementType != null) {
return fEnv.getTypeResolver().resolveListType(listElementType);
}
} else if(rawClass == LinkedHashSet.class) {
return resolveCollectionType(CollectionKind.ORDERED_SET_LITERAL, actualElementType, relationship);
}
else if(Set.class.isAssignableFrom(rawClass)) {
return resolveCollectionType(CollectionKind.SET_LITERAL, actualElementType, relationship);
}
else if(rawClass == Bag.class) {
return resolveCollectionType(CollectionKind.BAG_LITERAL, actualElementType, relationship);
}
else if(List.class.isAssignableFrom(rawClass)) {
return resolveCollectionType(CollectionKind.SEQUENCE_LITERAL, actualElementType, relationship);
}
else if(rawType == List.class) {
return resolveCollectionType(CollectionKind.SEQUENCE_LITERAL, actualElementType, relationship);
}
else if(rawType == Collection.class) {
return resolveCollectionType(CollectionKind.COLLECTION_LITERAL, actualElementType, relationship);
}
return lookupByInstanceClass(parameterizedType, relationship);
}
private EClassifier resolveCollectionType(CollectionKind kind, Type elementType, int relationship) {
TypeResolver<EClassifier, EOperation, EStructuralFeature> typeResolver = fEnv.getTypeResolver();
EClassifier actualElementClassifier = null;
if(elementType instanceof TypeVariable<?>) {
TypeVariable<?> typeVariable = (TypeVariable<?>)elementType;
String genericJavaTypeName = typeVariable.getName();
OCLStandardLibrary<EClassifier> oclStdLibrary = fEnv.getOCLStandardLibrary();
EClassifier typeT = oclStdLibrary.getT();
if(typeT.getName().equals(genericJavaTypeName)) {
actualElementClassifier = typeT;
} else {
EClassifier typeT2 = oclStdLibrary.getT2();
if(typeT2.getName().equals(genericJavaTypeName)) {
actualElementClassifier = typeT2;
}
}
}
else if(elementType != null) {
actualElementClassifier = toEClassifier(elementType, relationship);
}
if(actualElementClassifier != null) {
return (EClassifier) typeResolver.resolveCollectionType(kind, actualElementClassifier);
}
return null;
}
private EClassifier handleType(Class<?> type, int relationship) {
OCLStandardLibrary<EClassifier> stdLibrary = fEnv.getOCLStandardLibrary();
if(type == Object.class) {
return stdLibrary.getOclAny();
}
else if(type == String.class) {
return stdLibrary.getString();
}
else if(type == Boolean.class || type == boolean.class) {
return stdLibrary.getBoolean();
}
else if(type == Integer.class || type == int.class) {
return stdLibrary.getInteger();
}
else if(type == Double.class || type == double.class) {
return stdLibrary.getReal();
}
else if(type == void.class) {
return stdLibrary.getOclVoid();
}
else if(type == JavaModelInstance.class) {
return fEnv.getQVTStandardLibrary().getModelClass();
}
return lookupByInstanceClass(type, relationship);
}
private EClassifier lookupByInstanceClass(Class<?> type, int relationship) {
assert type != null;
SortedSet<EClassifier> subtypes = new TreeSet<EClassifier>(HIERARCHY_COMPARATOR_DESC);
SortedSet<EClassifier> supertypes = new TreeSet<EClassifier>(HIERARCHY_COMPARATOR_ASC);
Iterable<String> packageURIs = fPackageURIs.isEmpty() ? fEnv.getEPackageRegistry().keySet() : fPackageURIs;
for (String nsURI : packageURIs) {
EPackage ePackage = resolvePackage(nsURI, fDiagnostics);
if (ePackage != null) {
// early return for same-named classifier
EClassifier sameNamedClassifier = ePackage.getEClassifier(type.getSimpleName());
if (sameNamedClassifier != null && type == sameNamedClassifier.getInstanceClass()) {
return sameNamedClassifier;
}
for (EClassifier eClassifier : ePackage.getEClassifiers()) {
Class<?> instanceClass = eClassifier.getInstanceClass();
if(type == instanceClass) {
return eClassifier;
}
// fall-back strategy for resolving sub/super types
if ((relationship & ALLOW_SUBTYPE) == ALLOW_SUBTYPE) {
if(isAssignableFromTo(type, instanceClass)) {
subtypes.add(eClassifier);
}
}
if ((relationship & ALLOW_SUPERTYPE) == ALLOW_SUPERTYPE) {
if(isAssignableFromTo(instanceClass, type)) {
supertypes.add(eClassifier);
}
}
}
}
}
if ((relationship & ALLOW_SUBTYPE) == ALLOW_SUBTYPE && !subtypes.isEmpty()) {
return subtypes.first();
}
if ((relationship & ALLOW_SUPERTYPE) == ALLOW_SUPERTYPE && !supertypes.isEmpty()) {
return supertypes.first();
}
return null;
}
private boolean isAssignableFromTo(Class<?> from, Class<?> to) {
return from != null && to != null && to.isAssignableFrom(from);
}
private EClassifier lookupByInstanceClass(ParameterizedType type, int relationship) {
assert type != null;
Type rawType = type.getRawType();
EClassifier rawClassifier = toEClassifier(rawType, relationship);
if (rawClassifier != null) {
Iterator<ETypeParameter> typeParameters = rawClassifier.getETypeParameters().iterator();
for (Type argumentType : type.getActualTypeArguments()) {
EClassifier argumentClassifier = toEClassifier(argumentType, relationship);
if (argumentClassifier != null && typeParameters.hasNext()) {
ETypeParameter typeParameter = typeParameters.next();
for (EGenericType genericType : typeParameter.getEBounds()) {
EClassifier genericClassifier = genericType.getEClassifier();
Class<?> genericInstanceClass = genericClassifier.getInstanceClass();
if (argumentType instanceof Class<?>) {
if ((relationship & ALLOW_SUBTYPE) == ALLOW_SUBTYPE) {
if(!isAssignableFromTo((Class<?>) argumentType, genericInstanceClass)) {
return null;
}
}
if ((relationship & ALLOW_SUPERTYPE) == ALLOW_SUPERTYPE) {
if(!isAssignableFromTo(genericInstanceClass, (Class<?>) argumentType)) {
return null;
}
}
}
}
}
}
}
return rawClassifier;
}
private EClassifier asEClassifier(Class<?> javaClass) {
if(fHelperEClassiferAdapter == null) {
EPackage ownerPackage = EcoreFactory.eINSTANCE.createEPackage();
ownerPackage.setName("helper"); //$NON-NLS-1$
ownerPackage.setNsURI(javaClass.getName());
fHelperEClassiferAdapter = EcoreFactory.eINSTANCE.createEDataType();
ownerPackage.getEClassifiers().add(fHelperEClassiferAdapter);
}
fHelperEClassiferAdapter.setName(javaClass.getSimpleName());
fHelperEClassiferAdapter.setInstanceClass(javaClass);
return fHelperEClassiferAdapter;
}
private static final Comparator<EClassifier> HIERARCHY_COMPARATOR_ASC = new Comparator<EClassifier>() {
public int compare(EClassifier o1, EClassifier o2) {
Class<?> o1Class = o1.getInstanceClass();
Class<?> o2Class = o2.getInstanceClass();
if (o2Class.equals(o1Class)) {
return 0;
} else if (o1Class.isAssignableFrom(o2Class)) {
return -1;
} else if (o2Class.isAssignableFrom(o1Class)) {
return 1;
} else {
return 0;
}
}
};
private static final Comparator<EClassifier> HIERARCHY_COMPARATOR_DESC = new Comparator<EClassifier>() {
public int compare(EClassifier o1, EClassifier o2) {
Class<?> o1Class = o1.getInstanceClass();
Class<?> o2Class = o2.getInstanceClass();
if (o2Class.equals(o1Class)) {
return 0;
} else if (o1Class.isAssignableFrom(o2Class)) {
return 1;
} else if (o2Class.isAssignableFrom(o1Class)) {
return -1;
} else {
return 0;
}
}
};
private EPackage resolvePackage(String nsURI, DiagnosticChain diagnosticChain) {
EPackage.Registry registry = fEnv.getEPackageRegistry();
EPackage resolvedPackage;
try {
resolvedPackage = registry.getEPackage(nsURI);
}
catch (Throwable t) {
resolvedPackage = null;
}
if(resolvedPackage != null) {
return resolvedPackage;
} else {
diagnosticChain.add(DiagnosticUtil.createErrorDiagnostic(
NLS.bind(JavaBlackboxMessages.UnresolvedMetamodelURI, nsURI)));
}
return null;
}
}