/**
* Copyright 2010-2016 Ralph Schaer <ralphschaer@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 ch.ralscha.extdirectspring.bean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.immutables.value.Value;
import org.immutables.value.Value.Style.ImplementationVisibility;
import org.springframework.context.MessageSource;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
/**
* Represents the result of a FORM_POST method call.
*/
@JsonSerialize(as = ImmutableEdFormPostResult.class)
@Value.Style(visibility = ImplementationVisibility.PACKAGE)
@Value.Immutable
public abstract class EdFormPostResult {
private static final String ERRORS_PROPERTY = "errors";
private static final String SUCCESS_PROPERTY = "success";
@Value.Parameter
public abstract Map<String, Object> result();
public static EdFormPostResult success() {
return ImmutableEdFormPostResult.builder()
.putResult(SUCCESS_PROPERTY, Boolean.TRUE).build();
}
public static EdFormPostResult success(Map<String, Object> result) {
return ImmutableEdFormPostResult.builder().result(result)
.putResult(SUCCESS_PROPERTY, Boolean.TRUE).build();
}
public static EdFormPostResult create(BindingResult bindingResult) {
return ImmutableEdFormPostResult.builder().addErrors(null, null, bindingResult)
.build();
}
public static EdFormPostResult create(BindingResult bindingResult, boolean success) {
return ImmutableEdFormPostResult.builder().addErrors(null, null, bindingResult)
.putResult(SUCCESS_PROPERTY, success).build();
}
public static EdFormPostResult create(Locale locale, MessageSource messageSource,
BindingResult bindingResult) {
return ImmutableEdFormPostResult.builder()
.addErrors(locale, messageSource, bindingResult).build();
}
public static EdFormPostResult create(Locale locale, MessageSource messageSource,
BindingResult bindingResult, boolean success) {
ImmutableEdFormPostResult.Builder builder = ImmutableEdFormPostResult.builder();
builder.addErrors(locale, messageSource, bindingResult);
return builder.putResult(SUCCESS_PROPERTY, success).build();
}
public static Builder builder() {
return ImmutableEdFormPostResult.builder();
}
public static abstract class Builder {
public abstract Builder result(Map<String, ? extends java.lang.Object> entries);
public abstract Builder putResult(String key, Object value);
public abstract EdFormPostResult build();
/**
* Extracts errors from the bindingResult and inserts them into the error
* properties. Sets the property success to false if there are errors. Sets the
* property success to true if there are no errors.
*
* @param builder
* @param locale
* @param messageSource
* @param bindingResult
*/
public Builder addErrors(Locale locale, MessageSource messageSource,
BindingResult bindingResult) {
if (bindingResult != null && bindingResult.hasFieldErrors()) {
Map<String, List<String>> errorMap = new HashMap<String, List<String>>();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
String message = fieldError.getDefaultMessage();
if (messageSource != null) {
Locale loc = locale != null ? locale : Locale.getDefault();
message = messageSource.getMessage(fieldError.getCode(),
fieldError.getArguments(), loc);
}
List<String> fieldErrors = errorMap.get(fieldError.getField());
if (fieldErrors == null) {
fieldErrors = new ArrayList<String>();
errorMap.put(fieldError.getField(), fieldErrors);
}
fieldErrors.add(message);
}
if (errorMap.isEmpty()) {
putResult(SUCCESS_PROPERTY, Boolean.TRUE);
}
else {
putResult(ERRORS_PROPERTY, errorMap);
putResult(SUCCESS_PROPERTY, Boolean.FALSE);
}
}
else {
putResult(SUCCESS_PROPERTY, Boolean.TRUE);
}
return this;
}
/**
* resolve the messages codes along the implementation described in
* {@link org.springframework.validation.DefaultMessageCodesResolver}<br>
* stop at first message found<br>
* method is useless if no specific validation message have been set (example:
* javax.validation.constraints.NotNull.message.fax=Fax number is mandatory)<br>
* it will behave {@link #addErrors(Locale, MessageSource, BindingResult)} with a
* big overhead
*
* @param locale locale for internationalization
* @param messageSource source of validation code and message
* @param bindingResult Errors list to resolve
* @return this {@link #ExtDirectFormPostResult} for easy chaining
*/
public Builder addErrorsResolveCode(Locale locale, MessageSource messageSource,
BindingResult bindingResult) {
if (bindingResult != null && bindingResult.hasFieldErrors()) {
Map<String, List<String>> errorMap = new HashMap<String, List<String>>();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
String message = fieldError.getDefaultMessage();
if (messageSource != null) {
Locale loc = locale != null ? locale : Locale.getDefault();
for (String code : fieldError.getCodes()) {
try {
message = messageSource.getMessage(code,
fieldError.getArguments(), loc);
}
catch (Exception e) {
/**
* expected if code/message doesn't exist, default
* behavior to counter that, set to your message bundle,
* {@link org.springframework.context.support.AbstractMessageSource#setUseCodeAsDefaultMessage(true)}
* beware of side effects
*/
}
if (message != null && !message.equals(code)) {
break;
}
}
}
List<String> fieldErrors = errorMap.get(fieldError.getField());
if (fieldErrors == null) {
fieldErrors = new ArrayList<String>();
errorMap.put(fieldError.getField(), fieldErrors);
}
fieldErrors.add(message);
}
if (errorMap.isEmpty()) {
putResult(SUCCESS_PROPERTY, Boolean.TRUE);
}
else {
putResult(ERRORS_PROPERTY, errorMap);
putResult(SUCCESS_PROPERTY, Boolean.FALSE);
}
}
else {
putResult(SUCCESS_PROPERTY, Boolean.TRUE);
}
return this;
}
public Builder addError(BindingResult bindingResult) {
addErrors(null, null, bindingResult);
return this;
}
public Builder fail() {
putResult(SUCCESS_PROPERTY, Boolean.FALSE);
return this;
}
public Builder success() {
putResult(SUCCESS_PROPERTY, Boolean.TRUE);
return this;
}
/**
* Adds one error message to a specific field. Does not overwrite already existing
* errors.
*
* @param field the name of the field
* @param error the error message
*/
public Builder addError(String field, String error) {
Assert.notNull(field, "field must not be null");
Assert.notNull(error, "field must not be null");
return addErrors(field, Collections.singletonList(error));
}
/**
* Adds multiple error messages to a specific field. Does not overwrite already
* existing errors.
*
* @param field the name of the field
* @param errors a collection of error messages
*/
private Map<String, List<String>> helper = new LinkedHashMap<String, List<String>>();
public Builder addErrors(String field, List<String> errors) {
Assert.notNull(field, "field must not be null");
Assert.notNull(errors, "field must not be null");
List<String> fieldErrors = helper.get(field);
if (fieldErrors == null) {
fieldErrors = new ArrayList<String>();
helper.put(field, fieldErrors);
}
fieldErrors.addAll(errors);
putResult(ERRORS_PROPERTY, helper);
putResult(SUCCESS_PROPERTY, Boolean.FALSE);
return this;
}
}
}