All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.mygreen.supercsv.validation.beanvalidation.CsvBeanValidator Maven / Gradle / Ivy
package com.github.mygreen.supercsv.validation.beanvalidation;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.MessageInterpolator;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.metadata.ConstraintDescriptor;
import org.hibernate.validator.internal.engine.MessageInterpolatorContext;
import org.hibernate.validator.internal.engine.ValidatorImpl;
import com.github.mygreen.supercsv.builder.ColumnMapping;
import com.github.mygreen.supercsv.validation.CsvBindingErrors;
import com.github.mygreen.supercsv.validation.CsvFieldError;
import com.github.mygreen.supercsv.validation.CsvValidator;
import com.github.mygreen.supercsv.validation.ValidationContext;
/**
* BeanValidaion JSR-303(ver.1.0)/JSR-349(ver.1.1)にブリッジする{@link CsvValidator}。
*
* @version 2.2
* @since 2.0
* @author T.TSUCHIE
*
*/
public class CsvBeanValidator implements CsvValidator {
/**
* BeanValidationのアノテーションの属性で、メッセージ中の変数から除外するもの。
* メッセージの再構築を行う際に必要
*/
private static final Set EXCLUDE_MESSAGE_ANNOTATION_ATTRIBUTES;
static {
Set set = new HashSet(3);
set.add("message");
set.add("groups");
set.add("payload");
EXCLUDE_MESSAGE_ANNOTATION_ATTRIBUTES = Collections.unmodifiableSet(set);
}
private final Validator targetValidator;
private final MessageInterpolator messageInterpolator;
public CsvBeanValidator(final Validator targetValidator) {
Objects.requireNonNull(targetValidator);
this.targetValidator = targetValidator;
this.messageInterpolator = getMessageInterpolatorFromValidator(targetValidator);
}
public CsvBeanValidator() {
this.targetValidator = createDefaultValidator();
this.messageInterpolator = getMessageInterpolatorFromValidator(targetValidator);
}
/**
* Bean Validatonのデフォルトのインスタンスを取得する。
* @return
*/
private Validator createDefaultValidator() {
final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
final Validator validator = validatorFactory.usingContext()
.getValidator();
return validator;
}
/**
* ValidatorからMessageInterpolartorを取得する。
* @param validator
* @return {@link ValidatorImpl}出ない場合は、nullを返す。
* @throws IllegalStateException 取得に失敗した場合。
*/
private static MessageInterpolator getMessageInterpolatorFromValidator(Validator validator) {
if(!(validator instanceof ValidatorImpl)) {
return null;
}
try {
Field field = ValidatorImpl.class.getDeclaredField("messageInterpolator");
field.setAccessible(true);
MessageInterpolator interpolator = (MessageInterpolator)field.get(validator);
return interpolator;
} catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
throw new IllegalStateException("fail reflect MessageInterpolrator from ValidatorImpl.", e);
}
}
/**
* BeanValidationのValidatorを取得する。
* @return
*/
public Validator getTargetValidator() {
return targetValidator;
}
@Override
public void validate(final Object record, final CsvBindingErrors bindingErrors,
final ValidationContext validationContext) {
validate(record, bindingErrors, validationContext, validationContext.getBeanMapping().getGroups());
}
/**
* グループを指定して検証を実行する。
* @param record 検証対象のオブジェクト。
* @param bindingErrors エラーオブジェクト
* @param validationContext 入力値検証のためのコンテキスト情報
* @param groups BeanValiationのグループのクラス
*/
public void validate(final Object record, final CsvBindingErrors bindingErrors,
final ValidationContext validationContext, final Class>... groups) {
Objects.requireNonNull(record);
Objects.requireNonNull(bindingErrors);
Objects.requireNonNull(validationContext);
processConstraintViolation(getTargetValidator().validate(record, groups), bindingErrors, validationContext);
}
/**
* BeanValidationの検証結果をSheet用のエラーに変換する
* @param violations BeanValidationの検証結果
* @param bindingErrors エラー情報
* @param validationContext 入力値検証のためのコンテキスト情報
*/
private void processConstraintViolation(final Set> violations,
final CsvBindingErrors bindingErrors, final ValidationContext validationContext) {
for(ConstraintViolation violation : violations) {
final String field = violation.getPropertyPath().toString();
final ConstraintDescriptor> cd = violation.getConstraintDescriptor();
final String[] errorCodes = determineErrorCode(cd);
final Map errorVars = createVariableForConstraint(cd);
if(isCsvField(field, validationContext)) {
// フィールドエラーの場合
final CsvFieldError fieldError = bindingErrors.getFirstFieldError(field);
if(fieldError != null && fieldError.isProcessingFailure()) {
// CellProcessorで発生したエラーが既ににある場合は、処理をスキップする。
continue;
}
final ColumnMapping columnMapping = validationContext.getBeanMapping().getColumnMapping(field).get();
errorVars.put("lineNumber", validationContext.getCsvContext().getLineNumber());
errorVars.put("rowNumber", validationContext.getCsvContext().getRowNumber());
errorVars.put("columnNumber", columnMapping.getNumber());
errorVars.put("label", columnMapping.getLabel());
errorVars.computeIfAbsent("printer", key -> columnMapping.getFormatter());
// 実際の値を取得する
final Object fieldValue = violation.getInvalidValue();
errorVars.computeIfAbsent("validatedValue", key -> fieldValue);
String defaultMessage = determineDefaltMessage(errorVars, violation);
bindingErrors.rejectValue(field, columnMapping.getField().getType(),
errorCodes, errorVars, defaultMessage);
} else {
// オブジェクトエラーの場合
bindingErrors.reject(errorCodes, errorVars, violation.getMessage());
}
}
}
/**
* エラーコードの決定する
* @param descriptor フィールド情報
* @return エラーコード
*/
protected String[] determineErrorCode(ConstraintDescriptor> descriptor) {
return new String[] {
descriptor.getAnnotation().annotationType().getSimpleName()/*,
descriptor.getAnnotation().annotationType().getCanonicalName(),
descriptor.getAnnotation().annotationType().getCanonicalName() + ".message"
*/
};
}
/**
* CSVのカラムのフィールドか判定する。
* @param field フィールド名
* @param validationContext CSVの検証情報
* @return trueのとき、CSVのカラムフィールド。
*/
private boolean isCsvField(final String field, final ValidationContext validationContext) {
return validationContext.getBeanMapping().getColumnMapping(field).isPresent();
}
/**
* BeanValidationのアノテーションの値を元に、メッセージ変数を作成する。
* @param descriptor
* @return メッセージ変数
*/
private Map createVariableForConstraint(final ConstraintDescriptor> descriptor) {
final Map vars = new HashMap<>();
for(Map.Entry entry : descriptor.getAttributes().entrySet()) {
final String attrName = entry.getKey();
final Object attrValue = entry.getValue();
// メッセージ変数で必要ないものを除外する
if(EXCLUDE_MESSAGE_ANNOTATION_ATTRIBUTES.contains(attrName)) {
continue;
}
vars.put(attrName, attrValue);
}
return vars;
}
/**
* CSVの標準メッセージを取得する。
* CSVメッセージ変数で、再度フォーマットを試みる。
*
* @param errorVars エラー時の変数
* @param violation エラー情報
* @return デフォルトメッセージ
*/
protected String determineDefaltMessage(final Map errorVars, ConstraintViolation violation) {
String message = violation.getMessage();
if(messageInterpolator == null) {
return message;
} else if(!(message.contains("{") && message.contains("}"))) {
// 変数形式が含まれていない場合は、そのまま返す。
return message;
}
MessageInterpolatorContext context = new MessageInterpolatorContext(
violation.getConstraintDescriptor(),
violation.getInvalidValue(),
violation.getRootBeanClass(),
errorVars,
Collections.emptyMap());
return messageInterpolator.interpolate(violation.getMessageTemplate(), context);
}
}