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