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
52
53
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
82
83
84
85
86
87
88
89
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
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
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
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
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
404 return errors;
405 }
406 }