001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.tags.form;
018
019import java.util.Collection;
020
021import javax.servlet.jsp.JspException;
022
023import org.springframework.web.bind.WebDataBinder;
024
025/**
026 * The {@code <checkbox>} tag renders an HTML 'input' tag with type 'checkbox'.
027 * May be used in one of three different approaches depending on the
028 * type of the {@link #getValue bound value}.
029 *
030 * <h3>Approach One</h3>
031 * When the bound value is of type {@link Boolean} then the '{@code input(checkbox)}'
032 * is marked as 'checked' if the bound value is {@code true}. The '{@code value}'
033 * attribute corresponds to the resolved value of the {@link #setValue(Object) value} property.
034 * <h3>Approach Two</h3>
035 * When the bound value is of type {@link Collection} then the '{@code input(checkbox)}'
036 * is marked as 'checked' if the configured {@link #setValue(Object) value} is present in
037 * the bound {@link Collection}.
038 * <h3>Approach Three</h3>
039 * For any other bound value type, the '{@code input(checkbox)}' is marked as 'checked'
040 * if the configured {@link #setValue(Object) value} is equal to the bound value.
041 *
042 * <p>
043 * <table>
044 * <caption>Attribute Summary</caption>
045 * <thead>
046 * <tr>
047 * <th class="colFirst">Attribute</th>
048 * <th class="colOne">Required?</th>
049 * <th class="colOne">Runtime Expression?</th>
050 * <th class="colLast">Description</th>
051 * </tr>
052 * </thead>
053 * <tbody>
054 * <tr class="altColor">
055 * <td><p>accesskey</p></td>
056 * <td><p>false</p></td>
057 * <td><p>true</p></td>
058 * <td><p>HTML Standard Attribute</p></td>
059 * </tr>
060 * <tr class="rowColor">
061 * <td><p>cssClass</p></td>
062 * <td><p>false</p></td>
063 * <td><p>true</p></td>
064 * <td><p>HTML Optional Attribute</p></td>
065 * </tr>
066 * <tr class="altColor">
067 * <td><p>cssErrorClass</p></td>
068 * <td><p>false</p></td>
069 * <td><p>true</p></td>
070 * <td><p>HTML Optional Attribute. Used when the bound field has errors.</p></td>
071 * </tr>
072 * <tr class="rowColor">
073 * <td><p>cssStyle</p></td>
074 * <td><p>false</p></td>
075 * <td><p>true</p></td>
076 * <td><p>HTML Optional Attribute</p></td>
077 * </tr>
078 * <tr class="altColor">
079 * <td><p>dir</p></td>
080 * <td><p>false</p></td>
081 * <td><p>true</p></td>
082 * <td><p>HTML Standard Attribute</p></td>
083 * </tr>
084 * <tr class="rowColor">
085 * <td><p>disabled</p></td>
086 * <td><p>false</p></td>
087 * <td><p>true</p></td>
088 * <td><p>HTML Optional Attribute. Setting the value of this attribute to 'true'
089 * will disable the HTML element.</p></td>
090 * </tr>
091 * <tr class="altColor">
092 * <td><p>htmlEscape</p></td>
093 * <td><p>false</p></td>
094 * <td><p>true</p></td>
095 * <td><p>Enable/disable HTML escaping of rendered values.</p></td>
096 * </tr>
097 * <tr class="rowColor">
098 * <td><p>id</p></td>
099 * <td><p>false</p></td>
100 * <td><p>true</p></td>
101 * <td><p>HTML Standard Attribute</p></td>
102 * </tr>
103 * <tr class="altColor">
104 * <td><p>label</p></td>
105 * <td><p>false</p></td>
106 * <td><p>true</p></td>
107 * <td><p>Value to be displayed as part of the tag</p></td>
108 * </tr>
109 * <tr class="rowColor">
110 * <td><p>lang</p></td>
111 * <td><p>false</p></td>
112 * <td><p>true</p></td>
113 * <td><p>HTML Standard Attribute</p></td>
114 * </tr>
115 * <tr class="altColor">
116 * <td><p>onblur</p></td>
117 * <td><p>false</p></td>
118 * <td><p>true</p></td>
119 * <td><p>HTML Event Attribute</p></td>
120 * </tr>
121 * <tr class="rowColor">
122 * <td><p>onchange</p></td>
123 * <td><p>false</p></td>
124 * <td><p>true</p></td>
125 * <td><p>HTML Event Attribute</p></td>
126 * </tr>
127 * <tr class="altColor">
128 * <td><p>onclick</p></td>
129 * <td><p>false</p></td>
130 * <td><p>true</p></td>
131 * <td><p>HTML Event Attribute</p></td>
132 * </tr>
133 * <tr class="rowColor">
134 * <td><p>ondblclick</p></td>
135 * <td><p>false</p></td>
136 * <td><p>true</p></td>
137 * <td><p>HTML Event Attribute</p></td>
138 * </tr>
139 * <tr class="altColor">
140 * <td><p>onfocus</p></td>
141 * <td><p>false</p></td>
142 * <td><p>true</p></td>
143 * <td><p>HTML Event Attribute</p></td>
144 * </tr>
145 * <tr class="rowColor">
146 * <td><p>onkeydown</p></td>
147 * <td><p>false</p></td>
148 * <td><p>true</p></td>
149 * <td><p>HTML Event Attribute</p></td>
150 * </tr>
151 * <tr class="altColor">
152 * <td><p>onkeypress</p></td>
153 * <td><p>false</p></td>
154 * <td><p>true</p></td>
155 * <td><p>HTML Event Attribute</p></td>
156 * </tr>
157 * <tr class="rowColor">
158 * <td><p>onkeyup</p></td>
159 * <td><p>false</p></td>
160 * <td><p>true</p></td>
161 * <td><p>HTML Event Attribute</p></td>
162 * </tr>
163 * <tr class="altColor">
164 * <td><p>onmousedown</p></td>
165 * <td><p>false</p></td>
166 * <td><p>true</p></td>
167 * <td><p>HTML Event Attribute</p></td>
168 * </tr>
169 * <tr class="rowColor">
170 * <td><p>onmousemove</p></td>
171 * <td><p>false</p></td>
172 * <td><p>true</p></td>
173 * <td><p>HTML Event Attribute</p></td>
174 * </tr>
175 * <tr class="altColor">
176 * <td><p>onmouseout</p></td>
177 * <td><p>false</p></td>
178 * <td><p>true</p></td>
179 * <td><p>HTML Event Attribute</p></td>
180 * </tr>
181 * <tr class="rowColor">
182 * <td><p>onmouseover</p></td>
183 * <td><p>false</p></td>
184 * <td><p>true</p></td>
185 * <td><p>HTML Event Attribute</p></td>
186 * </tr>
187 * <tr class="altColor">
188 * <td><p>onmouseup</p></td>
189 * <td><p>false</p></td>
190 * <td><p>true</p></td>
191 * <td><p>HTML Event Attribute</p></td>
192 * </tr>
193 * <tr class="rowColor">
194 * <td><p>path</p></td>
195 * <td><p>true</p></td>
196 * <td><p>true</p></td>
197 * <td><p>Path to property for data binding</p></td>
198 * </tr>
199 * <tr class="altColor">
200 * <td><p>tabindex</p></td>
201 * <td><p>false</p></td>
202 * <td><p>true</p></td>
203 * <td><p>HTML Standard Attribute</p></td>
204 * </tr>
205 * <tr class="rowColor">
206 * <td><p>title</p></td>
207 * <td><p>false</p></td>
208 * <td><p>true</p></td>
209 * <td><p>HTML Standard Attribute</p></td>
210 * </tr>
211 * <tr class="altColor">
212 * <td><p>value</p></td>
213 * <td><p>false</p></td>
214 * <td><p>true</p></td>
215 * <td><p>HTML Optional Attribute</p></td>
216 * </tr>
217 * </tbody>
218 * </table>
219 *
220 * @author Rob Harrop
221 * @author Juergen Hoeller
222 * @since 2.0
223 */
224@SuppressWarnings("serial")
225public class CheckboxTag extends AbstractSingleCheckedElementTag {
226
227        @Override
228        protected int writeTagContent(TagWriter tagWriter) throws JspException {
229                super.writeTagContent(tagWriter);
230
231                if (!isDisabled()) {
232                        // Write out the 'field was present' marker.
233                        tagWriter.startTag("input");
234                        tagWriter.writeAttribute("type", "hidden");
235                        String name = WebDataBinder.DEFAULT_FIELD_MARKER_PREFIX + getName();
236                        tagWriter.writeAttribute("name", name);
237                        tagWriter.writeAttribute("value", processFieldValue(name, "on", "hidden"));
238                        tagWriter.endTag();
239                }
240
241                return SKIP_BODY;
242        }
243
244        @Override
245        protected void writeTagDetails(TagWriter tagWriter) throws JspException {
246                tagWriter.writeAttribute("type", getInputType());
247
248                Object boundValue = getBoundValue();
249                Class<?> valueType = getBindStatus().getValueType();
250
251                if (Boolean.class == valueType || boolean.class == valueType) {
252                        // the concrete type may not be a Boolean - can be String
253                        if (boundValue instanceof String) {
254                                boundValue = Boolean.valueOf((String) boundValue);
255                        }
256                        Boolean booleanValue = (boundValue != null ? (Boolean) boundValue : Boolean.FALSE);
257                        renderFromBoolean(booleanValue, tagWriter);
258                }
259
260                else {
261                        Object value = getValue();
262                        if (value == null) {
263                                throw new IllegalArgumentException("Attribute 'value' is required when binding to non-boolean values");
264                        }
265                        Object resolvedValue = (value instanceof String ? evaluate("value", value) : value);
266                        renderFromValue(resolvedValue, tagWriter);
267                }
268        }
269
270        @Override
271        protected String getInputType() {
272                return "checkbox";
273        }
274
275}