/*
* <copyright>
*
* Copyright (c) 2005-2008 Sven Efftinge 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:
* Sven Efftinge - Initial API and implementation
* Alexander Shatalin (Borland)
*
* </copyright>
*/
package org.eclipse.gmf.internal.xpand.expression.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.gmf.internal.xpand.BuiltinMetaModel;
import org.eclipse.gmf.internal.xpand.BuiltinMetaModel.Operation;
import org.eclipse.gmf.internal.xpand.expression.AnalysationIssue;
import org.eclipse.gmf.internal.xpand.expression.EvaluationException;
import org.eclipse.gmf.internal.xpand.expression.ExecutionContext;
import org.eclipse.gmf.internal.xpand.expression.Variable;
import org.eclipse.gmf.internal.xpand.migration.OperationCallTrace;
import org.eclipse.gmf.internal.xpand.migration.OperationCallTrace.Type;
import org.eclipse.gmf.internal.xpand.xtend.ast.Extension;
/**
* @author Sven Efftinge
* @author Arno Haase
*/
public class OperationCall extends FeatureCall {
private Expression[] params;
public OperationCall(final int start, final int end, final int line, final int startOffset, final int endOffset, final Identifier name,
final Expression target, final Expression[] params) {
super(start, end, line, startOffset, endOffset, name, target);
this.params = params;
}
public Expression[] getParams() {
return params;
}
@Override
public Object evaluateInternal(final ExecutionContext ctx) {
final Object[] params = new Object[getParams().length];
final EClassifier[] paramTypes = new EClassifier[params.length];
for (int i = 0; i < getParams().length; i++) {
params[i] = getParams()[i].evaluate(ctx);
paramTypes[i] = BuiltinMetaModel.getType(params[i]);
}
Object targetObj = null;
if (getTarget() == null) {
// extension
final Extension f = ctx.getExtension(getName().getValue(), paramTypes);
if (f != null) {
return f.evaluate(params, ctx);
}
// implicite
final Variable var = ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE);
if (var == null) {
// FIXME "could not find *context*, not *extension*?
throw new EvaluationException("Couldn't find extension '" + getName().getValue()
+ getParamTypes(params, ctx) + "'!", this);
}
targetObj = var.getValue();
} else {
targetObj = getTarget().evaluate(ctx);
}
// operation
Operation op = BuiltinMetaModel.executableOperation(getName().getValue(), params, targetObj);
if (op != null) {
return op.evaluate();
}
// extension as members
EClassifier[] ps = new EClassifier[paramTypes.length + 1];
ps[0] = BuiltinMetaModel.getType(targetObj);
System.arraycopy(paramTypes, 0, ps, 1, paramTypes.length);
Extension f = ctx.getExtension(getName().getValue(), ps);
if (f != null) {
try {
Object[] paramsAll = new Object[params.length + 1];
paramsAll[0] = targetObj;
System.arraycopy(params, 0, paramsAll, 1, params.length);
return f.evaluate(paramsAll, ctx);
} finally {
}
}
if (targetObj instanceof Collection) {
final List<Object> result = new ArrayList<Object>();
final Collection col = (Collection) targetObj;
for (final Iterator iter = col.iterator(); iter.hasNext();) {
final Object element = iter.next();
// operation
op = BuiltinMetaModel.executableOperation(getName().getValue(), params, element);
if (op != null) {
final Object r = op.evaluate();
if (r instanceof Collection) {
result.addAll((Collection<?>) r);
} else {
result.add(r);
}
} else {
// extension as members
ps = new EClassifier[paramTypes.length + 1];
ps[0] = BuiltinMetaModel.getType(element);
System.arraycopy(paramTypes, 0, ps, 1, paramTypes.length);
f = ctx.getExtension(getName().getValue(), ps);
if (f != null) {
Object[] paramsAll = new Object[params.length + 1];
paramsAll[0] = element;
System.arraycopy(params, 0, paramsAll, 1, params.length);
final Object r = f.evaluate(paramsAll, ctx);
if (r instanceof Collection) {
result.addAll((Collection<?>) r);
} else {
result.add(r);
}
} else {
throw new EvaluationException("Couldn't find operation '" + getName().getValue()
+ getParamTypes(params, ctx) + "' for " + BuiltinMetaModel.getType(targetObj).getName() + "!", this);
}
}
}
return result;
}
if ((targetObj != null) && (f == null) && (op == null)) {
throw new EvaluationException("Couldn't find operation '" + getName().getValue()
+ getParamTypes(params, ctx) + "' for " + BuiltinMetaModel.getType(targetObj).getName() + ".", this);
} else {
return null;
}
}
@Override
public EClassifier analyze(final ExecutionContext ctx, final Set<AnalysationIssue> issues) {
final EClassifier[] paramTypes = new EClassifier[getParams().length];
for (int i = 0; i < getParams().length; i++) {
paramTypes[i] = getParams()[i].analyze(ctx, issues);
if (paramTypes[i] == null) {
return createAnalyzeTrace(ctx, new OperationCallTrace(Type.UNDESOLVED_PARAMETER_TYPE));
}
}
// extension
EClassifier targetType = null;
if (getTarget() == null) {
Extension f = null;
try {
f = ctx.getExtension(getName().getValue(), paramTypes);
} catch (final Exception e) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.INTERNAL_ERROR, "Error parsing extensions : "
+ e.getMessage(), this));
}
if (f != null) {
return createAnalyzeTrace(ctx, new OperationCallTrace(f.getReturnType(paramTypes, ctx, issues), OperationCallTrace.getParamTypes(f, ctx), OperationCallTrace.getNativeLibraryName(f),
Type.STATIC_EXTENSION_REF, OperationCallTrace.isStaticQvtoCall(ctx, f)));
}
final Variable var = ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE);
if (var != null) {
targetType = (EClassifier) var.getValue();
} else {
// FIXME could not find *this*
issues.add(new AnalysationIssue(AnalysationIssue.Type.FEATURE_NOT_FOUND, "Couldn't find extensions : "
+ toString(), this));
}
} else {
targetType = getTarget().analyze(ctx, issues);
}
if (targetType == null) {
return createAnalyzeTrace(ctx, new OperationCallTrace(Type.UNDESOLVED_TARGET_TYPE));
}
// operation
EOperation op = BuiltinMetaModel.findOperation(targetType, getName().getValue(), paramTypes);
if (op != null) {
return createAnalyzeTrace(ctx, new OperationCallTrace(op.getEType() == null ? BuiltinMetaModel.VOID : BuiltinMetaModel.getTypedElementType(op), OperationCallTrace.getParamTypes(op), targetType, op));
}
// extension as members
final int issueSize = issues.size();
Extension extension = getStaticExtension(ctx, issues, paramTypes, targetType);
EClassifier rt = getExtensionReturnType(extension, ctx, issues, paramTypes, targetType);
if (rt != null) {
// [AS] This can be only "contextual" extension call - see comment
// below.
return createAnalyzeTrace(ctx, new OperationCallTrace(rt, OperationCallTrace.getParamTypes(extension, ctx), OperationCallTrace.getNativeLibraryName(extension), Type.EXTENSION_REF,
OperationCallTrace.isStaticQvtoCall(ctx, extension)));
} else if (issueSize < issues.size()) {
return null;
}
String additionalMsg = "";
if (BuiltinMetaModel.isParameterizedType(targetType)) {
final EClassifier innerType = BuiltinMetaModel.getInnerType(targetType);
op = BuiltinMetaModel.findOperation(innerType, getName().getValue(), paramTypes);
if (op != null) {
rt = op.getEType() == null ? BuiltinMetaModel.VOID : op.getEType();
if (BuiltinMetaModel.isParameterizedType(rt)) {
rt = BuiltinMetaModel.getInnerType(rt);
}
return createAnalyzeTrace(ctx, new OperationCallTrace(BuiltinMetaModel.getListType(rt), OperationCallTrace.getParamTypes(op), targetType, op, OperationCallTrace.Type.IMPLICIT_COLLECT_OPERATION_REF));
}
extension = getStaticExtension(ctx, issues, paramTypes, innerType);
rt = getExtensionReturnType(extension, ctx, issues, paramTypes, innerType);
if (rt != null) {
if (BuiltinMetaModel.isParameterizedType(rt)) {
rt = BuiltinMetaModel.getInnerType(rt);
}
return createAnalyzeTrace(ctx, new OperationCallTrace(BuiltinMetaModel.getListType(rt), OperationCallTrace.getParamTypes(extension, ctx), targetType, OperationCallTrace.getNativeLibraryName(extension), OperationCallTrace.isStaticQvtoCall(ctx, extension)));
}
additionalMsg = " or type '" + innerType + "'";
}
issues.add(new AnalysationIssue(AnalysationIssue.Type.FEATURE_NOT_FOUND, "Couldn't find operation '"
+ getName().getValue() + getParamsString(paramTypes) + "' for type '" + targetType.getName() + "'"
+ additionalMsg, this));
return null;
}
private EClassifier getExtensionReturnType(Extension extension, ExecutionContext ctx, Set<AnalysationIssue> issues, EClassifier[] paramEClassifiers, EClassifier targetEClassifier) {
if (extension == null) {
return null;
}
EClassifier[] pts = getStaticCallParameters(targetEClassifier, paramEClassifiers);
Set<AnalysationIssue> temp = new HashSet<AnalysationIssue>();
EClassifier rt = extension.getReturnType(pts, ctx, temp);
if (rt == null) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.INTERNAL_ERROR, "couldn't resolve return type for extension " + extension + "! Errors : " + temp.toString(), this));
}
return rt;
}
private EClassifier[] getStaticCallParameters(EClassifier targetEClassifier, EClassifier[] paramEClassifiers) {
EClassifier[] pts = new EClassifier[paramEClassifiers.length + 1];
pts[0] = targetEClassifier;
System.arraycopy(paramEClassifiers, 0, pts, 1, paramEClassifiers.length);
return pts;
}
private Extension getStaticExtension(final ExecutionContext ctx, final Set<AnalysationIssue> issues, final EClassifier[] paramEClassifiers,
final EClassifier targetEClassifier) {
final EClassifier[] pts = getStaticCallParameters(targetEClassifier, paramEClassifiers);
Extension f = null;
try {
f = ctx.getExtension(getName().getValue(), pts);
} catch (final Exception e) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.INTERNAL_ERROR, "Error parsing extensions : "
+ e.getMessage(), this));
}
if (f != null) {
return f;
} else if (getTarget() == null) { // try without implicite this
// [AS]: looks like this case was already covered while looking for
// static extension if target == null, so skipping it in a migration
try {
f = ctx.getExtension(getName().getValue(), paramEClassifiers);
} catch (final Exception e) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.INTERNAL_ERROR, "Error parsing extensions : "
+ e.getMessage(), this));
}
return f;
}
return null;
}
private String getParamTypes(final Object[] params2, final ExecutionContext ctx) {
final StringBuffer buff = new StringBuffer("(");
for (int i = 0; i < params2.length; i++) {
final EClassifier type = BuiltinMetaModel.getType(params2[i]);
buff.append(type.getName());
if (i + 1 < params2.length) {
buff.append(",");
}
}
return buff.append(")").toString();
}
private String getParamsString(final EClassifier[] paramTypes) {
final StringBuffer buff = new StringBuffer("(");
for (int i = 0; i < paramTypes.length; i++) {
final EClassifier type = paramTypes[i];
buff.append(type.getName());
if (i + 1 < paramTypes.length) {
buff.append(",");
}
}
return buff.append(")").toString();
}
@Override
public String toString() {
return (getTarget() != null ? getTarget().toString() + "." : "") + getName()
+ getParamsExpressionString(getParams());
}
private String getParamsExpressionString(final Expression[] params2) {
final StringBuffer buff = new StringBuffer("(");
for (int i = 0; i < params2.length; i++) {
buff.append(params2[i]);
if (i + 1 < params2.length) {
buff.append(",");
}
}
return buff.append(")").toString();
}
}