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 * <span wicket:id="checkboxgroup"> 045 * ... 046 * <input type="checkbox" wicket:id="checkbox1">choice 1</input> 047 * ... 048 * <input type="checkbox" wicket:id="checkbox2">choice 2</input> 049 * ... 050 * </span> 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}