org.eclipse.acceleo.engine.internal.environment.AcceleoEvaluationEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.acceleo.engine.internal.environment.AcceleoEvaluationEnvironment.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2012 Obeo.
 * 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:
 *     Obeo - initial API and implementation
 *     Jerome Benois - eInverse initial implementation
 *     Goulwen Le Fur - caching of the eInverse cross referencer
 *******************************************************************************/
package org.eclipse.acceleo.engine.internal.environment;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.eclipse.acceleo.common.IAcceleoConstants;
import org.eclipse.acceleo.common.internal.utils.workspace.BundleURLConverter;
import org.eclipse.acceleo.common.utils.AcceleoCollections;
import org.eclipse.acceleo.common.utils.CircularArrayDeque;
import org.eclipse.acceleo.common.utils.CompactHashSet;
import org.eclipse.acceleo.common.utils.CompactLinkedHashSet;
import org.eclipse.acceleo.common.utils.Deque;
import org.eclipse.acceleo.common.utils.ModelUtils;
import org.eclipse.acceleo.engine.AcceleoEngineMessages;
import org.eclipse.acceleo.engine.AcceleoEnginePlugin;
import org.eclipse.acceleo.engine.AcceleoEvaluationException;
import org.eclipse.acceleo.engine.service.AcceleoDynamicTemplatesRegistry;
import org.eclipse.acceleo.engine.service.AcceleoModulePropertiesAdapter;
import org.eclipse.acceleo.model.mtl.Module;
import org.eclipse.acceleo.model.mtl.ModuleElement;
import org.eclipse.acceleo.model.mtl.Template;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.EvaluationEnvironment;
import org.eclipse.ocl.ecore.AnyType;
import org.eclipse.ocl.ecore.BagType;
import org.eclipse.ocl.ecore.CollectionType;
import org.eclipse.ocl.ecore.EcoreEvaluationEnvironment;
import org.eclipse.ocl.ecore.OrderedSetType;
import org.eclipse.ocl.ecore.SequenceType;
import org.eclipse.ocl.ecore.SetType;
import org.eclipse.ocl.ecore.Variable;
import org.eclipse.ocl.options.EvaluationOptions;
import org.eclipse.ocl.util.Bag;

/**
 * This will allow us to accurately evaluate custom operations defined in the Acceleo standard library and
 * resolve the right template for each call (guards, overrides, namesakes, ...).
 * 
 * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
 */
public class AcceleoEvaluationEnvironment extends EcoreEvaluationEnvironment {
    /** This will be used as a place holder when trying to call templates with a <code>null</code> argument. */
    private static final Object NULL_ARGUMENT = new Object();

    /** Holds the prefix we'll use for the temporary context variables created to hold context values. */
    private static final String TEMPORARY_CONTEXT_VAR_PREFIX = "context$"; //$NON-NLS-1$

    /** Holds the prefix we'll use for the temporary variables created to hold argument values. */
    private static final String TEMPORARY_INVOCATION_ARG_PREFIX = "temporaryInvocationVariable$"; //$NON-NLS-1$

    /** This will allow the environment to know of the modules currently in the generation context. */
    private final Set<Module> currentModules = new CompactHashSet<Module>();

    /** Maps dynamic overrides as registered in the {@link AcceleoDynamicTemplatesRegistry}. */
    private final SetMultimap<Template, Template> dynamicOverrides = AcceleoCollections
            .newCompactLinkedHashSetMultimap();

    /** Maps all overriding templates to their <code>super</code>. */
    private final SetMultimap<Template, Template> overridingTemplates = AcceleoCollections
            .newCompactLinkedHashSetMultimap();

    /** This will hold a reference to the class allowing for properties lookup. */
    private AcceleoPropertiesLookup propertiesLookup;

    /** This will allow us to map all accessible templates to their name. */
    private final SetMultimap<String, Template> templates = AcceleoCollections.newCompactLinkedHashSetMultimap();

    /**
     * Allows us to totally get rid of the inherited map. This will mainly serve the purpose of allowing
     * multiple bindings against the same variable name.
     */
    private final Deque<ListMultimap<String, Object>> scopedVariableMap = new CircularArrayDeque<ListMultimap<String, Object>>();

    /**
     * This will contain variables that are global to a generation module.
     */
    private final ListMultimap<String, Object> globalVariableMap = AcceleoCollections
            .newCircularArrayDequeMultimap();

    /**
     * This constructor is needed by the factory.
     * 
     * @param parent
     *            Parent evaluation environment.
     * @param module
     *            We will resolve dependencies for this module and keep references to all accessible
     *            templates.
     * @param properties
     *            The class allowing for properties lookup for this generation.
     */
    public AcceleoEvaluationEnvironment(
            EvaluationEnvironment<EClassifier, EOperation, EStructuralFeature, EClass, EObject> parent,
            Module module, AcceleoPropertiesLookup properties) {
        super(parent);
        scopedVariableMap.add(AcceleoCollections.<String, Object>newCircularArrayDequeMultimap());
        mapAllTemplates(module);

        AcceleoModulePropertiesAdapter adapter = (AcceleoModulePropertiesAdapter) EcoreUtil
                .getAdapter(module.eAdapters(), AcceleoModulePropertiesAdapter.class);
        if (adapter == null || !adapter.getProperties().contains(IAcceleoConstants.DISABLE_DYNAMIC_MODULES)) {
            mapDynamicOverrides();
        }

        setOption(EvaluationOptions.LAX_NULL_HANDLING, Boolean.FALSE);
        propertiesLookup = properties;
    }

    /**
     * This constructor will create our environment given the module from which to resolve dependencies.
     * 
     * @param module
     *            We will resolve dependencies for this module and keep references to all accessible
     *            templates.
     * @param properties
     *            The class allowing for properties lookup for this generation.
     */
    public AcceleoEvaluationEnvironment(Module module, AcceleoPropertiesLookup properties) {
        super();
        scopedVariableMap.add(AcceleoCollections.<String, Object>newCircularArrayDequeMultimap());
        mapAllTemplates(module);

        AcceleoModulePropertiesAdapter adapter = (AcceleoModulePropertiesAdapter) EcoreUtil
                .getAdapter(module.eAdapters(), AcceleoModulePropertiesAdapter.class);
        if (adapter == null || !adapter.getProperties().contains(IAcceleoConstants.DISABLE_DYNAMIC_MODULES)) {
            mapDynamicOverrides();
        }

        setOption(EvaluationOptions.LAX_NULL_HANDLING, Boolean.FALSE);
        propertiesLookup = properties;
    }

    /**
     * Returns the last value of the given list.
     * <p>
     * Makes no effort to try and check whether the argument is valid.
     * </p>
     * 
     * @param values
     *            List we need the last value from.
     * @param <V>
     *            Type of the list's values.
     * @return The last value of the given list.
     */
    private static <V> V getLast(List<V> values) {
        final ListIterator<V> iterator = values.listIterator(values.size());
        return iterator.previous();
    }

    /**
     * Removes the last value of the given list.
     * <p>
     * Makes no effort to try and check whether the argument is valid.
     * </p>
     * 
     * @param values
     *            List we need the last value from.
     * @param <V>
     *            Type of the list's values.
     * @return The last value of the given list.
     */
    private static <V> V removeLast(List<V> values) {
        final ListIterator<V> iterator = values.listIterator(values.size());
        final V last = iterator.previous();
        iterator.remove();
        return last;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.AbstractEvaluationEnvironment#add(java.lang.String, java.lang.Object)
     */
    @Override
    public void add(String name, Object value) {
        ListMultimap<String, Object> variableMap;
        if (name.startsWith(TEMPORARY_CONTEXT_VAR_PREFIX) || name.startsWith(TEMPORARY_INVOCATION_ARG_PREFIX)) {
            variableMap = globalVariableMap;
        } else {
            variableMap = scopedVariableMap.getLast();
        }
        variableMap.put(name, value);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.ecore.EcoreEvaluationEnvironment#callOperation(org.eclipse.emf.ecore.EOperation,
     *      int, java.lang.Object, java.lang.Object[])
     */
    @Override
    public Object callOperation(EOperation operation, int opcode, Object source, Object[] args) {
        Object result = null;
        /*
         * Shortcut OCL for operations returning "EJavaObject" : these could be collections, but OCL would
         * discard any value other than the first in such cases. See bug 287052.
         */
        if (operation.getEType() == EcorePackage.eINSTANCE.getEJavaObject()) {
            result = callOperationWorkaround287052(operation, opcode, source, args);
        } else {
            result = super.callOperation(operation, opcode, source, args);
        }
        return result;
    }

    /**
     * Copied from
     * {@link org.eclipse.ocl.AbstractEvaluationEnvironment#callOperation(Object, int, Object, Object[])} to
     * bypass the {@link EcoreEvaluationEnvironment} "coerce" behavior as it is the cause of bug 287052.This
     * will only be called for EOperations which return type is EJavaObject.
     * 
     * @param operation
     *            Operation we are to call.
     * @param opcode
     *            OCL code for this operation.
     * @param source
     *            Source of the call.
     * @param args
     *            Arguments of the call.
     * @return Result of the invocation.
     * @throws IllegalArgumentException
     *             Thrown if we couldn't find an appropriate method to call.
     */
    private Object callOperationWorkaround287052(EOperation operation, int opcode, Object source, Object[] args)
            throws IllegalArgumentException {
        if (getParent() != null) {
            return getParent().callOperation(operation, opcode, source, args);
        }

        Method method = getJavaMethodFor(operation, source);

        Object noResult = new Object();
        Object result = noResult;
        if (method != null) {
            try {
                // coerce any collection arguments to EList as necessary
                Class<?>[] parmTypes = method.getParameterTypes();
                for (int i = 0; i < parmTypes.length; i++) {
                    if (EList.class.isAssignableFrom(parmTypes[i])) {
                        if (args[i] == null) {
                            args[i] = ECollections.EMPTY_ELIST;
                        } else if (!(args[i] instanceof Collection<?>)) {
                            EList<Object> list = new BasicEList.FastCompare<Object>(1);
                            list.add(args[i]);
                            args[i] = list;
                        } else if (!(args[i] instanceof EList<?>)) {
                            args[i] = new BasicEList.FastCompare<Object>((Collection<?>) args[i]);
                        }
                    }
                }

                result = method.invoke(source, args);
                // CHECKSTYLE:OFF
                // This is mostly copied from AbstractEvaluationEnvironment, which catches Exception.
            } catch (Exception e) {
                // CHECKSTYLE:ON
                AcceleoEnginePlugin.log(e, false);
                result = getInvalidResult();
            }
        }

        if (result == noResult) {
            throw new IllegalArgumentException();
        }
        return result;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.AbstractEvaluationEnvironment#clear()
     */
    @Override
    public void clear() {
        super.clear();
        scopedVariableMap.clear();
        globalVariableMap.clear();
    }

    /**
     * Creates a new variable scope. This will typically be called when we enter a new TemplateInvocation or
     * QueryInvocation.
     */
    public void createVariableScope() {
        scopedVariableMap.add(AcceleoCollections.<String, Object>newCircularArrayDequeMultimap());
    }

    /**
     * This will return the List of all applicable candidates for the given template call with the given
     * arguments. These will be ordered as described on {@link #reorderCandidatesPriority(Module, Set)}.
     * 
     * @param origin
     *            Origin of the template call.
     * @param call
     *            The called element.
     * @param arguments
     *            Arguments of the call.
     * @return The set of all applicable templates for these arguments
     */
    public List<Template> getAllCandidates(Module origin, Template call, Object[] arguments) {
        final List<Object> argumentTypes = new ArrayList<Object>(arguments.length);
        for (int i = 0; i < arguments.length; i++) {
            if (arguments[i] instanceof EObject) {
                argumentTypes.add(((EObject) arguments[i]).eClass());
            } else if (arguments[i] != null) {
                argumentTypes.add(arguments[i].getClass());
            } else {
                argumentTypes.add(NULL_ARGUMENT);
            }
        }

        /*
         * NOTE : we depend on the ordering offered by List types. Do not change implementation without
         * testing.
         */
        final List<Template> orderedNamesakes = reorderCandidatesPriority(origin,
                getAllCandidateNamesakes(origin, call, argumentTypes));
        final List<Template> dynamicOverriding = reorderDynamicOverrides(
                getAllDynamicCandidateOverriding(orderedNamesakes, argumentTypes));
        final List<Template> overriding = getAllCandidateOverriding(origin, orderedNamesakes, argumentTypes);

        // overriding templates come first, then namesakes
        return Lists.newArrayList(Iterables.concat(dynamicOverriding, overriding, orderedNamesakes));
    }

    /**
     * Returns the most specific template for the given arguments in the given list.
     * 
     * @param candidates
     *            List of templates candidates to be substituted.
     * @param arguments
     *            Arguments of the call.
     * @return The most specific templates for <code>arguments</code>.
     */
    public Template getMostSpecificTemplate(Iterable<Template> candidates, Object[] arguments) {
        final Iterator<Template> candidateIterator = candidates.iterator();
        Template mostSpecific = candidateIterator.next();
        if (!candidateIterator.hasNext()) {
            return mostSpecific;
        }

        while (candidateIterator.hasNext()) {
            mostSpecific = mostSpecificTemplate(mostSpecific, candidateIterator.next(), arguments);
        }
        return mostSpecific;
    }

    /**
     * Returns the list of available properties files.
     * 
     * @return The list of available properties files.
     */
    public AcceleoPropertiesLookup getPropertiesLookup() {
        return propertiesLookup;
    }

    /**
     * This will return the map of currently available variables. Take note that this is not meant to be used
     * to alter the variables (which won't work since what we return is a copy of the variable map) but more
     * to query the variable state.
     * 
     * @return The map of currently available variables.
     */
    public Map<String, Object> getCurrentVariables() {
        ListMultimap<String, Object> variableMap = scopedVariableMap.getLast();
        Map<String, Object> availableVariables = new HashMap<String, Object>();
        for (String key : variableMap.keys()) {
            List<Object> values = variableMap.get(key);
            availableVariables.put(key, getLast(values));
        }
        return availableVariables;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.ecore.EcoreEvaluationEnvironment#getInvalidResult()
     */
    @Override
    public Object getInvalidResult() {
        return super.getInvalidResult();
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.AbstractEvaluationEnvironment#getValueOf(java.lang.String)
     */
    @Override
    public Object getValueOf(String name) {
        Object value = null;
        ListMultimap<String, Object> variableMap = scopedVariableMap.getLast();
        if (variableMap.containsKey(name)) {
            value = getLast(variableMap.get(name));
        } else if (globalVariableMap.containsKey(name)) {
            value = getLast(globalVariableMap.get(name));
        }
        return value;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.AbstractEvaluationEnvironment#remove(java.lang.String)
     */
    @Override
    public Object remove(String name) {
        ListMultimap<String, Object> variableMap;
        if (scopedVariableMap.getLast().containsKey(name)) {
            variableMap = scopedVariableMap.getLast();
        } else if (globalVariableMap.containsKey(name)) {
            variableMap = globalVariableMap;
        } else {
            return null;
        }

        return removeLast(variableMap.get(name));
    }

    /**
     * This will remove and return the last variable scope.
     * 
     * @return Removes and return the last variable scope.
     */
    public Map<String, Collection<Object>> removeVariableScope() {
        return scopedVariableMap.removeLast().asMap();
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.AbstractEvaluationEnvironment#replace(java.lang.String, java.lang.Object)
     */
    @Override
    public void replace(String name, Object value) {
        ListMultimap<String, Object> variableMap;
        if (name.startsWith(TEMPORARY_CONTEXT_VAR_PREFIX) || name.startsWith(TEMPORARY_INVOCATION_ARG_PREFIX)) {
            variableMap = globalVariableMap;
        } else {
            variableMap = scopedVariableMap.getLast();
        }

        if (variableMap.containsKey(name)) {
            removeLast(variableMap.get(name));
        }
        add(name, value);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ocl.AbstractEvaluationEnvironment#toString()
     */
    @Override
    public String toString() {
        ListMultimap<String, Object> variableMap = scopedVariableMap.getLast();
        return variableMap.toString();
    }

    /**
     * Filters non-applicable templates out of the candidates list.
     * 
     * @param candidates
     *            List of templates that needs to be filtered out.
     * @param argumentTypes
     *            Types of the arguments for the call.
     * @return The set of applicable templates.
     */
    private Set<Template> applicableTemplates(Set<Template> candidates, final List<Object> argumentTypes) {
        Predicate<Template> argumentSizeMatch = new Predicate<Template>() {
            public boolean apply(Template input) {
                final List<Variable> parameters = input.getParameter();
                return parameters.size() == argumentTypes.size()
                        || parameters.isEmpty() && argumentTypes.size() == 1;
            }
        };
        Predicate<Template> argumentsMatch = new Predicate<Template>() {
            public boolean apply(Template input) {
                final List<Variable> parameters = input.getParameter();
                if (parameters.isEmpty() && argumentTypes.size() == 1) {
                    return true;
                }
                boolean argumentMatch = true;
                for (int i = 0; i < argumentTypes.size() && argumentMatch; i++) {
                    final Variable param = parameters.get(i);
                    final Object parameterType = param.getType();
                    argumentMatch = isApplicableArgument(parameterType, argumentTypes.get(i));
                }
                return argumentMatch;
            }
        };

        return Sets.filter(candidates, Predicates.and(argumentSizeMatch, argumentsMatch));
    }

    /**
     * This will return the list of all namesakes of the template <code>call</code> applicable for
     * <code>arguments</code>.
     * 
     * @param origin
     *            Origin of the template call.
     * @param call
     *            The called element.
     * @param argumentTypes
     *            Types of the arguments of the call.
     * @return All of the applicable templates of this name in the current context.
     */
    private Set<Template> getAllCandidateNamesakes(Module origin, Template call, List<Object> argumentTypes) {
        final Set<Template> namesakes = new CompactLinkedHashSet<Template>();
        final Set<Template> candidates = templates.get(call.getName());
        final int candidateSize = candidates.size();
        if (candidateSize == 0) {
            throw new AcceleoEvaluationException(
                    AcceleoEngineMessages.getString("AcceleoEvaluationEnvironment.ModuleResolutionError")); //$NON-NLS-1$
        }
        Set<Module> scope = Sets.union(Collections.singleton(origin), getScopeOf(origin));
        for (Template candidate : candidates) {
            if (scope.contains(candidate.eContainer())) {
                namesakes.add(candidate);
            }
        }
        if (namesakes.size() == 1) {
            return namesakes;
        }
        namesakes.retainAll(applicableTemplates(namesakes, argumentTypes));
        return namesakes;
    }

    /**
     * Returns the whole scope of modules visible from <code>module</code>.
     * 
     * @param module
     *            Module of which we need the scope.
     * @return The whole scope of modules visible from <code>module</code>.
     */
    private Set<Module> getScopeOf(Module module) {
        Set<Module> scope = new CompactHashSet<Module>();

        if (module.getExtends().size() > 0) {
            // Only supports single inheritance
            Module extended = module.getExtends().get(0);
            scope.add(extended);
            scope = Sets.union(scope, getExtendedScope(extended));
        }

        List<Module> imports = module.getImports();
        for (int i = 0; i < imports.size(); i++) {
            final Module importedModule = imports.get(i);
            scope = Sets.union(scope, Collections.singleton(importedModule));
            scope = Sets.union(scope, getExtendedScope(importedModule));
        }

        return scope;
    }

    /**
     * Returns the whole scope of modules visible thanks to an extends.
     * 
     * @param module
     *            Module of which we need the scope.
     * @return The whole scope of modules visible thanks to an extends.
     */
    private Set<Module> getExtendedScope(Module module) {
        Set<Module> scope = new CompactHashSet<Module>();

        if (module.getExtends().size() > 0) {
            // Only supports single inheritance
            Module extended = module.getExtends().get(0);
            scope.add(extended);
            scope = Sets.union(scope, getExtendedScope(extended));
        }

        return scope;
    }

    /**
     * This will return the list of all templates overriding one of <code>overridenTemplates</code> that are
     * applicable for <code>arguments</code>. These will be ordered as specified on
     * {@link #reorderCandidatesPriority(Module, Set)}.
     * 
     * @param origin
     *            Origin of the template call.
     * @param overridenTemplates
     *            List of templates we seek overriding templates of.
     * @param argumentTypes
     *            Types of the arguments of the call.
     * @return All of the applicable templates overriding one of <code>overridenTemplates</code> in the
     *         current context.
     */
    private List<Template> getAllCandidateOverriding(Module origin, List<Template> overridenTemplates,
            List<Object> argumentTypes) {
        final List<Template> candidateOverriding = new ArrayList<Template>();
        for (int i = 0; i < overridenTemplates.size(); i++) {
            final Set<Template> candidates = overridingTemplates.get(overridenTemplates.get(i));
            if (candidates != null) {
                final Set<Template> applicableCandidates = applicableTemplates(candidates, argumentTypes);
                for (Template template : applicableCandidates) {
                    EObject eContainer = template.eContainer();
                    if (eContainer instanceof Module
                            && (getScopeOf(origin).contains(eContainer) || eContainer.equals(origin))) {
                        candidateOverriding.add(template);
                    }
                }
                // no need to order this, it'll be ordered later on
                candidateOverriding.addAll(getAllCandidateOverriding(origin,
                        new ArrayList<Template>(applicableCandidates), argumentTypes));
            }
        }
        Collections.reverse(candidateOverriding);
        return candidateOverriding;
    }

    /**
     * This will return the list of all templates dynamically overriding one of
     * <code>overridenTemplates</code> that are applicable for <code>arguments</code>.
     * 
     * @param overridenTemplates
     *            List of templates we seek overriding templates of.
     * @param argumentTypes
     *            Types of the arguments of the call.
     * @return All of the applicable templates dynamically overriding one of <code>overridenTemplates</code>.
     */
    private Set<Template> getAllDynamicCandidateOverriding(List<Template> overridenTemplates,
            List<Object> argumentTypes) {
        final Set<Template> dynamicOverriding = new CompactLinkedHashSet<Template>();
        for (int i = 0; i < overridenTemplates.size(); i++) {
            final Set<Template> candidates = dynamicOverrides.get(overridenTemplates.get(i));
            if (candidates != null && !candidates.isEmpty()) {
                final Set<Template> applicableCandidates = applicableTemplates(candidates, argumentTypes);
                dynamicOverriding.addAll(applicableCandidates);
            }
        }
        return dynamicOverriding;
    }

    /**
     * This is meant to be used in the Dynamic URI converter only.
     * 
     * @return The set of all currently accessible modules.
     */
    Set<Module> getCurrentModules() {
        return new CompactHashSet<Module>(currentModules);
    }

    /**
     * Returns <code>true</code> if the value is applicable to the given type, <code>false</code> otherwise.
     * 
     * @param expectedType
     *            Expected type of the argument.
     * @param argumentType
     *            type of the argument we're trying to use as an argument.
     * @return <code>true</code> if the value is applicable to the given type, <code>false</code> otherwise.
     */
    private boolean isApplicableArgument(Object expectedType, Object argumentType) {
        boolean isApplicable = false;
        if (argumentType == NULL_ARGUMENT) {
            isApplicable = true;
        } else if (expectedType == argumentType) {
            isApplicable = true;
        } else if (expectedType instanceof EClass && argumentType instanceof EClass) {
            isApplicable = expectedType == argumentType || isSubTypeOf(expectedType, argumentType);
        } else if (expectedType instanceof Class<?> && argumentType instanceof Class<?>) {
            isApplicable = ((Class<?>) expectedType).isAssignableFrom((Class<?>) argumentType);
        } else if (expectedType instanceof EDataType && argumentType instanceof Class<?>) {
            if (expectedType instanceof BagType && argumentType instanceof Class<?>) {
                Class<?> clazz = (Class<?>) argumentType;
                isApplicable = Bag.class.isAssignableFrom(clazz);
            } else if (expectedType instanceof OrderedSetType && argumentType instanceof Class<?>) {
                Class<?> clazz = (Class<?>) argumentType;
                isApplicable = Set.class.isAssignableFrom(clazz);
            } else if (expectedType instanceof SetType && argumentType instanceof Class<?>) {
                Class<?> clazz = (Class<?>) argumentType;
                isApplicable = Set.class.isAssignableFrom(clazz);
            } else if (expectedType instanceof SequenceType && argumentType instanceof Class<?>) {
                Class<?> clazz = (Class<?>) argumentType;
                isApplicable = List.class.isAssignableFrom(clazz);
            } else if (expectedType instanceof CollectionType && argumentType instanceof Class<?>) {
                Class<?> clazz = (Class<?>) argumentType;
                isApplicable = Collection.class.isAssignableFrom(clazz);
            } else {
                isApplicable = ((EDataType) expectedType).getInstanceClass() == argumentType;
            }
        } else if (expectedType instanceof EEnum && argumentType instanceof EClass) {
            isApplicable = argumentType.equals(EcorePackage.eINSTANCE.getEEnumLiteral());
        } else if (expectedType instanceof AnyType) {
            isApplicable = true;
        } else {
            isApplicable = expectedType.getClass().isInstance(argumentType);
        }
        return isApplicable;
    }

    /**
     * Returns <code>true</code> if <code>eClass</code> is a sub-type of <code>superType</code>,
     * <code>false</code> otherwise.
     * 
     * @param superType
     *            Expected super type of <code>eClass</code>.
     * @param eClass
     *            EClass to consider.
     * @return <code>true</code> if <code>eClass</code> is a sub-type of <code>superType</code>,
     *         <code>false</code> otherwise.
     */
    private boolean isSubTypeOf(Object superType, Object eClass) {
        // if both types are EClass(es) then do the usual stuff
        boolean result = false;
        if (superType instanceof EClass && eClass instanceof EClass) {
            for (final EClass candidate : ((EClass) eClass).getEAllSuperTypes()) {
                if (candidate == superType) {
                    result = true;
                    break;
                }
            }
        } else if (superType instanceof AnyType) {
            result = true;
        }

        return result;
    }

    /**
     * This will load all dynamic modules in the first {@link ResourceSet} found by iterating over the
     * {@link #currentModules}.
     * 
     * @return The set of loaded modules.
     */
    private Set<Module> loadDynamicModules() {
        final Set<File> dynamicModuleFiles = new CompactLinkedHashSet<File>();
        final Set<Module> dynamicModules = new CompactLinkedHashSet<Module>();
        // shortcut
        ResourceSet resourceSet = null;
        for (Module module : currentModules) {
            if (module.eResource() != null && module.eResource().getResourceSet() != null) {
                resourceSet = module.eResource().getResourceSet();
                break;
            }
        }
        // If we couldn't find a resourceSet, break the loading loop and log an exception
        if (resourceSet == null) {
            // set as a blocker so that it is logged as an error
            AcceleoEnginePlugin.log(
                    AcceleoEngineMessages.getString("AcceleoEvaluationEnvironment.DynamicModulesLoadingFailure"), //$NON-NLS-1$
                    true);
            return dynamicModules;
        }
        if (!(resourceSet.getURIConverter() instanceof DynamicModulesURIConverter)) {
            resourceSet.setURIConverter(new DynamicModulesURIConverter(resourceSet.getURIConverter(), this));
        }
        // We have a resource set, let's find out where its module are coming from
        List<Resource> resources = resourceSet.getResources();
        Iterator<Resource> iterator = resources.iterator();
        while (iterator.hasNext()) {
            Resource resource = iterator.next();
            URI uri = resource.getURI();
            String generatorID = uri.toString();

            // Chicken sacrifice done right! /!\ Warning voodoo magic /!\
            if (uri.isPlatformPlugin() && uri.segments().length > 2) {
                generatorID = uri.segment(1);
            } else if (uri.isPlatformResource() && uri.segments().length > 2) {
                // Not supposed to happen since extension point works only when deployed in eclipse
                generatorID = uri.segment(1);
            } else if (uri.isPlatform() && uri.segments().length > 2) {
                // Not supposed to happen since extension point works only when deployed in eclipse
                generatorID = uri.segment(1);
            } else if (uri.isFile() || generatorID.startsWith("jar:file:")) { //$NON-NLS-1$
                BundleURLConverter converter = new BundleURLConverter(generatorID);
                generatorID = converter.resolveAsPlatformPlugin();
                // generatorID = AcceleoWorkspaceUtil.resolveAsPlatformPlugin(generatorID);
                if (generatorID != null && generatorID.startsWith("platform:/plugin/") //$NON-NLS-1$
                        && URI.createURI(generatorID).segments().length > 2) {
                    URI tmpURI = URI.createURI(generatorID);
                    generatorID = tmpURI.segment(1);
                }
            }
            final Set<File> dynamicAcceleoModulesFiles = AcceleoDynamicTemplatesRegistry.INSTANCE
                    .getRegisteredModules(generatorID);
            dynamicModuleFiles.addAll(dynamicAcceleoModulesFiles);
        }
        for (File moduleFile : dynamicModuleFiles) {
            if (moduleFile.exists() && moduleFile.canRead()) {
                try {
                    Resource res = ModelUtils.load(moduleFile, resourceSet).eResource();
                    for (EObject root : res.getContents()) {
                        if (root instanceof Module) {
                            dynamicModules.add((Module) root);
                        }
                    }
                } catch (IOException e) {
                    AcceleoEnginePlugin.log(e, false);
                }
            }
        }

        return dynamicModules;
    }

    /**
     * This will resolve all dependencies of the given module and keep references to all accessible templates.
     * 
     * @param module
     *            We will resolve dependencies for this module and keep references to all accessible
     *            templates.
     */
    private void mapAllTemplates(Module module) {
        if (currentModules.contains(module)) {
            return;
        }
        currentModules.add(module);

        for (final ModuleElement elem : module.getOwnedModuleElement()) {
            if (elem instanceof Template) {
                templates.put(elem.getName(), (Template) elem);
                mapOverridingTemplate((Template) elem);
            }
        }
        for (final Module extended : module.getExtends()) {
            mapAllTemplates(extended);
        }
        for (final Module imported : module.getImports()) {
            mapAllTemplates(imported);
        }
    }

    /**
     * Handles the mapping of a single dynamic module.
     * 
     * @param module
     *            Module that is to be mapped as a dynamic module.
     * @param dynamicModules
     *            The set of all dynamic modules as returned by {@link #loadDynamicModules()}.
     */
    private void mapDynamicModule(Module module, Set<Module> dynamicModules) {
        boolean map = false;

        final Set<Module> unMappedRequiredModules = new CompactLinkedHashSet<Module>();
        for (Module extended : module.getExtends()) {
            if (dynamicModules.contains(extended)) {
                mapDynamicModule(extended, dynamicModules);
            }
            if (currentModules.contains(extended)) {
                map = true;
            } else {
                unMappedRequiredModules.add(extended);
            }
        }
        // This module shouldn't be added to the context. Go to next.
        if (!map) {
            return;
        }

        for (Module imported : module.getImports()) {
            if (!currentModules.contains(imported)) {
                unMappedRequiredModules.add(imported);
            }
        }

        for (Module required : unMappedRequiredModules) {
            mapAllTemplates(required);
        }

        for (final ModuleElement elem : module.getOwnedModuleElement()) {
            if (elem instanceof Template) {
                final Template ownedTemplate = (Template) elem;
                for (final Template overriden : ownedTemplate.getOverrides()) {
                    Template match = null;
                    final Iterator<Template> templateIterator = templates.get(overriden.getName()).iterator();
                    while (match == null && templateIterator.hasNext()) {
                        final Template template = templateIterator.next();
                        if (EcoreUtil.equals(template, overriden)) {
                            match = template;
                        }
                    }
                    if (match != null) {
                        dynamicOverrides.put(match, ownedTemplate);
                    }
                }
                templates.put(ownedTemplate.getName(), ownedTemplate);
            }
        }
        currentModules.add(module);
    }

    /**
     * Maps dynamic overriding templates for smoother polymorphic resolution.
     */
    private void mapDynamicOverrides() {
        if (AcceleoDynamicTemplatesRegistry.INSTANCE.getRegisteredModules().isEmpty()) {
            return;
        }

        Set<Module> dynamicModules = loadDynamicModules();

        for (Module module : dynamicModules) {
            mapDynamicModule(module, dynamicModules);
        }
    }

    /**
     * This will create entries for the given template in the overriding map as needed.
     * 
     * @param elem
     *            The template which we need to map if overriding.
     */
    private void mapOverridingTemplate(Template elem) {
        for (final Template overriden : elem.getOverrides()) {
            overridingTemplates.put(overriden, elem);
        }
    }

    /**
     * Returns the most specific template of the given two for the given arguments.
     * 
     * @param template1
     *            First of the two compared templates.
     * @param template2
     *            Second of the compared templates.
     * @param actualArgumentTypes
     *            Types of the actual arguments of the call.
     * @return The most specific templates for <code>actualArgumentTypes</code>.
     */
    private Template mostSpecificTemplate(Template template1, Template template2, Object[] actualArgumentTypes) {
        Template mostSpecific;
        // number of arguments which are more specific on template1 as compared to template2
        int template1SpecificArgumentCount = 0;
        // ...
        int template2SpecificArgumentCount = 0;
        for (int i = 0; i < actualArgumentTypes.length; i++) {
            final Object actualArgumentType = actualArgumentTypes[i];
            if (template1.getParameter().size() == 0 && template2.getParameter().size() == 0) {
                continue;
            }
            final EClassifier template1Type = template1.getParameter().get(i).getType();
            final EClassifier template2Type = template2.getParameter().get(i).getType();
            if (template1Type == template2Type) {
                continue;
            }
            if (actualArgumentType instanceof EObject) {
                if (isSubTypeOf(template1Type, template2Type)) {
                    template2SpecificArgumentCount++;
                } else {
                    template1SpecificArgumentCount++;
                }
            } else if (actualArgumentType instanceof Collection<?>) {
                template1SpecificArgumentCount++;
            } else {
                // are there any chance the argument would not be an EObject?
                throw new AcceleoEvaluationException(AcceleoEngineMessages
                        .getString("AcceleoEvaluationVisitor.ArgumentNotEObjectOrCollection", actualArgumentType)); //$NON-NLS-1$
            }
        }
        if (template1SpecificArgumentCount >= template2SpecificArgumentCount) {
            mostSpecific = template1;
        } else {
            mostSpecific = template2;
        }
        return mostSpecific;
    }

    /**
     * Reorders the <code>candidates</code> list so that templates overriding <code>origin</code> come first.
     * Templates in the same module as <code>call</code> come second, then templates in extended modules, and
     * finally templates in imported modules.
     * 
     * @param origin
     *            The originating module.
     * @param candidates
     *            List that is to be reordered.
     * @return The reordered list.
     */
    private List<Template> reorderCandidatesPriority(Module origin, Set<Template> candidates) {
        final List<Template> reorderedList = new ArrayList<Template>(candidates.size());

        // We only support single inheritance. get(0) comes from that.
        Iterator<Template> candidateIterator = candidates.iterator();
        while (candidateIterator.hasNext()) {
            final Template candidate = candidateIterator.next();
            boolean isOverridingCandidate = false;
            Module module = (Module) candidate.eContainer();
            while (!isOverridingCandidate && module != null && module.getExtends().size() > 0) {
                if (module.getExtends().get(0) == origin) {
                    reorderedList.add(candidate);
                    candidateIterator.remove();
                    isOverridingCandidate = true;
                }
                module = module.getExtends().get(0);
            }
        }

        candidateIterator = candidates.iterator();
        while (candidateIterator.hasNext()) {
            final Template candidate = candidateIterator.next();
            if (candidate.eContainer() == origin) {
                reorderedList.add(candidate);
                candidateIterator.remove();
            }
        }

        candidateIterator = candidates.iterator();
        while (candidateIterator.hasNext()) {
            final Template candidate = candidateIterator.next();
            for (final Module extended : origin.getExtends()) {
                if (candidate.eContainer() == extended) {
                    reorderedList.add(candidate);
                    candidateIterator.remove();
                }
            }
        }

        candidateIterator = candidates.iterator();
        while (candidateIterator.hasNext()) {
            final Template candidate = candidateIterator.next();
            for (final Module imported : origin.getImports()) {
                if (candidate.eContainer() == imported) {
                    reorderedList.add(candidate);
                    candidateIterator.remove();
                }
            }
        }

        candidateIterator = candidates.iterator();
        while (candidateIterator.hasNext()) {
            final Template candidate = candidateIterator.next();
            for (final Module imported : origin.getImports()) {
                Module myImportedModule = imported;

                boolean shouldBreak = false;
                while (myImportedModule.getExtends().size() > 0) {
                    if (myImportedModule.getExtends().get(0) == candidate.eContainer()) {
                        reorderedList.add(candidate);
                        candidateIterator.remove();
                        shouldBreak = true;
                    }
                    myImportedModule = myImportedModule.getExtends().get(0);
                }

                if (shouldBreak) {
                    break;
                }
            }
        }

        return reorderedList;
    }

    /**
     * Reorders the given list of candidates by order of overriding. For example if the set contains T11
     * overriding T1, T21 overriding T11, T31 overriding T11 and T12 overriding T1, The returned list will
     * contain in this order : {T31, T21, T12, T11}.
     * 
     * @param candidates
     *            Set of candidates that are to be reordered.
     * @return The reordered list of candidates.
     */
    private List<Template> reorderDynamicOverrides(Set<Template> candidates) {
        Template[] array = candidates.toArray(new Template[candidates.size()]);
        Arrays.sort(array, new TemplateComparator());
        return Arrays.asList(array);
    }

    /**
     * This comparator will allow us to reorder Templates according to their overrides.
     * 
     * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
     */
    private class TemplateComparator implements Comparator<Template> {
        /**
         * {@inheritDoc}
         * 
         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
         */
        public int compare(Template o1, Template o2) {
            // Tries and find which template is "deeper" in the overriding tree.
            // We only consider simple overrides, hence the "get(0)" in the following code.
            Template currentLevel1 = o1;
            Template currentLevel2 = o2;
            boolean no1Override = currentLevel1.getOverrides().isEmpty();
            boolean no2Override = currentLevel2.getOverrides().isEmpty();
            while (!no1Override && !no2Override) {
                currentLevel1 = currentLevel1.getOverrides().get(0);
                currentLevel2 = currentLevel2.getOverrides().get(0);
                no1Override = currentLevel1.getOverrides().isEmpty();
                no2Override = currentLevel2.getOverrides().isEmpty();
            }

            int result = 0;
            if (no1Override && no2Override) {
                // same "level". Use the name's ordering
                result = o1.getName().compareTo(o2.getName());
            } else if (no1Override) {
                result = 1;
            } else if (no2Override) {
                result = -1;
            }
            return result;
        }
    }
}