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.Collection;
020import java.util.List;
021
022import org.apache.wicket.WicketRuntimeException;
023import org.apache.wicket.markup.ComponentTag;
024import org.apache.wicket.markup.html.WebMarkupContainer;
025import org.apache.wicket.model.IModel;
026import org.apache.wicket.model.util.CollectionModel;
027import org.apache.wicket.util.convert.ConversionException;
028import org.apache.wicket.util.lang.Generics;
029import org.apache.wicket.util.string.Strings;
030import org.apache.wicket.util.visit.IVisit;
031import org.apache.wicket.util.visit.IVisitor;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * Component used to connect instances of Check components into a group. Instances of Check have to
038 * be in the component hierarchy somewhere below the group component. The model of the CheckGroup
039 * component has to be an instance of java.util.Collection. The model collection of the group is
040 * filled with model objects of all selected Check components.
041 *
042 * <p>
043 * i.e. <code>
044 * &lt;span wicket:id="checkboxgroup"&gt;
045 *   ...
046 *   &lt;input type="checkbox" wicket:id="checkbox1"&gt;choice 1&lt;/input&gt;
047 *   ...
048 *   &lt;input type="checkbox" wicket:id="checkbox2"&gt;choice 2&lt;/input&gt;
049 *   ...
050 * &lt;/span&gt;
051 * </code>
052 * </p>
053 * 
054 * @see org.apache.wicket.markup.html.form.Check
055 * @see org.apache.wicket.markup.html.form.CheckGroupSelector <p>
056 *      Note: This component does not support cookie persistence
057 * 
058 * @author Igor Vaynberg
059 * 
060 * @param <T>
061 *            The model object type
062 */
063public class CheckGroup<T> extends FormComponent<Collection<T>>
064{
065        private static final long serialVersionUID = 1L;
066
067        private static final Logger log = LoggerFactory.getLogger(CheckGroup.class);
068
069        /**
070         * Constructor that will create a default model collection
071         * 
072         * @param id
073         *            component id
074         */
075        public CheckGroup(String id)
076        {
077                super(id);
078                setRenderBodyOnly(true);
079        }
080
081        /**
082         * Constructor that wraps the provided collection with the org.apache.wicket.model.Model object
083         * 
084         * @param id
085         *            component id
086         * @param collection
087         *            collection to be used as the model
088         * 
089         */
090        public CheckGroup(String id, Collection<T> collection)
091        {
092                this(id, new CollectionModel<T>(collection));
093        }
094
095        /**
096         * @param id
097         * @param model
098         * @see WebMarkupContainer#WebMarkupContainer(String, IModel)
099         */
100        @SuppressWarnings("unchecked")
101        public CheckGroup(String id, IModel<? extends Collection<T>> model)
102        {
103                super(id, (IModel<Collection<T>>)model);
104                setRenderBodyOnly(true);
105        }
106
107        /**
108         * @see FormComponent#getModelValue()
109         */
110        @Override
111        protected String getModelValue()
112        {
113                final StringBuilder builder = new StringBuilder();
114
115                final Collection<T> ts = getModelObject();
116
117                visitChildren(Check.class, new IVisitor<Check<T>, Void>()
118                {
119                        @Override
120                        public void component(Check<T> check, IVisit<Void> visit)
121                        {
122                                if (ts.contains(check.getModelObject()))
123                                {
124                                        if (builder.length() > 0)
125                                        {
126                                                builder.append(VALUE_SEPARATOR);
127                                        }
128                                        builder.append(check.getValue());
129                                }
130                        }
131                });
132
133                return builder.toString();
134        }
135
136        @Override
137        protected Collection<T> convertValue(String[] values) throws ConversionException
138        {
139                List<T> collection = Generics.newArrayList();
140
141                /*
142                 * if the input is null we do not need to do anything since the model collection has already
143                 * been cleared
144                 */
145
146                if (values != null && values.length > 0)
147                {
148                        for (final String value : values)
149                        {
150                                if (value != null)
151                                {
152                                        Check<T> checkbox = visitChildren(Check.class,
153                                                new org.apache.wicket.util.visit.IVisitor<Check<T>, Check<T>>()
154                                                {
155                                                        @Override
156                                                        public void component(final Check<T> check, final IVisit<Check<T>> visit)
157                                                        {
158                                                                if (String.valueOf(check.getValue()).equals(value))
159                                                                {
160                                                                        visit.stop(check);
161                                                                }
162                                                        }
163                                                });
164
165                                        if (checkbox == null)
166                                        {
167                                                throw new WicketRuntimeException(
168                                                        "submitted http post value [" +
169                                                                Strings.join(",", values) +
170                                                                "] for CheckGroup component [" +
171                                                                getPath() +
172                                                                "] contains an illegal value [" +
173                                                                value +
174                                                                "] which does not point to a Check component. Due to this the CheckGroup component cannot resolve the selected Check component pointed to by the illegal value. A possible reason is that component hierarchy changed between rendering and form submission.");
175                                        }
176
177                                        // assign the value of the group's model
178                                        collection.add(checkbox.getModelObject());
179                                }
180                        }
181                }
182                return collection;
183        }
184
185        /**
186         * See {@link FormComponent#updateCollectionModel(FormComponent)} for details on how the model
187         * is updated.
188         */
189        @Override
190        public void updateModel()
191        {
192                FormComponent.updateCollectionModel(this);
193        }
194
195        @Override
196        protected void onComponentTag(ComponentTag tag)
197        {
198                super.onComponentTag(tag);
199
200                // No longer applicable, breaks XHTML validation.
201                tag.remove("disabled");
202                tag.remove("name");
203        }
204}