/******************************************************************************* * Copyright (c) 2005, 2007 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.xtend.expression; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; 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.xtend.expression.ast.AbstractVisitor; import org.eclipse.internal.xtend.expression.ast.DeclaredParameter; import org.eclipse.internal.xtend.expression.ast.Identifier; import org.eclipse.internal.xtend.expression.ast.SyntaxElement; import org.eclipse.internal.xtend.type.baseimpl.PolymorphicResolver; import org.eclipse.internal.xtend.type.baseimpl.TypesComparator; 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.XtendFile; import org.eclipse.internal.xtend.xtend.ast.Around; import org.eclipse.internal.xtend.xtend.ast.Extension; import org.eclipse.internal.xtend.xtend.ast.ExtensionFile; import org.eclipse.internal.xtend.xtend.types.AdviceContext; import org.eclipse.xtend.typesystem.MetaModel; import org.eclipse.xtend.typesystem.Operation; import org.eclipse.xtend.typesystem.Property; import org.eclipse.xtend.typesystem.Type; /** * @author Sven Efftinge (http://www.efftinge.de) * @author Arno Haase * @author Peter Friese */ public class ExecutionContextImpl implements ExecutionContext { private final static Log log = LogFactory.getLog(ExecutionContextImpl.class); protected ResourceManager resourceManager; private final Map<String, Variable> variables = new HashMap<String, Variable>(); private Map<String, Variable> globalVars; private ProgressMonitor monitor; /** * this field is conceptually final, i.e. it is set only at object * construction time. To simplify implementation, it is however technically * not final. This is done so that the cloneWith/WithoutResource methods can * delegate to cloneContext and afterwards modify the instance. That * provides cloneContext as a single method for subclasses to override. */ private Resource currentResource; protected final TypeSystemImpl typeSystem; protected ExceptionHandler exceptionHandler; protected NullEvaluationHandler nullEvaluationHandler; protected VetoableCallback callback; public ExecutionContextImpl() { this((Map<String, Variable>) null); } public ExecutionContextImpl(TypeSystemImpl ts) { this(ts, null); } public ExecutionContextImpl(Map<String, Variable> globalVars) { this(new ResourceManagerDefaultImpl(), null, new TypeSystemImpl(), new HashMap<String, Variable>(), globalVars, null, null, null, null, null, null,null, null); } public ExecutionContextImpl(TypeSystemImpl ts, Map<String, Variable> globalVars) { this(new ResourceManagerDefaultImpl(), null, ts, new HashMap<String, Variable>(), globalVars, null, null, null, null, null, null,null, null); } public ExecutionContextImpl(ResourceManager resourceManager, TypeSystemImpl typeSystem, Map<String, Variable> globalVars) { this(resourceManager, null, typeSystem, new HashMap<String, Variable>(), globalVars, null, null, null, null, null, null,null, null); } public ExecutionContextImpl(ResourceManager resourceManager, Resource resource, TypeSystemImpl typeSystem, Map<String, Variable> variables, Map<String, Variable> globalVars, ProgressMonitor monitor, ExceptionHandler exceptionHandler, List<Around> advices, NullEvaluationHandler neh2, Map<Resource, Set<Extension>> extensionPerResourceMap, VetoableCallback callback, Cache<Triplet<Resource,String,List<Type>>,Extension> extensionsForNameAndTypesCache, Map<Pair<String, List<Type>>, Type> extensionsReturnTypeCache) { if (extensionPerResourceMap != null) { this.allExtensionsPerResource = extensionPerResourceMap; } else { this.allExtensionsPerResource = new HashMap<Resource, Set<Extension>>(); } if (extensionsForNameAndTypesCache!=null) this.extensionsForNameAndTypesCache = extensionsForNameAndTypesCache; if (extensionsReturnTypeCache!=null) this.extensionsReturnTypeCache = extensionsReturnTypeCache; this.resourceManager = resourceManager; this.currentResource = resource; this.typeSystem = typeSystem; if (variables != null) { this.variables.putAll(variables); } if (globalVars == null) { this.globalVars = new HashMap<String, Variable>(); } else { this.globalVars = globalVars; } this.monitor = monitor; this.exceptionHandler = exceptionHandler; if (advices != null) { registeredExtensionAdvices = advices; } this.nullEvaluationHandler = neh2; this.callback = callback; } public VetoableCallback getCallback() { return callback; } public void registerMetaModel(final MetaModel mm) { typeSystem.registerMetaModel(mm); } public List<MetaModel> getMetaModels () { return typeSystem.getMetaModels(); } public Operation findOperation(final String name, final Object target, final Object[] params) { return typeSystem.findOperation(name, target, params); } public Property findProperty(final String name, final Object target) { return typeSystem.findProperty(name, target); } public Type[] findTypesForPrefix(final String prefix) { return typeSystem.findTypesForPrefix(prefix, getImportedNamespaces()); } public Type[] getAllTypes() { return typeSystem.getAllTypes(); } public Set<String> getNamespaces() { return typeSystem.getNamespaces(); } public Type getBooleanType() { return typeSystem.getBooleanType(); } public Type getCollectionType(final Type innerType) { return typeSystem.getCollectionType(innerType); } public Type getFeatureType() { return typeSystem.getFeatureType(); } public Type getIntegerType() { return typeSystem.getIntegerType(); } public Type getListType(final Type innerType) { return typeSystem.getListType(innerType); } public Type getObjectType() { return typeSystem.getObjectType(); } public Type getOperationType() { return typeSystem.getOperationType(); } public Type getPropertyType() { return typeSystem.getPropertyType(); } public Type getRealType() { return typeSystem.getRealType(); } public Type getSetType(final Type innerType) { return typeSystem.getSetType(innerType); } public Type getStaticPropertyType() { return typeSystem.getStaticPropertyType(); } public Type getStringType() { return typeSystem.getStringType(); } public Type getType(final Object obj) { return typeSystem.getType(obj); } public Type getTypeForName(final String name) { return typeSystem.getTypeForName(name, getImportedNamespaces()); } protected String[] getImportedNamespaces() { return currentResource != null ? currentResource.getImportedNamespaces() : new String[0]; } public Type getTypeType() { return typeSystem.getTypeType(); } public Type getVoidType() { return typeSystem.getVoidType(); } public ExecutionContextImpl cloneContext() { return new ExecutionContextImpl(resourceManager, currentResource, typeSystem, variables, globalVars, monitor, exceptionHandler, registeredExtensionAdvices, nullEvaluationHandler, allExtensionsPerResource, callback, extensionsForNameAndTypesCache, extensionsReturnTypeCache); } /** * @deprecated Use getResourceManager().setFileEncoding() instead */ @Deprecated public void setFileEncoding(final String encoding) { if (resourceManager != null) { resourceManager.setFileEncoding(encoding); } } public Variable getVariable(final String name) { return variables.get(name); } public Map<String, Variable> getVisibleVariables() { return Collections.unmodifiableMap(variables); } public Map<String, Variable> getGlobalVariables() { return globalVars; } public ExecutionContext cloneWithVariable(final Variable v) { final ExecutionContextImpl result = cloneContext(); result.variables.put(v.getName(), v); return result; } public ExecutionContext cloneWithoutVariables() { final ExecutionContextImpl result = cloneContext(); result.variables.clear(); return result; } public ExecutionContext cloneWithResource(final Resource ns) { final ExecutionContextImpl ctx = cloneContext(); ctx.currentResource = ns; return ctx; } public ExecutionContext cloneWithoutResource() { final ExecutionContextImpl ctx = cloneContext(); ctx.currentResource = null; return ctx; } public ExecutionContext cloneWithoutMonitor() { final ExecutionContextImpl ctx = cloneContext(); ctx.setMonitor(null); return ctx; } public Resource currentResource() { return currentResource; } public Set<? extends Extension> getAllExtensions() { return internalAllExtensions(currentResource()); } protected Map<Resource, Set<Extension>> allExtensionsPerResource = null; private Set<? extends Extension> internalAllExtensions(Resource currentResource2) { Set<Extension> allExtensions = allExtensionsPerResource.get(currentResource2); if (allExtensions == null) { ExecutionContext ctx = this.cloneWithResource(currentResource2); allExtensions = new HashSet<Extension>(); final Resource res = currentResource2; if (res != null) { if (res instanceof XtendFile) { final List<Extension> extensionList = ((XtendFile) res).getExtensions(); for (Extension element : extensionList) { element.init(ctx); allExtensions.add(advise(element)); } } final String[] extensions = res.getImportedExtensions(); for (final String extension : extensions) { final Object o = resourceManager.loadResource(extension, XtendFile.FILE_EXTENSION); final XtendFile extFile = (XtendFile) o; if (extFile != null) { ctx = cloneWithResource(extFile); final List<Extension> extensionList = extFile.getPublicExtensions(resourceManager, ctx); for (final Extension element : extensionList) { element.init(ctx); allExtensions.add(advise(element)); } } } } allExtensionsPerResource.put(currentResource(), allExtensions); } return allExtensions; } protected Cache<Triplet<Resource,String,List<Type>>, Extension> extensionsForNameAndTypesCache = new Cache<Triplet<Resource,String,List<Type>>, Extension>(){ @Override protected Extension createNew(Triplet<Resource, String, List<Type>> arg0) { return PolymorphicResolver.getExtension(internalAllExtensions(arg0.getFirst()), arg0.getSecond(), arg0.getThird()); }}; public Extension getExtensionForTypes(final String functionName, final Type[] parameterTypes) { Triplet<Resource, String, List<Type>> key = new Triplet<Resource, String, List<Type>>(currentResource(),functionName,Arrays.asList(parameterTypes)); return extensionsForNameAndTypesCache.get(key); // return PolymorphicResolver.getExtension(getAllExtensions(), functionName, Arrays.asList(parameterTypes)); } public Extension getExtension(final String functionName, final Object[] actualParameters) { final Type[] types = new Type[actualParameters.length]; for (int i = 0; i < types.length; i++) { types[i] = getType(actualParameters[i]); } return getExtensionForTypes(functionName, types); } public void setMonitor(ProgressMonitor monitor) { this.monitor = monitor; } public ProgressMonitor getMonitor() { return monitor; } public void preTask(Object element) { if (monitor == null) return; monitor.preTask(element, this); } public void postTask(Object element) { if (monitor == null) return; monitor.postTask(element, this); } public void handleRuntimeException(RuntimeException ex, SyntaxElement element, Map<String, Object> additionalContextInfo) { if (this.exceptionHandler == null) throw ex; exceptionHandler.handleRuntimeException(ex, element, this, additionalContextInfo); } public ResourceManager getResourceManager() { return this.resourceManager; } protected List<Around> registeredExtensionAdvices = new ArrayList<Around>(); public void registerExtensionAdvices(final String fullyQualifiedName) { final XtendFile ext = (XtendFile) resourceManager.loadResource(fullyQualifiedName, XtendFile.FILE_EXTENSION); if (ext == null) throw new IllegalArgumentException("Couldn't find extension file '" + fullyQualifiedName + "'"); final List<Around> as = ext.getArounds(); for (Around around : as) { if (registeredExtensionAdvices.contains(around)) { log.warn("advice " + around.toString() + " allready registered!"); } else { registeredExtensionAdvices.add(around); } } } public List<Around> getExtensionAdvices() { return registeredExtensionAdvices; } private final TypesComparator typesComparator = new TypesComparator(); private Extension advise(Extension element) { for (Around a : getExtensionAdvices()) { if (a.nameMatches(element.getQualifiedName())) { List<Type> paramTypes = a.getParamTypes(this); List<Type> extPTypes = element.getParameterTypes(); int diff = extPTypes.size() - paramTypes.size(); if (diff >= 0) { if (diff > 0 && a.isWildparams()) { // fill wildcard // params with // Object types for (int i = 0; i < diff; i++) { paramTypes.add(getObjectType()); } } if (typesComparator.compare(paramTypes, extPTypes) >= 0) { element = new ExtensionAdvisor(a, element); } } } } return element; } class ExtensionAdvisor implements Extension { private final Extension delegate; private final Around advice; public ExtensionAdvisor(Around advice, Extension delegate) { this.delegate = delegate; this.advice = advice; } public Object evaluate(Object[] parameters, ExecutionContext ctx) { ctx = ctx.cloneWithVariable(new Variable(Around.CONTEXT_PARAM_NAME, new AdviceContext(delegate, ctx, parameters))); for (int i = 0; i < advice.getParams().size(); i++) { ctx = ctx .cloneWithVariable(new Variable(advice.getParams().get(i).getName().getValue(), parameters[i])); } ctx = ctx.cloneWithResource(advice.getParent()); return advice.getExpression().evaluate(ctx); } public void analyze(ExecutionContext ctx, Set<AnalysationIssue> issues) { delegate.analyze(ctx, issues); } public int getEnd() { return delegate.getEnd(); } public ExtensionFile getExtensionFile() { return delegate.getExtensionFile(); } public String getFileName() { return delegate.getFileName(); } public List<DeclaredParameter> getFormalParameters() { return delegate.getFormalParameters(); } public int getLine() { return delegate.getLine(); } public String getName() { return delegate.getName(); } public List<String> getParameterNames() { return delegate.getParameterNames(); } public List<Type> getParameterTypes() { return delegate.getParameterTypes(); } public Type getReturnType(Type[] parameters, ExecutionContext ctx, Set<AnalysationIssue> issues) { return ctx.getReturnType(delegate, parameters, issues); } public Type getReturnType() { return delegate.getReturnType(); } public Identifier getReturnTypeIdentifier() { return delegate.getReturnTypeIdentifier(); } public int getStart() { return delegate.getStart(); } public String getNameString(ExecutionContext context) { return delegate.getNameString(context); } public void init(ExecutionContext ctx) { delegate.init(ctx); } public boolean isCached() { return delegate.isCached(); } public boolean isPrivate() { return delegate.isPrivate(); } public void setExtensionFile(ExtensionFile file) { delegate.setExtensionFile(file); } public String toOutlineString() { return delegate.toOutlineString(); } @Override public String toString() { return delegate.toString(); } public String getQualifiedName() { return delegate.getQualifiedName(); } public final Object accept(final AbstractVisitor visitor) { return visitor.visit(this); } } public Object handleNullEvaluation(SyntaxElement element) { if (nullEvaluationHandler != null) return nullEvaluationHandler.handleNullEvaluation(element, this); return null; } public void release() { typeSystem.release(); } /** * @deprecated Use {@link #setVetoableCallBack(VetoableCallback)} */ @Deprecated public void setCallBack(Callback callback) { this.callback = new VetoableCallbackAdapter(callback); } public void setVetoableCallBack(VetoableCallback callback) { this.callback = callback; } protected Map<Pair<String, List<Type>>, Type> extensionsReturnTypeCache = new HashMap<Pair<String,List<Type>>, Type>(); public Type getReturnType(Extension extension, Type[] paramTypes, Set<AnalysationIssue> issues) { Pair<String, List<Type>> key = new Pair<String, List<Type>>(extension.getQualifiedName(), Arrays.asList(paramTypes)); if(extensionsReturnTypeCache.containsKey(key)) return extensionsReturnTypeCache.get(key); else { Type result = extension.getReturnType(paramTypes, this, issues); extensionsReturnTypeCache.put(key, result); return result; } } }