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