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