Coverage Report - org.kuali.student.common.dictionary.service.impl.DictionaryValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
DictionaryValidator
0%
0/140
0%
0/83
10.125
DictionaryValidator$1
0%
0/1
N/A
10.125
 
 1  
 package org.kuali.student.common.dictionary.service.impl;
 2  
 
 3  
 import java.util.ArrayList;
 4  
 import java.util.Date;
 5  
 import java.util.HashSet;
 6  
 import java.util.List;
 7  
 import java.util.Set;
 8  
 import java.util.regex.Pattern;
 9  
 import java.util.regex.PatternSyntaxException;
 10  
 
 11  
 import org.kuali.student.common.dictionary.dto.CaseConstraint;
 12  
 import org.kuali.student.common.dictionary.dto.DataType;
 13  
 import org.kuali.student.common.dictionary.dto.FieldDefinition;
 14  
 import org.kuali.student.common.dictionary.dto.LookupConstraint;
 15  
 import org.kuali.student.common.dictionary.dto.ObjectStructureDefinition;
 16  
 import org.kuali.student.common.dictionary.dto.ValidCharsConstraint;
 17  
 import org.kuali.student.common.dictionary.dto.WhenConstraint;
 18  
 import org.kuali.student.common.validator.ServerDateParser;
 19  
 import org.kuali.student.common.validator.ValidatorUtils;
 20  
 
 21  
 
 22  
 public class DictionaryValidator
 23  
 {
 24  
 
 25  
  private ObjectStructureDefinition os;
 26  0
  private boolean processSubStructures = false;
 27  
  private Set<ObjectStructureDefinition> alreadyValidated;
 28  
 
 29  
  public DictionaryValidator (ObjectStructureDefinition os,
 30  
                              Set<ObjectStructureDefinition> alreadyValidated,
 31  
                              boolean processSubstructures)
 32  0
  {
 33  0
   this.os = os;
 34  0
   this.alreadyValidated = alreadyValidated;
 35  0
   this.processSubStructures = processSubstructures;
 36  0
  }
 37  
 
 38  
  public List<String> validate ()
 39  
  {
 40  0
   List<String> errors = new ArrayList ();
 41  0
   if (os.getName () == null)
 42  
   {
 43  0
    errors.add ("The name cannbe be left null");
 44  
   }
 45  0
   if (os.getBusinessObjectClass () != null)
 46  
   {
 47  0
    errors.add (
 48  
      "The business object class is not used and should not be filled in");
 49  
   }
 50  
 //  else if (this.getClass (os.getName ()) == null)
 51  
 //  {
 52  
 //   errors.add ("The name does not exist on the class path");
 53  
 //  }
 54  
 
 55  0
   if (os.getAttributes () == null)
 56  
   {
 57  0
    errors.add ("getAttribues () is null -- null for field defintion");
 58  0
    return errors;
 59  
   }
 60  0
   if (os.getAttributes ().size () == 0)
 61  
   {
 62  0
    errors.add ("No fields defined for complex object structure");
 63  0
    return errors;
 64  
   }
 65  0
   Set<String> fieldNames = new HashSet ();
 66  0
   for (FieldDefinition fd : os.getAttributes ())
 67  
   {
 68  0
    if (fd.getName () != null)
 69  
    {
 70  0
     if ( ! fieldNames.add (fd.getName ()))
 71  
     {
 72  0
      errors.add (fd.getName () + " is defined more than once");
 73  
     }
 74  
    }
 75  0
    errors.addAll (validateField (fd));
 76  
   }
 77  0
   return errors;
 78  
  }
 79  
 
 80  
 // private Class getClass (String className)
 81  
 // {
 82  
 //  try
 83  
 //  {
 84  
 //   return Class.forName (className);
 85  
 //  }
 86  
 //  catch (ClassNotFoundException ex)
 87  
 //  {
 88  
 //   return null;
 89  
 //  }
 90  
 // }
 91  
 
 92  
  private List<String> validateField (FieldDefinition fd)
 93  
  {
 94  0
   List<String> errors = new ArrayList ();
 95  0
   if (fd.getName () == null)
 96  
   {
 97  0
    errors.add ("name cannot be null");
 98  
   }
 99  0
   else if (fd.getName ().trim ().equals (""))
 100  
   {
 101  0
    errors.add ("name cannot be blank");
 102  
   }
 103  0
   if (fd.getDataType ().equals (DataType.COMPLEX))
 104  
   {
 105  0
    errorIfNotNull (errors, fd, "exclusiveMin", fd.getExclusiveMin ());
 106  0
    errorIfNotNull (errors, fd, "inclusiveMax", fd.getInclusiveMax ());
 107  0
    errorIfNotNull (errors, fd, "max length", fd.getMaxLength ());
 108  0
    errorIfNotNull (errors, fd, "min length", fd.getMinLength ());
 109  0
    errorIfNotNull (errors, fd, "valid chars", fd.getValidChars ());
 110  0
    errorIfNotNull (errors, fd, "lookup", fd.getLookupDefinition ());
 111  0
    if (fd.getDataObjectStructure () == null)
 112  
    {
 113  0
     errors.add (
 114  
       "field " + fd.getName ()
 115  
       + " does not have an object structure definition but it required on a complex type");
 116  
    }
 117  
    else
 118  
    {
 119  0
     if (this.processSubStructures)
 120  
     {
 121  0
      if (alreadyValidated.add (fd.getDataObjectStructure ()))
 122  
      {
 123  0
       errors.addAll (new DictionaryValidator (fd.getDataObjectStructure (),
 124  
                                               alreadyValidated,
 125  
                                               processSubStructures).validate ());
 126  
      }
 127  
     }
 128  
    }
 129  
   }
 130  0
   validateConversion (errors, fd.getName (), "defaultValue", fd.getDataType (), fd.getDefaultValue ());
 131  0
   validateConversion (errors, fd.getName (), "exclusiveMin", fd.getDataType (), fd.getExclusiveMin ());
 132  0
   validateConversion (errors, fd.getName (), "inclusiveMax", fd.getDataType (), fd.getInclusiveMax ());
 133  
   //TODO: Cross compare to make sure min is not greater than max and that default value is valid itself
 134  0
   if (fd.getMaxLength () != null)
 135  
   {
 136  
    try
 137  
    {
 138  0
     Integer.parseInt (fd.getMaxLength ());
 139  
    }
 140  0
    catch (NumberFormatException ex)
 141  
    {
 142  0
     errors.add (
 143  
       "field " + fd.getName ()
 144  
       + " has a maxlength that is not an integer");
 145  0
    }
 146  
   }
 147  
 
 148  0
   if (fd.getLookupDefinition () != null)
 149  
   {
 150  0
    errors.addAll (validateLookup (fd, fd.getLookupDefinition ()));
 151  
   }
 152  0
   if (fd.getCaseConstraint () != null)
 153  
   {
 154  0
    errors.addAll (validateCase (fd, fd.getCaseConstraint ()));
 155  
   }
 156  0
   if (fd.getValidChars () != null)
 157  
   {
 158  0
    errors.addAll (validateValidChars (fd, fd.getValidChars ()));
 159  
   }
 160  0
   return errors;
 161  
  }
 162  
 
 163  
  private void errorIfNotNull (List<String> errors, FieldDefinition fd,
 164  
                               String validation,
 165  
                               Object value)
 166  
  {
 167  0
   if (value != null)
 168  
   {
 169  0
    errors.add ("field " + fd.getName () + " has a " + validation
 170  
                + " but it cannot be specified on a complex type");
 171  
   }
 172  0
  }
 173  
 
 174  
  private Object validateConversion (List<String> errors, String fieldName,
 175  
                                     String propertyName, DataType dataType,
 176  
                                     Object value)
 177  
  {
 178  0
   if (value == null)
 179  
   {
 180  0
    return null;
 181  
   }
 182  0
   switch (dataType)
 183  
   {
 184  
    case STRING:
 185  0
     return value.toString ().trim ();
 186  
 //    case DATE, TRUNCATED_DATE, BOOLEAN, INTEGER, FLOAT, DOUBLE, LONG, COMPLEX
 187  
    case LONG:
 188  
     try
 189  
     {
 190  0
      return ValidatorUtils.getLong (value);
 191  
     }
 192  0
     catch (NumberFormatException ex)
 193  
     {
 194  0
      errors.add (
 195  
        "field " + fieldName
 196  
        + " has a " + propertyName
 197  
        + " that cannot be converted into a long integer");
 198  
     }
 199  0
     return null;
 200  
    case INTEGER:
 201  
     try
 202  
     {
 203  0
      return ValidatorUtils.getInteger (value);
 204  
     }
 205  0
     catch (NumberFormatException ex)
 206  
     {
 207  0
      errors.add (
 208  
        "field " + fieldName
 209  
        + " has a " + propertyName + " that cannot be converted into an integer");
 210  
     }
 211  0
     return null;
 212  
    case FLOAT:
 213  
     try
 214  
     {
 215  0
      return ValidatorUtils.getFloat (value);
 216  
     }
 217  0
     catch (NumberFormatException ex)
 218  
     {
 219  0
      errors.add (
 220  
        "field " + fieldName
 221  
        + " has a " + propertyName
 222  
        + " that cannot be converted into a floating point value");
 223  
     }
 224  0
     return null;
 225  
    case DOUBLE:
 226  
     try
 227  
     {
 228  0
      return ValidatorUtils.getFloat (value);
 229  
     }
 230  0
     catch (NumberFormatException ex)
 231  
     {
 232  0
      errors.add (
 233  
        "field " + fieldName
 234  
        + " has a " + propertyName
 235  
        + " that cannot be converted into a double sized floating point value");
 236  
     }
 237  0
     return null;
 238  
    case BOOLEAN:
 239  0
     if (value instanceof Boolean)
 240  
     {
 241  0
      return ((Boolean) value).booleanValue ();
 242  
     }
 243  0
     if (value instanceof String)
 244  
     {
 245  0
      if (((String) value).trim ().equalsIgnoreCase ("true"))
 246  
      {
 247  0
       return true;
 248  
      }
 249  0
      if (((String) value).trim ().equalsIgnoreCase ("false"))
 250  
      {
 251  0
       return true;
 252  
      }
 253  
     }
 254  0
     errors.add (
 255  
       "field " + fieldName
 256  
       + " has a " + propertyName
 257  
       + " that cannot be converted into a boolean true/false");
 258  0
     return null;
 259  
    case DATE:
 260  
    case TRUNCATED_DATE:
 261  0
     if (value instanceof Date)
 262  
     {
 263  0
      return (Date) value;
 264  
     }
 265  
     try
 266  
     {
 267  
      // TODO: make the date parser configurable like the validator is
 268  0
      return new ServerDateParser ().parseDate (value.toString ());
 269  
     }
 270  0
     catch (Exception e)
 271  
     {
 272  0
      errors.add (
 273  
        "field " + fieldName
 274  
        + " has a " + propertyName
 275  
        + " that cannot be converted into a date");
 276  
     }
 277  0
     return null;
 278  
    default:
 279  0
      errors.add (
 280  
        "field " + fieldName
 281  
        + " has a " + propertyName
 282  
        + " that cannot be converted into an unknown/unhandled data type");
 283  0
     return null;
 284  
   }
 285  
  }
 286  
 
 287  
  private List<String> validateValidChars (FieldDefinition fd,
 288  
                                           ValidCharsConstraint vc)
 289  
  {
 290  0
   List<String> errors = new ArrayList ();
 291  0
   String validChars = vc.getValue ();
 292  0
   int typIdx = validChars.indexOf (":");
 293  0
   String processorType = "regex";
 294  0
   if (-1 == typIdx)
 295  
   {
 296  0
    validChars = "[" + validChars + "]*";
 297  
   }
 298  
   else
 299  
   {
 300  0
    processorType = validChars.substring (0, typIdx);
 301  0
    validChars = validChars.substring (typIdx + 1);
 302  
   }
 303  0
   if ( ! processorType.equalsIgnoreCase ("regex"))
 304  
   {
 305  0
    errors.add (
 306  
      "field " + fd.getName ()
 307  
      + " has an invalid valid chars processor type: a simple list of characters or a regex: is supported");
 308  0
    return errors;
 309  
   }
 310  
   try
 311  
   {
 312  0
    Pattern pattern = Pattern.compile (validChars);
 313  
   }
 314  0
   catch (PatternSyntaxException ex)
 315  
   {
 316  0
    errors.add ("field " + fd.getName ()
 317  
                + " has in invalid character pattern for a regular expression: "
 318  
                + validChars);
 319  0
   }
 320  0
   return errors;
 321  
  }
 322  
 
 323  
  private List<String> validateLookup (FieldDefinition fd, LookupConstraint lc)
 324  
  {
 325  0
   List<String> errors = new ArrayList ();
 326  0
   if (lc.getParams () == null)
 327  
   {
 328  0
    errors.add ("field " + fd.getName () + " has a lookup with null parameters");
 329  
   }
 330  
   //TODO: more validation
 331  0
   return errors;
 332  
  }
 333  
  public static final String GREATER_THAN_EQUAL = "greater_than_equal";
 334  
  public static final String LESS_THAN_EQUAL = "less_than_equal";
 335  
  public static final String GREATER_THAN = "greater_than";
 336  
  public static final String LESS_THAN = "less_than";
 337  
  public static final String EQUALS = "equals";
 338  
  public static final String NOT_EQUAL = "not_equal";
 339  0
  private static final String[] VALID_OPERATORS =
 340  
  {
 341  
   NOT_EQUAL, EQUALS, GREATER_THAN_EQUAL, LESS_THAN_EQUAL, GREATER_THAN, LESS_THAN
 342  
  };
 343  
 
 344  
  private List<String> validateCase (FieldDefinition fd, CaseConstraint cc)
 345  
  {
 346  0
   List<String> errors = new ArrayList ();
 347  0
   if (cc.getOperator () == null)
 348  
   {
 349  0
    errors.add ("field " + fd.getName ()
 350  
                + " has a case constraint with no operator");
 351  
   }
 352  
   else
 353  
   {
 354  0
    boolean found = false;
 355  0
    for (int i = 0; i < VALID_OPERATORS.length; i ++)
 356  
    {
 357  0
     if (VALID_OPERATORS[i].equalsIgnoreCase (cc.getOperator ()))
 358  
     {
 359  0
      found = true;
 360  0
      break;
 361  
     }
 362  
    }
 363  0
    if ( ! found)
 364  
    {
 365  0
     errors.add ("field " + fd.getName ()
 366  
                 + " has a case constraint with an unknown operator "
 367  
                 + cc.getOperator ());
 368  
    }
 369  
   }
 370  0
   if (cc.getFieldPath () == null)
 371  
   {
 372  0
    errors.add (
 373  
      "field " + fd.getName ()
 374  
      + " has a case constraint with a null for the field to use for the comparison");
 375  
   }
 376  0
   else if (cc.getFieldPath ().trim ().equals (""))
 377  
   {
 378  0
    errors.add (
 379  
      "field " + fd.getName ()
 380  
      + " has a case constraint with blanks for the field to use for the comparison");
 381  
   }
 382  0
   if (cc.getWhenConstraint () == null)
 383  
   {
 384  0
    errors.add ("field " + fd.getName ()
 385  
                + " has a case constraint but null when statements");
 386  0
    return errors;
 387  
   }
 388  0
   if (cc.getWhenConstraint ().size () == 0)
 389  
   {
 390  0
    errors.add ("field " + fd.getName ()
 391  
                + " has a case constraint but has no when statements");
 392  
   }
 393  0
   for (WhenConstraint wc : cc.getWhenConstraint ())
 394  
   {
 395  0
    if (wc.getConstraint () == null)
 396  
    {
 397  0
     errors.add (
 398  
       "field " + fd.getName ()
 399  
       + " has a as case constraint with a when statement that has no overriding constraints specified");
 400  
    }
 401  
   }
 402  
   //TODO: more validation
 403  0
   return errors;
 404  
  }
 405  
 }