utils.play.BugWorkaroundForm.java Source code

Java tutorial

Introduction

Here is the source code for utils.play.BugWorkaroundForm.java

Source

/*
 * Copyright 2014 Ricardo Lorenzo<unshakablespirit@gmail.com>
 *
 *    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 utils.play;

/**
 * Created by ricardolorenzo on 22/07/2014.
 */

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.FieldError;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
import play.data.Form;
import play.data.validation.Validation;
import play.data.validation.ValidationError;
import play.libs.F;

import javax.validation.ConstraintViolation;
import java.util.*;

/**
 * This class is a patched version of a Play bug.<br>
 * The bug happens when you submit a form with errors and you want to prefill
 * the already entered data.
 *
 * @param <T>
 * @author ndeverge
 */
public class BugWorkaroundForm<T> extends Form<T> {

    private final String rootName;
    private final Class<T> backedType;
    private final Map<String, List<ValidationError>> errors;

    public BugWorkaroundForm(final Class<T> clazz) {

        this(null, clazz);
    }

    @SuppressWarnings("unchecked")
    public BugWorkaroundForm(final String name, final Class<T> clazz) {
        this(name, clazz, new HashMap<String, String>(), new HashMap<String, List<ValidationError>>(),
                play.libs.F.None());
    }

    /**
     * Creates a new <code>Form</code>.
     *
     * @param clazz  wrapped class
     * @param data   the current form data (used to display the form)
     * @param errors the collection of errors associated with this form
     * @param value  optional concrete value of type <code>T</code> if the form
     *               submission was successful
     */
    public BugWorkaroundForm(final String rootName, final Class<T> clazz, final Map<String, String> data,
            final Map<String, List<ValidationError>> errors, final F.Option<T> value) {
        super(rootName, clazz, data, errors, value);
        this.rootName = rootName;
        this.backedType = clazz;
        this.errors = errors;
    }

    private T blankInstance() {
        try {
            return backedType.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Cannot instantiate " + backedType + ". It must have a default constructor",
                    e);
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public Form<T> bind(final Map<String, String> data, final String... allowedFields) {
        DataBinder dataBinder = null;
        Map<String, String> objectData = data;
        if (rootName == null) {
            dataBinder = new DataBinder(blankInstance());
        } else {
            dataBinder = new DataBinder(blankInstance(), rootName);
            objectData = new HashMap<String, String>();
            for (String key : data.keySet()) {
                if (key.startsWith(rootName + ".")) {
                    objectData.put(key.substring(rootName.length() + 1), data.get(key));
                }
            }
        }
        if (allowedFields.length > 0) {
            dataBinder.setAllowedFields(allowedFields);
        }
        SpringValidatorAdapter validator = new SpringValidatorAdapter(Validation.getValidator());
        dataBinder.setValidator(validator);
        dataBinder.setConversionService(play.data.format.Formatters.conversion);
        dataBinder.setAutoGrowNestedPaths(true);
        dataBinder.bind(new MutablePropertyValues(objectData));

        Set<ConstraintViolation<Object>> validationErrors = validator.validate(dataBinder.getTarget());
        BindingResult result = dataBinder.getBindingResult();

        for (ConstraintViolation<Object> violation : validationErrors) {
            String field = violation.getPropertyPath().toString();
            FieldError fieldError = result.getFieldError(field);
            if (fieldError == null || !fieldError.isBindingFailure()) {
                try {
                    result.rejectValue(field,
                            violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),
                            getArgumentsForConstraint(result.getObjectName(), field,
                                    violation.getConstraintDescriptor()),
                            violation.getMessage());
                } catch (NotReadablePropertyException ex) {
                    throw new IllegalStateException("JSR-303 validated property '" + field
                            + "' does not have a corresponding accessor for data binding - "
                            + "check your DataBinder's configuration (bean property versus direct field access)",
                            ex);
                }
            }
        }

        if (result.hasErrors()) {
            Map<String, List<ValidationError>> errors = new HashMap<String, List<ValidationError>>();
            for (FieldError error : result.getFieldErrors()) {
                String key = error.getObjectName() + "." + error.getField();
                System.out.println("Error field:" + key);
                if (key.startsWith("target.") && rootName == null) {
                    key = key.substring(7);
                }
                List<Object> arguments = new ArrayList<>();
                for (Object arg : error.getArguments()) {
                    if (!(arg instanceof org.springframework.context.support.DefaultMessageSourceResolvable)) {
                        arguments.add(arg);
                    }
                }
                if (!errors.containsKey(key)) {
                    errors.put(key, new ArrayList<ValidationError>());
                }
                errors.get(key).add(new ValidationError(key,
                        error.isBindingFailure() ? "error.invalid" : error.getDefaultMessage(), arguments));
            }
            return new Form(rootName, backedType, data, errors, F.Option.None());
        } else {
            Object globalError = null;
            if (result.getTarget() != null) {
                try {
                    java.lang.reflect.Method v = result.getTarget().getClass().getMethod("validate");
                    globalError = v.invoke(result.getTarget());
                } catch (NoSuchMethodException e) {
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
            if (globalError != null) {
                Map<String, List<ValidationError>> errors = new HashMap<String, List<ValidationError>>();
                if (globalError instanceof String) {
                    errors.put("", new ArrayList<ValidationError>());
                    errors.get("").add(new ValidationError("", (String) globalError, new ArrayList()));
                } else if (globalError instanceof List) {
                    for (ValidationError error : (List<ValidationError>) globalError) {
                        List<ValidationError> errorsForKey = errors.get(error.key());
                        if (errorsForKey == null) {
                            errors.put(error.key(), errorsForKey = new ArrayList<ValidationError>());
                        }
                        errorsForKey.add(error);
                    }
                } else if (globalError instanceof Map) {
                    errors = (Map<String, List<ValidationError>>) globalError;
                }

                if (result.getTarget() != null) {
                    return new Form(rootName, backedType, data, errors, F.Option.Some((T) result.getTarget()));
                } else {
                    return new Form(rootName, backedType, data, errors, F.Option.None());
                }
            }
            return new Form(rootName, backedType, new HashMap<String, String>(data),
                    new HashMap<String, List<ValidationError>>(errors), F.Option.Some((T) result.getTarget()));
        }
    }
}