| // Copyright 2006, 2007, 2008 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry5.internal.structure; |
| |
| import org.apache.tapestry5.*; |
| import org.apache.tapestry5.internal.InternalComponentResources; |
| import org.apache.tapestry5.internal.services.Instantiator; |
| import org.apache.tapestry5.ioc.AnnotationProvider; |
| import org.apache.tapestry5.ioc.Location; |
| import org.apache.tapestry5.ioc.Messages; |
| import org.apache.tapestry5.ioc.Resource; |
| import org.apache.tapestry5.ioc.internal.util.CollectionFactory; |
| import org.apache.tapestry5.ioc.internal.util.Defense; |
| import org.apache.tapestry5.ioc.internal.util.InternalUtils; |
| import org.apache.tapestry5.ioc.internal.util.TapestryException; |
| import org.apache.tapestry5.model.ComponentModel; |
| import org.apache.tapestry5.model.ParameterModel; |
| import org.apache.tapestry5.runtime.Component; |
| import org.apache.tapestry5.runtime.PageLifecycleListener; |
| import org.apache.tapestry5.runtime.RenderQueue; |
| import org.slf4j.Logger; |
| |
| import java.lang.annotation.Annotation; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| /** |
| * The bridge between a component and its {@link ComponentPageElement}, that supplies all kinds of resources to the |
| * component, including access to its parameters, parameter bindings, and persistent field data. |
| */ |
| public class InternalComponentResourcesImpl implements InternalComponentResources |
| { |
| private final Page page; |
| |
| private final String completeId; |
| |
| private final String nestedId; |
| |
| private final ComponentModel componentModel; |
| |
| private final ComponentPageElement element; |
| |
| private final Component component; |
| |
| private final ComponentResources containerResources; |
| |
| private final ComponentPageElementResources componentPageElementResources; |
| |
| // Case insensitive |
| private Map<String, Binding> bindings; |
| |
| private Messages messages; |
| |
| // Case insensitive |
| private Map<String, Object> renderVariables; |
| |
| public InternalComponentResourcesImpl(Page page, ComponentPageElement element, |
| ComponentResources containerResources, |
| ComponentPageElementResources componentPageElementResources, |
| String completeId, String nestedId, Instantiator componentInstantiator |
| ) |
| { |
| this.page = page; |
| this.element = element; |
| this.containerResources = containerResources; |
| this.componentPageElementResources = componentPageElementResources; |
| this.completeId = completeId; |
| this.nestedId = nestedId; |
| |
| componentModel = componentInstantiator.getModel(); |
| component = componentInstantiator.newInstance(this); |
| } |
| |
| public Location getLocation() |
| { |
| return element.getLocation(); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return String.format("InternalComponentResources[%s]", getCompleteId()); |
| } |
| |
| public ComponentModel getComponentModel() |
| { |
| return componentModel; |
| } |
| |
| public Component getEmbeddedComponent(String embeddedId) |
| { |
| return element.getEmbeddedElement(embeddedId).getComponent(); |
| } |
| |
| public Object getFieldChange(String fieldName) |
| { |
| return page.getFieldChange(nestedId, fieldName); |
| } |
| |
| public String getId() |
| { |
| return element.getId(); |
| } |
| |
| public boolean hasFieldChange(String fieldName) |
| { |
| return getFieldChange(fieldName) != null; |
| } |
| |
| public Link createEventLink(String eventType, Object... context) |
| { |
| return element.createEventLink(eventType, context); |
| } |
| |
| public Link createActionLink(String eventType, boolean forForm, Object... context) |
| { |
| return element.createActionLink(eventType, forForm, context); |
| } |
| |
| public Link createFormEventLink(String eventType, Object... context) |
| { |
| return element.createFormEventLink(eventType, context); |
| } |
| |
| public Link createPageLink(String pageName, boolean override, Object... context) |
| { |
| return element.createPageLink(pageName, override, context); |
| } |
| |
| public Link createPageLink(Class pageClass, boolean override, Object... context) |
| { |
| return element.createPageLink(pageClass, override, context); |
| } |
| |
| public void discardPersistentFieldChanges() |
| { |
| page.discardPersistentFieldChanges(); |
| } |
| |
| public String getElementName() |
| { |
| return getElementName(null); |
| } |
| |
| public List<String> getInformalParameterNames() |
| { |
| return InternalUtils.sortedKeys(getInformalParameterBindings()); |
| } |
| |
| public <T> T getInformalParameter(String name, Class<T> type) |
| { |
| if (getBinding(name) != null) return readParameter(name, type); |
| |
| return null; |
| } |
| |
| public Block getBody() |
| { |
| return element.getBody(); |
| } |
| |
| public boolean hasBody() |
| { |
| return element.hasBody(); |
| } |
| |
| public String getCompleteId() |
| { |
| return completeId; |
| } |
| |
| public Component getComponent() |
| { |
| return component; |
| } |
| |
| public boolean isBound(String parameterName) |
| { |
| return getBinding(parameterName) != null; |
| } |
| |
| public <T extends Annotation> T getParameterAnnotation(String parameterName, Class<T> annotationType) |
| { |
| Binding b = getBinding(parameterName); |
| |
| if (b == null) return null; |
| |
| return b.getAnnotation(annotationType); |
| } |
| |
| public boolean isRendering() |
| { |
| return element.isRendering(); |
| } |
| |
| public boolean triggerEvent(String eventType, Object[] context, ComponentEventCallback handler) |
| { |
| return element.triggerEvent(eventType, context, handler); |
| } |
| |
| public boolean triggerContextEvent(String eventType, EventContext context, ComponentEventCallback callback) |
| { |
| return element.triggerContextEvent(eventType, context, callback); |
| } |
| |
| public String getNestedId() |
| { |
| return nestedId; |
| } |
| |
| public Component getPage() |
| { |
| return element.getContainingPage().getRootComponent(); |
| } |
| |
| public boolean isInvariant(String parameterName) |
| { |
| Binding b = getBinding(parameterName); |
| |
| return b != null && b.isInvariant(); |
| } |
| |
| public boolean isLoaded() |
| { |
| return element.isLoaded(); |
| } |
| |
| public void persistFieldChange(String fieldName, Object newValue) |
| { |
| try |
| { |
| page.persistFieldChange(this, fieldName, newValue); |
| } |
| catch (Exception ex) |
| { |
| throw new TapestryException(StructureMessages.fieldPersistFailure(getCompleteId(), fieldName, ex), |
| getLocation(), ex); |
| } |
| } |
| |
| public void bindParameter(String parameterName, Binding binding) |
| { |
| if (bindings == null) bindings = CollectionFactory.newCaseInsensitiveMap(); |
| |
| bindings.put(parameterName, binding); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public <T> T readParameter(String parameterName, Class<T> expectedType) |
| { |
| Binding b = getBinding(parameterName); |
| T result; |
| |
| try |
| { |
| // Will throw NPE if binding is null, but this should never be called if the |
| // parameter is not bound. |
| |
| Object boundValue = b.get(); |
| |
| result = componentPageElementResources.coerce(boundValue, expectedType); |
| } |
| catch (Exception ex) |
| { |
| throw new TapestryException(StructureMessages.getParameterFailure(parameterName, getCompleteId(), ex), b, |
| ex); |
| } |
| |
| if (result == null && !isAllowNull(parameterName)) |
| throw new TapestryException(String.format( |
| "Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.", |
| parameterName, |
| getCompleteId()), b, null); |
| |
| return result; |
| } |
| |
| private boolean isAllowNull(String parameterName) |
| { |
| ParameterModel parameterModel = getComponentModel().getParameterModel(parameterName); |
| |
| return parameterModel == null ? true : parameterModel.isAllowNull(); |
| } |
| |
| |
| public Object readParameter(String parameterName, String desiredTypeName) |
| { |
| Class parameterType = componentPageElementResources.toClass(desiredTypeName); |
| |
| return readParameter(parameterName, parameterType); |
| } |
| |
| public Class getBoundType(String parameterName) |
| { |
| Binding b = getBinding(parameterName); |
| |
| return b != null ? b.getBindingType() : null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public <T> void writeParameter(String parameterName, T parameterValue) |
| { |
| Binding b = getBinding(parameterName); |
| |
| Class bindingType = b.getBindingType(); |
| |
| try |
| { |
| Object coerced = componentPageElementResources.coerce(parameterValue, bindingType); |
| |
| b.set(coerced); |
| } |
| catch (Exception ex) |
| { |
| throw new TapestryException(StructureMessages.writeParameterFailure(parameterName, getCompleteId(), ex), b, |
| ex); |
| } |
| } |
| |
| private Binding getBinding(String parameterName) |
| { |
| return bindings == null ? null : bindings.get(parameterName); |
| } |
| |
| public AnnotationProvider getAnnotationProvider(String parameterName) |
| { |
| return getBinding(parameterName); |
| } |
| |
| public Logger getLogger() |
| { |
| return componentModel.getLogger(); |
| } |
| |
| public Component getMixinByClassName(String mixinClassName) |
| { |
| return element.getMixinByClassName(mixinClassName); |
| } |
| |
| public void renderInformalParameters(MarkupWriter writer) |
| { |
| if (bindings == null) return; |
| |
| for (String name : bindings.keySet()) |
| { |
| // Skip all formal parameters. |
| |
| if (componentModel.getParameterModel(name) != null) continue; |
| |
| Binding b = bindings.get(name); |
| |
| Object value = b.get(); |
| |
| if (value == null) continue; |
| |
| // Because Blocks can be passed in (right from the template, using <t:parameter>), |
| // we want to skip those when rending informal parameters. |
| |
| if (value instanceof Block) continue; |
| |
| String valueString = componentPageElementResources.coerce(value, String.class); |
| |
| writer.attributes(name, valueString); |
| } |
| } |
| |
| public Component getContainer() |
| { |
| if (containerResources == null) return null; |
| |
| return containerResources.getComponent(); |
| } |
| |
| public ComponentResources getContainerResources() |
| { |
| return containerResources; |
| } |
| |
| public Messages getContainerMessages() |
| { |
| return containerResources != null ? containerResources.getMessages() : null; |
| } |
| |
| public Locale getLocale() |
| { |
| return element.getLocale(); |
| } |
| |
| public Messages getMessages() |
| { |
| if (messages == null) messages = componentPageElementResources.getMessages(componentModel); |
| |
| return messages; |
| } |
| |
| public String getElementName(String defaultElementName) |
| { |
| return element.getElementName(defaultElementName); |
| } |
| |
| public void queueRender(RenderQueue queue) |
| { |
| queue.push(element); |
| } |
| |
| public Block getBlock(String blockId) |
| { |
| return element.getBlock(blockId); |
| } |
| |
| public Block getBlockParameter(String parameterName) |
| { |
| return getInformalParameter(parameterName, Block.class); |
| } |
| |
| public Block findBlock(String blockId) |
| { |
| return element.findBlock(blockId); |
| } |
| |
| public Resource getBaseResource() |
| { |
| return componentModel.getBaseResource(); |
| } |
| |
| public String getPageName() |
| { |
| return element.getPageName(); |
| } |
| |
| public Map<String, Binding> getInformalParameterBindings() |
| { |
| Map<String, Binding> result = CollectionFactory.newMap(); |
| |
| if (bindings != null) |
| { |
| for (String name : bindings.keySet()) |
| { |
| |
| if (componentModel.getParameterModel(name) != null) continue; |
| |
| result.put(name, bindings.get(name)); |
| } |
| } |
| |
| return result; |
| } |
| |
| public Object getRenderVariable(String name) |
| { |
| Object result = InternalUtils.get(renderVariables, name); |
| |
| if (result == null) throw new IllegalArgumentException(StructureMessages.missingRenderVariable(getCompleteId(), |
| name, |
| renderVariables == null ? null : renderVariables.keySet())); |
| |
| return result; |
| } |
| |
| public void storeRenderVariable(String name, Object value) |
| { |
| Defense.notBlank(name, "name"); |
| Defense.notNull(value, "value"); |
| |
| if (!element.isRendering()) |
| throw new IllegalStateException(StructureMessages.renderVariableSetWhenNotRendering(getCompleteId(), name)); |
| |
| if (renderVariables == null) renderVariables = CollectionFactory.newCaseInsensitiveMap(); |
| |
| renderVariables.put(name, value); |
| } |
| |
| public void postRenderCleanup() |
| { |
| if (renderVariables != null) renderVariables.clear(); |
| } |
| |
| public void addPageLifecycleListener(PageLifecycleListener listener) |
| { |
| page.addLifecycleListener(listener); |
| } |
| } |