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