1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.kuali.student.lum.ui;
16
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.kuali.student.r1.common.assembly.data.Data;
22 import org.kuali.student.r1.common.assembly.data.LookupMetadata;
23 import org.kuali.student.r1.common.assembly.data.LookupParamMetadata;
24 import org.kuali.student.r1.common.assembly.data.LookupResultMetadata;
25 import org.kuali.student.r1.common.assembly.data.Metadata;
26 import org.kuali.student.r1.core.personsearch.service.impl.QuickViewByGivenNameSearchTypeCreator;
27 import org.kuali.student.r2.common.search.dto.*;
28 import org.springframework.context.ApplicationContext;
29 import org.springframework.context.support.ClassPathXmlApplicationContext;
30
31 public class MetadataServiceDictionaryValidator {
32
33 private SearchTypeInfo getSearchTypeInfo(String searchType) {
34 return this.getSearchInfoTypeMap().get(searchType);
35 }
36
37 private Map<String, SearchTypeInfo> searchInfoTypeMap = null;
38
39 String[] excludingSearchTypeIDs = { "atp.search.atpSeasonTypes", "atp.search.atpDurationTypes" };
40
41 @SuppressWarnings("unchecked")
42 private Map<String, SearchTypeInfo> getSearchInfoTypeMap() {
43 if (this.searchInfoTypeMap != null) {
44 return this.searchInfoTypeMap;
45 }
46 String[] searchConfigFiles = { "lu", "lo", "lrc", "proposal", "organization", "atp", "em" };
47
48 for (int i = 0; i < searchConfigFiles.length; i++) {
49 System.out.println("loading search configurations for "
50 + searchConfigFiles[i]);
51 ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:" + searchConfigFiles[i] + "-search-config.xml");
52 if (searchInfoTypeMap == null) {
53 searchInfoTypeMap = ac.getBeansOfType(SearchTypeInfo.class);
54 } else {
55 searchInfoTypeMap.putAll(ac.getBeansOfType(SearchTypeInfo.class));
56 }
57 }
58
59 SearchTypeInfo personSearchType = new QuickViewByGivenNameSearchTypeCreator().get();
60 searchInfoTypeMap.put(personSearchType.getKey(), personSearchType);
61
62 return searchInfoTypeMap;
63 }
64
65 public List<String> validateMetadata(Metadata md, String name, String type) {
66 List<String> errors = new ArrayList<String>();
67 if (md.getInitialLookup() != null && md.getInitialLookup().getSearchTypeId() != null) {
68 errors.addAll(validateLookup(md.getInitialLookup(), name, type,
69 "initial"));
70 }
71 if (md.getAdditionalLookups() != null) {
72 for (LookupMetadata lookup : md.getAdditionalLookups()) {
73 errors.addAll(validateLookup(lookup, name, type, "additional"));
74 }
75 }
76 if (md.getDataType().equals(Data.DataType.DATA)) {
77 if (md.getProperties() == null) {
78 errors
79 .add(buildErrorPrefix1(name, type)
80 + " is of type DATA but it has null for it's properties");
81 } else if (md.getProperties().size() == 0) {
82 errors.add(buildErrorPrefix1(name, type)
83 + " is of type DATA but it has no properties");
84 }
85 }
86
87 if (md.getProperties() != null && md.getProperties().size() != 0) {
88 if (!(md.getDataType().equals(Data.DataType.DATA) || md
89 .getDataType().equals(Data.DataType.LIST))) {
90 errors
91 .add(buildErrorPrefix1(name, type)
92 + " is NOT of type DATA or LIST but it does have properties");
93 }
94 for (String key : md.getProperties().keySet()) {
95 Metadata childMd = md.getProperties().get(key);
96 errors.addAll(this.validateMetadata(childMd, name + "." + key,
97 type));
98 }
99 }
100 return errors;
101 }
102
103 private List<String> validateLookup(LookupMetadata lookup, String name,
104 String type, String lookupType) {
105 System.out.println("Validating lookup " + name + "(" + type + ") "
106 + lookupType);
107 List<String> errors = new ArrayList();
108
109 if (ignoreExcludedSearchIDs(lookup.getSearchTypeId())) {
110 return errors;
111 }
112
113 SearchTypeInfo st = getSearchTypeInfo(lookup.getSearchTypeId());
114 if (st == null) {
115 errors.add(buildErrorPrefix3(lookup, name, type, lookupType)
116 + " that has an underlying search type "
117 + lookup.getSearchTypeId() + " that does not exist");
118 return errors;
119 }
120 if (lookup.getResultDisplayKey() != null) {
121 String key = lookup.getResultDisplayKey().trim();
122 if (!key.equals("")) {
123 ResultColumnInfo rc = findResultColumn(st, key);
124 if (rc == null) {
125 errors
126 .add(buildErrorPrefix3(lookup, name, type,
127 lookupType)
128 + " that has a result display column "
129 + key
130 + " that does not exist in the underlying search "
131 + lookup.getSearchTypeId());
132 }
133 }
134 }
135 if (lookup.getResultReturnKey() != null) {
136 String key = lookup.getResultReturnKey().trim();
137 if ((!key.equals(""))) {
138 ResultColumnInfo rc = findResultColumn(st, key);
139 if (rc == null) {
140 errors
141 .add(buildErrorPrefix3(lookup, name, type,
142 lookupType)
143 + " that has a result return key "
144 + key
145 + " that does not exist in the underlying search "
146 + lookup.getSearchTypeId());
147 }
148 }
149 }
150 if (lookup.getResultSortKey() != null) {
151 String key = lookup.getResultSortKey().trim();
152 if (!key.equals("")) {
153 ResultColumnInfo rc = findResultColumn(st, key);
154 if (rc == null) {
155 errors
156 .add(buildErrorPrefix3(lookup, name, type,
157 lookupType)
158 + " that has a result sort key "
159 + key
160 + " that does not exist in the underlying search "
161 + lookup.getSearchTypeId());
162 }
163 }
164 }
165
166 for (LookupParamMetadata param : lookup.getParams()) {
167 QueryParamInfo qp = findQueryParam(st, param.getKey());
168 if (qp == null && !(st instanceof CrossSearchTypeInfo)) {
169
170
171 errors.add(buildErrorPrefix3(lookup, name, type, lookupType)
172 + " that has a parameter " + param.getKey()
173 + " that does not exist in the underlying search "
174 + lookup.getSearchTypeId());
175 continue;
176 }
177
178 if (qp != null && !dataTypeMatches(qp.getFieldDescriptor().getDataType(), param.getDataType())) {
179
180 errors.add(buildErrorPrefix3(lookup, name, type, lookupType)
181 + " that has a parameter "
182 + param.getKey()
183 + " who's datatype does not match the underlying parameter "
184 + qp.getFieldDescriptor().getDataType()
185 + " vs. " + param.getDataType());
186 continue;
187 }
188 }
189
190 for (LookupResultMetadata result : lookup.getResults()) {
191 ResultColumnInfo rc = findResultColumn(st, result.getKey());
192 if (rc == null) {
193 errors.add(buildErrorPrefix3(lookup, name, type, lookupType)
194 + " that has a result column " + result.getKey()
195 + " that does not exist in the underlying search "
196 + lookup.getSearchTypeId());
197 continue;
198 }
199 if (!dataTypeMatches(rc.getDataType(), result.getDataType())) {
200 errors
201 .add(buildErrorPrefix3(lookup, name, type, lookupType)
202 + " that has a result column "
203 + result.getKey()
204 + " who's datatype does not match the underlying result column "
205 + rc.getDataType() + " vs. "
206 + result.getDataType());
207 continue;
208 }
209 }
210 return errors;
211 }
212
213 private boolean dataTypeMatches(String qp, Data.DataType dt) {
214 if (dt == null) {
215 dt = Data.DataType.STRING;
216 }
217 if (qp == null) {
218 qp = "string";
219 }
220 switch (dt) {
221 case STRING:
222 if (qp.equalsIgnoreCase("string")) {
223 return true;
224 }
225 if (qp == null) {
226 return true;
227 }
228 return false;
229 case INTEGER:
230 if (qp.equalsIgnoreCase("int")) {
231 return true;
232 }
233 return false;
234 case LONG:
235 case FLOAT:
236 case DOUBLE:
237 case BOOLEAN:
238 if (qp.equalsIgnoreCase("boolean")) {
239 return true;
240 }
241 return false;
242 case DATE:
243 case TRUNCATED_DATE:
244 if (qp.equalsIgnoreCase("date")) {
245 return true;
246 }
247 if (qp.equalsIgnoreCase("dateTime")) {
248 return true;
249 }
250 return false;
251 case DATA:
252 if (qp.equalsIgnoreCase("complex")) {
253 return true;
254 }
255 return false;
256 case LIST:
257 return true;
258 }
259 return true;
260 }
261
262 private ResultColumnInfo findResultColumn(SearchTypeInfo st, String paramKey) {
263 for (ResultColumnInfo rc : st.getSearchResultTypeInfo()
264 .getResultColumns()) {
265 if (rc.getKey().equals(paramKey)) {
266 return rc;
267 }
268 }
269 if (st instanceof CrossSearchTypeInfo) {
270
271
272 CrossSearchTypeInfo cst = (CrossSearchTypeInfo) st;
273 if (cst.getJoinResultMappings() != null) {
274 for (JoinResultMappingInfo jrm : cst.getJoinResultMappings()) {
275 if (jrm.getResultParam().equalsIgnoreCase(paramKey)) {
276 for (SubSearchInfo ss : cst.getSubSearches()) {
277 if (ss.getKey().equalsIgnoreCase(
278 jrm.getSubSearchKey())) {
279 SearchTypeInfo subSearchType = this
280 .getSearchTypeInfo(ss.getSearchkey());
281 if (subSearchType == null) {
282 return null;
283 }
284 ResultColumnInfo rc = findResultColumn(
285 subSearchType, jrm
286 .getSubSearchResultParam());
287 if (rc == null) {
288 throw new RuntimeException(
289 "Cross-Search "
290 + st.getKey()
291 + " is not configured properly "
292 + jrm
293 .getSubSearchResultParam()
294 + " is not defined as a result in the subSearchTyp e"
295 + ss.getSearchkey());
296 }
297 }
298 }
299 }
300 }
301 }
302 }
303 return null;
304 }
305
306 private QueryParamInfo findQueryParam(SearchTypeInfo st, String paramKey) {
307 for (QueryParamInfo qp : st.getSearchCriteriaTypeInfo()
308 .getQueryParams()) {
309 if (qp.getKey().equals(paramKey)) {
310 return qp;
311 }
312 }
313 return null;
314 }
315
316 private String buildErrorPrefix3(LookupMetadata lookup, String name,
317 String type, String lookupType) {
318 return buildErrorPrefix2(name, type, lookupType) + ": "
319 + lookup.getId();
320 }
321
322 private String buildErrorPrefix2(String name, String type, String lookupType) {
323 String msg = buildErrorPrefix1(name, type);
324 msg += " has an " + lookupType + " lookup ";
325 return msg;
326 }
327
328 private String buildErrorPrefix1(String name, String type) {
329 String msg = name;
330 if (type != null) {
331 msg += " with type " + type;
332 }
333
334
335 return msg;
336 }
337
338 private boolean ignoreExcludedSearchIDs(String searchID) {
339 for (int i = 0; i < excludingSearchTypeIDs.length; i++) {
340 String item = excludingSearchTypeIDs[i];
341 if (item.equals(searchID)) {
342 return true;
343 }
344 }
345 return false;
346
347 }
348 }