001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.form;
018
019import java.util.List;
020import java.util.Map;
021
022import org.apache.wicket.markup.ComponentTag;
023import org.apache.wicket.model.IModel;
024import org.apache.wicket.settings.DebugSettings;
025import org.apache.wicket.util.lang.Args;
026import org.apache.wicket.util.string.AppendingStringBuffer;
027import org.apache.wicket.util.string.Strings;
028import org.apache.wicket.util.value.IValueMap;
029
030
031/**
032 * A choice subclass that shows choices in radio style.
033 * <p>
034 * Java:
035 * 
036 * <pre>
037 * List SITES = Arrays.asList(new String[] { &quot;The Server Side&quot;, &quot;Java Lobby&quot;, &quot;Java.Net&quot; });
038 * // Add a radio choice component that uses Input's 'site' property to designate the
039 * // current selection, and that uses the SITES list for the available options.
040 * form.add(new RadioChoice(&quot;site&quot;, SITES));
041 * </pre>
042 * 
043 * HTML:
044 * 
045 * <pre>
046 *    &lt;span style=&quot;vertical-align: top;&quot; wicket:id=&quot;site&quot;&gt;
047 *      &lt;input type=&quot;radio&quot;&gt;site 1&lt;/input&gt;
048 *      &lt;input type=&quot;radio&quot;&gt;site 2&lt;/input&gt;
049 *    &lt;/span&gt;
050 * </pre>
051 * 
052 * </p>
053 * 
054 * @author Jonathan Locke
055 * @author Igor Vaynberg (ivaynberg)
056 * 
057 * @param <T>
058 *            The model object type
059 */
060public class RadioChoice<T> extends AbstractSingleSelectChoice<T>
061{
062        private static final long serialVersionUID = 1L;
063
064        private String prefix = "";
065        private String suffix = "";
066
067        private LabelPosition labelPosition = LabelPosition.AFTER;
068
069        /**
070         * Constructor
071         * 
072         * @param id
073         *            See Component
074         * @see org.apache.wicket.Component#Component(String)
075         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String)
076         */
077        public RadioChoice(final String id)
078        {
079                super(id);
080        }
081
082        /**
083         * Constructor
084         * 
085         * @param id
086         *            See Component
087         * @param choices
088         *            The list of choices in the radio choice
089         * @see org.apache.wicket.Component#Component(String)
090         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String, List)
091         */
092        public RadioChoice(final String id, final List<? extends T> choices)
093        {
094                super(id, choices);
095        }
096
097        /**
098         * Constructor
099         * 
100         * @param id
101         *            See Component
102         * @param renderer
103         *            The rendering engine
104         * @param choices
105         *            The list of choices in the radio choice
106         * @see org.apache.wicket.Component#Component(String)
107         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
108         *      List,IChoiceRenderer)
109         */
110        public RadioChoice(final String id, final List<? extends T> choices,
111                final IChoiceRenderer<? super T> renderer)
112        {
113                super(id, choices, renderer);
114        }
115
116        /**
117         * Constructor
118         * 
119         * @param id
120         *            See Component
121         * @param model
122         *            See Component
123         * @param choices
124         *            The list of choices in the radio choice
125         * @see org.apache.wicket.Component#Component(String, IModel)
126         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String, IModel, List)
127         */
128        public RadioChoice(final String id, IModel<T> model, final List<? extends T> choices)
129        {
130                super(id, model, choices);
131        }
132
133        /**
134         * Constructor
135         * 
136         * @param id
137         *            See Component
138         * @param model
139         *            See Component
140         * @param choices
141         *            The list of choices in the radio choice
142         * @param renderer
143         *            The rendering engine
144         * @see org.apache.wicket.Component#Component(String, IModel)
145         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String, IModel,
146         *      List,IChoiceRenderer)
147         */
148        public RadioChoice(final String id, IModel<T> model, final List<? extends T> choices,
149                final IChoiceRenderer<? super T> renderer)
150        {
151                super(id, model, choices, renderer);
152        }
153
154        /**
155         * Constructor
156         * 
157         * @param id
158         *            See Component
159         * @param choices
160         *            The list of choices in the radio choice
161         * @see org.apache.wicket.Component#Component(String)
162         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String, IModel)
163         */
164        public RadioChoice(String id, IModel<? extends List<? extends T>> choices)
165        {
166                super(id, choices);
167        }
168
169        /**
170         * Constructor
171         * 
172         * @param id
173         *            See Component
174         * @param model
175         *            The model that is updated with changes in this component. See Component
176         * @param choices
177         *            The list of choices in the radio choice
178         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String, IModel,IModel)
179         * @see org.apache.wicket.Component#Component(String, IModel)
180         */
181        public RadioChoice(String id, IModel<T> model, IModel<? extends List<? extends T>> choices)
182        {
183                super(id, model, choices);
184        }
185
186        /**
187         * Constructor
188         * 
189         * @param id
190         *            See Component
191         * @param choices
192         *            The list of choices in the radio choice
193         * @param renderer
194         *            The rendering engine
195         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
196         *      IModel,IChoiceRenderer)
197         * @see org.apache.wicket.Component#Component(String)
198         */
199        public RadioChoice(String id, IModel<? extends List<? extends T>> choices,
200                IChoiceRenderer<? super T> renderer)
201        {
202                super(id, choices, renderer);
203        }
204
205
206        /**
207         * Constructor
208         * 
209         * @param id
210         *            See Component
211         * @param model
212         *            The model that is updated with changes in this component. See Component
213         * @param choices
214         *            The list of choices in the radio choice
215         * @param renderer
216         *            The rendering engine
217         * @see org.apache.wicket.Component#Component(String, IModel)
218         * @see org.apache.wicket.markup.html.form.AbstractChoice#AbstractChoice(String, IModel,
219         *      IModel,IChoiceRenderer)
220         */
221        public RadioChoice(String id, IModel<T> model, IModel<? extends List<? extends T>> choices,
222                IChoiceRenderer<? super T> renderer)
223        {
224                super(id, model, choices, renderer);
225        }
226
227        /**
228         * @see org.apache.wicket.markup.html.form.FormComponent#onComponentTag(org.apache.wicket.markup.ComponentTag)
229         */
230        @Override
231        protected void onComponentTag(ComponentTag tag)
232        {
233                super.onComponentTag(tag);
234                // since this component cannot be attached to input tag the name
235                // variable is illegal
236                tag.remove("name");
237        }
238
239        /**
240         * @return Prefix to use before choice
241         */
242        public String getPrefix()
243        {
244                return prefix;
245        }
246
247        /**
248         * @param index
249         *            index of the choice
250         * @param choice
251         *            the choice itself
252         * @return Prefix to use before choice. The default implementation just returns
253         *         {@link #getPrefix()}. Override to have a prefix dependent on the choice item.
254         */
255        protected String getPrefix(int index, T choice)
256        {
257                return getPrefix();
258        }
259
260        /**
261         * @param index
262         *            index of the choice
263         * @param choice
264         *            the choice itself
265         * @return Separator to use between radio options. The default implementation just returns
266         *         {@link #getSuffix()}. Override to have a prefix dependent on the choice item.
267         */
268        protected String getSuffix(int index, T choice)
269        {
270                return getSuffix();
271        }
272
273        /**
274         * @param prefix
275         *            Prefix to use before choice
276         * @return this
277         */
278        public final RadioChoice<T> setPrefix(String prefix)
279        {
280                // Tell the page that this component's prefix was changed
281                addStateChange();
282                this.prefix = prefix;
283                return this;
284        }
285
286        /**
287         * @return Separator to use between radio options
288         */
289        public String getSuffix()
290        {
291                return suffix;
292        }
293
294        /**
295         * @param suffix
296         *            Separator to use between radio options
297         * @return this
298         */
299        public final RadioChoice<T> setSuffix(String suffix)
300        {
301                // Tell the page that this component's suffix was changed
302                addStateChange();
303                this.suffix = suffix;
304                return this;
305        }
306
307        /**
308         * Sets the preferred position of the &lt;label&gt; for each choice
309         *
310         * @param labelPosition
311         *              The preferred position for the label
312         * @return {@code this} instance, for chaining
313         */
314        public RadioChoice<T> setLabelPosition(LabelPosition labelPosition)
315        {
316                Args.notNull(labelPosition, "labelPosition");
317                this.labelPosition = labelPosition;
318                return this;
319        }
320
321        /**
322         * Not supported - does nothing.
323         */
324        @Override
325        protected CharSequence getDefaultChoice(String selectedValue)
326        {
327                return "";
328        }
329
330        /**
331         * Generates and appends html for a single choice into the provided buffer
332         * 
333         * @param buffer
334         *            Appending string buffer that will have the generated html appended
335         * @param choice
336         *            Choice object
337         * @param index
338         *            The index of this option
339         * @param selected
340         *            The currently selected string value
341         */
342        @Override
343        protected void appendOptionHtml(final AppendingStringBuffer buffer, final T choice, int index,
344                final String selected)
345        {
346                // Append option suffix
347                buffer.append(getPrefix(index, choice));
348
349                String id = getChoiceRenderer().getIdValue(choice, index);
350                final String idAttr = getMarkupId() + "-" + id;
351
352                boolean enabled = isEnabledInHierarchy() && !isDisabled(choice, index, selected);
353
354                CharSequence renderValue = renderValue(choice);
355
356                // Allows user to add attributes to the <label..> tag
357                IValueMap labelAttrs = getAdditionalAttributesForLabel(index, choice);
358                StringBuilder extraLabelAttributes = new StringBuilder();
359                if (labelAttrs != null)
360                {
361                        for (Map.Entry<String, Object> attr : labelAttrs.entrySet())
362                        {
363                                extraLabelAttributes.append(' ')
364                                                .append(Strings.escapeMarkup(attr.getKey()))
365                                                .append("=\"")
366                                                .append(Strings.escapeMarkup(attr.getValue().toString()))
367                                                .append('"');
368                        }
369                }
370
371                labelPosition.before(buffer, idAttr, extraLabelAttributes, renderValue);
372
373                // Add radio tag
374                buffer.append("<input name=\"")
375                        .append(getInputName())
376                        .append('"')
377                        .append(" type=\"radio\"")
378                        .append((isSelected(choice, index, selected) ? " checked=\"checked\"" : ""))
379                        .append((enabled ? "" : " disabled=\"disabled\""))
380                        .append(" value=\"")
381                        .append(Strings.escapeMarkup(id))
382                        .append("\" id=\"")
383                        .append(Strings.escapeMarkup(idAttr))
384                        .append('"');
385
386                // Allows user to add attributes to the <input..> tag
387                {
388                        IValueMap attrs = getAdditionalAttributes(index, choice);
389                        if (attrs != null)
390                        {
391                                for (Map.Entry<String, Object> attr : attrs.entrySet())
392                                {
393                                        buffer.append(' ')
394                                                .append(Strings.escapeMarkup(attr.getKey()))
395                                                .append("=\"")
396                                                .append(Strings.escapeMarkup(attr.getValue().toString()))
397                                                .append('"');
398                                }
399                        }
400                }
401
402                DebugSettings debugSettings = getApplication().getDebugSettings();
403                String componentPathAttributeName = debugSettings.getComponentPathAttributeName();
404                if (Strings.isEmpty(componentPathAttributeName) == false)
405                {
406                        CharSequence path = getPageRelativePath();
407                        path = Strings.replaceAll(path, "_", "__");
408                        path = Strings.replaceAll(path, ":", "_");
409                        buffer.append(' ').append(componentPathAttributeName).append("=\"")
410                                .append(path)
411                                .append("_input_")
412                                .append(index)
413                                .append('"');
414                }
415
416                buffer.append("/>");
417
418                labelPosition.after(buffer, idAttr, extraLabelAttributes, renderValue);
419
420                // Append option suffix
421                buffer.append(getSuffix(index, choice));
422        }
423
424        /**
425         * You may subclass this method to provide additional attributes to the &lt;label ..&gt; tag.
426         *
427         * @param index
428         *            index of the choice
429         * @param choice
430         *            the choice itself
431         * @return tag attribute name/value pairs.
432         */
433        protected IValueMap getAdditionalAttributesForLabel(int index, T choice)
434        {
435                return null;
436        }
437
438        /**
439         * You may subclass this method to provide additional attributes to the &lt;input ..&gt; tag.
440         * 
441         * @param index
442         *            index of the choice
443         * @param choice
444         *            the choice itself
445         * @return tag attribute name/value pairs.
446         */
447        protected IValueMap getAdditionalAttributes(final int index, final T choice)
448        {
449                return null;
450        }
451}