/******************************************************************************* * 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.xpand2; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.emf.mwe.core.monitor.ProgressMonitor; import org.eclipse.internal.xpand2.NoSuchTemplateException; import org.eclipse.internal.xpand2.XpandUtil; import org.eclipse.internal.xpand2.ast.Statement; import org.eclipse.internal.xpand2.model.AdvicedDefinition; import org.eclipse.internal.xpand2.model.XpandAdvice; import org.eclipse.internal.xpand2.model.XpandDefinition; import org.eclipse.internal.xpand2.model.XpandResource; import org.eclipse.internal.xpand2.parser.XpandParseFacade; import org.eclipse.internal.xpand2.pr.ProtectedRegionResolver; import org.eclipse.internal.xpand2.type.XpandTypesMetaModel; import org.eclipse.internal.xtend.expression.parser.SyntaxConstants; import org.eclipse.internal.xtend.type.baseimpl.PolymorphicResolver; import org.eclipse.internal.xtend.util.Cache; import org.eclipse.internal.xtend.util.Pair; import org.eclipse.internal.xtend.util.Triplet; import org.eclipse.internal.xtend.xtend.ast.Around; import org.eclipse.internal.xtend.xtend.ast.Extension; import org.eclipse.xpand2.output.Output; import org.eclipse.xtend.expression.ExceptionHandler; import org.eclipse.xtend.expression.ExecutionContextImpl; import org.eclipse.xtend.expression.NullEvaluationHandler; import org.eclipse.xtend.expression.Resource; import org.eclipse.xtend.expression.ResourceManager; import org.eclipse.xtend.expression.ResourceParser; import org.eclipse.xtend.expression.TypeSystemImpl; import org.eclipse.xtend.expression.Variable; import org.eclipse.xtend.expression.VetoableCallback; import org.eclipse.xtend.typesystem.Callable; import org.eclipse.xtend.typesystem.Operation; import org.eclipse.xtend.typesystem.Type; /** * * * * @author Sven Efftinge (http://www.efftinge.de) * */ public class XpandExecutionContextImpl extends ExecutionContextImpl implements XpandExecutionContext { private static final Log log = LogFactory.getLog(XpandExecutionContextImpl.class); protected final Output output; protected final ProtectedRegionResolver protectedRegionResolver; private List<XpandAdvice> registeredAdvices = new ArrayList<XpandAdvice>(); private List<Pair<XpandExecutionContextImpl,Statement>> deferredStatements = null; public XpandExecutionContextImpl(Output output, ProtectedRegionResolver prs) { this (output, prs, null, null, null); } public XpandExecutionContextImpl(Output output, ProtectedRegionResolver prs, String fileEncoding) { this (output, prs, null, null, null); resourceManager.setFileEncoding(fileEncoding); } public XpandExecutionContextImpl(Output output, ProtectedRegionResolver prs, Map<String, Variable> globalVars, ExceptionHandler exceptionHandler, NullEvaluationHandler nullEvaluationHandler) { this(new TypeSystemImpl(), output, prs, globalVars, exceptionHandler, nullEvaluationHandler); } public XpandExecutionContextImpl( ResourceManager resourceManager, Output output, ProtectedRegionResolver prs, Map<String, Variable> globalVars, ProgressMonitor monitor, ExceptionHandler exceptionHandler, NullEvaluationHandler nullEvaluationHandler, VetoableCallback callback) { this( resourceManager, /*Resource*/ null, new TypeSystemImpl(), /*Map<String, Variable> vars*/ null, globalVars, output, prs, monitor, exceptionHandler, /*List<Around>*/ null, nullEvaluationHandler, /*Map<Resource, Set<Extension>> allExtensionsPerResource*/ null, callback, /*Cache<Triplet<Resource,String,List<Type>>,Extension> extensionsForNameAndTypesCache*/ null, /*Map<Pair<String, List<Type>>, Type> extensionsReturnTypeCache*/ null, /*List<Pair<XpandExecutionContextImpl,Statement>> deferredStatements*/ null ); } protected XpandExecutionContextImpl( final TypeSystemImpl ts, Output output, ProtectedRegionResolver prs, Map<String, Variable> globalVars, ExceptionHandler exceptionHandler, NullEvaluationHandler nullEvaluationHandler) { super(ts, globalVars); registerMetaModel(new XpandTypesMetaModel(this)); registerParser(resourceManager); this.output = output; this.protectedRegionResolver = prs; this.exceptionHandler = exceptionHandler; this.nullEvaluationHandler = nullEvaluationHandler; } protected XpandExecutionContextImpl ( ResourceManager resourceManager, Resource currentResource, TypeSystemImpl typeSystem, Map<String, Variable> vars, Map<String, Variable> globalVars, Output output, ProtectedRegionResolver protectedRegionResolver, ProgressMonitor monitor, ExceptionHandler exceptionHandler, List<Around> advices, NullEvaluationHandler nullEvaluationHandler, Map<Resource, Set<Extension>> allExtensionsPerResource, VetoableCallback callback, Cache<Triplet<Resource,String,List<Type>>,Extension> extensionsForNameAndTypesCache, Map<Pair<String, List<Type>>, Type> extensionsReturnTypeCache, List<Pair<XpandExecutionContextImpl,Statement>> deferredStatements) { super ( resourceManager, currentResource, typeSystem, vars, globalVars, monitor, exceptionHandler, advices, nullEvaluationHandler, allExtensionsPerResource, callback, extensionsForNameAndTypesCache, extensionsReturnTypeCache); registerMetaModel(new XpandTypesMetaModel(this)); registerParser(resourceManager); this.output = output; this.protectedRegionResolver = protectedRegionResolver; this.exceptionHandler = exceptionHandler; this.deferredStatements = deferredStatements; } private void registerParser(ResourceManager resourceManager) { resourceManager.registerParser(XpandUtil.TEMPLATE_EXTENSION, new ResourceParser() { public Resource parse(Reader in, String fileName) { return XpandParseFacade.file(in, fileName); }}); } @Override public ExecutionContextImpl cloneContext() { final XpandExecutionContextImpl result = new XpandExecutionContextImpl (resourceManager, currentResource(), typeSystem, getVisibleVariables(), getGlobalVariables(), output, protectedRegionResolver, getMonitor(), exceptionHandler,registeredExtensionAdvices, nullEvaluationHandler,allExtensionsPerResource, callback,this.extensionsForNameAndTypesCache, this.extensionsReturnTypeCache, this.deferredStatements); result.registeredAdvices.addAll(registeredAdvices); //todo: [aha] before I refactored, there was an assignment in this place. Is this modification correct? return result; } public XpandExecutionContextImpl cloneWithStatement(Statement stmt) { final XpandExecutionContextImpl result = new XpandExecutionContextImpl (resourceManager, currentResource(), typeSystem, getVisibleVariables(), getGlobalVariables(), output, protectedRegionResolver, getMonitor(), exceptionHandler,registeredExtensionAdvices, nullEvaluationHandler,allExtensionsPerResource, callback,this.extensionsForNameAndTypesCache, this.extensionsReturnTypeCache, this.deferredStatements); result.registeredAdvices.addAll(registeredAdvices); //todo: [aha] before I refactored, there was an assignment in this place. Is this modification correct? if (deferredStatements==null) { deferredStatements = new ArrayList<Pair<XpandExecutionContextImpl,Statement>>(); } deferredStatements.add(new Pair(this, stmt)); return result; } public List<XpandDefinition> getAllDefinitions() { XpandResource tpl = null; tpl = (XpandResource) currentResource(); if (tpl == null) return null; XpandDefinition[] localDefinitions = tpl.getDefinitions(); List<XpandDefinition> advicedDefinitions = new ArrayList<XpandDefinition>(localDefinitions.length); for (int i = 0; i < localDefinitions.length; i++) { XpandDefinition xpandDefinition = localDefinitions[i]; for (int x = registeredAdvices.size() - 1; x >= 0; x--) { final XpandAdvice adv = registeredAdvices.get(x); if (adv.matches(xpandDefinition, this)) { xpandDefinition = new AdvicedDefinition(adv, xpandDefinition); } } advicedDefinitions.add(xpandDefinition); } return advicedDefinitions; } public XpandDefinition findDefinition(final String name, final Type target, final Type[] paramTypes) { XpandResource tpl = null; if (name.indexOf(SyntaxConstants.NS_DELIM) != -1) { // local call tpl = findTemplate(XpandUtil.withoutLastSegment(name)); } else { tpl = (XpandResource) currentResource(); } if (tpl == null) return null; final XpandExecutionContext ctx = (XpandExecutionContext) cloneWithResource(tpl); XpandDefinition def = findDefinition(tpl.getDefinitions(), name, target, paramTypes, ctx); if (def != null) { for (int x = registeredAdvices.size() - 1; x >= 0; x--) { final XpandAdvice adv = registeredAdvices.get(x); if (adv.matches(def, this)) { def = new AdvicedDefinition(adv, def); } } } return def; } public void registerAdvices(final String fullyQualifiedName) { final XpandResource tpl = findTemplate(fullyQualifiedName); if (tpl == null) throw new NoSuchTemplateException(fullyQualifiedName); final XpandAdvice[] as = tpl.getAdvices(); for (int i = 0; i < as.length; i++) { final XpandAdvice advice = as[i]; if (registeredAdvices.contains(advice)) { log.warn("advice " + advice.toString() + " allready registered!"); } else { registeredAdvices.add(advice); } } } public ProtectedRegionResolver getProtectedRegionResolver() { return protectedRegionResolver; } public Output getOutput() { return output; } public XpandResource findTemplate(final String templateName) { return findTemplate(templateName, getImportedNamespaces()); } public XpandResource findTemplate(final String templateName, String[] importedNs) { final List<?> possibleNames = typeSystem.getPossibleNames(templateName, importedNs); for (final Iterator<?> iter = possibleNames.iterator(); iter.hasNext();) { final String element = (String) iter.next(); final XpandResource tpl = (XpandResource) resourceManager.loadResource(element, XpandUtil.TEMPLATE_EXTENSION); if (tpl != null) return tpl; } return null; } /** * resolves the correct definition (using parametric polymorphism) * * @param definitions * @param target * @param paramTypes * @return */ private XpandDefinition findDefinition(final XpandDefinition[] definitions, final String name, final Type target, Type[] paramTypes, final XpandExecutionContext ctx) { if (paramTypes == null) { paramTypes = new Type[0]; } final Set<Callable> features = new HashSet<Callable>(); for (int i = 0; i < definitions.length; i++) { final XpandDefinition def = definitions[i]; if (def.getParams().length == paramTypes.length) { final List<Type> defsParamTypes = new ArrayList<Type>(); Type t = null; boolean complete = true; for (int j = 0; j < paramTypes.length && complete; j++) { t = ctx.getTypeForName(def.getParams()[j].getType().getValue()); if (t == null) { complete = false; } defsParamTypes.add(t); } t = ctx.getTypeForName(def.getTargetType()); if (t == null) { complete = false; } if (complete) { features.add(new DefinitionOperationAdapter(def, def.getName(), t, defsParamTypes)); } } } final DefinitionOperationAdapter defAdapter = (DefinitionOperationAdapter) PolymorphicResolver.getOperation( features, XpandUtil.getLastSegment(name), target, Arrays.asList(paramTypes)); if (defAdapter != null) return defAdapter.def; return null; } public class DefinitionOperationAdapter implements Operation { private String name; private Type owner; private List<Type> paramTypes; public XpandDefinition def; public DefinitionOperationAdapter(final XpandDefinition def, final String name, final Type owner, final List<Type> paramTypes) { this.name = name; this.owner = owner; this.paramTypes = paramTypes; this.def = def; } public String getName() { return name; } public Type getReturnType() { throw new UnsupportedOperationException(); } public Type getOwner() { return owner; } public List<Type> getParameterTypes() { return paramTypes; } public Object evaluate(final Object target, final Object[] params) { throw new UnsupportedOperationException(); } public String getDocumentation() { return "Xpand definition " + getName() + " adapted in an Operation"; } public Type getReturnType(final Type targetType, final Type[] paramTpes) { return getReturnType(); } } /** * @deprecated Context must be immutable, use the existing constructors */ @Deprecated public void setResourceManager(ResourceManager resourceManager) { throw new UnsupportedOperationException("Context must be immutable, use the existing constructors"); } @SuppressWarnings("unchecked") public List<Pair<XpandExecutionContextImpl, Statement>> getDeferredStatements() { return (List<Pair<XpandExecutionContextImpl, Statement>>) (deferredStatements != null ? deferredStatements : Collections.emptyList()); } }