Skip to content

Instantly share code, notes, and snippets.

@ndeverge
Created July 9, 2012 06:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ndeverge/3074629 to your computer and use it in GitHub Desktop.
Save ndeverge/3074629 to your computer and use it in GitHub Desktop.
package patch;
import static play.libs.F.None;
import static play.libs.F.Some;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.MutablePropertyValues;
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.ValidationError;
import play.libs.F.Option;
/**
* 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.
*
* @author ndeverge
*
* @param <T>
*/
public class PatchedForm<T> extends Form<T> {
private final String rootName;
private final Class<T> backedType;
private final Map<String, List<ValidationError>> errors;
public PatchedForm(final Class<T> clazz) {
this(null, clazz);
}
@SuppressWarnings("unchecked")
public PatchedForm(final String name, final Class<T> clazz) {
this(name, clazz, new HashMap<String, String>(),
new HashMap<String, List<ValidationError>>(), 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 PatchedForm(final String rootName, final Class<T> clazz,
final Map<String, String> data,
final Map<String, List<ValidationError>> errors,
final 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);
}
dataBinder.setValidator(new SpringValidatorAdapter(
play.data.validation.Validation.getValidator()));
dataBinder.setConversionService(play.data.format.Formatters.conversion);
dataBinder.setAutoGrowNestedPaths(true);
dataBinder.bind(new MutablePropertyValues(objectData));
dataBinder.validate();
BindingResult result = dataBinder.getBindingResult();
if (result.hasErrors()) {
Map<String, List<ValidationError>> errors = new HashMap<String, List<ValidationError>>();
for (FieldError error : result.getFieldErrors()) {
String key = error.getObjectName() + "." + error.getField();
if (key.startsWith("target.") && rootName == null) {
key = key.substring(7);
}
List<Object> arguments = new ArrayList<Object>();
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, 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) {
errors.put("", (List<ValidationError>) globalError);
} else if (globalError instanceof Map) {
errors = (Map<String, List<ValidationError>>) globalError;
}
// Here is the patch !!
if (result.getTarget() != null) {
return new Form(rootName, backedType, data, errors,
Some((T) result.getTarget()));
} else {
return new Form(rootName, backedType, data, errors, None());
}
}
return new Form(rootName, backedType, new HashMap<String, String>(
data), new HashMap<String, List<ValidationError>>(errors),
Some((T) result.getTarget()));
}
}
}
@msegeya
Copy link

msegeya commented Jun 17, 2014

hi. am new in play framework and have the problem with which this patch can resolve , my question is how do I apply the patch ?

@msegeya
Copy link

msegeya commented Aug 4, 2014

i figured it out. Thanks for the patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment