blob: a815174ed788aabf0af67bd85548e0bf9aecefd9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2015 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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.ast.env;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictionaryType;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeOCLPackage;
import org.eclipse.ocl.ecore.CollectionType;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.types.TupleType;
import org.eclipse.ocl.types.TypeType;
import org.eclipse.ocl.types.util.TypesSwitch;
import org.eclipse.ocl.util.CollectionUtil;
import org.eclipse.ocl.utilities.TypedElement;
class GenericsResolver {
final private QVTOEnvironment fEnv;
final private OCLStandardLibrary<EClassifier> fOCLStdLib;
final private BindSwitch fBindSwitch;
final private Map<EClassifier, EClassifier> formal2ActualBinding = new HashMap<EClassifier, EClassifier>(3);
GenericsResolver(QVTOEnvironment env) {
if(env == null) {
throw new IllegalArgumentException();
}
this.fEnv = env;
this.fOCLStdLib = env.getOCLStandardLibrary();
this.fBindSwitch = new BindSwitch();
}
public EClassifier resolveOperationReturnType(EClassifier source, EOperation operation, List<? extends TypedElement<EClassifier>> args) {
EClassifier owner = fEnv.getUMLReflection().getOwningClassifier(operation);
resolve(owner, source, QvtOperationalParserUtil.isStaticOperation(operation));
int paramIndex = 0;
for (EParameter eParameter : operation.getEParameters()) {
EClassifier paramFormalType = eParameter.getEType();
if(paramFormalType != null) {
EClassifier actualArgType = args.get(paramIndex++).getType();
resolve(paramFormalType, actualArgType, false);
}
}
EClassifier returnType = operation.getEType();
if(formal2ActualBinding.isEmpty()) {
return returnType;
}
if(returnType != null) {
returnType = fBindSwitch.doSwitch(returnType);
formal2ActualBinding.clear();
}
return returnType;
}
@SuppressWarnings("unchecked")
private void resolve(EClassifier formalType, EClassifier actualType, boolean isStatic) {
if(formalType == null || actualType == null) {
return;
}
if (formalType instanceof CollectionType && actualType instanceof CollectionType) {
resolveCollection((CollectionType) formalType, (CollectionType) actualType, isStatic);
} else if (formalType instanceof TypeType && actualType instanceof TypeType) {
TypeType<EClassifier, EOperation> actualTypeType = (TypeType<EClassifier, EOperation>) actualType;
if (formalType == fEnv.getOCLStandardLibrary().getOclType()) {
resolve(fOCLStdLib.getT(), actualTypeType.getReferredType(), isStatic);
}
} else if (isStatic
&& actualType instanceof TypeType
&& ((TypeType<EClassifier, EOperation>) actualType).getReferredType() instanceof EClass
&& ((EClass) ((TypeType<EClassifier, EOperation>) actualType).getReferredType()).getEAllSuperTypes().contains(formalType)) {
resolve(fOCLStdLib.getT(), ((TypeType<EClassifier, EOperation>) actualType).getReferredType(), isStatic);
} else {
if (isGeneric(formalType)) {
bind(formalType, actualType);
if (!formal2ActualBinding.containsKey(fOCLStdLib.getT2())) {
if (actualType instanceof CollectionType) {
bind(fOCLStdLib.getT2(), CollectionUtil.getFlattenedElementType((CollectionType) actualType));
} else {
bind(fOCLStdLib.getT2(), actualType);
}
}
}
}
}
void resolveCollection(CollectionType formal, CollectionType actual, boolean isStatic) {
EClassifier formalElementType = formal.getElementType();
EClassifier actualElementType = actual.getElementType();
resolve(formalElementType, actualElementType, isStatic);
if(formal instanceof DictionaryType && actual instanceof DictionaryType) {
DictionaryType formalDict = (DictionaryType) formal;
DictionaryType actualDict = (DictionaryType) actual;
resolve(formalDict.getKeyType(), actualDict.getKeyType(), isStatic);
}
}
private boolean isGeneric(EClassifier type) {
return type == fOCLStdLib.getT() || type == fOCLStdLib.getT2() ||
type == fEnv.getQVTStandardLibrary().getKeyT();
}
void bind(EClassifier generic, EClassifier actualBound) {
formal2ActualBinding.put(generic, actualBound);
}
private class BindSwitch extends TypesSwitch<EClassifier> {
private QVTOTypeResolver typeResolver = fEnv.getTypeResolver();
private EClassifier getBoundType(EClassifier type) {
EClassifier bound = formal2ActualBinding.get(type);
return (bound != null) ? bound : type;
}
@Override
public <C, O> EClassifier caseCollectionType(org.eclipse.ocl.types.CollectionType<C, O> object) {
EClassifier boundElementType = typeResolver.resolve(getBoundType((EClassifier) object.getElementType()));
if(object.eClass() == ImperativeOCLPackage.eINSTANCE.getListType()) {
return (EClassifier) typeResolver.resolveListType(boundElementType);
} else if(object.eClass() == ImperativeOCLPackage.eINSTANCE.getDictionaryType()) {
DictionaryType dictionaryType = (DictionaryType) object;
EClassifier boundKeyType = getBoundType(dictionaryType.getKeyType());
QvtTypeResolverImpl qvtTypeResolve = (QvtTypeResolverImpl) typeResolver;
qvtTypeResolve.resolveDictionaryType(boundKeyType, boundElementType);
}
return (EClassifier) typeResolver.resolveCollectionType(object.getKind(), boundElementType);
}
@Override
public <O, P> EClassifier caseTupleType(TupleType<O, P> object) {
return (EClassifier) object;
}
@Override
public <C, O> EClassifier caseTypeType(TypeType<C, O> object) {
EClassifier referredType = getBoundType((EClassifier) object.getReferredType());
if(referredType == null && fOCLStdLib.getOclType() == object) {
referredType = getBoundType(fOCLStdLib.getT());
}
return (EClassifier) typeResolver.resolveTypeType(typeResolver.resolve(referredType));
}
@Override
public EClassifier defaultCase(EObject object) {
return getBoundType((EClassifier) object);
}
}
}