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    
015    package org.apache.tapestry5.corelib.components;
016    
017    import org.apache.tapestry5.BindingConstants;
018    import org.apache.tapestry5.ComponentResources;
019    import org.apache.tapestry5.Field;
020    import org.apache.tapestry5.MarkupWriter;
021    import org.apache.tapestry5.RadioContainer;
022    import org.apache.tapestry5.annotations.Environmental;
023    import org.apache.tapestry5.annotations.Mixin;
024    import org.apache.tapestry5.annotations.Parameter;
025    import org.apache.tapestry5.corelib.mixins.DiscardBody;
026    import org.apache.tapestry5.corelib.mixins.RenderDisabled;
027    import org.apache.tapestry5.corelib.mixins.RenderInformals;
028    import org.apache.tapestry5.ioc.annotations.Inject;
029    import org.apache.tapestry5.services.ComponentDefaultProvider;
030    import 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     */
044    public 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    }