blob: 079e7322cbfbc9f7a902a6e3622597853a49b793 [file] [log] [blame]
/*
* <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;
}
}