Java tutorial
/* * 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())); } } }