Coverage Report - org.kuali.student.datadictionary.util.DictionaryValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
DictionaryValidator
0%
0/132
0%
0/77
7.9
DictionaryValidator$1
0%
0/1
N/A
7.9
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 1.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl1.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.student.datadictionary.util;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Date;
 20  
 import java.util.HashSet;
 21  
 import java.util.List;
 22  
 import java.util.Set;
 23  
 import java.util.regex.Pattern;
 24  
 import java.util.regex.PatternSyntaxException;
 25  
 import org.kuali.rice.core.api.DateTimeService;
 26  
 import org.kuali.rice.kns.datadictionary.AttributeDefinition;
 27  
 import org.kuali.rice.kns.datadictionary.DataObjectEntry;
 28  
 import org.kuali.rice.kns.datadictionary.validation.DataType;
 29  
 import org.kuali.rice.kns.datadictionary.validation.ValidationUtils;
 30  
 import org.kuali.rice.kns.datadictionary.validation.constraint.CaseConstraint;
 31  
 import org.kuali.rice.kns.datadictionary.validation.constraint.LookupConstraint;
 32  
 import org.kuali.rice.kns.datadictionary.validation.constraint.ValidCharactersConstraint;
 33  
 import org.kuali.rice.kns.datadictionary.validation.constraint.WhenConstraint;
 34  
 
 35  
 
 36  
 
 37  
 
 38  
 public class DictionaryValidator {
 39  
 
 40  
     private DateTimeService dateTimeService;
 41  
     private DataObjectEntry ode;
 42  
     private Set<DataObjectEntry> alreadyValidated;
 43  
 
 44  
     public DictionaryValidator(DataObjectEntry ode,
 45  0
             Set<DataObjectEntry> alreadyValidated) {
 46  0
         this.ode = ode;
 47  0
         this.alreadyValidated = alreadyValidated;
 48  0
     }
 49  
 
 50  
     public DateTimeService getDateTimeService() {
 51  0
         return dateTimeService;
 52  
     }
 53  
 
 54  
     public void setDateTimeService(DateTimeService dateTimeService) {
 55  0
         this.dateTimeService = dateTimeService;
 56  0
     }
 57  
     
 58  
     public List<String> validate() {
 59  0
         List<String> errors = new ArrayList();
 60  0
         if (ode.getFullClassName() == null) {
 61  0
             errors.add("The class name cannot be be left null");
 62  
         }
 63  0
         if (ode.getEntryClass() == null) {
 64  0
             errors.add("The entry class should not be left null");
 65  
         }
 66  0
         if ( ! ode.getEntryClass().getName ().equals(ode.getFullClassName())) {
 67  0
             errors.add("The entry class should match the full class name");
 68  
         }
 69  
 
 70  
 //  else if (this.getClass (ode.getName ()) == null)
 71  
 //  {
 72  
 //   errors.add ("The name does not exist on the class path");
 73  
 //  }
 74  
 
 75  0
         if (ode.getAttributes() == null) {
 76  0
             errors.add("getAttribues () is null -- null for field defintion");
 77  0
             return errors;
 78  
         }
 79  0
         if (ode.getAttributes().size() == 0) {
 80  0
             errors.add("No fields defined for complex object structure");
 81  0
             return errors;
 82  
         }
 83  0
         Set<String> fieldNames = new HashSet();
 84  0
         for (AttributeDefinition ad : ode.getAttributes()) {
 85  0
             if (ad.getName() != null) {
 86  0
                 if (!fieldNames.add(ad.getName())) {
 87  0
                     errors.add(ad.getName() + " is defined more than once");
 88  
                 }
 89  
             }
 90  0
             errors.addAll(validateAttributeDefinition(ad));
 91  
         }
 92  0
         return errors;
 93  
     }
 94  
 
 95  
 // private Class getClass (String className)
 96  
 // {
 97  
 //  try
 98  
 //  {
 99  
 //   return Class.forName (className);
 100  
 //  }
 101  
 //  catch (ClassNotFoundException ex)
 102  
 //  {
 103  
 //   return null;
 104  
 //  }
 105  
 // }
 106  
     private List<String> validateAttributeDefinition(AttributeDefinition ad) {
 107  0
         List<String> errors = new ArrayList();
 108  0
         if (ad.getName() == null) {
 109  0
             errors.add("name cannot be null");
 110  0
         } else if (ad.getName().trim().equals("")) {
 111  0
             errors.add("name cannot be blank");
 112  
         }
 113  0
         if (ad.getDataType().equals(DataType.COMPLEX)) {
 114  0
             errorIfNotNull(errors, ad, "exclusiveMin", ad.getExclusiveMin());
 115  0
             errorIfNotNull(errors, ad, "inclusiveMax", ad.getInclusiveMax());
 116  0
             errorIfNotNull(errors, ad, "max length", ad.getMaxLength());
 117  0
             errorIfNotNull(errors, ad, "min length", ad.getMinLength());
 118  0
             errorIfNotNull(errors, ad, "valid chars", ad.getValidCharactersConstraint());
 119  0
             errorIfNotNull(errors, ad, "lookup", ad.getLookupDefinition());
 120  
         }
 121  
 //        validateConversion(errors, ad.getName(), "defaultValue", ad.getDataType(), ad.getDefaultValue());
 122  0
         validateConversion(errors, ad.getName(), "exclusiveMin", ad.getDataType(), ad.getExclusiveMin());
 123  0
         validateConversion(errors, ad.getName(), "inclusiveMax", ad.getDataType(), ad.getInclusiveMax());
 124  
         //TODO: Cross compare to make sure min is not greater than max and that default value is valid itself
 125  
 
 126  0
         if (ad.getLookupDefinition() != null) {
 127  0
             errors.addAll(validateLookup(ad, ad.getLookupDefinition()));
 128  
         }
 129  0
         if (ad.getCaseConstraint() != null) {
 130  0
             errors.addAll(validateCase(ad, ad.getCaseConstraint()));
 131  
         }
 132  0
         if (ad.getValidCharactersConstraint() != null) {
 133  0
             errors.addAll(validateValidChars(ad, ad.getValidCharactersConstraint()));
 134  
         }
 135  0
         return errors;
 136  
     }
 137  
 
 138  
     private void errorIfNotNull(List<String> errors, AttributeDefinition fd,
 139  
             String validation,
 140  
             Object value) {
 141  0
         if (value != null) {
 142  0
             errors.add("field " + fd.getName() + " has a " + validation
 143  
                     + " but it cannot be specified on a complex type");
 144  
         }
 145  0
     }
 146  
 
 147  
     private Object validateConversion(List<String> errors, String fieldName,
 148  
             String propertyName, DataType dataType,
 149  
             Object value) {
 150  0
         if (value == null) {
 151  0
             return null;
 152  
         }
 153  0
         switch (dataType) {
 154  
             case STRING:
 155  0
                 return value.toString().trim();
 156  
 //    case DATE, TRUNCATED_DATE, BOOLEAN, INTEGER, FLOAT, DOUBLE, LONG, COMPLEX
 157  
             case LONG:
 158  
                 try {
 159  0
                     return ValidationUtils.getLong(value);
 160  0
                 } catch (NumberFormatException ex) {
 161  0
                     errors.add(
 162  
                             "field " + fieldName
 163  
                             + " has a " + propertyName
 164  
                             + " that cannot be converted into a long integer");
 165  
                 }
 166  0
                 return null;
 167  
             case INTEGER:
 168  
                 try {
 169  0
                     return ValidationUtils.getInteger(value);
 170  0
                 } catch (NumberFormatException ex) {
 171  0
                     errors.add(
 172  
                             "field " + fieldName
 173  
                             + " has a " + propertyName + " that cannot be converted into an integer");
 174  
                 }
 175  0
                 return null;
 176  
             case FLOAT:
 177  
                 try {
 178  0
                     return ValidationUtils.getFloat(value);
 179  0
                 } catch (NumberFormatException ex) {
 180  0
                     errors.add(
 181  
                             "field " + fieldName
 182  
                             + " has a " + propertyName
 183  
                             + " that cannot be converted into a floating point value");
 184  
                 }
 185  0
                 return null;
 186  
             case DOUBLE:
 187  
                 try {
 188  0
                     return ValidationUtils.getFloat(value);
 189  0
                 } catch (NumberFormatException ex) {
 190  0
                     errors.add(
 191  
                             "field " + fieldName
 192  
                             + " has a " + propertyName
 193  
                             + " that cannot be converted into a double sized floating point value");
 194  
                 }
 195  0
                 return null;
 196  
             case BOOLEAN:
 197  0
                 if (value instanceof Boolean) {
 198  0
                     return ((Boolean) value).booleanValue();
 199  
                 }
 200  0
                 if (value instanceof String) {
 201  0
                     if (((String) value).trim().equalsIgnoreCase("true")) {
 202  0
                         return true;
 203  
                     }
 204  0
                     if (((String) value).trim().equalsIgnoreCase("false")) {
 205  0
                         return true;
 206  
                     }
 207  
                 }
 208  0
                 errors.add(
 209  
                         "field " + fieldName
 210  
                         + " has a " + propertyName
 211  
                         + " that cannot be converted into a boolean true/false");
 212  0
                 return null;
 213  
             case DATE:
 214  
             case TRUNCATED_DATE:
 215  0
                 if (value instanceof Date) {
 216  0
                     return (Date) value;
 217  
                 }
 218  
                 try {
 219  
                     // TODO: make the date parser configurable like the validator is
 220  0
                     return ValidationUtils.getDate(value, dateTimeService);
 221  0
                 } catch (Exception e) {
 222  0
                     errors.add(
 223  
                             "field " + fieldName
 224  
                             + " has a " + propertyName
 225  
                             + " that cannot be converted into a date");
 226  
                 }
 227  0
                 return null;
 228  
             default:
 229  0
                 errors.add(
 230  
                         "field " + fieldName
 231  
                         + " has a " + propertyName
 232  
                         + " that cannot be converted into an unknown/unhandled data type");
 233  0
                 return null;
 234  
         }
 235  
     }
 236  
 
 237  
     private List<String> validateValidChars(AttributeDefinition fd,
 238  
             ValidCharactersConstraint vc) {
 239  0
         List<String> errors = new ArrayList();
 240  0
         String validChars = vc.getValue();
 241  0
         int typIdx = validChars.indexOf(":");
 242  0
         String processorType = "regex";
 243  0
         if (-1 == typIdx) {
 244  0
             validChars = "[" + validChars + "]*";
 245  
         } else {
 246  0
             processorType = validChars.substring(0, typIdx);
 247  0
             validChars = validChars.substring(typIdx + 1);
 248  
         }
 249  0
         if (!processorType.equalsIgnoreCase("regex")) {
 250  0
             errors.add(
 251  
                     "field " + fd.getName()
 252  
                     + " has an invalid valid chars processor type: a simple list of characters or a regex: is supported");
 253  0
             return errors;
 254  
         }
 255  
         try {
 256  0
             Pattern pattern = Pattern.compile(validChars);
 257  0
         } catch (PatternSyntaxException ex) {
 258  0
             errors.add("field " + fd.getName()
 259  
                     + " has in invalid character pattern for a regular expression: "
 260  
                     + validChars);
 261  0
         }
 262  0
         return errors;
 263  
     }
 264  
 
 265  
     private List<String> validateLookup(AttributeDefinition fd, LookupConstraint lc) {
 266  0
         List<String> errors = new ArrayList();
 267  0
         if (lc.getParams() == null) {
 268  0
             errors.add("field " + fd.getName() + " has a lookup with null parameters");
 269  
         }
 270  
         //TODO: more validation
 271  0
         return errors;
 272  
     }
 273  
     public static final String GREATER_THAN_EQUAL = "greater_than_equal";
 274  
     public static final String LESS_THAN_EQUAL = "less_than_equal";
 275  
     public static final String GREATER_THAN = "greater_than";
 276  
     public static final String LESS_THAN = "less_than";
 277  
     public static final String EQUALS = "equals";
 278  
     public static final String NOT_EQUAL = "not_equal";
 279  0
     private static final String[] VALID_OPERATORS = {
 280  
         NOT_EQUAL, EQUALS, GREATER_THAN_EQUAL, LESS_THAN_EQUAL, GREATER_THAN, LESS_THAN
 281  
     };
 282  
 
 283  
     private List<String> validateCase(AttributeDefinition fd, CaseConstraint cc) {
 284  0
         List<String> errors = new ArrayList();
 285  0
         if (cc.getOperator() == null) {
 286  0
             errors.add("field " + fd.getName()
 287  
                     + " has a case constraint with no operator");
 288  
         } else {
 289  0
             boolean found = false;
 290  0
             for (int i = 0; i < VALID_OPERATORS.length; i++) {
 291  0
                 if (VALID_OPERATORS[i].equalsIgnoreCase(cc.getOperator())) {
 292  0
                     found = true;
 293  0
                     break;
 294  
                 }
 295  
             }
 296  0
             if (!found) {
 297  0
                 errors.add("field " + fd.getName()
 298  
                         + " has a case constraint with an unknown operator "
 299  
                         + cc.getOperator());
 300  
             }
 301  
         }
 302  0
         if (cc.getFieldPath() == null) {
 303  0
             errors.add(
 304  
                     "field " + fd.getName()
 305  
                     + " has a case constraint with a null for the field to use for the comparison");
 306  0
         } else if (cc.getFieldPath().trim().equals("")) {
 307  0
             errors.add(
 308  
                     "field " + fd.getName()
 309  
                     + " has a case constraint with blanks for the field to use for the comparison");
 310  
         }
 311  0
         if (cc.getWhenConstraint() == null) {
 312  0
             errors.add("field " + fd.getName()
 313  
                     + " has a case constraint but null when statements");
 314  0
             return errors;
 315  
         }
 316  0
         if (cc.getWhenConstraint().size() == 0) {
 317  0
             errors.add("field " + fd.getName()
 318  
                     + " has a case constraint but has no when statements");
 319  
         }
 320  0
         for (WhenConstraint wc : cc.getWhenConstraint()) {
 321  0
             if (wc.getConstraint() == null) {
 322  0
                 errors.add(
 323  
                         "field " + fd.getName()
 324  
                         + " has a as case constraint with a when statement that has no overriding constraints specified");
 325  
             }
 326  
         }
 327  
         //TODO: more validation
 328  0
         return errors;
 329  
     }
 330  
 }