/*
* <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.xtend.ast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.gmf.internal.xpand.eval.EvaluationListener;
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.expression.ast.DeclaredParameter;
import org.eclipse.gmf.internal.xpand.expression.ast.Identifier;
import org.eclipse.gmf.internal.xpand.expression.ast.SyntaxElement;
public abstract class Extension extends SyntaxElement /*implements ParameterizedCallable*/ {
private final Identifier name;
private final List<DeclaredParameter> formalParameters;
protected ExtensionFile file;
protected final boolean cached;
private final boolean isPrivate;
protected final Identifier returnType;
private List<EClassifier> resolvedParameterTypes = null;
public Extension(final int start, final int end, final int line, final int startOffset, final int endOffset, final Identifier name,
final Identifier returnType, final List<DeclaredParameter> formalParameters, final boolean cached, final boolean isPrivate) {
super(start, end, line, startOffset, endOffset);
this.name = name;
this.formalParameters = formalParameters;
this.returnType = returnType;
this.cached = cached;
this.isPrivate = isPrivate;
}
public List<DeclaredParameter> getFormalParameters() {
return formalParameters;
}
public String getName() {
return name.getValue();
}
public final EClassifier getReturnType(final EClassifier[] parameters, ExecutionContext ctx, final Set<AnalysationIssue> issues) {
ctx = ctx.cloneWithResource(getExtensionFile());
return internalGetReturnType(parameters, ctx, issues);
}
protected abstract EClassifier internalGetReturnType(EClassifier[] parameters, ExecutionContext ctx, Set<AnalysationIssue> issues);
public final void analyze(ExecutionContext ctx, final Set<AnalysationIssue> issues) {
final List<DeclaredParameter> params = getFormalParameters();
final Set<String> usedNames = new HashSet<String>();
for (final Iterator<DeclaredParameter> iter = params.iterator(); iter.hasNext();) {
final DeclaredParameter p = iter.next();
final EClassifier pt = ctx.getTypeForName(p.getType().getValue());
if (pt == null) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.TYPE_NOT_FOUND, "Type not found: "
+ p.getType().getValue(), p.getType()));
}
if (!usedNames.add(p.getName().getValue())) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.SYNTAX_ERROR, "Duplicate parameter name: "
+ p.getName().getValue(), p.getName()));
}
ctx = ctx.cloneWithVariable(new Variable(p.getName().getValue(), pt));
}
if (returnType != null) {
final EClassifier pt = ctx.getTypeForName(returnType.getValue());
if (pt == null) {
issues.add(new AnalysationIssue(AnalysationIssue.Type.TYPE_NOT_FOUND, "Type not found: "
+ returnType.getValue(), returnType));
}
}
analyzeInternal(ctx, issues);
}
protected abstract void analyzeInternal(ExecutionContext ctx, Set<AnalysationIssue> issues);
private final Map<List<Object>, Object> cache = new HashMap<List<Object>, Object>();
public Object evaluate(final Object[] parameters, ExecutionContext ctx) {
if (cached) {
final List<Object> l = Arrays.asList(parameters);
if (cache.containsKey(l)) {
return cache.get(l);
}
}
if (getExtensionFile() == null) {
throw new IllegalStateException("No containing file!");
}
ctx = ctx.cloneWithResource(getExtensionFile());
notifyEnter(ctx);
final Object result;
try {
result = evaluateInternal(parameters, ctx);
} finally {
notifyLeave(ctx);
}
if (cached) {
cache.put(Arrays.asList(parameters), result);
}
return result;
}
public final void setExtensionFile(final ExtensionFile f) {
file = f;
}
public ExtensionFile getExtensionFile() {
return file;
}
protected abstract Object evaluateInternal(Object[] parameters, ExecutionContext ctx);
public List<String> getParameterNames() {
final List<String> names = new ArrayList<String>();
for (final Iterator<DeclaredParameter> iter = getFormalParameters().iterator(); iter.hasNext();) {
names.add((iter.next()).getName().getValue());
}
return names;
}
public void init(final ExecutionContext ctx) {
if (resolvedParameterTypes == null) {
try {
resolvedParameterTypes = new ArrayList<EClassifier>();
for (final Iterator<DeclaredParameter> iter = getFormalParameters().iterator(); iter.hasNext();) {
final String name = (iter.next()).getType().getValue();
final EClassifier t = ctx.getTypeForName(name);
if (t == null) {
throw new EvaluationException("Couldn't resolve type for '" + name
+ "'. Did you forget to configure the corresponding metamodel?", this);
}
resolvedParameterTypes.add(t);
}
resolvedParameterTypes = Collections.unmodifiableList(resolvedParameterTypes);
} catch (final RuntimeException e) {
resolvedParameterTypes = null;
throw e;
}
}
}
public List<EClassifier> getParameterTypes() {
return resolvedParameterTypes;
}
public Identifier getReturnTypeIdentifier() {
return returnType;
}
@Override
public String toString() {
return (returnType != null ? returnType.getValue() + " " : "") + getName() + "(" + paramsToString() + ")";
}
private String paramsToString() {
final StringBuffer buff = new StringBuffer();
for (final Iterator<DeclaredParameter> iter = getFormalParameters().iterator(); iter.hasNext();) {
final DeclaredParameter element = iter.next();
buff.append(element.getType() + " " + element.getName());
if (iter.hasNext()) {
buff.append(",");
}
}
return buff.toString();
}
public boolean isPrivate() {
return isPrivate;
}
private void notifyEnter(ExecutionContext ctx) {
EvaluationListener l = ctx.getEvaluationListener();
if (l != null) {
l.enter(this, ctx);
}
}
private void notifyLeave(ExecutionContext ctx) {
EvaluationListener l = ctx.getEvaluationListener();
if (l != null) {
l.leave(this, ctx);
}
}
}