package de.andreassiegel.constraint.validation;

import de.andreassiegel.constraint.ExclusiveField;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class FieldListValidator implements ConstraintValidator<ExclusiveField, Object> {

    private String[] fields;

    @Override
    public void initialize(ExclusiveField constraintAnnotation) {
        this.fields = constraintAnnotation.fields();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {

        constraintValidatorContext.disableDefaultConstraintViolation();

        if (fields.length > 0) {

            List<String> setFields = definedFields(object);

            if (setFields.size() == 1) {
                return true;
            } else {
                String message = constraintViolationMessage(setFields);

                for (String fieldName : setFields) {
                    constraintValidatorContext.buildConstraintViolationWithTemplate(message)
                            .addPropertyNode(fieldName)
                            .addConstraintViolation();
                }

                return false;
            }
        }

        return true;
    }

    private String constraintViolationMessage(List<String> setFields) {
        String message;

        if (setFields.isEmpty()) {
            if (fields.length > 1) {
                message = "one of " + Arrays.toString(fields) + " may not be empty";
            } else {
                message = "may not be empty";
            }
        } else {
            message = "only a single field of " + Arrays.toString(fields) + " may be defined";
        }

        return message;
    }

    private List<String> definedFields(Object object) {
        List<String> setFields = new ArrayList<String>();

        for (String fieldName : fields) {
            try {
                if (!isNullOrEmpty(getValue(object, fieldName))) {
                    setFields.add(fieldName);
                }
            } catch (NoSuchFieldException e) {
                System.out.println("The field '" + fieldName + "' declared for validation does not exist in class '" + object.getClass().getName() + "'");
                e.printStackTrace();
            }
        }

        return setFields;
    }

    private Object getValue(Object object, String fieldName) throws NoSuchFieldException {
        Field field = object.getClass().getDeclaredField(fieldName);

        try {
            return field.get(object);
        } catch (IllegalAccessException e) {
            field.setAccessible(true);
            return getValue(object, fieldName);
        }
    }

    private boolean isNullOrEmpty(Object value) {
        return value == null
                || (value instanceof String && ((String) value).isEmpty())
                || value.toString().isEmpty();
    }
}

@@ -954,126 +954,628 @@
     
-S
+if (setFields.isEmpty()) %7B%0A                    undefinedFieldsCons
 tr
+a
 in
-g message = constraintViolationMessage(setFields);%0A%0A                for (String fieldName : setFields) %7B%0A        
+tViolation(constraintValidatorContext);%0A                %7D else %7B%0A                    overspecifiedFieldsConstraintViolations(constraintValidatorContext, setFields);%0A                %7D%0A%0A                return false;%0A            %7D%0A        %7D%0A%0A        return true;%0A    %7D%0A%0A    private void overspecifiedFieldsConstraintViolations(ConstraintValidatorContext constraintValidatorContext, List%3CString%3E setFields) %7B%0A        String message = %22only a single field of %22 + Arrays.toString(fields) + %22 may be defined%22;%0A%0A        for (String fieldName : setFields) %7B%0A
     
@@ -1671,32 +1671,24 @@
             
-        
 .addProperty
@@ -1719,32 +1719,24 @@
             
-        
 .addConstrai
@@ -1762,170 +1762,122 @@
     
-  
-      %7D%0A%0A                return false;%0A            %7D%0A        %7D%0A%0A        return true;%0A    %7D%0A%0A    private String constraintViolationMessage(List%3CString%3E setFields
+%7D%0A    %7D%0A%0A    private void undefinedFieldsConstraintViolation(ConstraintValidatorContext constraintValidatorContext
 ) %7B%0A
@@ -1905,47 +1905,8 @@
 e;%0A%0A
-        if (setFields.isEmpty()) %7B%0A    
     
@@ -1930,28 +1930,24 @@
 ngth %3E 1) %7B%0A
-    
             
@@ -2015,36 +2015,32 @@
 empty%22;%0A        
-    
 %7D else %7B%0A       
@@ -2036,36 +2036,32 @@
 e %7B%0A            
-   
- 
 message = %22may n
@@ -2086,134 +2086,197 @@
     
+%7D%0A%0A
     
-%7D%0A
     
-    %7D else %7B%0A            message = %22only a single field of %22 + Arrays.toString(fields) + %22 may be 
+for (String fieldName : fields) %7B%0A            constraintValidatorContext.buildConstraintViolationWithTemplate(message)%0A                    .addPropertyNo
 de
+(
 fi
-ned%22;%0A
+eldName)%0A   
         
 %7D%0A%0A 
@@ -2267,35 +2267,33 @@
 %0A           
-%7D%0A%0A
+ 
         
 return messa
@@ -2284,23 +2284,44 @@
     
-return message;
+.addConstraintViolation();%0A        %7D
 %0A   
@@ -2865,24 +2865,244 @@
 ackTrace();%0A
+            %7D catch (IllegalAccessException e) %7B%0A                System.out.println(%22The field '%22 + fieldName + %22' in class '%22 + object.getClass().getName() + %22' is not accessible%22);%0A                e.printStackTrace();%0A
             
@@ -3235,16 +3235,40 @@
 xception
+, IllegalAccessException
  %7B%0A     
@@ -3335,110 +3335,8 @@
 e);%0A
-%0A        try %7B%0A            return field.get(object);%0A        %7D catch (IllegalAccessException e) %7B%0A    
     
@@ -3378,57 +3378,33 @@
     
- 
-   
 return 
-getValue(object, fieldName);%0A        %7D
+field.get(object);
 %0A   

Fix empty setFields list bug