1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.datetime.DateTimeService;
26 import org.kuali.rice.core.api.uif.DataType;
27 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
28 import org.kuali.rice.krad.datadictionary.DataObjectEntry;
29 import org.kuali.rice.krad.datadictionary.validation.ValidationUtils;
30 import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
31 import org.kuali.rice.krad.datadictionary.validation.constraint.LookupConstraint;
32 import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
33 import org.kuali.rice.krad.datadictionary.validation.constraint.WhenConstraint;
34
35 public class DictionaryValidator {
36
37 private DateTimeService dateTimeService;
38 private DataObjectEntry doe;
39 private Set<DataObjectEntry> alreadyValidated;
40
41 public DictionaryValidator(DataObjectEntry doe,
42 Set<DataObjectEntry> alreadyValidated) {
43 this.doe = doe;
44 this.alreadyValidated = alreadyValidated;
45 }
46
47 public DateTimeService getDateTimeService() {
48 return dateTimeService;
49 }
50
51 public void setDateTimeService(DateTimeService dateTimeService) {
52 this.dateTimeService = dateTimeService;
53 }
54
55 public List<String> validate() {
56 List<String> errors = new ArrayList();
57 if (doe.getFullClassName() == null) {
58 errors.add("The class name cannot be be left null");
59 }
60 if (doe.getEntryClass() == null) {
61 errors.add("The entry class should not be left null");
62 }
63 if (!doe.getEntryClass().getName().equals(doe.getFullClassName())) {
64 errors.add("The entry class should match the full class name");
65 }
66
67
68
69
70
71
72 if (doe.getAttributes() == null) {
73 errors.add("getAttribues () is null");
74 return errors;
75 }
76 if (doe.getCollections() == null) {
77 errors.add("getCollections () is null");
78 return errors;
79 }
80 if (doe.getComplexAttributes() == null) {
81 errors.add("getComplexAttributes ()");
82 return errors;
83 }
84 if (doe.getCollections().isEmpty()
85 && doe.getComplexAttributes().isEmpty()
86 && doe.getAttributes().isEmpty()) {
87 errors.add("No fields of any kind defined for this complex object structure");
88 return errors;
89 }
90
91 Set<String> fieldNames = new HashSet();
92
93 for (AttributeDefinition ad : doe.getAttributes()) {
94 if (ad.getName() != null) {
95 if (!fieldNames.add(ad.getName())) {
96 errors.add(ad.getName() + " is defined more than once");
97 }
98 }
99 errors.addAll(validateAttributeDefinition(ad));
100 }
101
102 doe.completeValidation();
103
104 return errors;
105 }
106
107 private List<String> validateAttributeDefinition(AttributeDefinition ad) {
108 List<String> errors = new ArrayList();
109 if (ad.getName() == null) {
110 errors.add("name cannot be null");
111 } else if (ad.getName().trim().equals("")) {
112 errors.add("name cannot be blank");
113 } else if (ad.getDataType() == null) {
114 errors.add(ad.getName() + " has a null data type");
115
116
117
118
119
120
121
122 }
123
124 validateConversion(errors, ad.getName(), "exclusiveMin", ad.getDataType(), ad.getExclusiveMin());
125 validateConversion(errors, ad.getName(), "inclusiveMax", ad.getDataType(), ad.getInclusiveMax());
126
127
128 if (ad.getLookupDefinition() != null) {
129 errors.addAll(validateLookup(ad, ad.getLookupDefinition()));
130 }
131 if (ad.getCaseConstraint() != null) {
132 errors.addAll(validateCase(ad, ad.getCaseConstraint()));
133 }
134 if (ad.getValidCharactersConstraint() != null) {
135 errors.addAll(validateValidChars(ad, ad.getValidCharactersConstraint()));
136 }
137 return errors;
138 }
139
140 private void errorIfNotNull(List<String> errors, AttributeDefinition fd,
141 String validation,
142 Object value) {
143 if (value != null) {
144 errors.add("field " + fd.getName() + " has a " + validation
145 + " but it cannot be specified on a complex type");
146 }
147 }
148
149 private Object validateConversion(List<String> errors, String fieldName,
150 String propertyName, DataType dataType,
151 Object value) {
152 if (value == null) {
153 return null;
154 }
155 switch (dataType) {
156 case STRING:
157 return value.toString().trim();
158
159 case LONG:
160 try {
161 return ValidationUtils.getLong(value);
162 } catch (NumberFormatException ex) {
163 errors.add(
164 "field " + fieldName
165 + " has a " + propertyName
166 + " that cannot be converted into a long integer");
167 }
168 return null;
169 case INTEGER:
170 try {
171 return ValidationUtils.getInteger(value);
172 } catch (NumberFormatException ex) {
173 errors.add(
174 "field " + fieldName
175 + " has a " + propertyName + " that cannot be converted into an integer");
176 }
177 return null;
178 case FLOAT:
179 try {
180 return ValidationUtils.getFloat(value);
181 } catch (NumberFormatException ex) {
182 errors.add(
183 "field " + fieldName
184 + " has a " + propertyName
185 + " that cannot be converted into a floating point value");
186 }
187 return null;
188 case DOUBLE:
189 try {
190 return ValidationUtils.getFloat(value);
191 } catch (NumberFormatException ex) {
192 errors.add(
193 "field " + fieldName
194 + " has a " + propertyName
195 + " that cannot be converted into a double sized floating point value");
196 }
197 return null;
198 case BOOLEAN:
199 if (value instanceof Boolean) {
200 return ((Boolean) value).booleanValue();
201 }
202 if (value instanceof String) {
203 if (((String) value).trim().equalsIgnoreCase("true")) {
204 return true;
205 }
206 if (((String) value).trim().equalsIgnoreCase("false")) {
207 return true;
208 }
209 }
210 errors.add(
211 "field " + fieldName
212 + " has a " + propertyName
213 + " that cannot be converted into a boolean true/false");
214 return null;
215 case DATE:
216 case TRUNCATED_DATE:
217 if (value instanceof Date) {
218 return (Date) value;
219 }
220 try {
221
222 return ValidationUtils.getDate(value, dateTimeService);
223 } catch (Exception e) {
224 errors.add(
225 "field " + fieldName
226 + " has a " + propertyName
227 + " that cannot be converted into a date");
228 }
229 return null;
230 default:
231 errors.add(
232 "field " + fieldName
233 + " has a " + propertyName
234 + " that cannot be converted into an unknown/unhandled data type");
235 return null;
236 }
237 }
238
239 private List<String> validateValidChars(AttributeDefinition fd,
240 ValidCharactersConstraint vc) {
241 List<String> errors = new ArrayList();
242 String validChars = vc.getValue();
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 try {
260 Pattern pattern = Pattern.compile(validChars);
261 } catch (PatternSyntaxException ex) {
262 errors.add("field " + fd.getName()
263 + " has in invalid character pattern for a regular expression: "
264 + validChars);
265 }
266 return errors;
267 }
268
269 private List<String> validateLookup(AttributeDefinition fd, LookupConstraint lc) {
270 List<String> errors = new ArrayList();
271 if (lc.getParams() == null) {
272 errors.add("field " + fd.getName() + " has a lookup with null parameters");
273 }
274
275 return errors;
276 }
277 public static final String GREATER_THAN_EQUAL = "greater_than_equal";
278 public static final String LESS_THAN_EQUAL = "less_than_equal";
279 public static final String GREATER_THAN = "greater_than";
280 public static final String LESS_THAN = "less_than";
281 public static final String EQUALS = "equals";
282 public static final String NOT_EQUAL = "not_equal";
283 private static final String[] VALID_OPERATORS = {
284 NOT_EQUAL, EQUALS, GREATER_THAN_EQUAL, LESS_THAN_EQUAL, GREATER_THAN, LESS_THAN
285 };
286
287 private List<String> validateCase(AttributeDefinition fd, CaseConstraint cc) {
288 List<String> errors = new ArrayList();
289 if (cc.getOperator() == null) {
290 errors.add("field " + fd.getName()
291 + " has a case constraint with no operator");
292 } else {
293 boolean found = false;
294 for (int i = 0; i < VALID_OPERATORS.length; i++) {
295 if (VALID_OPERATORS[i].equalsIgnoreCase(cc.getOperator())) {
296 found = true;
297 break;
298 }
299 }
300 if (!found) {
301 errors.add("field " + fd.getName()
302 + " has a case constraint with an unknown operator "
303 + cc.getOperator());
304 }
305 }
306 if (cc.getPropertyName() == null) {
307 errors.add(
308 "field " + fd.getName()
309 + " has a case constraint with a null for the field to use for the comparison");
310 } else if (cc.getPropertyName().trim().equals("")) {
311 errors.add(
312 "field " + fd.getName()
313 + " has a case constraint with blanks for the field to use for the comparison");
314 }
315 if (cc.getWhenConstraint() == null) {
316 errors.add("field " + fd.getName()
317 + " has a case constraint but null when statements");
318 return errors;
319 }
320 if (cc.getWhenConstraint().size() == 0) {
321 errors.add("field " + fd.getName()
322 + " has a case constraint but has no when statements");
323 }
324 for (WhenConstraint wc : cc.getWhenConstraint()) {
325 if (wc.getConstraint() == null) {
326 errors.add(
327 "field " + fd.getName()
328 + " has a as case constraint with a when statement that has no overriding constraints specified");
329 }
330 }
331
332 return errors;
333 }
334 }