001// Copyright 2007, 2008, 2010, 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
015package org.apache.tapestry5.corelib.components;
016
017import org.apache.tapestry5.BindingConstants;
018import org.apache.tapestry5.ComponentResources;
019import org.apache.tapestry5.Field;
020import org.apache.tapestry5.MarkupWriter;
021import org.apache.tapestry5.RadioContainer;
022import org.apache.tapestry5.annotations.Environmental;
023import org.apache.tapestry5.annotations.Mixin;
024import org.apache.tapestry5.annotations.Parameter;
025import org.apache.tapestry5.corelib.mixins.DiscardBody;
026import org.apache.tapestry5.corelib.mixins.RenderDisabled;
027import org.apache.tapestry5.corelib.mixins.RenderInformals;
028import org.apache.tapestry5.ioc.annotations.Inject;
029import org.apache.tapestry5.services.ComponentDefaultProvider;
030import org.apache.tapestry5.services.javascript.JavaScriptSupport;
031
032/**
033 * A radio button (i.e., &lt;input type="radio"&gt;). Radio buttons <strong>must</strong> operate within a
034 * {@link RadioContainer} (normally, the {@link RadioGroup} component).
035 * <p/>
036 * If the value parameter is not bound, then the default value is a property of the container component whose name
037 * matches the Radio component's id.
038 * 
039 * @tapestrydoc
040 * @see RadioGroup
041 * @see Form
042 * @see Select
043 */
044public class Radio implements Field
045{
046    @Environmental
047    private RadioContainer container;
048
049    /**
050     * The user presentable label for the field. If not provided, a reasonable label is generated from the component's
051     * id, first by looking for a message key named "id-label" (substituting the component's actual id), then by
052     * converting the actual id to a presentable string (for example, "userId" to "User Id").
053     */
054    @Parameter(defaultPrefix = BindingConstants.LITERAL)
055    private String label;
056
057    /**
058     * The value associated with this radio button. This is used to determine which radio button will be selected when
059     * the page is rendered, and also becomes the value assigned when the form is submitted.
060     */
061    @Parameter(required = true, principal = true, autoconnect = true)
062    private Object value;
063
064    @Inject
065    private ComponentDefaultProvider defaultProvider;
066
067    @Inject
068    private ComponentResources resources;
069
070    @SuppressWarnings("unused")
071    @Mixin
072    private RenderInformals renderInformals;
073
074    @SuppressWarnings("unused")
075    @Mixin
076    private RenderDisabled renderDisabled;
077
078    @SuppressWarnings("unused")
079    @Mixin
080    private DiscardBody discardBody;
081
082    @Inject
083    private JavaScriptSupport jsSupport;
084
085    private String clientId;
086
087    private String controlName;
088
089    /**
090     * If true, then the field will render out with a disabled attribute (to turn off client-side behavior). Further, a
091     * disabled field ignores any value in the request when the form is submitted.
092     */
093    @Parameter("false")
094    private boolean disabled;
095
096    String defaultLabel()
097    {
098        return defaultProvider.defaultLabel(resources);
099    }
100
101    /**
102     * Returns the control name provided by the containing {@link org.apache.tapestry5.RadioContainer}.
103     */
104    public String getControlName()
105    {
106        return controlName;
107    }
108
109    public String getLabel()
110    {
111        return label;
112    }
113
114    /**
115     * Returns true if this component has been expressly disabled (via its disabled parameter), or if the
116     * {@link RadioContainer container} has been disabled.
117     */
118    public boolean isDisabled()
119    {
120        return disabled || container.isDisabled();
121    }
122
123    public String getClientId()
124    {
125        return clientId;
126    }
127
128    void beginRender(MarkupWriter writer)
129    {
130        String value = container.toClient(this.value);
131
132        clientId = jsSupport.allocateClientId(resources);
133        controlName = container.getControlName();
134
135        writer.element("input", "type", "radio", "id", clientId, "name", controlName, "value", value);
136
137        if (container.isSelected(this.value))
138            writer.attributes("checked", "checked");
139    }
140
141    void afterRender(MarkupWriter writer)
142    {
143        writer.end();
144    }
145
146    /**
147     * Returns false; the RadioComponent component does not support declarative field validation.
148     */
149    public boolean isRequired()
150    {
151        return false;
152    }
153}