| /* |
| * <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; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.ENamedElement; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EcorePackage; |
| import org.eclipse.gmf.internal.xpand.BuiltinMetaModel; |
| import org.eclipse.gmf.internal.xpand.ResourceManager; |
| import org.eclipse.gmf.internal.xpand.ResourceMarker; |
| import org.eclipse.gmf.internal.xpand.eval.EvaluationListener; |
| import org.eclipse.gmf.internal.xpand.migration.Activator; |
| import org.eclipse.gmf.internal.xpand.util.ClassLoadContext; |
| import org.eclipse.gmf.internal.xpand.xtend.ast.Extension; |
| import org.eclipse.gmf.internal.xpand.xtend.ast.XtendResource; |
| |
| /** |
| * @author Sven Efftinge |
| * @author Arno Haase |
| */ |
| public class ExecutionContextImpl implements ExecutionContext { |
| |
| private final Map<String, Variable> variables = new HashMap<String, Variable> (); |
| |
| private final Map<String, Variable> globalVars = new HashMap<String, Variable> (); |
| |
| private ClassLoadContext contextClassLoader; |
| |
| private EvaluationListener evaluationListener; |
| |
| /** |
| * 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 ResourceMarker currentResource; |
| |
| private final ResourceManager resourceManager; |
| |
| private Collection<EPackage> fallbackVisibleModels = new LinkedHashSet<EPackage>(); |
| |
| public ExecutionContextImpl(ResourceManager resourceManager, EPackage... fallbackVisibleModels) { |
| this (resourceManager, (Collection<Variable>) null); |
| for (EPackage ePackage : fallbackVisibleModels) { |
| this.fallbackVisibleModels.add(ePackage); |
| } |
| } |
| |
| public ExecutionContextImpl(ResourceManager resourceManager, Collection<Variable> globalVars) { |
| this (resourceManager, null, null, globalVars); |
| } |
| |
| public ExecutionContextImpl(ResourceManager resourceManager, ResourceMarker resource, Collection<Variable> variables, Collection<Variable> globalVars) { |
| this.resourceManager = resourceManager; |
| this.currentResource = resource; |
| if (variables != null) { |
| for (Variable v : variables) { |
| this.variables.put(v.getName(), v); |
| } |
| } |
| if (globalVars != null) { |
| for (Variable v : globalVars) { |
| this.globalVars.put(v.getName(), v); |
| } |
| } |
| fallbackVisibleModels.add(EcorePackage.eINSTANCE); |
| } |
| |
| // copy constuctor |
| protected ExecutionContextImpl(ExecutionContextImpl original) { |
| this.resourceManager = original.resourceManager; |
| this.currentResource = original.currentResource; |
| this.variables.putAll(original.variables); |
| this.globalVars.putAll(original.globalVars); |
| this.contextClassLoader = original.contextClassLoader; |
| this.evaluationListener = original.evaluationListener; |
| this.fallbackVisibleModels = original.fallbackVisibleModels; |
| } |
| |
| /* |
| * Need this for code completion only? move to proposal computer than |
| */ |
| @SuppressWarnings("unchecked") |
| public EClassifier[] findTypesForPrefix(final String prefix) { |
| final EPackage[] importedNamespaces; |
| final String typeName = TypeNameUtil.getTypeName(prefix); |
| final String typesMetamodelName = TypeNameUtil.getMetaModelName(typeName); |
| // TODO filter importedNamespaces with metamodel name (use ePackage.nsPrefix for metamodel name? |
| final String collectionTypeName = TypeNameUtil.getCollectionTypeName(prefix); |
| final String simpleTypeName; |
| // XXX handle cases like "metamodelName!" and "List[" as prefixes |
| if (TypeNameUtil.isQualifiedName(typeName)) { |
| EPackage exactNamespace = findImportedNamespace(TypeNameUtil.withoutLastSegment(typeName)); |
| if (exactNamespace == null) { |
| return new EClassifier[0]; |
| } else { |
| importedNamespaces = new EPackage[] { exactNamespace }; |
| } |
| simpleTypeName = TypeNameUtil.getLastSegment(typeName); |
| } else { |
| importedNamespaces = getAllVisibleModels(); |
| simpleTypeName = TypeNameUtil.toCanonicalNameFromAlias(typeName); |
| } |
| if (collectionTypeName != null) { |
| if (!BuiltinMetaModel.isCollectionMetaType(collectionTypeName)) { |
| return new EClassifier[0]; |
| } |
| } |
| final List<EClassifier> result = new ArrayList<EClassifier>(); |
| for (final EPackage namespace : importedNamespaces) { |
| if (simpleTypeName == null || simpleTypeName.trim().length() == 0) { |
| result.addAll(namespace.getEClassifiers()); |
| } else { |
| result.addAll(filterByNamePrefix(simpleTypeName, namespace.getEClassifiers())); |
| } |
| } |
| if (collectionTypeName == null) { |
| return result.toArray(new EClassifier[result.size()]); |
| } |
| EClassifier[] rv = new EClassifier[result.size()]; |
| final Iterator<EClassifier> iterator = result.iterator(); |
| for (int i = 0; i < rv.length; i++) { |
| rv[i] = BuiltinMetaModel.getCollectionType(collectionTypeName, iterator.next()); |
| } |
| return rv; |
| } |
| |
| private EPackage findImportedNamespace(String namespace) { |
| assert namespace != null; |
| LinkedList<EPackage> potentialMatches = new LinkedList<EPackage>(); |
| potentialMatches.addAll(Arrays.asList(getAllVisibleModels())); |
| String[] namespaceChain = namespace.split(SyntaxConstants.NS_DELIM); |
| EPackage returnValue = null; |
| for (String namespacePart : namespaceChain) { |
| LinkedList<EPackage> subPackages = new LinkedList<EPackage>(); |
| returnValue = null; |
| for (EPackage candidate : potentialMatches) { |
| if (candidate.getName().equals(namespacePart)) { |
| subPackages.addAll(candidate.getESubpackages()); |
| returnValue = candidate; |
| break; // no more than one package with specified namespacePart |
| } |
| } |
| potentialMatches = subPackages; |
| } |
| return returnValue; |
| } |
| |
| private static <T extends ENamedElement> Collection<T> filterByNamePrefix(final String namePrefix, final Collection<? extends T> knownNamedElements) { |
| final Set<T> result = new HashSet<T>(); |
| for (T t : knownNamedElements) { |
| if (t.getName().startsWith(namePrefix)) { |
| result.add(t); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * {@link ClassLoadContext} to use in {@link #loadClass(String)}. |
| * @param classLoader loader to use or null to use default system-wide |
| */ |
| public void setContextClassLoader(ClassLoadContext classLoadContext) { |
| this.contextClassLoader = classLoadContext; |
| } |
| |
| public Class<?> loadClass(String value) { |
| // FIXME delegate to resourcemanager or Environment |
| try { |
| if (contextClassLoader != null) { |
| Class<?> c = contextClassLoader.load(value); |
| if (c != null) { |
| return c; |
| } |
| } |
| return Class.forName(value); |
| } catch (ClassNotFoundException ex) { |
| ex.getMessage(); |
| // IGNORE? |
| } |
| return null; |
| } |
| |
| protected EPackage[] getAllVisibleModels() { |
| String[] importedNamespaces = getImportedNamespaces(); |
| assert importedNamespaces != null; |
| // TODO respect meta-models imported not only with nsURI |
| LinkedList<EPackage> result = new LinkedList<EPackage>(); |
| for (String namespace : importedNamespaces) { |
| EPackage pkg = Activator.findMetaModel(namespace); |
| if (pkg != null) { |
| result.add(pkg); |
| } |
| } |
| if (result.isEmpty()) { |
| // hack for tests |
| result.addAll(fallbackVisibleModels); |
| } |
| // result.add(BuiltinMetaModel.VOID.getEPackage()); |
| return result.toArray(new EPackage[result.size()]); |
| } |
| |
| public EClassifier getTypeForName(String name) { |
| final String simpleTypeName = TypeNameUtil.toCanonicalNameFromAlias(TypeNameUtil.getSimpleName(TypeNameUtil.getTypeName(name))); |
| final String collectionTypeName = TypeNameUtil.getCollectionTypeName(name); |
| for (EClassifier potentialMatch : findTypesForPrefix(name)) { |
| // we don't know the order types get returned from #findTypesForPrefix, thus, need to |
| // look for exact match |
| if (collectionTypeName != null) { |
| assert BuiltinMetaModel.isParameterizedType(potentialMatch); |
| if (BuiltinMetaModel.getInnerType(potentialMatch).getName().equals(simpleTypeName)) { |
| return potentialMatch; |
| } |
| } else { |
| if (potentialMatch.getName().equals(simpleTypeName)) { |
| return potentialMatch; |
| } |
| } |
| } |
| return null; |
| } |
| |
| protected String[] getImportedNamespaces() { |
| if (currentResource() instanceof XtendResource) { |
| return ((XtendResource) currentResource()).getImportedNamespaces(); |
| } |
| return new String[0]; |
| } |
| |
| protected String[] getImportedExtensions() { |
| if (currentResource() instanceof XtendResource) { |
| return ((XtendResource) currentResource()).getImportedExtensions(); |
| } |
| return new String[0]; |
| } |
| |
| public ExecutionContext cloneContext() { |
| return new ExecutionContextImpl(this); |
| } |
| |
| protected final ResourceManager getResourceManager() { |
| return resourceManager; |
| } |
| |
| public Variable getVariable(final String name) { |
| return variables.get(name); |
| } |
| |
| public Collection<Variable> getVisibleVariables() { |
| return Collections.unmodifiableCollection(variables.values()); |
| } |
| |
| public Collection<Variable> getGlobalVariables() { |
| return Collections.unmodifiableCollection(globalVars.values()); |
| } |
| |
| public Variable getGlobalVariable(String name) { |
| return globalVars.get(name); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public ExecutionContext cloneWithVariable(final Variable... vars) { |
| final ExecutionContextImpl result = (ExecutionContextImpl) cloneContext(); |
| for (Variable v : vars) { |
| result.variables.put(v.getName(), v); |
| } |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public <T extends ExecutionContext> T cloneWithVariable(Collection<Variable> v) { |
| return (T) cloneWithVariable(v.toArray(new Variable[v.size()])); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public ExecutionContext cloneWithoutVariables() { |
| final ExecutionContextImpl result = (ExecutionContextImpl) cloneContext(); |
| result.variables.clear(); |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public ExecutionContext cloneWithResource(final ResourceMarker ns) { |
| final ExecutionContextImpl ctx = (ExecutionContextImpl) cloneContext(); |
| ctx.currentResource = ns; |
| return ctx; |
| } |
| |
| public ResourceMarker currentResource() { |
| return currentResource; |
| } |
| |
| private Set<Extension> allExtensions = null; |
| |
| public Set<Extension> getAllExtensions() { |
| if (allExtensions == null) { |
| allExtensions = new HashSet<Extension>(); |
| final ResourceMarker res = currentResource(); |
| if (res != null) { |
| final String[] extensions = getImportedExtensions(); |
| for (String extension : extensions) { |
| final XtendResource extFile = resourceManager.loadXtendResource(extension); |
| if (extFile == null) { |
| throw new RuntimeException("Unable to load extension file : " + extension); |
| } |
| final ExecutionContext ctx = cloneWithResource(extFile); |
| final List<Extension> extensionList = extFile.getPublicExtensions(resourceManager); |
| for (Extension element : extensionList) { |
| element.init(ctx); |
| allExtensions.add(element); |
| } |
| } |
| if (res instanceof XtendResource) { |
| final List<Extension> extensionList = ((XtendResource) res).getExtensions(); |
| for (Extension element : extensionList) { |
| element.init(this); |
| allExtensions.add(element); |
| } |
| } |
| } |
| } |
| return allExtensions; |
| } |
| |
| public Extension getExtension(final String functionName, final EClassifier[] parameterTypes) { |
| return PolymorphicResolver.getExtension(getAllExtensions(), functionName, Arrays.asList(parameterTypes)); |
| } |
| |
| public EvaluationListener getEvaluationListener() { |
| return evaluationListener; |
| } |
| |
| public void setEvaluationListener(EvaluationListener listener) { |
| this.evaluationListener = listener; |
| } |
| } |