/******************************************************************************* * Copyright (c) 2005, 2009 committers of openArchitectureWare 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: * committers of openArchitectureWare - initial API and implementation *******************************************************************************/ package org.eclipse.internal.xpand2.ast; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.internal.xpand2.XpandTokens; import org.eclipse.internal.xpand2.XpandUtil; import org.eclipse.internal.xpand2.model.XpandDefinition; import org.eclipse.internal.xtend.expression.ast.Expression; import org.eclipse.internal.xtend.expression.ast.Identifier; import org.eclipse.internal.xtend.util.ProfileCollector; import org.eclipse.xpand2.XpandCompilerIssue; import org.eclipse.xpand2.XpandExecutionContext; import org.eclipse.xpand2.XpandExecutionContextImpl; import org.eclipse.xpand2.output.InsertionPointSupport; import org.eclipse.xtend.expression.AnalysationIssue; import org.eclipse.xtend.expression.EvaluationException; import org.eclipse.xtend.expression.ExecutionContext; import org.eclipse.xtend.expression.Variable; import org.eclipse.xtend.typesystem.ParameterizedType; import org.eclipse.xtend.typesystem.Type; /** * * @author Sven Efftinge (http://www.efftinge.de) * @author Karsten Thoms */ public class ExpandStatement extends Statement { private boolean foreach = false; private Expression[] parameters = new Expression[0]; private Expression separator = null; private Expression target = null; private Identifier definition; private boolean onFileClose = false; private boolean deferredToFileClose = false; private String targetNamespace; public ExpandStatement(final Identifier definition, final Expression target, final Expression separator, final Expression[] parameters, final boolean foreach, final boolean onFileClose) { this.definition = definition; this.target = target; this.separator = separator; this.parameters = parameters != null ? parameters : new Expression[0]; this.foreach = foreach; this.onFileClose = onFileClose; } public Identifier getDefinition() { return definition; } public boolean isForeach() { return foreach; } public Expression[] getParameters() { return parameters; } public List<Expression> getParametersAsList() { return Arrays.asList(parameters); } public Expression getSeparator() { return separator; } public Expression getTarget() { return target; } public boolean isOnFileClose() { return onFileClose; } /** * Retrieves the namespace of the target definition(s). * This method requires that either analyzeInternal() or evaluateInternal() * was invoked before, otherwise result will be null. * @since 1.0.0 M5 */ public String getTargetNamespace () { return targetNamespace; } @Override public void analyzeInternal(final XpandExecutionContext ctx, final Set<AnalysationIssue> issues) { final Type[] paramTypes = new Type[getParameters().length]; for (int i = 0; i < getParameters().length; i++) { paramTypes[i] = getParameters()[i].analyze(ctx, issues); } Type targetType = null; if (isForeach()) { targetType = target.analyze(ctx, issues); if (ctx.getCollectionType(ctx.getObjectType()).isAssignableFrom(targetType)) { if (targetType instanceof ParameterizedType) { targetType = ((ParameterizedType) targetType).getInnerType(); } else { targetType = ctx.getObjectType(); } } else { issues.add(new AnalysationIssue(AnalysationIssue.INCOMPATIBLE_TYPES, "Collection type expected!", target)); return; } } else { final Variable var = ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE); if (var == null) { issues.add(new AnalysationIssue(AnalysationIssue.INTERNAL_ERROR, "No implicit variable 'this' could be found!", target)); return; } targetType = (Type) var.getValue(); if (target != null) { targetType = target.analyze(ctx, issues); } } if (targetType == null || Arrays.asList(paramTypes).contains(null)) return; final XpandDefinition def = ctx.findDefinition(getDefinition().getValue(), targetType, paramTypes); if (def == null) { issues.add(new AnalysationIssue(XpandCompilerIssue.DEFINITION_NOT_FOUND, "Couldn't find definition " + getDefinition().getValue() + getParamTypeString(paramTypes) + " for type " + targetType.getName(), this)); } else { targetNamespace = XpandUtil.withoutLastSegment(def.getOwner().getFullyQualifiedName()); } } @Override public void evaluateInternal(XpandExecutionContext ctx) { final String defName = getDefinition().getValue(); final String sep = (String) (separator != null ? separator.evaluate(ctx) : null); if (onFileClose && !deferredToFileClose) { if (!(ctx.getOutput() instanceof InsertionPointSupport)) { throw new EvaluationException("Output implementation must implement InsertionPointSupport when using "+XpandTokens.ONFILECLOSE, this, ctx); } ctx = ((XpandExecutionContextImpl)ctx).cloneWithStatement(this); ((InsertionPointSupport)ctx.getOutput()).registerInsertionPoint(this); deferredToFileClose = true; return; } // reset here, otherwise we would need a try-finally block deferredToFileClose = false; Object targetObject = null; if (isForeach()) { targetObject = target.evaluate(ctx); if (targetObject != null) { if (!(targetObject instanceof Collection<?>)) throw new EvaluationException("Collection expected!", target, ctx); final Collection<?> col = (Collection<?>) targetObject; for (final Iterator<?> iter = col.iterator(); iter.hasNext();) { final Object targetObj = iter.next(); final Object[] params = new Object[getParameters().length]; for (int i = 0; i < getParameters().length; i++) { params[i] = getParameters()[i].evaluate(ctx); } final Type[] paramTypes = new Type[params.length]; for (int i = 0; i < params.length; i++) { paramTypes[i] = ctx.getType(params[i]); } ctx.preTask(this); invokeDefinition(defName, targetObj, params, paramTypes, ctx); ctx.postTask(this); if (sep != null && iter.hasNext()) { if (onFileClose && ctx.getOutput() instanceof InsertionPointSupport) { ((InsertionPointSupport)ctx.getOutput()).activateInsertionPoint(this); } try { ctx.getOutput().write(sep); } finally { if (onFileClose && ctx.getOutput() instanceof InsertionPointSupport) { ((InsertionPointSupport)ctx.getOutput()).deactivateInsertionPoint(this); } } } } } } else { final Object[] params = new Object[getParameters().length]; for (int i = 0; i < getParameters().length; i++) { params[i] = getParameters()[i].evaluate(ctx); } final Type[] paramTypes = new Type[params.length]; for (int i = 0; i < params.length; i++) { paramTypes[i] = ctx.getType(params[i]); } if (target != null) { targetObject = target.evaluate(ctx); } else { final Variable var = ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE); targetObject = var.getValue(); } if (targetObject != null) invokeDefinition(defName, targetObject, params, paramTypes, ctx); } } private void invokeDefinition(final String defName, final Object targetObj, final Object[] params, final Type[] paramTypes, XpandExecutionContext ctx) { final Type t = ctx.getType(targetObj); final XpandDefinition def = ctx.findDefinition(defName, t, paramTypes); if (def == null) { String errorMsg = "No Definition '" + defName + getParamTypeString(paramTypes) + " for " + t.getName() + "' found!"; if (t.getTypeSystem().getObjectType().equals(t)) { errorMsg += " Maybe your forgot to configure the correct meta model in the workflow?"; } throw new EvaluationException(errorMsg, this, ctx); } targetNamespace = XpandUtil.withoutLastSegment(def.getOwner().getFullyQualifiedName()); try { ProfileCollector.getInstance().enter(def.toString()); def.evaluate((XpandExecutionContext) ctx.cloneWithoutVariables(), targetObj, params); } catch (EvaluationException e) { e.addStackElement(this, ctx); throw e; } finally { ProfileCollector.getInstance().leave(); } } private String getParamTypeString(final Type[] paramTypes) { if (paramTypes.length == 0) return ""; final StringBuffer buff = new StringBuffer("("); for (int i = 0; i < paramTypes.length; i++) { final Type type = paramTypes[i]; buff.append(type.getName()); if (i + 1 < paramTypes.length) { buff.append(", "); } } return buff.append(")").toString(); } private String getParamString(final Expression[] paramTypes) { if (paramTypes.length == 0) return ""; final StringBuffer buff = new StringBuffer("("); for (int i = 0; i < paramTypes.length; i++) { final Expression type = paramTypes[i]; buff.append(type); if (i + 1 < paramTypes.length) { buff.append(", "); } } return buff.append(")").toString(); } @Override public String toString() { return "EXPAND " + definition + getParamString(getParameters()) + (target != null ? (isForeach() ? " FOREACH " : " FOR ") + target : "") + (separator != null ? " SEPARATOR " + separator : "") + (onFileClose ? " ONFILECLOSE " : ""); } @Override public String getNameString(ExecutionContext context) { return XpandTokens.EXPAND; } }