| // Copyright 2004, 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.tapestry; |
| |
| import ognl.OgnlRuntime; |
| import org.apache.tapestry.bean.BeanProvider; |
| import org.apache.tapestry.bean.BeanProviderPropertyAccessor; |
| import org.apache.tapestry.engine.ExpressionEvaluator; |
| import org.apache.tapestry.engine.IPageLoader; |
| import org.apache.tapestry.event.*; |
| import org.apache.tapestry.listener.ListenerMap; |
| import org.apache.tapestry.param.ParameterManager; |
| import org.apache.tapestry.spec.BaseLocatable; |
| import org.apache.tapestry.spec.IComponentSpecification; |
| import org.apache.tapestry.util.prop.PropertyFinder; |
| import org.apache.tapestry.util.prop.PropertyInfo; |
| |
| import java.util.*; |
| |
| /** |
| * Abstract base class implementing the {@link IComponent} interface. |
| * |
| * @author Howard Lewis Ship |
| * @version $Id$ |
| * |
| **/ |
| |
| public abstract class AbstractComponent extends BaseLocatable implements IComponent |
| { |
| /** |
| * Used to check that subclasses invoke this implementation of prepareForRender(). |
| * @see Tapestry#checkMethodInvocation(Object, String, Object) |
| * @since 3.0 |
| */ |
| private final static String PREPAREFORRENDER_METHOD_ID = "AbstractComponent.prepareForRender()"; |
| |
| /** |
| * Used to check that subclasses invoke this implementation of cleanupAfterRender(). |
| * @see Tapestry#checkMethodInvocation(Object, String, Object) |
| * @since 3.0 |
| */ |
| |
| private final static String CLEANUPAFTERRENDER_METHOD_ID = |
| "AbstractComponent.cleanupAfterRender()"; |
| |
| static { |
| // Register the BeanProviderHelper to provide access to the |
| // beans of a bean provider as named properties. |
| |
| OgnlRuntime.setPropertyAccessor(IBeanProvider.class, new BeanProviderPropertyAccessor()); |
| } |
| |
| /** |
| * The specification used to originally build the component. |
| * |
| * |
| **/ |
| |
| private IComponentSpecification _specification; |
| |
| /** |
| * The page that contains the component, possibly itself (if the component is |
| * in fact, a page). |
| * |
| * |
| **/ |
| |
| private IPage _page; |
| |
| /** |
| * The component which contains the component. This will only be |
| * null if the component is actually a page. |
| * |
| **/ |
| |
| private IComponent _container; |
| |
| /** |
| * The simple id of this component. |
| * |
| * |
| **/ |
| |
| private String _id; |
| |
| /** |
| * The fully qualified id of this component. This is calculated the first time |
| * it is needed, then cached for later. |
| * |
| **/ |
| |
| private String _idPath; |
| |
| private static final int MAP_SIZE = 5; |
| |
| /** |
| * A {@link Map} of all bindings (for which there isn't a corresponding |
| * JavaBeans property); the keys are the names of formal and informal |
| * parameters. |
| * |
| **/ |
| |
| private Map _bindings; |
| |
| private Map _components; |
| private static final int BODY_INIT_SIZE = 5; |
| |
| private INamespace _namespace; |
| |
| /** |
| * Used in place of JDK 1.3's Collections.EMPTY_MAP (which is not |
| * available in JDK 1.2). |
| * |
| **/ |
| |
| private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap(1)); |
| |
| /** |
| * The number of {@link IRender} objects in the body of |
| * this component. |
| * |
| * |
| **/ |
| |
| private int _bodyCount = 0; |
| |
| /** |
| * An aray of elements in the body of this component. |
| * |
| * |
| **/ |
| |
| private IRender[] _body; |
| |
| /** |
| * The components' asset map. |
| * |
| **/ |
| |
| private Map _assets; |
| |
| /** |
| * A mapping that allows public instance methods to be dressed up |
| * as {@link IActionListener} listener |
| * objects. |
| * |
| * @since 1.0.2 |
| * |
| **/ |
| |
| private ListenerMap _listeners; |
| |
| /** |
| * A bean provider; these are lazily created as needed. |
| * |
| * @since 1.0.4 |
| * |
| **/ |
| |
| private IBeanProvider _beans; |
| |
| /** |
| * Manages setting and clearing parameter properties for the component. |
| * |
| * @since 2.0.3 |
| * |
| **/ |
| |
| private ParameterManager _parameterManager; |
| |
| /** |
| * Provides access to localized Strings for this component. |
| * |
| * @since 2.0.4 |
| * |
| **/ |
| |
| private IMessages _strings; |
| |
| private ExpressionEvaluator _evaluator; |
| |
| public void addAsset(String name, IAsset asset) |
| { |
| if (_assets == null) |
| _assets = new HashMap(MAP_SIZE); |
| |
| _assets.put(name, asset); |
| } |
| |
| public void addComponent(IComponent component) |
| { |
| if (_components == null) |
| _components = new HashMap(MAP_SIZE); |
| |
| _components.put(component.getId(), component); |
| } |
| |
| /** |
| * Adds an element (which may be static text or a component) as a body |
| * element of this component. Such elements are rendered |
| * by {@link #renderBody(IMarkupWriter, IRequestCycle)}. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public void addBody(IRender element) |
| { |
| // Should check the specification to see if this component |
| // allows body. Curently, this is checked by the component |
| // in render(), which is silly. |
| |
| if (_body == null) |
| { |
| _body = new IRender[BODY_INIT_SIZE]; |
| _body[0] = element; |
| |
| _bodyCount = 1; |
| return; |
| } |
| |
| // No more room? Make the array bigger. |
| |
| if (_bodyCount == _body.length) |
| { |
| IRender[] newWrapped; |
| |
| newWrapped = new IRender[_body.length * 2]; |
| |
| System.arraycopy(_body, 0, newWrapped, 0, _bodyCount); |
| |
| _body = newWrapped; |
| } |
| |
| _body[_bodyCount++] = element; |
| } |
| |
| /** |
| * Registers this component as a listener of the page if it |
| * implements {@link org.apache.tapestry.event.PageDetachListener} or |
| * {@link org.apache.tapestry.event.PageRenderListener}. |
| * |
| * <p> |
| * Invokes {@link #finishLoad()}. Subclasses may overide as needed, but |
| * must invoke this implementation. |
| * {@link BaseComponent} |
| * loads its HTML template. |
| * |
| **/ |
| |
| public void finishLoad( |
| IRequestCycle cycle, |
| IPageLoader loader, |
| IComponentSpecification specification) |
| { |
| if (this instanceof PageDetachListener) |
| _page.addPageDetachListener((PageDetachListener) this); |
| |
| if (this instanceof PageRenderListener) |
| _page.addPageRenderListener((PageRenderListener) this); |
| |
| if (this instanceof PageValidateListener) |
| _page.addPageValidateListener((PageValidateListener) this); |
| |
| finishLoad(); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, int)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, int newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, Object)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, Object newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, boolean)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, boolean newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, double)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, double newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, float)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, float newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, long)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, long newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, char)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, char newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, byte)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, byte newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. |
| * Use {@link Tapestry#fireObservedChange(IComponent, String, short)} instead. |
| */ |
| protected void fireObservedChange(String propertyName, short newValue) |
| { |
| Tapestry.fireObservedChange(this, propertyName, newValue); |
| } |
| |
| /** |
| * @deprecated To be removed in 3.1. Use |
| * {@link #renderInformalParameters(IMarkupWriter, IRequestCycle)} |
| * instead. |
| * |
| **/ |
| |
| protected void generateAttributes(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| renderInformalParameters(writer, cycle); |
| } |
| |
| /** |
| * Converts informal parameters into additional attributes on the |
| * curently open tag. |
| * |
| * <p>Invoked from subclasses to allow additional attributes to |
| * be specified within a tag (this works best when there is a |
| * one-to-one corespondence between an {@link IComponent} and a |
| * HTML element. |
| * |
| * <p>Iterates through the bindings for this component. Filters |
| * out bindings when the name matches a formal parameter (as of 1.0.5, |
| * informal bindings are weeded out at page load / template load time, |
| * if they match a formal parameter, or a specificied reserved name). |
| * For the most part, all the bindings here are either informal parameter, |
| * or formal parameter without a corresponding JavaBeans property. |
| * |
| * <p>For each acceptible key, the value is extracted using {@link IBinding#getObject()}. |
| * If the value is null, no attribute is written. |
| * |
| * <p>If the value is an instance of {@link IAsset}, then |
| * {@link IAsset#buildURL(IRequestCycle)} is invoked to convert the asset |
| * to a URL. |
| * |
| * <p>Finally, {@link IMarkupWriter#attribute(String,String)} is |
| * invoked with the value (or the URL). |
| * |
| * <p>The most common use for informal parameters is to support |
| * the HTML class attribute (for use with cascading style sheets) |
| * and to specify JavaScript event handlers. |
| * |
| * <p>Components are only required to generate attributes on the |
| * result phase; this can be skipped during the rewind phase. |
| **/ |
| |
| protected void renderInformalParameters(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| String attribute; |
| |
| if (_bindings == null) |
| return; |
| |
| Iterator i = _bindings.entrySet().iterator(); |
| |
| while (i.hasNext()) |
| { |
| Map.Entry entry = (Map.Entry) i.next(); |
| String name = (String) entry.getKey(); |
| |
| IBinding binding = (IBinding) entry.getValue(); |
| |
| Object value = binding.getObject(); |
| if (value == null) |
| continue; |
| |
| if (value instanceof IAsset) |
| { |
| IAsset asset = (IAsset) value; |
| |
| // Get the URL of the asset and insert that. |
| |
| attribute = asset.buildURL(cycle); |
| } |
| else |
| attribute = value.toString(); |
| |
| writer.attribute(name, attribute); |
| } |
| |
| } |
| |
| /** |
| * Returns an object used to resolve classes. |
| * @since 3.0 |
| * |
| **/ |
| private IResourceResolver getResourceResolver() |
| { |
| return getPage().getEngine().getResourceResolver(); |
| } |
| |
| /** |
| * Returns the named binding, or null if it doesn't exist. |
| * |
| * <p>This method looks for a JavaBeans property with an |
| * appropriate name, of type {@link IBinding}. The property |
| * should be named <code><i>name</i>Binding</code>. If it exists |
| * and is both readable and writable, then it is accessor method |
| * is invoked. Components which implement such methods can |
| * access their own binding through their instance variables |
| * instead of invoking this method, a performance optimization. |
| * |
| * @see #setBinding(String,IBinding) |
| * |
| **/ |
| |
| public IBinding getBinding(String name) |
| { |
| String bindingPropertyName = name + Tapestry.PARAMETER_PROPERTY_NAME_SUFFIX; |
| PropertyInfo info = PropertyFinder.getPropertyInfo(getClass(), bindingPropertyName); |
| |
| if (info != null && info.isReadWrite() && info.getType().equals(IBinding.class)) |
| { |
| return (IBinding) getEvaluator().read(this, bindingPropertyName); |
| //IResourceResolver resolver = getPage().getEngine().getResourceResolver(); |
| |
| //return (IBinding) OgnlUtils.get(bindingPropertyName, resolver, this); |
| } |
| |
| if (_bindings == null) |
| return null; |
| |
| return (IBinding) _bindings.get(name); |
| } |
| |
| |
| /** |
| * Return's the page's change observer. In practical terms, this |
| * will be an {@link org.apache.tapestry.engine.IPageRecorder}. |
| * |
| * @see IPage#getChangeObserver() |
| * @deprecated To be removed in 3.1; use {@link IPage#getChangeObserver()}. |
| **/ |
| |
| public ChangeObserver getChangeObserver() |
| { |
| return _page.getChangeObserver(); |
| } |
| |
| public IComponent getComponent(String id) |
| { |
| IComponent result = null; |
| |
| if (_components != null) |
| result = (IComponent) _components.get(id); |
| |
| if (result == null) |
| throw new ApplicationRuntimeException( |
| Tapestry.format("no-such-component", this, id), |
| this, |
| null, |
| null); |
| |
| return result; |
| } |
| |
| public IComponent getContainer() |
| { |
| return _container; |
| } |
| |
| public void setContainer(IComponent value) |
| { |
| if (_container != null) |
| throw new ApplicationRuntimeException( |
| Tapestry.getMessage("AbstractComponent.attempt-to-change-container")); |
| |
| _container = value; |
| } |
| |
| /** |
| * Returns the name of the page, a slash, and this component's id path. |
| * Pages are different, they simply return their name. |
| * |
| * @see #getIdPath() |
| * |
| **/ |
| |
| public String getExtendedId() |
| { |
| if (_page == null) |
| return null; |
| |
| return _page.getPageName() + "/" + getIdPath(); |
| } |
| |
| public String getId() |
| { |
| return _id; |
| } |
| |
| public void setId(String value) |
| { |
| if (_id != null) |
| throw new ApplicationRuntimeException( |
| Tapestry.getMessage("AbstractComponent.attempt-to-change-component-id")); |
| |
| _id = value; |
| } |
| |
| public String getIdPath() |
| { |
| String containerIdPath; |
| |
| if (_container == null) |
| throw new NullPointerException( |
| Tapestry.format("AbstractComponent.null-container", this)); |
| |
| containerIdPath = _container.getIdPath(); |
| |
| if (containerIdPath == null) |
| _idPath = _id; |
| else |
| _idPath = containerIdPath + "." + _id; |
| |
| return _idPath; |
| } |
| |
| public IPage getPage() |
| { |
| return _page; |
| } |
| |
| public void setPage(IPage value) |
| { |
| if (_page != null) |
| throw new ApplicationRuntimeException( |
| Tapestry.getMessage("AbstractComponent.attempt-to-change-page")); |
| |
| _page = value; |
| } |
| |
| public IComponentSpecification getSpecification() |
| { |
| return _specification; |
| } |
| |
| public void setSpecification(IComponentSpecification value) |
| { |
| if (_specification != null) |
| throw new ApplicationRuntimeException( |
| Tapestry.getMessage("AbstractComponent.attempt-to-change-spec")); |
| |
| _specification = value; |
| } |
| |
| /** |
| * Renders all elements wrapped by the receiver. |
| * |
| **/ |
| |
| public void renderBody(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| for (int i = 0; i < _bodyCount; i++) |
| _body[i].render(writer, cycle); |
| } |
| |
| /** |
| * Adds the binding with the given name, replacing any existing binding |
| * with that name. |
| * |
| * <p>This method checks to see if a matching JavaBeans property |
| * (with a name of <code><i>name</i>Binding</code> and a type of |
| * {@link IBinding}) exists. If so, that property is updated. |
| * An optimized component can simply implement accessor and |
| * mutator methods and then access its bindings via its own |
| * instance variables, rather than going through {@link |
| * #getBinding(String)}. |
| * |
| * <p>Informal parameters should <em>not</em> be stored in |
| * instance variables if {@link |
| * #renderInformalParameters(IMarkupWriter, IRequestCycle)} is to be used. |
| * It relies on using the collection of bindings (to store informal parameters). |
| **/ |
| |
| public void setBinding(String name, IBinding binding) |
| { |
| String bindingPropertyName = name + Tapestry.PARAMETER_PROPERTY_NAME_SUFFIX; |
| |
| PropertyInfo info = PropertyFinder.getPropertyInfo(getClass(), bindingPropertyName); |
| |
| if (info != null && info.isReadWrite() && info.getType().equals(IBinding.class)) |
| { |
| getEvaluator().write(this, bindingPropertyName, binding); |
| // IResourceResolver resolver = getPage().getEngine().getResourceResolver(); |
| // OgnlUtils.set(bindingPropertyName, resolver, this, binding); |
| return; |
| } |
| |
| if (_bindings == null) |
| _bindings = new HashMap(MAP_SIZE); |
| |
| _bindings.put(name, binding); |
| } |
| |
| |
| public String toString() |
| { |
| StringBuffer buffer; |
| |
| buffer = new StringBuffer(super.toString()); |
| |
| buffer.append('['); |
| |
| buffer.append(getExtendedId()); |
| |
| buffer.append(']'); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns an unmodifiable {@link Map} of components, keyed on component id. |
| * Never returns null, but may return an empty map. The returned map is |
| * immutable. |
| * |
| **/ |
| |
| public Map getComponents() |
| { |
| if (_components == null) |
| return EMPTY_MAP; |
| |
| return _components; |
| } |
| |
| public Map getAssets() |
| { |
| if (_assets == null) |
| return EMPTY_MAP; |
| |
| return _assets; |
| } |
| |
| public IAsset getAsset(String name) |
| { |
| if (_assets == null) |
| return null; |
| |
| return (IAsset) _assets.get(name); |
| } |
| |
| public Collection getBindingNames() |
| { |
| // If no conainer, i.e. a page, then no bindings. |
| |
| if (_container == null) |
| return null; |
| |
| HashSet result = new HashSet(); |
| |
| // All the informal bindings go into the bindings Map. |
| |
| if (_bindings != null) |
| result.addAll(_bindings.keySet()); |
| |
| // Now, iterate over the formal parameters and add the formal parameters |
| // that have a binding. |
| |
| List names = _specification.getParameterNames(); |
| |
| int count = names.size(); |
| |
| for (int i = 0; i < count; i++) |
| { |
| String name = (String) names.get(i); |
| |
| if (result.contains(name)) |
| continue; |
| |
| if (getBinding(name) != null) |
| result.add(name); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * |
| * Returns a {@link Map} of all bindings for this component. This implementation |
| * is expensive, since it has to merge the disassociated bindings (informal parameters, |
| * and parameters without a JavaBeans property) with the associated bindings (formal |
| * parameters with a JavaBeans property). |
| * |
| * @since 1.0.5 |
| * |
| **/ |
| |
| public Map getBindings() |
| { |
| Map result = new HashMap(); |
| |
| // Add any informal parameters. |
| |
| if (_bindings != null) |
| result.putAll(_bindings); |
| |
| // Now work on the formal parameters |
| |
| Iterator i = _specification.getParameterNames().iterator(); |
| while (i.hasNext()) |
| { |
| String name = (String) i.next(); |
| |
| if (result.containsKey(name)) |
| continue; |
| |
| IBinding binding = getBinding(name); |
| |
| if (binding != null) |
| result.put(name, binding); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Returns a {@link ListenerMap} for the component. A {@link ListenerMap} contains a number of |
| * synthetic read-only properties that implement the {@link IActionListener} |
| * interface, but in fact, cause public instance methods to be invoked. |
| * |
| * @since 1.0.2 |
| **/ |
| |
| public ListenerMap getListeners() |
| { |
| if (_listeners == null) |
| _listeners = new ListenerMap(this); |
| |
| return _listeners; |
| } |
| |
| /** |
| * Returns the {@link IBeanProvider} for this component. This is lazily created the |
| * first time it is needed. |
| * |
| * @since 1.0.4 |
| * |
| **/ |
| |
| public IBeanProvider getBeans() |
| { |
| if (_beans == null) |
| _beans = new BeanProvider(this); |
| |
| return _beans; |
| } |
| |
| /** |
| * |
| * Invoked, as a convienience, |
| * from {@link #finishLoad(IRequestCycle, IPageLoader, IComponentSpecification)}. |
| * This implemenation does nothing. Subclasses may override without invoking |
| * this implementation. |
| * |
| * @since 1.0.5 |
| * |
| **/ |
| |
| protected void finishLoad() |
| { |
| } |
| |
| /** |
| * The main method used to render the component. |
| * Invokes {@link #prepareForRender(IRequestCycle)}, then |
| * {@link #renderComponent(IMarkupWriter, IRequestCycle)}. |
| * {@link #cleanupAfterRender(IRequestCycle)} is invoked in a |
| * <code>finally</code> block. |
| * |
| * <p>Subclasses should not override this method; instead they |
| * will implement {@link #renderComponent(IMarkupWriter, IRequestCycle)}. |
| * |
| * @since 2.0.3 |
| * |
| **/ |
| |
| public final void render(IMarkupWriter writer, IRequestCycle cycle) |
| { |
| try |
| { |
| Tapestry.clearMethodInvocations(); |
| |
| prepareForRender(cycle); |
| |
| Tapestry.checkMethodInvocation(PREPAREFORRENDER_METHOD_ID, "prepareForRender()", this); |
| |
| renderComponent(writer, cycle); |
| } |
| finally |
| { |
| Tapestry.clearMethodInvocations(); |
| |
| cleanupAfterRender(cycle); |
| |
| Tapestry.checkMethodInvocation( |
| CLEANUPAFTERRENDER_METHOD_ID, |
| "cleanupAfterRender()", |
| this); |
| } |
| } |
| |
| /** |
| * Invoked by {@link #render(IMarkupWriter, IRequestCycle)} |
| * to prepare the component to render. This implementation |
| * sets JavaBeans properties from matching bound parameters. |
| * Subclasses that override this method must invoke this |
| * implementation as well. |
| * |
| * @since 2.0.3 |
| * |
| **/ |
| |
| protected void prepareForRender(IRequestCycle cycle) |
| { |
| Tapestry.addMethodInvocation(PREPAREFORRENDER_METHOD_ID); |
| |
| if (_parameterManager == null) |
| { |
| // Pages inherit from this class too, but pages (by definition) |
| // never have parameters. |
| |
| if (getSpecification().isPageSpecification()) |
| return; |
| |
| _parameterManager = new ParameterManager(this); |
| } |
| |
| _parameterManager.setParameters(cycle); |
| } |
| |
| /** |
| * Invoked by {@link #render(IMarkupWriter, IRequestCycle)} |
| * to actually render the component (with any parameter values |
| * already set). This is the method that subclasses must implement. |
| * |
| * @since 2.0.3 |
| * |
| **/ |
| |
| protected abstract void renderComponent(IMarkupWriter writer, IRequestCycle cycle); |
| |
| /** |
| * Invoked by {@link #render(IMarkupWriter, IRequestCycle)} |
| * after the component renders, to clear any parameters back to |
| * null (or 0, or false, or whatever the correct default is). |
| * Primarily, this is used to ensure |
| * that the component doesn't hold onto any objects that could |
| * otherwise be garbage collected. |
| * |
| * <p>Subclasses may override this implementation, but must |
| * also invoke it. |
| * |
| * @since 2.0.3 |
| * |
| **/ |
| |
| protected void cleanupAfterRender(IRequestCycle cycle) |
| { |
| Tapestry.addMethodInvocation(CLEANUPAFTERRENDER_METHOD_ID); |
| |
| if (_parameterManager != null) |
| _parameterManager.resetParameters(cycle); |
| } |
| |
| /** @since 3.0 **/ |
| |
| public IMessages getMessages() |
| { |
| if (_strings == null) |
| _strings = getPage().getEngine().getComponentMessagesSource().getMessages(this); |
| |
| return _strings; |
| } |
| |
| /** |
| * Obtains the {@link IMessages} for this component |
| * (if necessary), and gets the string from it. |
| * |
| **/ |
| |
| public String getString(String key) |
| { |
| return getMessages().getMessage(key); |
| } |
| |
| public String getMessage(String key) |
| { |
| // Invoke the deprecated implementation (for code coverage reasons). |
| // In 3.1, remove getString() and move its implementation |
| // here. |
| |
| return getString(key); |
| } |
| |
| /** |
| * Formats a message string, using |
| * {@link IMessages#format(java.lang.String, java.lang.Object[])}. |
| * |
| * @param key the key used to obtain a localized pattern using |
| * {@link #getString(String)} |
| * @param arguments passed to the formatter |
| * |
| * @since 2.2 |
| * @deprecated To be removed in 3.1. Use {@link #format(String, Object[])} instead. |
| **/ |
| |
| public String formatString(String key, Object[] arguments) |
| { |
| return getMessages().format(key, arguments); |
| } |
| |
| /** |
| * Formats a localized message string, using |
| * {@link IMessages#format(java.lang.String, java.lang.Object[])}. |
| * |
| * @param key the key used to obtain a localized pattern using |
| * {@link #getString(String)} |
| * @param arguments passed to the formatter |
| * |
| * @since 3.0 |
| */ |
| |
| public String format(String key, Object[] arguments) |
| { |
| // SOP: New name invokes deprecated method (consistency and |
| // code coverage); in 3.1 we move the implementation here. |
| |
| return formatString(key, arguments); |
| } |
| |
| /** |
| * Convienience method for invoking {@link IMessages#format(String, Object[])} |
| * |
| * @since 2.2 |
| * @deprecated To be removed in 3.1. Use {@link #format(String, Object)} instead. |
| * |
| **/ |
| |
| public String formatString(String key, Object argument) |
| { |
| return getMessages().format(key, argument); |
| } |
| |
| /** |
| * Convienience method for invoking {@link IMessages#format(String, Object)} |
| * |
| * @since 3.0 |
| * |
| */ |
| |
| public String format(String key, Object argument) |
| { |
| return formatString(key, argument); |
| } |
| |
| /** |
| * Convienience method for invoking {@link IMessages#format(String, Object, Object)}. |
| * |
| * @since 2.2 |
| * @deprecated To be removed in 3.1. Use {@link #format(String, Object, Object)} instead. |
| **/ |
| |
| public String formatString(String key, Object argument1, Object argument2) |
| { |
| return getMessages().format(key, argument1, argument2); |
| } |
| |
| /** |
| * Convienience method for invoking {@link IMessages#format(String, Object, Object)}. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| public String format(String key, Object argument1, Object argument2) |
| { |
| return formatString(key, argument1, argument2); |
| } |
| |
| /** |
| * Convienience method for {@link IMessages#format(String, Object, Object, Object)}. |
| * |
| * @since 2.2 |
| * @deprecated To be removed in 3.1. Use {@link #format(String, Object, Object, Object)} instead. |
| */ |
| |
| public String formatString(String key, Object argument1, Object argument2, Object argument3) |
| { |
| return getMessages().format(key, argument1, argument2, argument3); |
| } |
| |
| /** |
| * Convienience method for {@link IMessages#format(String, Object, Object, Object)}. |
| * |
| * @since 3.0 |
| */ |
| |
| public String format(String key, Object argument1, Object argument2, Object argument3) |
| { |
| return formatString(key, argument1, argument2, argument3); |
| } |
| |
| public INamespace getNamespace() |
| { |
| return _namespace; |
| } |
| |
| public void setNamespace(INamespace namespace) |
| { |
| _namespace = namespace; |
| } |
| |
| /** |
| * Returns the body of the component, the element (which may be static HTML or components) |
| * that the component immediately wraps. May return null. Do not modify the returned |
| * array. The array may be padded with nulls. |
| * |
| * @since 2.3 |
| * @see #getBodyCount() |
| * |
| **/ |
| |
| public IRender[] getBody() |
| { |
| return _body; |
| } |
| |
| /** |
| * Returns the active number of elements in the the body, which may be zero. |
| * |
| * @since 2.3 |
| * @see #getBody() |
| * |
| **/ |
| |
| public int getBodyCount() |
| { |
| return _bodyCount; |
| } |
| |
| /** |
| * Empty implementation of |
| * {@link org.apache.tapestry.event.PageRenderListener#pageEndRender(PageEvent)}. |
| * This allows classes to implement |
| * {@link org.apache.tapestry.event.PageRenderListener} and only |
| * implement the |
| * {@link org.apache.tapestry.event.PageRenderListener#pageBeginRender(PageEvent)} |
| * method. |
| * @since 3.0 |
| */ |
| |
| public void pageEndRender(PageEvent event) |
| { |
| } |
| |
| ExpressionEvaluator getEvaluator() |
| { |
| if (_evaluator == null) |
| { |
| _evaluator = _page.getEngine().getExpressionEvaluator(); |
| } |
| |
| return _evaluator; |
| } |
| |
| /** |
| * Sets a property of a component. |
| * @see IComponent |
| * @since 3.0 |
| */ |
| public void setProperty(String propertyName, Object value) |
| { |
| getEvaluator().write(this, propertyName, value); |
| |
| // IResourceResolver resolver = getResourceResolver(); |
| // OgnlUtils.set(propertyName, resolver, this, value); |
| } |
| /** |
| * Gets a property of a component. |
| * @see IComponent |
| * @since 3.0 |
| */ |
| public Object getProperty(String propertyName) |
| { |
| return getEvaluator().read(this, propertyName); |
| |
| //IResourceResolver resolver = getResourceResolver(); |
| //return OgnlUtils.get(propertyName, resolver, this); |
| } |
| } |