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; 020 021import org.apache.wicket.Component; 022import org.apache.wicket.IGenericComponent; 023import org.apache.wicket.WicketRuntimeException; 024import org.apache.wicket.markup.ComponentTag; 025import org.apache.wicket.markup.html.WebMarkupContainer; 026import org.apache.wicket.model.IModel; 027import org.apache.wicket.util.string.Strings; 028 029 030/** 031 * Component representing a single checkbox choice in a 032 * org.apache.wicket.markup.html.form.CheckGroup. 033 * 034 * Must be attached to an <input type="checkbox" ... > markup. 035 * <p> 036 * STATELESS NOTES: By default this component cannot be used inside a stateless form. If it is 037 * desirable to use this inside a stateless form then 038 * <ul> 039 * <li> 040 * override #getValue() and return some stateless value to uniquely identify this radio (eg relative 041 * component path from group to this radio)</li> 042 * <li> 043 * override {@link #getStatelessHint()} and return <code>true</code></li> 044 * </ul> 045 * </p> 046 * 047 * @see org.apache.wicket.markup.html.form.CheckGroup 048 * 049 * @author Igor Vaynberg 050 * 051 * @param <T> 052 * The model object type 053 */ 054public class Check<T> extends LabeledWebMarkupContainer implements IGenericComponent<T, Check<T>> 055{ 056 private static final long serialVersionUID = 1L; 057 058 private static final String ATTR_DISABLED = "disabled"; 059 060 /** 061 * page-scoped uuid of this check. this property must not be accessed directly, instead 062 * {@link #getValue()} must be used 063 */ 064 private int uuid = -1; 065 066 private final CheckGroup<T> group; 067 068 /** 069 * @see WebMarkupContainer#WebMarkupContainer(String) 070 */ 071 public Check(String id) 072 { 073 this(id, null, null); 074 } 075 076 /** 077 * @param id 078 * @param model 079 * @see WebMarkupContainer#WebMarkupContainer(String, IModel) 080 */ 081 public Check(String id, IModel<T> model) 082 { 083 this(id, model, null); 084 } 085 086 /** 087 * @param id 088 * @param group 089 * parent {@link CheckGroup} of this check 090 * @see WebMarkupContainer#WebMarkupContainer(String) 091 */ 092 public Check(String id, CheckGroup<T> group) 093 { 094 this(id, null, group); 095 } 096 097 /** 098 * @param id 099 * @param model 100 * @param group 101 * parent {@link CheckGroup} of this check 102 * @see WebMarkupContainer#WebMarkupContainer(String, IModel) 103 */ 104 public Check(String id, IModel<T> model, CheckGroup<T> group) 105 { 106 super(id, model); 107 this.group = group; 108 setOutputMarkupId(true); 109 } 110 111 112 /** 113 * Form submission value used for the Html <code>value</code> attribute of the <code>input</code> tag. 114 * <p> 115 * If {@link Check}s are recreated on each render of their {@link CheckGroup}, this method should 116 * be overridden to return a 'stable' value, otherwise its selection will be lost after a {@link Form} 117 * was submitted and resulted in {@link Form#hasError()}. 118 * 119 * @return input value 120 */ 121 public String getValue() 122 { 123 if (uuid < 0) 124 { 125 uuid = getPage().getAutoIndex(); 126 } 127 return "check" + uuid; 128 } 129 130 @SuppressWarnings("unchecked") 131 protected CheckGroup<T> getGroup() 132 { 133 CheckGroup<T> group = this.group; 134 if (group == null) 135 { 136 group = findParent(CheckGroup.class); 137 if (group == null) 138 { 139 throw new WicketRuntimeException("Check component [" + getPath() + 140 "] cannot find its parent CheckGroup"); 141 } 142 } 143 return group; 144 } 145 146 /** 147 * @see Component#onComponentTag(ComponentTag) 148 * @param tag 149 * the abstraction representing html tag of this component 150 */ 151 @Override 152 protected void onComponentTag(final ComponentTag tag) 153 { 154 // Default handling for component tag 155 super.onComponentTag(tag); 156 157 // must be attached to <input type="checkbox" .../> tag 158 checkComponentTag(tag, "input"); 159 checkComponentTagAttribute(tag, "type", "checkbox"); 160 161 CheckGroup<?> group = getGroup(); 162 163 final String uuid = getValue(); 164 165 // assign name and value 166 tag.put("name", group.getInputName()); 167 tag.put("value", uuid); 168 169 // check if the model collection of the group contains the model object. 170 // if it does check the check box. 171 Collection<?> collection = (Collection<?>)group.getDefaultModelObject(); 172 173 // check for npe in group's model object 174 if (collection == null) 175 { 176 throw new WicketRuntimeException("CheckGroup [" + group.getPath() + 177 "] contains a null model object, must be an object of type java.util.Collection"); 178 } 179 180 if (group.hasRawInput()) 181 { 182 final String raw = group.getRawInput(); 183 if (!Strings.isEmpty(raw)) 184 { 185 final String[] values = raw.split(FormComponent.VALUE_SEPARATOR); 186 for (String value : values) 187 { 188 if (uuid.equals(value)) 189 { 190 tag.put("checked", "checked"); 191 } 192 } 193 } 194 } 195 else if (collection.contains(getDefaultModelObject())) 196 { 197 tag.put("checked", "checked"); 198 } 199 200 if (!isActionAuthorized(ENABLE) || !isEnabledInHierarchy() || !group.isEnabledInHierarchy()) 201 { 202 tag.put(ATTR_DISABLED, ATTR_DISABLED); 203 } 204 205 } 206 207 /** 208 * The value will be made available to the validator property by means of ${label}. It does not 209 * have any specific meaning to Check itself. 210 * 211 * @param labelModel 212 * @return this for chaining 213 */ 214 @Override 215 public Check<T> setLabel(IModel<String> labelModel) 216 { 217 super.setLabel(labelModel); 218 return this; 219 } 220 221 @Override 222 protected boolean getStatelessHint() 223 { 224 // because this component uses uuid field it cannot be stateless 225 return false; 226 } 227}