blob: 2cccb3a1918684369eeda1b061ea130c4729da82 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2021 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 289982, 427237, 472482
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.blackbox.java;
import java.lang.annotation.Annotation;
import java.lang.reflect.GenericSignatureFormatError;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
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.expressions.DirectionKind;
import org.eclipse.m2m.internal.qvt.oml.expressions.ExpressionsFactory;
import org.eclipse.m2m.internal.qvt.oml.expressions.Helper;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter;
import org.eclipse.m2m.qvt.oml.blackbox.java.Operation;
import org.eclipse.m2m.qvt.oml.blackbox.java.Operation.Kind;
import org.eclipse.m2m.qvt.oml.blackbox.java.Parameter;
import org.eclipse.m2m.qvt.oml.util.IContext;
import org.eclipse.ocl.Environment;
class OperationBuilder {
private Java2QVTTypeResolver fTypeResolver;
private BasicDiagnostic fProblems;
OperationBuilder(Java2QVTTypeResolver typeResolver) {
fTypeResolver = typeResolver;
}
public EOperation buildOperation(Method javaMethod) {
resetErrors();
return createOperation(javaMethod);
}
public Diagnostic getDiagnostics() {
return fProblems != null ? fProblems : Diagnostic.OK_INSTANCE;
}
private EOperation createOperation(Method method) {
EClassifier contextType = null;
String name = method.getName();
Type resultType = null;
Type[] paramTypes = {};
try {
resultType = method.getGenericReturnType();
paramTypes = method.getGenericParameterTypes();
}
catch (GenericSignatureFormatError e) {
reportError(e.getLocalizedMessage(), method);
}
catch (TypeNotPresentException e) {
reportError(e, method);
}
catch (MalformedParameterizedTypeException e) {
reportError(e, method);
}
Annotation[][] annotations = method.getParameterAnnotations();
int paramTypesCount = paramTypes.length;
Operation operAnnotation = method.getAnnotation(Operation.class);
boolean isWithExecutionContext = operAnnotation != null && operAnnotation.withExecutionContext();
if(isWithExecutionContext && (paramTypesCount == 0 || paramTypes[0] != IContext.class)) {
reportError(NLS.bind(JavaBlackboxMessages.QvtoContextParameterRequired, method.getName()), method);
--paramTypesCount;
}
boolean isContextual = operAnnotation != null && operAnnotation.contextual();
if(isContextual && paramTypesCount == 0) {
reportError(NLS.bind(JavaBlackboxMessages.FirstContextualOperationParameterRequired,
method), method);
}
Operation.Kind operKind = getOperationKind(operAnnotation);
EOperation operation = null;
switch (operKind) {
case OPERATION:
operation = EcoreFactory.eINSTANCE.createEOperation();
break;
case MAPPING:
operation = ExpressionsFactory.eINSTANCE.createMappingOperation();
break;
case CONSTRUCTOR:
if (!isContextual) {
reportError(NLS.bind(JavaBlackboxMessages.ConstructorRequiresContextualOperation, method.getName()), method);
}
operation = ExpressionsFactory.eINSTANCE.createConstructor();
break;
case QUERY: case HELPER:
Helper helper = ExpressionsFactory.eINSTANCE.createHelper();
helper.setIsQuery(operKind == Kind.QUERY);
operation = helper;
break;
case TRANSFORMATION:
if (isContextual) {
reportError(NLS.bind(JavaBlackboxMessages.TransformationRequiresContextlessOperation, method.getName()), method);
}
operation = EcoreFactory.eINSTANCE.createEOperation();
break;
default:
assert false : "unsupported operation kind"; //$NON-NLS-1$
operation = EcoreFactory.eINSTANCE.createEOperation();
break;
}
operation.setName(name);
operation.setEType(fTypeResolver.toEClassifier(resultType, Java2QVTTypeResolver.ALLOW_SUBTYPE));
if(operation.getEType() == null) {
reportError(NLS.bind(JavaBlackboxMessages.UnresolvedOclTypeForJavaType, method.getReturnType(), method), method);
}
int i = 0;
final QvtOperationalModuleEnv environment = fTypeResolver.getEnvironment();
for (Type paramType : paramTypes) {
if (i == 0 && paramType == IContext.class) {
i++;
continue;
}
VarParameter varParam = (operKind != Kind.OPERATION) ?
ExpressionsFactory.eINSTANCE.createVarParameter() : null;
EParameter eParameter = varParam != null ? varParam : EcoreFactory.eINSTANCE.createEParameter();
if(varParam != null) {
varParam.setKind(DirectionKind.IN);
}
eParameter.setEType(fTypeResolver.toEClassifier(paramType, Java2QVTTypeResolver.ALLOW_SUPERTYPE));
if(eParameter.getEType() == null) {
reportError(NLS.bind(JavaBlackboxMessages.UnresolvedOclTypeForJavaType, paramType, method), method);
}
Parameter paramAnno = getParameterAnnotation(annotations[i]);
String paramName;
if(paramAnno != null && paramAnno.name() != null) {
paramName = paramAnno.name();
} else {
paramName = "arg" + (isWithExecutionContext ? i-1 : i); //$NON-NLS-1$;
}
eParameter.setName(paramName);
if(isContextual && (isWithExecutionContext ? i == 1 : i == 0)) {
if(operKind != Kind.OPERATION) {
assert operation instanceof ImperativeOperation;
assert varParam != null;
varParam.setName(Environment.SELF_VARIABLE_NAME);
((ImperativeOperation)operation).setContext(varParam);
}
contextType = eParameter.getEType();
} else {
operation.getEParameters().add(eParameter);
}
i++;
} // iterate parameters
if(operation instanceof ImperativeOperation) {
ImperativeOperation imperativeOperation = (ImperativeOperation) operation;
environment.defineImperativeOperation(imperativeOperation, false, true);
} else if(contextType != null) {
environment.getTypeResolver().resolveAdditionalOperation(contextType, operation);
}
return operation;
}
static Kind getOperationKind(Operation operAnnotation) {
Operation.Kind operKind = (operAnnotation != null) ? operAnnotation.kind() : Operation.Kind.OPERATION;
if(operKind == Kind.OPERATION) {
// FIXME - avoid this, create typedef on the keeping this operation instead
operKind = Kind.HELPER;
}
return operKind;
}
private static Parameter getParameterAnnotation(Annotation[] allAnnotations) {
for (Annotation annotation : allAnnotations) {
if(annotation.annotationType() == Parameter.class) {
return (Parameter)annotation;
}
}
return null;
}
private void resetErrors() {
fProblems = null;
}
private void reportError(Exception exception, Method problemMethod) {
reportError(exception.getLocalizedMessage(), exception, problemMethod);
}
private void reportError(String message, Method problemMethod) {
reportError(message, null, problemMethod);
}
private void reportError(String message, Exception exception, Method problemMethod) {
if(fProblems == null) {
fProblems = DiagnosticUtil.createRootDiagnostic(NLS.bind(JavaBlackboxMessages.LoadOperationDiagnostics, problemMethod));
}
fProblems.add(DiagnosticUtil.createErrorDiagnostic(message, exception));
}
}