/*
 * 作成日： 2004/08/19
 */
package org.seasar.tapestry.engine;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.ApplicationRuntimeException;
import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IBinding;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.IEngine;
import org.apache.tapestry.ILocation;
import org.apache.tapestry.INamespace;
import org.apache.tapestry.IPage;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.IResourceLocation;
import org.apache.tapestry.IResourceResolver;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.asset.ContextAsset;
import org.apache.tapestry.asset.ExternalAsset;
import org.apache.tapestry.asset.PrivateAsset;
import org.apache.tapestry.binding.ExpressionBinding;
import org.apache.tapestry.binding.FieldBinding;
import org.apache.tapestry.binding.ListenerBinding;
import org.apache.tapestry.binding.StaticBinding;
import org.apache.tapestry.binding.StringBinding;
import org.apache.tapestry.engine.IComponentClassEnhancer;
import org.apache.tapestry.engine.IPageLoader;
import org.apache.tapestry.engine.ISpecificationSource;
import org.apache.tapestry.engine.ITemplateSource;
import org.apache.tapestry.event.PageDetachListener;
import org.apache.tapestry.pageload.ComponentTreeWalker;
import org.apache.tapestry.pageload.EstablishDefaultParameterValuesVisitor;
import org.apache.tapestry.pageload.IComponentVisitor;
import org.apache.tapestry.pageload.PropertyInitializer;
import org.apache.tapestry.pageload.VerifyRequiredParametersVisitor;
import org.apache.tapestry.request.RequestContext;
import org.apache.tapestry.resolver.ComponentSpecificationResolver;
import org.apache.tapestry.resource.ClasspathResourceLocation;
import org.apache.tapestry.resource.ContextResourceLocation;
import org.apache.tapestry.spec.AssetType;
import org.apache.tapestry.spec.BindingType;
import org.apache.tapestry.spec.IAssetSpecification;
import org.apache.tapestry.spec.IBindingSpecification;
import org.apache.tapestry.spec.IComponentSpecification;
import org.apache.tapestry.spec.IContainedComponent;
import org.apache.tapestry.spec.IListenerBindingSpecification;
import org.apache.tapestry.spec.IPropertySpecification;

/**
 * Runs the process of building the component hierarchy for an entire page.
 * 
 * <p>
 * This class is not threadsafe; however,
 * {@link org.apache.tapestry.pageload.PageSource}creates a new instance of it
 * for each page to be loaded, which bypasses multithreading issues.
 * 
 * @author Howard Lewis Ship
 * 
 * org.apache.tapestry.pageload.PageLoaderをコピってます。 下策なのでだれかいい案たのむ。
 * 
 * ver 3.0.2
 * @author maruo_syunsuke
 */
public abstract class CopyOfPageLoader implements IPageLoader {
    private static final Log LOG = LogFactory.getLog(CopyOfPageLoader.class);
    /**
     * Instantitates a page from its specification.
     * 
     * @param name
     *            the unqualified, simple, name for the page
     * @param namespace
     *            the namespace containing the page's specification
     * @param spec
     *            the page's specification
     * 
     * We instantiate the page object, then set its specification, names and
     * locale.
     * 
     * @see IEngine
     * @see ChangeObserver
     */

    private IPage instantiatePage(String name, INamespace namespace,
            IComponentSpecification spec) {
        IPage result = null;

        String pageName = namespace.constructQualifiedName(name);
        String className = spec.getComponentClassName();
        ILocation location = spec.getLocation();

        if (Tapestry.isBlank(className)) {
            if (LOG.isDebugEnabled())
                LOG.debug("Page " + namespace.constructQualifiedName(name)
                        + " does not specify a component class.");

            className = _engine.getPropertySource().getPropertyValue(
                    "org.apache.tapestry.default-page-class");

            if (className == null) {
                className = getDefaultPage();// diff
            }

            if (LOG.isDebugEnabled())
                LOG.debug("Defaulting to class " + className);
        }

        Class pageClass = _enhancer.getEnhancedClass(spec, className);
        String enhancedClassName = pageClass.getName();

        try {
            result = getPageComponent(pageClass); // diff
            //            result = (IPage) pageClass.newInstance();

            result.setNamespace(namespace);
            result.setSpecification(spec);
            result.setPageName(pageName);
            result.setPage(result);
            result.setLocale(_locale);
            result.setLocation(location);
        } catch (ClassCastException ex) {
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.class-not-page", enhancedClassName), location,
                    ex);
        } catch (Exception ex) {
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.unable-to-instantiate", enhancedClassName),
                    location, ex);
        }

        return result;
    }

    /**
     * @return
     */
    public abstract String getDefaultPage();

    /**
     * @param pageClass
     * @return 表示するページファイルを返します。
     */
    public abstract IPage getPageComponent(Class pageClass) throws Exception;
    /**
     * Constructor.
     *  
     */


    //==========================================================================
    //	The code under this is a copy from
    // org.apache.tapestry.pageload.PageLoader
    //==========================================================================

    private IEngine                        _engine;
    private IResourceResolver              _resolver;
    private IComponentClassEnhancer        _enhancer;
    private ISpecificationSource           _specificationSource;
    private ComponentSpecificationResolver _componentResolver;
    private List                           _inheritedBindingQueue = new ArrayList();
    private List                           _propertyInitializers  = new ArrayList();
    private ComponentTreeWalker            _establishDefaultParameterValuesWalker;
    private ComponentTreeWalker            _verifyRequiredParametersWalker;

    /**
     * The locale of the application, which is also the locale of the page being
     * loaded.
     *  
     */

    private Locale                         _locale;

    /**
     * Number of components instantiated, excluding the page itself.
     *  
     */

    private int                            _count;

    /**
     * The recursion depth. A page with no components is zero. A component on a
     * page is one.
     *  
     */

    private int                            _depth;

    /**
     * The maximum depth reached while building the page.
     *  
     */

    private int                            _maxDepth;

    /**
     * Used to figure relative paths for context assets.
     *  
     */

    private IResourceLocation              _servletLocation;

    public CopyOfPageLoader(IRequestCycle cycle) {
        IEngine engine = cycle.getEngine();

        _specificationSource = engine.getSpecificationSource();
        _resolver = engine.getResourceResolver();
        _enhancer = engine.getComponentClassEnhancer();
        _componentResolver = new ComponentSpecificationResolver(cycle);

        RequestContext context = cycle.getRequestContext();

        // Need the location of the servlet within the context as the basis
        // for building relative context asset paths.

        HttpServletRequest request = context.getRequest();

        String servletPath = request.getServletPath();

        _servletLocation = new ContextResourceLocation(context.getServlet()
                .getServletContext(), servletPath);

        // Create the mechanisms for walking the component tree when it is
        // complete
        IComponentVisitor verifyRequiredParametersVisitor = new VerifyRequiredParametersVisitor();
        _verifyRequiredParametersWalker = new ComponentTreeWalker(
                new IComponentVisitor[]{verifyRequiredParametersVisitor});

        IComponentVisitor establishDefaultParameterValuesVisitor = new EstablishDefaultParameterValuesVisitor(
                _resolver);
        _establishDefaultParameterValuesWalker = new ComponentTreeWalker(
                new IComponentVisitor[]{establishDefaultParameterValuesVisitor});
    }

    private static interface IQueuedInheritedBinding {
        void connect();
    }

    private static class QueuedInheritedBinding
            implements
                IQueuedInheritedBinding {
        private IComponent _component;
        private String     _containerParameterName;
        private String     _parameterName;

        private QueuedInheritedBinding(IComponent component,
                String containerParameterName, String parameterName) {
            _component = component;
            _containerParameterName = containerParameterName;
            _parameterName = parameterName;
        }

        public void connect() {
            IBinding binding = _component.getContainer().getBinding(
                    _containerParameterName);

            if (binding == null)
                return;

            _component.setBinding(_parameterName, binding);
        }
    }

    private static class QueuedInheritInformalBindings
            implements
                IQueuedInheritedBinding {
        private IComponent _component;

        private QueuedInheritInformalBindings(IComponent component) {
            _component = component;
        }

        public void connect() {

            IComponent container = _component.getContainer();

            for (Iterator it = container.getBindingNames().iterator(); it
                    .hasNext();) {
                String bindingName = (String) it.next();
                connectInformalBinding(container, _component, bindingName);
            }
        }

        private void connectInformalBinding(IComponent container,
                IComponent component, String bindingName) {
            IComponentSpecification componentSpec = component
                    .getSpecification();
            IComponentSpecification containerSpec = container
                    .getSpecification();

            // check if binding already exists in the component
            if (component.getBinding(bindingName) != null)
                return;

            // check if parameter is informal for the component
            if (componentSpec.getParameter(bindingName) != null
                    || componentSpec.isReservedParameterName(bindingName))
                return;

            // check if parameter is informal for the container
            if (containerSpec.getParameter(bindingName) != null
                    || containerSpec.isReservedParameterName(bindingName))
                return;

            // if everything passes, establish binding
            IBinding binding = container.getBinding(bindingName);
            component.setBinding(bindingName, binding);
        }
    }


    /**
     * Binds properties of the component as defined by the container's
     * specification.
     * 
     * <p>
     * This implementation is very simple, we will need a lot more sanity
     * checking and eror checking in the final version.
     * 
     * @param container
     *            The containing component. For a dynamic binding (
     *            {@link ExpressionBinding}) the property name is evaluated
     *            with the container as the root.
     * @param component
     *            The contained component being bound.
     * @param spec
     *            The specification of the contained component.
     * @param contained
     *            The contained component specification (from the container's
     *            {@link IComponentSpecification}).
     *  
     */

    private void bind(IComponent container, IComponent component,
            IContainedComponent contained) {
        IComponentSpecification spec = component.getSpecification();
        boolean formalOnly = !spec.getAllowInformalParameters();

        IComponentSpecification containerSpec = container.getSpecification();
        boolean containerFormalOnly = !containerSpec
                .getAllowInformalParameters();

        if (contained.getInheritInformalParameters()) {
            if (formalOnly)
                throw new ApplicationRuntimeException(
                        Tapestry
                                .format(
                                        "PageLoader.inherit-informal-invalid-component-formal-only",
                                        component.getExtendedId()), component,
                        contained.getLocation(), null);

            if (containerFormalOnly)
                throw new ApplicationRuntimeException(
                        Tapestry
                                .format(
                                        "PageLoader.inherit-informal-invalid-container-formal-only",
                                        container.getExtendedId(), component
                                                .getExtendedId()), component,
                        contained.getLocation(), null);

            IQueuedInheritedBinding queued = new QueuedInheritInformalBindings(
                    component);
            _inheritedBindingQueue.add(queued);
        }

        Iterator i = contained.getBindingNames().iterator();

        while (i.hasNext()) {
            String name = (String) i.next();

            boolean isFormal = spec.getParameter(name) != null;

            IBindingSpecification bspec = contained.getBinding(name);

            // If not allowing informal parameters, check that each binding
            // matches
            // a formal parameter.

            if (formalOnly && !isFormal)
                throw new ApplicationRuntimeException(Tapestry.format(
                        "PageLoader.formal-parameters-only", component
                                .getExtendedId(), name), component, bspec
                        .getLocation(), null);

            // If an informal parameter that conflicts with a reserved name,
            // then
            // skip it.

            if (!isFormal && spec.isReservedParameterName(name))
                continue;

            // The type determines how to interpret the value:
            // As a simple static String
            // As a nested property name (relative to the component)
            // As the name of a binding inherited from the containing component.
            // As the name of a public field
            // As a script for a listener

            BindingType type = bspec.getType();

            // For inherited bindings, defer until later. This gives components
            // a chance to setup bindings from static values and expressions in
            // the
            // template. The order of operations is tricky, template bindings
            // come
            // later.

            if (type == BindingType.INHERITED) {
                QueuedInheritedBinding queued = new QueuedInheritedBinding(
                        component, bspec.getValue(), name);
                _inheritedBindingQueue.add(queued);
                continue;
            }

            if (type == BindingType.LISTENER) {
                constructListenerBinding(component, name,
                        (IListenerBindingSpecification) bspec);
                continue;
            }

            IBinding binding = convert(container, bspec);

            if (binding != null)
                component.setBinding(name, binding);
        }
    }

    private IBinding convert(IComponent container, IBindingSpecification spec) {
        BindingType type = spec.getType();
        ILocation location = spec.getLocation();
        String value = spec.getValue();

        // The most common type.
        // TODO These bindings should be created somehow using the SpecFactory
        // in SpecificationParser
        if (type == BindingType.DYNAMIC)
            return new ExpressionBinding(_resolver, container, value, location);

        // String bindings are new in 2.0.4. For the momement,
        // we don't even try to cache and share them ... they
        // are most often unique within a page.

        if (type == BindingType.STRING)
            return new StringBinding(container, value, location);

        // static and field bindings are pooled. This allows the
        // same instance to be used with many components.

        if (type == BindingType.STATIC)
            return new StaticBinding(value, location);

        // BindingType.FIELD is on the way out, it is in the
        // 1.3 DTD but not the 1.4 DTD.

        if (type == BindingType.FIELD)
            return new FieldBinding(_resolver, value, location);

        // This code is unreachable, at least until a new type
        // of binding is created.

        throw new ApplicationRuntimeException("Unexpected type: " + type + ".");
    }

    /**
     * Construct a {@link ListenerBinding}for the component, and add it.
     * 
     * @since 3.0
     *  
     */

    private void constructListenerBinding(IComponent component,
            String bindingName, IListenerBindingSpecification spec) {
        String language = spec.getLanguage();

        // If not provided in the page or component specification, then
        // search for a default (factory default is "jython").

        if (Tapestry.isBlank(language))
            language = _engine.getPropertySource().getPropertyValue(
                    "org.apache.tapestry.default-script-language");

        // Construct the binding. The first parameter is the compononent
        // (not the DirectLink or Form, but the page or component containing the
        // link or form).

        IBinding binding = new ListenerBinding(component.getContainer(),
                language, spec.getScript(), spec.getLocation());

        component.setBinding(bindingName, binding);
    }

    /**
     * Sets up a component. This involves:
     * <ul>
     * <li>Instantiating any contained components.
     * <li>Add the contained components to the container.
     * <li>Setting up bindings between container and containees.
     * <li>Construct the containees recursively.
     * <li>Invoking
     * {@link IComponent#finishLoad(IRequestCycle, IPageLoader, IComponentSpecification)}
     * </ul>
     * 
     * @param cycle
     *            the request cycle for which the page is being (initially)
     *            constructed
     * @param page
     *            The page on which the container exists.
     * @param container
     *            The component to be set up.
     * @param containerSpec
     *            The specification for the container.
     * @param the
     *            namespace of the container
     *  
     */

    private void constructComponent(IRequestCycle cycle, IPage page,
            IComponent container, IComponentSpecification containerSpec,
            INamespace namespace) {
        _depth++;
        if (_depth > _maxDepth)
            _maxDepth = _depth;

        List ids = new ArrayList(containerSpec.getComponentIds());
        int count = ids.size();

        try {
            for (int i = 0; i < count; i++) {
                String id = (String) ids.get(i);

                // Get the sub-component specification from the
                // container's specification.

                IContainedComponent contained = containerSpec.getComponent(id);

                String type = contained.getType();
                ILocation location = contained.getLocation();

                _componentResolver.resolve(cycle, namespace, type, location);

                IComponentSpecification componentSpecification = _componentResolver
                        .getSpecification();
                INamespace componentNamespace = _componentResolver
                        .getNamespace();

                // Instantiate the contained component.

                IComponent component = instantiateComponent(page, container,
                        id, componentSpecification, componentNamespace,
                        location);

                // Add it, by name, to the container.

                container.addComponent(component);

                // Set up any bindings in the IContainedComponent specification

                bind(container, component, contained);

                // Now construct the component recusively; it gets its chance
                // to create its subcomponents and set their bindings.

                constructComponent(cycle, page, component,
                        componentSpecification, componentNamespace);
            }

            addAssets(container, containerSpec);

            // Finish the load of the component; most components (which
            // subclass BaseComponent) load their templates here.
            // That may cause yet more components to be created, and more
            // bindings to be set, so we defer some checking until
            // later.

            container.finishLoad(cycle, this, containerSpec);

            // Finally, we create an initializer for each
            // specified property.

            createPropertyInitializers(page, container, containerSpec);
        } catch (ApplicationRuntimeException ex) {
            throw ex;
        } catch (RuntimeException ex) {
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.unable-to-instantiate-component", container
                            .getExtendedId(), ex.getMessage()), container,
                    null, ex);
        }

        _depth--;
    }

    /**
     * Invoked to create an implicit component (one which is defined in the
     * containing component's template, rather that in the containing
     * component's specification).
     * 
     * @see org.apache.tapestry.BaseComponentTemplateLoader
     * @since 3.0
     *  
     */

    public IComponent createImplicitComponent(IRequestCycle cycle,
            IComponent container, String componentId, String componentType,
            ILocation location) {
        IPage page = container.getPage();

        _componentResolver.resolve(cycle, container.getNamespace(),
                componentType, location);

        INamespace componentNamespace = _componentResolver.getNamespace();
        IComponentSpecification spec = _componentResolver.getSpecification();

        IComponent result = instantiateComponent(page, container, componentId,
                spec, componentNamespace, location);

        container.addComponent(result);

        // Recusively build the component.

        constructComponent(cycle, page, result, spec, componentNamespace);

        return result;
    }

    /**
     * Instantiates a component from its specification. We instantiate the
     * component object, then set its specification, page, container and id.
     * 
     * @see AbstractComponent
     *  
     */

    private IComponent instantiateComponent(IPage page, IComponent container,
            String id, IComponentSpecification spec, INamespace namespace,
            ILocation location) {
        IComponent result = null;
        String className = spec.getComponentClassName();

        if (Tapestry.isBlank(className))
            className = BaseComponent.class.getName();

        Class componentClass = _enhancer.getEnhancedClass(spec, className);
        String enhancedClassName = componentClass.getName();

        try {
            result = (IComponent) componentClass.newInstance();

        } catch (ClassCastException ex) {
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.class-not-component", enhancedClassName),
                    container, spec.getLocation(), ex);
        } catch (Throwable ex) {
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.unable-to-instantiate", enhancedClassName),
                    container, spec.getLocation(), ex);
        }

        if (result instanceof IPage)
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.page-not-allowed", result.getExtendedId()),
                    result, null, null);

        result.setNamespace(namespace);
        result.setSpecification(spec);
        result.setPage(page);
        result.setContainer(container);
        result.setId(id);
        result.setLocation(location);

        _count++;

        return result;
    }


    /**
     * Invoked by the {@link PageSource}to load a specific page. This method is
     * not reentrant ... the PageSource ensures that any given instance of
     * PageLoader is loading only a single page at a time. The page is
     * immediately attached to the {@link IEngine engine}.
     * 
     * @param name
     *            the simple (unqualified) name of the page to load
     * @param namespace
     *            from which the page is to be loaded (used when resolving
     *            components embedded by the page)
     * @param cycle
     *            the request cycle the page is initially loaded for (this is
     *            used to define the locale of the new page, and provide access
     *            to the corect specification source, etc.).
     * @param specification
     *            the specification for the page
     *  
     */

    public IPage loadPage(String name, INamespace namespace,
            IRequestCycle cycle, IComponentSpecification specification) {
        IPage page = null;

        _engine = cycle.getEngine();

        _locale = _engine.getLocale();

        _count = 0;
        _depth = 0;
        _maxDepth = 0;

        try {
            page = instantiatePage(name, namespace, specification);

            page.attach(_engine);

            // As of 3.0.1, this is done now, rather than after constructing the
            // page and its
            // components.

            page.setRequestCycle(cycle);

            constructComponent(cycle, page, page, specification, namespace);

            // Walk through the complete component tree to set up the default
            // parameter values.
            _establishDefaultParameterValuesWalker.walkComponentTree(page);

            establishInheritedBindings();

            // Walk through the complete component tree to ensure that required
            // parameters are bound
            _verifyRequiredParametersWalker.walkComponentTree(page);

            establishDefaultPropertyValues();
        } finally {
            _locale = null;
            _engine = null;
            _inheritedBindingQueue.clear();
            _propertyInitializers.clear();
        }

        if (LOG.isDebugEnabled())
            LOG.debug("Loaded page " + page + " with " + _count
                    + " components (maximum depth " + _maxDepth + ")");

        return page;
    }

    private void establishInheritedBindings() {
        LOG.debug("Establishing inherited bindings");

        int count = _inheritedBindingQueue.size();

        for (int i = 0; i < count; i++) {
            IQueuedInheritedBinding queued = (IQueuedInheritedBinding) _inheritedBindingQueue
                    .get(i);

            queued.connect();
        }
    }

    private void establishDefaultPropertyValues() {
        LOG.debug("Setting default property values");

        int count = _propertyInitializers.size();

        for (int i = 0; i < count; i++) {
            PageDetachListener initializer = (PageDetachListener) _propertyInitializers
                    .get(i);

            initializer.pageDetached(null);
        }
    }

    private void addAssets(IComponent component,
            IComponentSpecification specification) {
        List names = specification.getAssetNames();

        if (names.isEmpty())
            return;

        IResourceLocation specLocation = specification
                .getSpecificationLocation();

        Iterator i = names.iterator();

        while (i.hasNext()) {
            String name = (String) i.next();
            IAssetSpecification assetSpec = specification.getAsset(name);
            IAsset asset = convert(name, component, assetSpec, specLocation);

            component.addAsset(name, asset);
        }
    }

    /**
     * Invoked from
     * {@link #constructComponent(IRequestCycle, IPage, IComponent, IComponentSpecification, INamespace)}
     * after
     * {@link IComponent#finishLoad(IRequestCycle, IPageLoader, IComponentSpecification)}
     * is invoked. This iterates over any
     * {@link org.apache.tapestry.spec.IPropertySpecification}s for the
     * component, create an initializer for each.
     *  
     */

    private void createPropertyInitializers(IPage page, IComponent component,
            IComponentSpecification spec) {
        List names = spec.getPropertySpecificationNames();
        int count = names.size();

        for (int i = 0; i < count; i++) {
            String name = (String) names.get(i);
            IPropertySpecification ps = spec.getPropertySpecification(name);
            String expression = ps.getInitialValue();

            PageDetachListener initializer = new PropertyInitializer(_resolver,
                    component, name, expression, ps.getLocation());

            _propertyInitializers.add(initializer);
            page.addPageDetachListener(initializer);
        }

    }

    /**
     * Builds an instance of {@link IAsset}from the specification.
     *  
     */

    private IAsset convert(String assetName, IComponent component,
            IAssetSpecification spec, IResourceLocation specificationLocation) {
        AssetType type = spec.getType();
        String path = spec.getPath();
        ILocation location = spec.getLocation();

        if (type == AssetType.EXTERNAL)
            return new ExternalAsset(path, location);

        if (type == AssetType.PRIVATE) {
            IResourceLocation baseLocation = specificationLocation;

            // Fudge a special case for private assets with complete paths. The
            // specificationLocation
            // can't be used because it is often a ContextResourceLocation,
            // not a ClasspathResourceLocation.

            if (path.startsWith("/")) {
                baseLocation = new ClasspathResourceLocation(_resolver, "/");
                path = path.substring(1);
            }

            return new PrivateAsset((ClasspathResourceLocation) findAsset(
                    assetName, component, baseLocation, path, location),
                    location);
        }

        return new ContextAsset((ContextResourceLocation) findAsset(assetName,
                component, _servletLocation, path, location), location);
    }

    private IResourceLocation findAsset(String assetName, IComponent component,
            IResourceLocation baseLocation, String path, ILocation location) {
        IResourceLocation assetLocation = baseLocation
                .getRelativeLocation(path);
        IResourceLocation localizedLocation = assetLocation
                .getLocalization(_locale);

        if (localizedLocation == null)
            throw new ApplicationRuntimeException(Tapestry.format(
                    "PageLoader.missing-asset", assetName, component
                            .getExtendedId(), assetLocation), component,
                    location, null);

        return localizedLocation;
    }

    public IEngine getEngine() {
        return _engine;
    }

    public ITemplateSource getTemplateSource() {
        return _engine.getTemplateSource();
    }
}