com.javaetmoi.core.mvc.tag.Html5InputTag.java Source code

Java tutorial

Introduction

Here is the source code for com.javaetmoi.core.mvc.tag.Html5InputTag.java

Source

/**
 * Copyright 2014 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.javaetmoi.core.mvc.tag;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.validation.Validator;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.URL;
import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.tags.form.InputTag;
import org.springframework.web.servlet.tags.form.TagWriter;

/**
 * Add HTML5 form validation to the default Spring MVC input tag by using Bean Validation
 * constraints.
 * 
 * <p>
 * Supports {@link Max}, {@link Min}, {@link NotNull} and {@link Size} form the Bean Validation API.<br/>
 * Supports custom {@link Email}, {@link NotEmpty} and {@link URL} annotations from the Hibernate Validator implementation.
 * 
 */
public class Html5InputTag extends InputTag {

    public static final String MAX_ATTRIBUTE = "max";

    public static final String MIN_ATTRIBUTE = "min";

    private Map<Class<? extends Annotation>, ConstraintDescriptor<?>> annotations;

    private Class<?> valueType;

    private static final Map<Class<?>, String> javaToHtmlTypes;

    static {
        javaToHtmlTypes = new HashMap<Class<?>, String>();
        javaToHtmlTypes.put(Integer.class, "number");
        javaToHtmlTypes.put(Long.class, "number");
    }

    @Override
    protected int writeTagContent(TagWriter tagWriter) throws JspException {
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor();
        if ((propertyDescriptor != null) && !propertyDescriptor.getConstraintDescriptors().isEmpty()) {
            annotations = constrainteByAnnotationType(propertyDescriptor);
        }
        valueType = getBindStatus().getValueType();
        return super.writeTagContent(tagWriter);
    }

    @Override
    protected void writeValue(TagWriter tagWriter) throws JspException {
        super.writeValue(tagWriter);
        if (annotations != null) {
            writeHtml5Attributes(tagWriter);
        }
    }

    protected void writeHtml5Attributes(TagWriter tagWriter) throws JspException {
        writeMaxLengthAttribute(tagWriter);
        writeRequiredAttribute(tagWriter);
        writeMaxAttribute(tagWriter);
        writeMinAttribute(tagWriter);
    }

    protected void writeMaxLengthAttribute(TagWriter tagWriter) throws JspException {
        if (annotations.containsKey(Size.class)) {
            Size size = getAnnotation(Size.class);
            if ((size.max() != Integer.MAX_VALUE) && StringUtils.isEmpty(getMaxlength())) {
                writeOptionalAttribute(tagWriter, MAXLENGTH_ATTRIBUTE, String.valueOf(size.max()));
            }
        }
    }

    protected void writeRequiredAttribute(TagWriter tagWriter) throws JspException {
        if (annotations.containsKey(NotEmpty.class) || annotations.containsKey(NotNull.class)) {
            writeOptionalAttribute(tagWriter, "required", "required");
        }
    }

    protected void writeMaxAttribute(TagWriter tagWriter) throws JspException {
        if (annotations.containsKey(Max.class)) {
            Max max = getAnnotation(Max.class);
            writeOptionalAttribute(tagWriter, MAX_ATTRIBUTE, String.valueOf(max.value()));
        }
    }

    protected void writeMinAttribute(TagWriter tagWriter) throws JspException {
        if (annotations.containsKey(Min.class)) {
            Min min = getAnnotation(Min.class);
            writeOptionalAttribute(tagWriter, MIN_ATTRIBUTE, String.valueOf(min.value()));
        }
    }

    @Override
    protected String getType() {
        String type = javaToHtmlTypes.get(valueType);
        if (type != null) {
            return type;
        }
        type = "text";
        if (annotations != null) {
            if (annotations.containsKey(Email.class)) {
                type = "email";
            } else if (annotations.containsKey(URL.class)) {
                type = "url";
            } else if (annotations.containsKey(Max.class) || annotations.containsKey(Min.class)) {
                type = "number";
            }
        }
        return type;
    }

    protected Object getBean(String beanName, Map<String, Object> model) {
        Object bean;
        if (model != null) {
            bean = model.get(beanName);
        } else {
            ConfigurablePropertyAccessor bw = PropertyAccessorFactory.forDirectFieldAccess(getRequestContext());
            HttpServletRequest request = (HttpServletRequest) bw.getPropertyValue("request");
            bean = request.getAttribute(beanName);
        }
        return bean;
    }

    private Map<Class<? extends Annotation>, ConstraintDescriptor<?>> constrainteByAnnotationType(
            PropertyDescriptor propertyDescriptor) {
        Map<Class<? extends Annotation>, ConstraintDescriptor<?>> annotationMap = new HashMap<Class<? extends Annotation>, ConstraintDescriptor<?>>();
        Set<ConstraintDescriptor<?>> constraintDescriptors = propertyDescriptor.getConstraintDescriptors();
        for (ConstraintDescriptor<?> constraintDescriptor : constraintDescriptors) {
            annotationMap.put(constraintDescriptor.getAnnotation().annotationType(), constraintDescriptor);
        }
        return annotationMap;
    }

    /**
     * @return PropertyDescriptor may be null when JavaBean do not have any Bean Validation
     *         annotations.
     */
    private PropertyDescriptor getPropertyDescriptor() throws JspException {
        String path = getBindStatus().getPath();
        int dotPos = path.indexOf('.');
        if (dotPos == -1) {
            return null;
        }
        String beanName = path.substring(0, dotPos);
        String expression = path.substring(dotPos + 1);

        Map<String, Object> model = getRequestContext().getModel();
        Object bean = getBean(beanName, model);

        Validator validator = getRequestContext().getWebApplicationContext().getBean(Validator.class);
        BeanDescriptor constraints = validator.getConstraintsForClass(bean.getClass());
        return constraints.getConstraintsForProperty(expression);
    }

    @SuppressWarnings("unchecked")
    private <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return (T) annotations.get(annotationClass).getAnnotation();
    }
}