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 org.apache.wicket.WicketRuntimeException;
020import org.apache.wicket.markup.ComponentTag;
021import org.apache.wicket.markup.html.WebMarkupContainer;
022import org.apache.wicket.model.IModel;
023import org.apache.wicket.util.convert.ConversionException;
024import org.apache.wicket.util.visit.IVisit;
025import org.apache.wicket.util.visit.IVisitor;
026
027/**
028 * Component used to connect instances of Radio components into a group. Instances of Radio have to
029 * be in the component hierarchy somewhere below the group component. The model object of the group
030 * is set to the model object of the selected radio component or null if none selected.
031 * 
032 * ie
033 * 
034 * <pre>
035 *    &lt;span wicket:id=&quot;radiochoicegroup&quot;&gt;
036 *      ...
037 *      &lt;input type=&quot;radio&quot; wicket:id=&quot;singleradiochoice1&quot;/&gt;choice 1
038 *      ...
039 *      &lt;input type=&quot;radio&quot; wicket:id=&quot;singleradiochoice2&quot;/&gt;choice 2
040 *      ...
041 *    &lt;/span&gt;
042 * </pre>
043 * 
044 * @author Igor Vaynberg
045 * @author Sven Meier (svenmeier)
046 * 
047 * @param <T>
048 *            The model object type
049 */
050public class RadioGroup<T> extends FormComponent<T>
051{
052        private static final long serialVersionUID = 1L;
053
054        /**
055         * @see WebMarkupContainer#WebMarkupContainer(String)
056         */
057        public RadioGroup(String id)
058        {
059                super(id);
060                setRenderBodyOnly(true);
061        }
062
063        /**
064         * @param id
065         * @param model
066         * @see WebMarkupContainer#WebMarkupContainer(String, IModel)
067         */
068        public RadioGroup(String id, IModel<T> model)
069        {
070                super(id, model);
071                setRenderBodyOnly(true);
072        }
073
074        @Override
075        protected String getModelValue()
076        {
077                String radioValue = visitChildren(Radio.class, new IVisitor<Radio<T>, String>()
078                {
079                        @Override
080                        public void component(Radio<T> radio, IVisit<String> visit)
081                        {
082                                if (getModelComparator().compare(RadioGroup.this, radio.getDefaultModelObject()))
083                                {
084                                        visit.stop(radio.getValue());
085                                }
086                        }
087                });
088
089                return radioValue;
090        }
091
092        /**
093         * @see org.apache.wicket.markup.html.form.FormComponent#convertValue(String[])
094         */
095        @Override
096        protected T convertValue(String[] input) throws ConversionException
097        {
098                if (input != null && input.length > 0)
099                {
100                        final String value = input[0];
101
102                        // retrieve the selected single radio choice component
103                        Radio<T> choice = visitChildren(Radio.class,
104                                new org.apache.wicket.util.visit.IVisitor<Radio<T>, Radio<T>>()
105                                {
106
107                                        @Override
108                                        public void component(final Radio<T> radio, final IVisit<Radio<T>> visit)
109                                        {
110                                                if (radio.getValue().equals(value))
111                                                {
112                                                        visit.stop(radio);
113                                                }
114                                        }
115
116                                });
117
118                        if (choice == null)
119                        {
120                                throw new WicketRuntimeException(
121                                        "submitted http post value [" +
122                                                value +
123                                                "] for RadioGroup component [" +
124                                                getPath() +
125                                                "] is illegal because it does not point to a Radio component. " +
126                                                "Due to this the RadioGroup component cannot resolve the selected Radio component pointed to by the illegal value. A possible reason is that component hierarchy changed between rendering and form submission.");
127                        }
128
129
130                        // assign the value of the group's model
131                        return choice.getModelObject();
132                }
133                return null;
134        }
135
136        /**
137         * @see org.apache.wicket.markup.html.form.FormComponent#onComponentTag(org.apache.wicket.markup.ComponentTag)
138         */
139        @Override
140        protected void onComponentTag(ComponentTag tag)
141        {
142                super.onComponentTag(tag);
143                // No longer applicable, breaks XHTML validation.
144                tag.remove("disabled");
145                tag.remove("name");
146        }
147}