001    // Copyright 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.corelib.components;
016    
017    import org.apache.tapestry5.BindingConstants;
018    import org.apache.tapestry5.Block;
019    import org.apache.tapestry5.ComponentResources;
020    import org.apache.tapestry5.annotations.Parameter;
021    import org.apache.tapestry5.annotations.SupportsInformalParameters;
022    import org.apache.tapestry5.ioc.annotations.Inject;
023    import org.apache.tapestry5.ioc.util.AvailableValues;
024    import org.apache.tapestry5.ioc.util.UnknownValueException;
025    import org.apache.tapestry5.runtime.RenderCommand;
026    import org.apache.tapestry5.services.dynamic.DynamicDelegate;
027    import org.apache.tapestry5.services.dynamic.DynamicTemplate;
028    import org.apache.tapestry5.services.dynamic.DynamicTemplateParser;
029    
030    /**
031     * The Dynamic component allows a component to render itself differently at different times, by making use of
032     * an external template file.
033     * <p>
034     * The content of the template file replaces the Dynamic component entirely with one exception: certain elements will be
035     * replaced with {@linkplain Block}s passed to the Dynamic component as informal parameters; this is triggered by
036     * <strong>id</strong> of the element. When the id attribute has the prefix {@code param:}, the remainder is the name of
037     * a Block parameter. There are no limitations on what can appear inside such a Block: text, components, forms, even the
038     * {@literal <t:body/>} directive.
039     * <p>
040     * Dynamic templates emulate how expansions work in standard Tapestry templates: Expansions (the <code>${ ... }</code>
041     * syntax) can appear in attribute values or interspersed in element text. This allows container properties, messages,
042     * assets, and so forth to be referenced within the external template. It should be noted that such access is quite a
043     * bit less efficient than putting such expansions inside a referenced Block, but this should not be a big concern
044     * outside of some kind of tight rendering loop.
045     * 
046     * @since 5.3
047     * @see DynamicTemplate
048     * @see DynamicTemplateParser
049     * @tapestrydoc
050     */
051    @SupportsInformalParameters
052    public class Dynamic
053    {
054        /** The dynamic template containing the content to be rendered by the component. */
055        @Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.ASSET)
056        private DynamicTemplate template;
057    
058        @Inject
059        private ComponentResources resources;
060    
061        private final DynamicDelegate delegate = new DynamicDelegate()
062        {
063            public ComponentResources getComponentResources()
064            {
065                return resources;
066            }
067    
068            public Block getBlock(String name)
069            {
070                Block result = resources.getBlockParameter(name);
071    
072                if (result != null)
073                    return result;
074    
075                throw new UnknownValueException(String.format(
076                        "Component %s does not have an informal Block parameter with id '%s'.", resources.getCompleteId(),
077                        name), null, null, new AvailableValues("Available Blocks", resources.getInformalParameterNames()));
078            }
079        };
080    
081        RenderCommand beginRender()
082        {
083            // Probably some room for caching here as well. It shouldn't be necessary to re-create the outermost
084            // RenderCommand every time, unless the template has changed from the previous render.
085            return template.createRenderCommand(delegate);
086        }
087    }