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[] { "The Server Side", "Java Lobby", "Java.Net" }); 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("site", SITES)); 041 * </pre> 042 * 043 * HTML: 044 * 045 * <pre> 046 * <span style="vertical-align: top;" wicket:id="site"> 047 * <input type="radio">site 1</input> 048 * <input type="radio">site 2</input> 049 * </span> 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 <label> 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 <label ..> 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 <input ..> 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}