/**
 *
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for license information.
 *
 */

package com.microsoft.rest;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.joda.time.DateTime;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * ServiceClient is the abstraction for accessing REST operations and their payload data types.
 */
public class Validator {
    public static void validate(Object request, Class type) throws NullPointerException {
        Field[] fields = FieldUtils.getAllFields(type);
        for (Field field : fields) {
            field.setAccessible(true);
            JsonProperty annotation = field.getAnnotation(JsonProperty.class);
            Object property = null;
            try {
                property = field.get(request);
            } catch (IllegalAccessException e) {
                // Ignore inaccessible fields
            }
            if (annotation != null && annotation.required()) {
                Objects.requireNonNull(property, field.getName() + " == null");
            }

            if (property != null) {
                Class propertyType = property.getClass();
                if (ClassUtils.isAssignable(propertyType, List.class)) {
                    List<?> items = (List<?>)property;
                    for (Object item : items) {
                        Validator.validate(item, item.getClass());
                    }
                }
                if (ClassUtils.isAssignable(propertyType, Map.class)) {
                    Map<?, ?> entries = (Map<?, ?>)property;
                    for (Map.Entry<?, ?> entry : entries.entrySet()) {
                        Validator.validate(entry.getKey(), entry.getKey().getClass());
                        Validator.validate(entry.getValue(), entry.getValue().getClass());
                    }
                }
                else if (!(ClassUtils.isPrimitiveOrWrapper(propertyType) ||
                        ClassUtils.isAssignable(propertyType, LocalDate.class) ||
                        ClassUtils.isAssignable(propertyType, DateTime.class) ||
                        ClassUtils.isAssignable(propertyType, String.class))) {
                    Validator.validate(property, propertyType);
                }
            }
        }
    }
}

@@ -374,16 +374,48 @@
 ateTime;
+%0Aimport org.joda.time.LocalDate;
 %0A%0Aimport
@@ -444,36 +444,8 @@
 ld;%0A
-import java.time.LocalDate;%0A
 impo
@@ -488,34 +488,8 @@
 Map;
-%0Aimport java.util.Objects;
 %0A%0A/*
@@ -658,58 +658,175 @@
 ect 
-request, Class type) throws NullPointerException %7B
+parameter) throws IllegalArgumentException %7B%0A        // Validation of top level payload is done outside%0A        if (parameter == null) %7B%0A            return;%0A        %7D%0A
 %0A   
@@ -871,20 +871,36 @@
 lFields(
-type
+parameter.getClass()
 );%0A     
@@ -1144,15 +1144,17 @@
 get(
-request
+parameter
 );%0A 
@@ -1253,32 +1253,72 @@
 s%0A            %7D%0A
+            if (property == null) %7B%0A    
             if (
@@ -1384,68 +1384,98 @@
     
-Objects.requireNonNull(property, field.getName() + %22 ==
+    throw new IllegalArgumentException(field.getName() + %22 is required and cannot be
  null
+.
 %22);%0A
@@ -1490,47 +1490,61 @@
     
-%7D%0A%0A            if (property != null) %7B%0A
+    %7D%0A            %7D else %7B%0A                try %7B%0A    
     
@@ -1605,32 +1605,36 @@
                 
+    
 if (ClassUtils.i
@@ -1694,16 +1694,20 @@
         
+   
+ 
 List%3C?%3E 
@@ -1749,24 +1749,28 @@
             
+    
 for (Object 
@@ -1801,32 +1801,36 @@
                 
+ 
+   
 Validator.valida
@@ -1840,25 +1840,8 @@
 item
-, item.getClass()
 );%0A 
@@ -1851,34 +1851,40 @@
                 
-%7D%0A
+    %7D%0A  
                 
@@ -1875,34 +1875,40 @@
                 
-%7D%0A
+  %7D%0A    
                 
@@ -1983,16 +1983,20 @@
         
+    
 Map%3C?, ?
@@ -2020,32 +2020,36 @@
 ?, ?%3E)property;%0A
+    
                 
@@ -2119,32 +2119,36 @@
                 
+    
 Validator.valida
@@ -2168,38 +2168,15 @@
 ey()
-, entry.getKey().getClass());%0A
+);%0A    
     
@@ -2234,40 +2234,15 @@
 ue()
-, entry.getValue().getClass());%0A
+);%0A    
     
@@ -2267,34 +2267,42 @@
                 
-%7D%0A
+    %7D%0A    
                 
@@ -2357,24 +2357,81 @@
 rtyType) %7C%7C%0A
+                            propertyType.isEnum() %7C%7C%0A    
             
@@ -2516,32 +2516,36 @@
                 
+    
 ClassUtils.isAss
@@ -2601,32 +2601,36 @@
                 
+  
+  
 ClassUtils.isAss
@@ -2665,24 +2665,28 @@
 .class))) %7B%0A
+    
             
@@ -2724,56 +2724,524 @@
 erty
-, propertyType);%0A                %7D%0A            %7D
+);%0A                    %7D%0A                %7D catch (IllegalArgumentException ex) %7B%0A                    // Build property chain%0A                    throw new IllegalArgumentException(field.getName() + %22.%22 + ex.getMessage());%0A                %7D%0A            %7D%0A        %7D%0A    %7D%0A%0A    public static void validate(Object parameter, ServiceCallback%3C?%3E serviceCallback) %7B%0A        try %7B%0A            validate(parameter);%0A        %7D catch (IllegalArgumentException ex) %7B%0A            serviceCallback.failure(new ServiceException(ex));
 %0A   

Implement validation