class AbstractEngineContext implements IEngineContext {

    // NOTE we are not extending AbstractContext or AbstractExpressionContext on purpose, as the variable-oriented
    // methods are going to be handled by the subclasses, not any superclasses.

    private final IEngineConfiguration configuration;
    private final Map<String,Object> templateResolutionAttributes;
    private final Locale locale;

    private IExpressionObjects expressionObjects = null;
    private IdentifierSequences identifierSequences = null;



    protected AbstractEngineContext(
            final IEngineConfiguration configuration,
            final Map<String,Object> templateResolutionAttributes,
            final Locale locale) {

        super();

        Validate.notNull(configuration, "Configuration cannot be null");
        // templateResolutionAttributes CAN be null
        Validate.notNull(locale, "Locale cannot be null");

        this.configuration = configuration;
        this.locale = locale;
        this.templateResolutionAttributes = templateResolutionAttributes;
        // Most templates will not need this, so we will initialize it lazily
        this.identifierSequences = null;

    }


    public final IEngineConfiguration getConfiguration() {
        return this.configuration;
    }


    public final Map<String,Object> getTemplateResolutionAttributes() {
        return this.templateResolutionAttributes;
    }


    public final Locale getLocale() {
        return this.locale;
    }


    public final IExpressionObjects getExpressionObjects() {<FILL_FUNCTION_BODY>}


    public final TemplateMode getTemplateMode() {
        return getTemplateData().getTemplateMode();
    }


    public final IModelFactory getModelFactory() {
        return this.configuration.getModelFactory(getTemplateMode());
    }


    public final String getMessage(
            final Class<?> origin, final String key, final Object[] messageParameters, final boolean useAbsentMessageRepresentation) {

        // origin CAN be null
        Validate.notNull(key, "Message key cannot be null");
        // messageParameter CAN be null

        final Set<IMessageResolver> messageResolvers = this.configuration.getMessageResolvers();

        // Try to resolve the message
        for (final IMessageResolver messageResolver : messageResolvers) {
            final String resolvedMessage =
                    messageResolver.resolveMessage(this, origin, key, messageParameters);
            if (resolvedMessage != null) {
                return resolvedMessage;
            }
        }

        // Message unresolved: try to create an "absent message representation" (if specified to do so)
        if (useAbsentMessageRepresentation) {

            for (final IMessageResolver messageResolver : messageResolvers) {
                final String absentMessageRepresentation =
                        messageResolver.createAbsentMessageRepresentation(this, origin, key, messageParameters);
                if (absentMessageRepresentation != null) {
                    return absentMessageRepresentation;
                }
            }

        }

        return null;

    }


    public final String buildLink(final String base, final Map<String, Object> parameters) {

        // base CAN be null
        // parameters CAN be null

        final Set<ILinkBuilder> linkBuilders = this.configuration.getLinkBuilders();

        // Try to resolve the message
        for (final ILinkBuilder linkBuilder : linkBuilders) {
            final String link = linkBuilder.buildLink(this, base, parameters);
            if (link != null) {
                return link;
            }
        }

        // Message unresolved: this should never happen, so we should fail

        throw new TemplateProcessingException(
                "No configured link builder instance was able to build link with base \"" + base + "\" and " +
                "parameters " + parameters);

    }


    public final IdentifierSequences getIdentifierSequences() {
        // No problem in lazily initializing this here, as context objects should not be used by
        // multiple threads.
        if (this.identifierSequences == null) {
            this.identifierSequences = new IdentifierSequences();
        }
        return this.identifierSequences;
    }



}

        // We delay creation of expression objects in case they are not needed at all
        if (this.expressionObjects == null) {
            this.expressionObjects = new ExpressionObjects(this, this.configuration.getExpressionObjectFactory());
        }
        return this.expressionObjects;
    
<no_super_class>