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;
020import javax.servlet.jsp.JspException;
021
022import org.springframework.web.bind.WebDataBinder;
023
024/**
025 * Databinding-aware JSP tag for rendering an HTML '{@code input}'
026 * element with a '{@code type}' of '{@code checkbox}'.
027 *
028 * <p>May be used in one of three different approaches depending on the
029 * type of the {@link #getValue bound value}.
030 *
031 * <h3>Approach One</h3>
032 * When the bound value is of type {@link Boolean} then the '{@code input(checkbox)}'
033 * is marked as 'checked' if the bound value is {@code true}. The '{@code value}'
034 * attribute corresponds to the resolved value of the {@link #setValue(Object) value} property.
035 * <h3>Approach Two</h3>
036 * When the bound value is of type {@link Collection} then the '{@code input(checkbox)}'
037 * is marked as 'checked' if the configured {@link #setValue(Object) value} is present in
038 * the bound {@link Collection}.
039 * <h3>Approach Three</h3>
040 * For any other bound value type, the '{@code input(checkbox)}' is marked as 'checked'
041 * if the configured {@link #setValue(Object) value} is equal to the bound value.
042 *
043 * @author Rob Harrop
044 * @author Juergen Hoeller
045 * @since 2.0
046 */
047@SuppressWarnings("serial")
048public class CheckboxTag extends AbstractSingleCheckedElementTag {
049
050        @Override
051        protected int writeTagContent(TagWriter tagWriter) throws JspException {
052                super.writeTagContent(tagWriter);
053
054                if (!isDisabled()) {
055                        // Write out the 'field was present' marker.
056                        tagWriter.startTag("input");
057                        tagWriter.writeAttribute("type", "hidden");
058                        String name = WebDataBinder.DEFAULT_FIELD_MARKER_PREFIX + getName();
059                        tagWriter.writeAttribute("name", name);
060                        tagWriter.writeAttribute("value", processFieldValue(name, "on", "hidden"));
061                        tagWriter.endTag();
062                }
063
064                return SKIP_BODY;
065        }
066
067        @Override
068        protected void writeTagDetails(TagWriter tagWriter) throws JspException {
069                tagWriter.writeAttribute("type", getInputType());
070
071                Object boundValue = getBoundValue();
072                Class<?> valueType = getBindStatus().getValueType();
073
074                if (Boolean.class == valueType || boolean.class == valueType) {
075                        // the concrete type may not be a Boolean - can be String
076                        if (boundValue instanceof String) {
077                                boundValue = Boolean.valueOf((String) boundValue);
078                        }
079                        Boolean booleanValue = (boundValue != null ? (Boolean) boundValue : Boolean.FALSE);
080                        renderFromBoolean(booleanValue, tagWriter);
081                }
082
083                else {
084                        Object value = getValue();
085                        if (value == null) {
086                                throw new IllegalArgumentException("Attribute 'value' is required when binding to non-boolean values");
087                        }
088                        Object resolvedValue = (value instanceof String ? evaluate("value", value) : value);
089                        renderFromValue(resolvedValue, tagWriter);
090                }
091        }
092
093        @Override
094        protected String getInputType() {
095                return "checkbox";
096        }
097
098}