package de.axone.web.rest;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.persistence.EntityManager;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import de.axone.exception.Assert;
import de.axone.tools.Mapper;
import de.axone.web.rest.Validator.ValidatorResult.Severity;
public abstract class Validator<T> {
public static final String ERROR_KEY = "__errors__";
private final String key;
public Validator( String key ){
this.key = key;
}
public ValidatorResult validate( EntityManager em, T object ){
ValidatorResult result = new ValidatorResultImpl();
validate( result, em, object );
return result;
}
public void validate( ValidatorResult result, EntityManager em, T object ){
try {
doValidate( result, em, object );
} catch( ValidatorException e ){
result.error( e );
}
}
public void cascade( ValidatorResult result, EntityManager em, T object ){
result = result.descent( key );
validate( result, em, object );
}
public void iterate( ValidatorResult result, EntityManager em, Iterable<T> objects ){
if( objects == null ) return;
ValidatorResultList list = result.descentList( key );
int i=0;
for( T o : objects ){
doValidate( list.descent( i++ ), em, o );
}
}
protected void doValidate( ValidatorResult result, EntityManager em, T object ){
// Override me
}
public interface ValidatorResult {
public enum Severity {
FATAL, ERROR, WARN, INFO;
public boolean is( Severity min ){
return min.ordinal() >= ordinal();
}
}
public void result( Severity severity, String field, String code );
public void result( Severity severity, String field, String code, String info );
public void fatal( String field, String code );
public void fatal( ValidatorException e );
public void error( String field, String code );
public void error( ValidatorException e );
public void warn( String field, String code );
public void info( String field, String code );
public ValidatorResult descent( String key );
public ValidatorResultList descentList( String key );
public void setInfo( String info );
public void clearInfo();
public void mergeInto( Map<String,Object> data );
public void writeInto( String prefix, List<Map<String,String>> list );
public List<Map<String,String>> asList( String prefix );
public boolean hasError();
public boolean isFatal();
}
public interface ValidatorResultList {
public ValidatorResult descent( int index );
public boolean hasError();
public boolean isFatal();
public void mergeInto( Iterable<Map<String,Object>> it );
public void writeInto( String prefix, List<Map<String,String>> list );
}
public interface ValidatorError {
public Severity getSeverity();
public String getField();
public String getCode();
public String getInfo();
public void setInfo( String info );
}
protected static class ValidatorErrorImpl implements ValidatorError {
private final String field, code;
private String info;
private final ValidatorResult.Severity severity;
public ValidatorErrorImpl( Severity severity, String field, String code ) {
this( severity, field, code, null );
}
public ValidatorErrorImpl( Severity severity, String field, String code, String info ) {
this.severity = severity;
this.field = field;
this.code = code;
this.info = info;
}
@Override
public Severity getSeverity() { return severity; }
@Override
@JsonIgnore
public String getField() { return field; }
@Override
public String getCode() { return code; }
@Override
public String getInfo() { return info; }
@Override
public void setInfo( String info ) {
this.info = info;
}
@Override
public String toString() {
return "["
+ ( field != null ? "field=" + field + ", " : "" )
+ ( code != null ? "code=" + code + ", " : "" )
+ ( info != null ? "info=" + info + ", " : "" )
+ ( severity != null ? "severity=" + severity : "" ) + "]";
}
}
public static final class ValidatorResultImpl
extends LinkedHashMap<String,Object>
implements ValidatorResult
{
private String currentInfo;
@Override
public void fatal( String field, String code ) {
result( Severity.FATAL, field, code );
}
@Override
public void fatal( ValidatorException e ) {
result( Severity.FATAL, e.getField(), e.getMessage() );
}
@Override
public void error( String field, String code ) {
result( Severity.ERROR, field, code );
}
@Override
public void error( ValidatorException e ) {
result( Severity.ERROR, e.getField(), e.getMessage() );
}
@Override
public void warn( String field, String code ) {
result( Severity.WARN, field, code );
}
@Override
public void info( String field, String code ) {
result( Severity.INFO, field, code );
}
@Override
public void result( Severity severity, String field, String code ) {
result( severity, field, code, currentInfo() );
}
@Override
public void result( Severity severity, String field, String code, String info ) {
Assert.notNull( field, "field" );
result( new ValidatorErrorImpl( severity, field, code, info ) );
}
public void result( ValidatorError error ){
error.setInfo( currentInfo() );
put( error.getField(), error );
}
@Override
public ValidatorResult descent( String key ) {
ValidatorResultImpl newResult = new ValidatorResultImpl();
put( key, newResult );
return newResult;
}
@Override
public ValidatorResultList descentList( String key ){
ValidatorResultList newList = new ValidatorResultListImpl();
put( key, newList );
return newList;
}
@Override
public void setInfo( String info ) {
this.currentInfo = info;
}
@Override
public void clearInfo() {
this.currentInfo = null;
}
protected String currentInfo(){
return currentInfo;
}
@SuppressWarnings( "unchecked" )
@Override
public void mergeInto( Map<String, Object> data ) {
for( Map.Entry<String,Object> entry : this.entrySet() ){
String key = entry.getKey();
Object value = entry.getValue();
if( value instanceof ValidatorError ){
ValidatorError error = (ValidatorError) value;
Map<String,ValidatorError> errorsField = (Map<String,ValidatorError>)data.get( ERROR_KEY );
if( errorsField == null ){
errorsField = new HashMap<>();
data.put( ERROR_KEY, errorsField );
}
errorsField.put( key, error );
}
if( value instanceof ValidatorResult ){
Object dataValue = data.get( key );
if( dataValue == null ) continue;
if( !( dataValue instanceof Map ) )
throw new RuntimeException( "Value is of wrong format: " + dataValue.getClass() );
((ValidatorResult)value).mergeInto( (Map<String,Object>)dataValue );
}
if( value instanceof ValidatorResultList ){
Object dataValue = data.get( key );
if( dataValue == null ) continue;
if( !( dataValue instanceof Iterable ) )
throw new RuntimeException( "Value is of wrong format: " + dataValue.getClass() );
((ValidatorResultList)value).mergeInto( (Iterable<Map<String,Object>>)dataValue );
}
}
}
@Override
public String toString(){
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion( Include.NON_EMPTY );
mapper.enable( SerializationFeature.INDENT_OUTPUT );
try {
return mapper.writeValueAsString( this );
} catch( JsonProcessingException e ) {
throw new RuntimeException( e );
}
}
@Override
public boolean hasError() {
if( size() == 0 ) return false;
for( Object value : values() ){
if( value instanceof ValidatorError &&
((ValidatorError)value).getSeverity().is( Severity.ERROR ) ) return true;
if( value instanceof ValidatorResultList )
if( ((ValidatorResultList)value).hasError() ) return true;
}
return false;
}
@Override
public boolean isFatal() {
if( size() == 0 ) return false;
for( Object value : values() ){
if( value instanceof ValidatorError &&
((ValidatorError)value).getSeverity().is( Severity.FATAL ) ) return true;
if( value instanceof ValidatorResultList )
if( ((ValidatorResultList)value).isFatal() ) return true;
}
return false;
}
@Override
public void writeInto( String prefix, List<Map<String, String>> list ) {
for( Map.Entry<String,Object> entry : entrySet() ){
String key = entry.getKey();
Object value = entry.getValue();
if( value instanceof ValidatorError ){
ValidatorError error = (ValidatorError) value;
list.add( Mapper.hashMap(
"severity", "" + error.getSeverity(),
"field", prefix + error.getField(),
"info", error.getInfo(),
"code", "ERROR." + error.getCode()
) );
} else if( value instanceof ValidatorResult ){
((ValidatorResult)value).writeInto( prefix + key + '.', list );
} else if( value instanceof ValidatorResultList ){
((ValidatorResultList)value).writeInto( prefix + key + '.', list );
}
}
}
@Override
public List<Map<String, String>> asList( String prefix ) {
List<Map<String,String>> result = new LinkedList<>();
writeInto( prefix, result );
return result;
}
}
public static final class ValidatorResultListImpl
extends LinkedHashMap<Integer,ValidatorResult>
implements ValidatorResultList
{
@Override
public ValidatorResult descent( int index ) {
ValidatorResultImpl newResult = new ValidatorResultImpl();
put( index, newResult );
return newResult;
}
@Override
public boolean hasError() {
for( ValidatorResult result : this.values() ){
if( result.hasError() ) return true;
}
return false;
}
@Override
public boolean isFatal() {
for( ValidatorResult result : this.values() ){
if( result.isFatal() ) return true;
}
return false;
}
@Override
public void mergeInto( Iterable<Map<String,Object>> it ){
int i=0;
for( Map<String,Object> o : it ){
if( containsKey( i ) ){
get( i ).mergeInto( o );
}
i++;
}
}
@Override
public void writeInto( String prefix, List<Map<String, String>> list ) {
for( Map.Entry<Integer, ValidatorResult> entry : entrySet() ){
entry.getValue().writeInto( prefix + entry.getKey() + '.', list );
}
}
}
public static class ValidatorException
extends IllegalArgumentException
implements FieldException {
private final String field;
public ValidatorException( String field, String message ) {
super( message );
this.field = field;
}
public ValidatorException( String field, Throwable cause ) {
super( cause );
this.field = field;
}
@Override
public String getField() {
return field;
}
}
/* ================================================ */
/* ==================== ASSERT ==================== */
/* ================================================ */
public static void assertNotNull( ValidatorResult result,
String field, Object value ){
if( value == null )
error( result, field , "IS_NULL" );
}
public static void assertNoDups( ValidatorResult result,
String field, List<String> list ){
if( list == null ) return;
Set<String> asSet = new TreeSet<String>( list );
if( list.size() > asSet.size() )
error( result, field , "HAS_DUPS" );
}
public static void assertEmpty( ValidatorResult result,
String field, String value ){
if( value != null && value.trim().length() > 0 )
error( result, field , "IS_NOT_EMPTY" );
}
public static void assertEmpty( ValidatorResult result,
String field, Collection<?> value ){
if( value != null && value.size() > 0 )
error( result, field , "IS_NOT_EMPTY" );
}
public static void assertNotEmpty( ValidatorResult result,
String field, Object value ){
if( value == null || value.toString().trim().length() == 0 )
error( result, field , "IS_EMPTY" );
}
public static void assertNotEmpty(
String field, Object value ){
if( value == null || value.toString().trim().length() == 0 )
except( field , "IS_EMPTY" );
}
public static void assertNotEmpty( ValidatorResult result,
String field, Collection<?> value ){
if( value == null || value.size() == 0 )
error( result, field , "IS_EMPTY" );
}
public static <O> void assertExists( ValidatorResult result,
String field, O value, Collection<O> set ){
if( value == null ) return;
if( set == null || ! set.contains( value ) )
error( result, field , "DOES_NOT_EXIST" );
}
public static <O> void assertNotExists( ValidatorResult result,
String field, O value, Collection<O> set ){
if( value == null ) return;
if( set != null && set.contains( value ) )
error( result, field , "EXISTS" );
}
public static void error( ValidatorResult result, String field, String message ){
result.error( field, message );
}
public static void error( ValidatorResult result, String field, String message, String info ){
result.result( Severity.ERROR, field, message, info );
}
public static void info( ValidatorResult result, String field, String message ){
result.info( field, message );
}
public static void info( ValidatorResult result, String field, String message, String info ){
result.result( Severity.INFO, field, message, info );
}
public static void warn( ValidatorResult result, String field, String message ){
result.warn( field, message );
}
public static void warn( ValidatorResult result, String field, String message, String info ){
result.result( Severity.WARN, field, message, info );
}
public static void except( String field, String message ){
throw new ValidatorException( field, message );
}
public static void except( String field ){
throw new ValidatorException( field, "ILLEGAL_ARGUMENT" );
}
}