View Javadoc

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