View Javadoc

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