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