Clover Coverage Report - KS Common Impl 1.2.1-SNAPSHOT
Coverage timestamp: Wed Nov 2 2011 05:12:50 EST
../../../../../../img/srcFileCovDistChart7.png 19% of files have more coverage
321   789   147   9.73
158   519   0.46   16.5
33     4.45  
2    
 
  MetadataServiceImpl       Line # 52 310 0% 143 151 69.5% 0.6949495
  MetadataServiceImpl.RecursionCounter       Line # 59 11 0% 4 3 82.4% 0.8235294
 
  (4)
 
1    /**
2    * Copyright 2010 The Kuali Foundation Licensed under the Educational Community License, Version 2.0 (the "License"); you may
3    * not use this file except in compliance with the License. You may obtain a copy of the License at
4    * http://www.osedu.org/licenses/ECL-2.0 Unless required by applicable law or agreed to in writing, software distributed
5    * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
6    * implied. See the License for the specific language governing permissions and limitations under the License.
7    */
8   
9    package org.kuali.student.common.assembly.dictionary;
10   
11    import java.text.DateFormat;
12    import java.text.ParseException;
13    import java.text.SimpleDateFormat;
14    import java.util.ArrayList;
15    import java.util.HashMap;
16    import java.util.List;
17    import java.util.Map;
18   
19    import org.apache.log4j.Logger;
20    import org.kuali.student.common.assembly.data.ConstraintMetadata;
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.Metadata;
25    import org.kuali.student.common.assembly.data.UILookupConfig;
26    import org.kuali.student.common.assembly.data.UILookupData;
27    import org.kuali.student.common.assembly.data.Data.DataType;
28    import org.kuali.student.common.assembly.data.Data.Value;
29    import org.kuali.student.common.assembly.data.LookupMetadata.WidgetOption;
30    import org.kuali.student.common.assembly.data.Metadata.WriteAccess;
31    import org.kuali.student.common.dictionary.dto.CaseConstraint;
32    import org.kuali.student.common.dictionary.dto.CommonLookupParam;
33    import org.kuali.student.common.dictionary.dto.Constraint;
34    import org.kuali.student.common.dictionary.dto.FieldDefinition;
35    import org.kuali.student.common.dictionary.dto.ObjectStructureDefinition;
36    import org.kuali.student.common.dictionary.dto.WhenConstraint;
37    import org.kuali.student.common.dictionary.service.DictionaryService;
38    import org.kuali.student.common.dto.DtoConstants;
39    import org.kuali.student.common.dto.DtoConstants.DtoState;
40    import org.kuali.student.common.validation.dto.ValidationResultInfo.ErrorLevel;
41    import org.springframework.beans.BeanUtils;
42    import org.springframework.context.ApplicationContext;
43    import org.springframework.context.support.ClassPathXmlApplicationContext;
44   
45    import edu.emory.mathcs.backport.java.util.Arrays;
46   
47    /**
48    * This class provides metadata lookup for service dto objects.
49    *
50    * @author Kuali Student Team
51    */
 
52    public class MetadataServiceImpl {
53    final Logger LOG = Logger.getLogger(MetadataServiceImpl.class);
54   
55    private Map<String, DictionaryService> dictionaryServiceMap = new HashMap<String, DictionaryService>();
56    private List<UILookupConfig> lookupObjectStructures;
57    private String uiLookupContext;
58   
 
59    private static class RecursionCounter {
60    public static final int MAX_DEPTH = 4;
61   
62    private Map<String, Integer> recursions = new HashMap<String, Integer>();
63   
 
64  18 toggle public int increment(String objectName) {
65  18 Integer hits = recursions.get(objectName);
66   
67  18 if (hits == null) {
68  18 hits = new Integer(1);
69    } else {
70  0 hits++;
71    }
72  18 recursions.put(objectName, hits);
73  18 return hits;
74    }
75   
 
76  18 toggle public int decrement(String objectName) {
77  18 Integer hits = recursions.get(objectName);
78  18 if (hits >= 1) {
79  18 hits--;
80    }
81   
82  18 recursions.put(objectName, hits);
83  18 return hits;
84    }
85    }
86   
 
87  0 toggle public MetadataServiceImpl() {
88  0 super();
89    }
90   
91    /**
92    * Create a metadata service initializing it with all known dictionary services
93    *
94    * @param dictionaryServices
95    */
 
96  4 toggle public MetadataServiceImpl(DictionaryService... dictionaryServices) {
97  4 setDictionaryServices(Arrays.asList(dictionaryServices));
98    }
99   
 
100  4 toggle public synchronized void setDictionaryServices(List<DictionaryService> dictionaryServices) {
101  4 if (dictionaryServices != null) {
102  4 this.dictionaryServiceMap.clear();
103  4 for (DictionaryService d : dictionaryServices) {
104  4 List<String> objectTypes = d.getObjectTypes();
105  4 for (String objectType : objectTypes) {
106  12 dictionaryServiceMap.put(objectType, d);
107    }
108    }
109    }
110    }
111   
112   
113    /**
114    * This method gets the metadata for the given object key, type, state and nextState
115    *
116    * @param objectKey
117    * @param type The type of the object (value can be null)
118    * @param state The state for which to retrieve object constraints (value can be null)
119    * @param nextState The state to to check requiredForNextState indicators (value can be null)
120    * @param documentTypeName The type of the document (value can be null)
121    * @return
122    */
 
123  15 toggle public Metadata getMetadata(String objectKey, String type, String state, String nextState, String documentTypeName) {
124  15 nextState = (nextState == null || nextState.length() <=0 ? DtoState.getNextStateAsString(state):nextState);
125  15 state = state==null?null:state.toUpperCase();
126  15 nextState = nextState==null?null:nextState.toUpperCase();
127    //FIXME/TODO: documentTypeName is only passed here, because it is eventually used in ProgramMetadataServiceImpl's getConstraints() method
128  15 return getMetadataFromDictionaryService(objectKey, type, state, nextState, null, documentTypeName);
129    }
130   
131    /**
132    * This method gets the metadata for the given object id key, workflowNode and documentTypeName
133    *
134    * @return
135    */
 
136  3 toggle public Metadata getMetadataByWorkflowNode(String objectKey, String workflowNode, String documentTypeName) {
137    //FIXME/TODO: documentTypeName is only passed here, because it is eventually used in ProgramMetadataServiceImpl's getConstraints() method
138  3 return getMetadataFromDictionaryService(objectKey, null, DtoState.DRAFT.toString(), null, workflowNode, documentTypeName);
139    }
140   
141    /**
142    * This method gets the metadata for the given object key, type and state
143    *
144    * @param objectKey
145    * @param type The type of the object (value can be null)
146    * @param state The state for which to retrieve object constraints (value can be null)
147    * @return
148    */
 
149  15 toggle public Metadata getMetadata(String objectKey, String type, String state) {
150  15 state = (state == null ? DtoState.DRAFT.toString():state);
151  15 String nextState = DtoState.getNextStateAsString(state);
152   
153  15 return getMetadata(objectKey, type, state.toUpperCase(), nextState, null);
154    }
155   
156   
157    /**
158    * This method gets the metadata for the given object key and state
159    *
160    * @param objectKey
161    * @param type The type of the object (value can be null)
162    */
 
163  1 toggle public Metadata getMetadata(String objectKey, String state) {
164  1 return getMetadata(objectKey, null, state);
165    }
166   
167    /**
168    * This method gets the metadata for the given object key for state DRAFT.
169    *
170    * @see MetadataServiceImpl#getMetadata(String, String)
171    * @param objectKey
172    * @return
173    */
 
174  9 toggle public Metadata getMetadata(String objectKey) {
175  9 return getMetadata(objectKey, null, null);
176    }
177   
178    /**
179    * This invokes the appropriate dictionary service to get the object structure and then converts it to a metadata
180    * structure.
181    *
182    * @param objectKey
183    * @param type
184    * @param state
185    * @param documentTypeName
186    * @return
187    */
 
188  18 toggle protected Metadata getMetadataFromDictionaryService(String objectKey, String type, String state, String nextState, String workflowNode, String documentTypeName) {
189   
190  18 Metadata metadata = new Metadata();
191   
192  18 ObjectStructureDefinition objectStructure = getObjectStructure(objectKey);
193    //FIXME/TODO: documentTypeName is only passed here, because it is eventually used in ProgramMetadataServiceImpl's getConstraints() method
194  18 metadata.setProperties(getProperties(objectStructure, type, state, nextState, workflowNode, new RecursionCounter(), documentTypeName));
195   
196  18 metadata.setWriteAccess(WriteAccess.ALWAYS);
197  18 metadata.setDataType(DataType.DATA);
198  18 addLookupstoMetadata(objectKey, metadata, type);
199  18 return metadata;
200    }
201   
202    /**
203    * This method is used to convert a list of dictionary fields into metadata properties
204    * @param type
205    * @param state
206    * @param documentTypeName TODO
207    * @param fields
208    *
209    * @return
210    */
 
211  18 toggle private Map<String, Metadata> getProperties(ObjectStructureDefinition objectStructure, String type, String state, String nextState, String workflowNode, RecursionCounter counter, String documentTypeName) {
212  18 String objectName = objectStructure.getName();
213  18 int hits = counter.increment(objectName);
214   
215  18 Map<String, Metadata> properties = null;
216   
217  18 if (hits < RecursionCounter.MAX_DEPTH) {
218  18 properties = new HashMap<String, Metadata>();
219   
220  18 List<FieldDefinition> attributes = objectStructure.getAttributes();
221  18 for (FieldDefinition fd : attributes) {
222   
223  93 Metadata metadata = new Metadata();
224   
225    // Set constraints, authz flags, default value
226  93 metadata.setWriteAccess(WriteAccess.ALWAYS);
227  93 metadata.setDataType(convertDictionaryDataType(fd.getDataType()));
228    //FIXME/TODO: documentTypeName is only passed here, because it is eventually used in ProgramMetadataServiceImpl's getConstraints() method
229  93 metadata.setConstraints(getConstraints(fd, type, state, nextState, workflowNode, documentTypeName));
230  93 metadata.setCanEdit(!fd.isReadOnly());
231  93 metadata.setCanUnmask(!fd.isMask());
232  93 metadata.setCanView(!fd.isHide());
233  93 metadata.setDynamic(fd.isDynamic());
234  93 metadata.setLabelKey(fd.getLabelKey());
235  93 metadata.setDefaultValue(convertDefaultValue(metadata.getDataType(), fd.getDefaultValue()));
236  93 metadata.setDefaultValuePath(fd.getDefaultValuePath());
237   
238  93 if (fd.isPartialMask()){
239  11 metadata.setPartialMaskFormatter(fd.getPartialMaskFormatter());
240    }
241   
242  93 if (fd.isMask()){
243  11 metadata.setMaskFormatter(fd.getMaskFormatter());
244    }
245   
246    // Get properties for nested object structure
247  93 Map<String, Metadata> nestedProperties = null;
248  93 if (fd.getDataType() == org.kuali.student.common.dictionary.dto.DataType.COMPLEX && fd.getDataObjectStructure() != null) {
249  0 nestedProperties = getProperties(fd.getDataObjectStructure(), type, state, nextState, workflowNode, counter, documentTypeName);
250    }
251   
252    // For repeating field, create a LIST with wildcard in metadata structure
253  93 if (isRepeating(fd)) {
254  0 Metadata repeatingMetadata = new Metadata();
255  0 metadata.setDataType(DataType.LIST);
256   
257  0 repeatingMetadata.setWriteAccess(WriteAccess.ALWAYS);
258  0 repeatingMetadata.setOnChangeRefreshMetadata(false);
259  0 repeatingMetadata.setDataType(convertDictionaryDataType(fd.getDataType()));
260   
261  0 if (nestedProperties != null) {
262  0 repeatingMetadata.setProperties(nestedProperties);
263    }
264   
265  0 Map<String, Metadata> repeatingProperty = new HashMap<String, Metadata>();
266  0 repeatingProperty.put("*", repeatingMetadata);
267  0 metadata.setProperties(repeatingProperty);
268  93 } else if (nestedProperties != null) {
269  0 metadata.setProperties(nestedProperties);
270    }
271   
272  93 properties.put(fd.getName(), metadata);
273   
274    }
275    }
276   
277  18 counter.decrement(objectName);
278  18 return properties;
279    }
280   
281    /**
282    * This method determines if a field is repeating
283    *
284    * @param fd
285    * @return
286    */
 
287  93 toggle protected boolean isRepeating(FieldDefinition fd) {
288  93 boolean isRepeating = false;
289  93 try {
290  93 int maxOccurs = Integer.parseInt(fd.getMaxOccurs());
291  28 isRepeating = maxOccurs > 1;
292    } catch (NumberFormatException nfe) {
293  65 isRepeating = FieldDefinition.UNBOUNDED.equals(fd.getMaxOccurs());
294    }
295   
296  93 return isRepeating;
297    }
298   
299    /**
300    * This method gets the object structure for given objectKey from a dictionaryService
301    *
302    * @param objectKey
303    * @return
304    */
 
305  18 toggle protected ObjectStructureDefinition getObjectStructure(String objectKey) {
306  18 DictionaryService dictionaryService = dictionaryServiceMap.get(objectKey);
307   
308  18 if (dictionaryService == null) {
309  0 throw new RuntimeException("Dictionary service not provided for objectKey=[" + objectKey + "].");
310    }
311   
312  18 return dictionaryService.getObjectStructure(objectKey);
313    }
314   
315    //FIXME/TODO: documentTypeName is only passed here, because it is used(overridden) in ProgramMetadataServiceImpl's getConstraints() method
 
316  93 toggle protected List<ConstraintMetadata> getConstraints(FieldDefinition fd, String type, String state, String nextState, String workflowNode, String documentTypeName) {
317  93 List<ConstraintMetadata> constraints = new ArrayList<ConstraintMetadata>();
318   
319  93 ConstraintMetadata constraintMetadata = new ConstraintMetadata();
320   
321  93 updateConstraintMetadata(constraintMetadata, (Constraint) fd, type, state, nextState, workflowNode);
322  93 constraints.add(constraintMetadata);
323   
324  93 return constraints;
325    }
326   
327    /**
328    * This updates the constraintMetadata with defintions from the dictionary constraint field.
329    *
330    * @param constraintMetadata
331    * @param constraint
332    */
 
333  104 toggle protected void updateConstraintMetadata(ConstraintMetadata constraintMetadata, Constraint constraint, String type, String state, String nextState, String workflowNode) {
334    // For now ignoring the serverSide flag and making determination of which constraints
335    // should be passed up to the UI via metadata.
336   
337    // Min Length
338  104 if (constraint.getMinLength() != null) {
339  60 constraintMetadata.setMinLength(constraint.getMinLength());
340    }
341   
342    // Max Length
343  104 try {
344  104 if (constraint.getMaxLength() != null) {
345  60 constraintMetadata.setMaxLength(Integer.parseInt(constraint.getMaxLength()));
346    }
347    // Do we need to add another constraint and label it required if minOccurs = 1
348    } catch (NumberFormatException nfe) {
349    // Ignoring an unbounded length, cannot be handled in metadata structure, maybe change Metadata to string or set
350    // to -1
351  0 constraintMetadata.setMaxLength(9999);
352    }
353   
354    // Min Occurs
355  104 if (constraint.getMinOccurs() != null) {
356  83 constraintMetadata.setMinOccurs(constraint.getMinOccurs());
357    }
358   
359    // Max Occurs
360  104 String maxOccurs = constraint.getMaxOccurs();
361  104 if (maxOccurs != null) {
362  28 try {
363  28 constraintMetadata.setMaxOccurs(Integer.parseInt(maxOccurs));
364  28 if (!FieldDefinition.SINGLE.equals(maxOccurs)) {
365  0 constraintMetadata.setId("repeating");
366    }
367    } catch (NumberFormatException nfe) {
368    // Setting unbounded to a value of 9999, since unbounded not handled by metadata
369  0 if (FieldDefinition.UNBOUNDED.equals(maxOccurs)) {
370  0 constraintMetadata.setId("repeating");
371  0 constraintMetadata.setMaxOccurs(9999);
372    }
373    }
374    }
375   
376    // Min Value
377  104 if (constraint.getExclusiveMin() != null) {
378  22 constraintMetadata.setMinValue(constraint.getExclusiveMin());
379    }
380   
381    // Max Value
382  104 if (constraint.getInclusiveMax() != null) {
383  11 constraintMetadata.setMaxValue(constraint.getInclusiveMax());
384    }
385   
386  104 if (constraint.getValidChars() != null) {
387  25 constraintMetadata.setValidChars(constraint.getValidChars().getValue());
388  25 constraintMetadata.setValidCharsMessageId(constraint.getValidChars().getLabelKey());
389    }
390   
391    // Case constraints
392  104 if (constraint.getCaseConstraint() != null) {
393  35 processCaseConstraint(constraintMetadata, constraint.getCaseConstraint(), type, state, nextState, workflowNode);
394    }
395    }
396   
397    /**
398    * Currently this only handles requiredness indicators for case constraints with the following field paths:
399    *
400    * type, state, and proposal/workflowNode
401    */
 
402  35 toggle protected void processCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String type, String state, String nextState, String workflowNode) {
403  35 String fieldPath = caseConstraint.getFieldPath();
404  35 fieldPath = (fieldPath != null ? fieldPath.toUpperCase() : fieldPath);
405   
406  35 if (workflowNode != null && fieldPath != null && fieldPath.startsWith("PROPOSAL/WORKFLOWNODE")){
407  3 processRequiredByNodeCaseConstraint(constraintMetadata, caseConstraint, workflowNode);
408  32 } else if ("STATE".equals(fieldPath)) {
409  11 processStateCaseConstraint(constraintMetadata, caseConstraint, type, state, nextState, workflowNode);
410  21 } else if ("TYPE".equals(fieldPath)) {
411  0 processTypeCaseConstraint(constraintMetadata, caseConstraint, type, state, nextState, workflowNode);
412    }
413    }
414   
415    /**
416    * Modifies the constraintMetadata to add required to save or required to approve constraints based on the
417    * workflow route node the proposal is currently in.
418    *
419    * @param constraintMetadata The fields constraintMetadata to be modified
420    * @param caseConstraint The caseConstraint defined in dictionary for field
421    * @param workflowNode The current node in workflow process
422    */
 
423  3 toggle private void processRequiredByNodeCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String workflowNode) {
424  3 List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint();
425   
426  3 if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) {
427  3 for (WhenConstraint whenConstraint : whenConstraints) {
428  3 List<Object> values = whenConstraint.getValues();
429  3 Constraint constraint = whenConstraint.getConstraint();
430   
431  3 if (constraint.getErrorLevel() == ErrorLevel.ERROR && constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0){
432    //This is a required field, so need to determine if it is required to save or required to approve based on the
433    //workflowNode parameter. The order of workflow nodes defined in the case constraint on this field is important in
434    //determining if required to approve or required to save. If the workflowNode parameter equals is the first
435    //node defined in the constraint, then it's required to approve, otherwise it's required to save.
436   
437  3 if (isWorkflowNodeFirstConstraintValue(workflowNode, values)) {
438    //Field is required to approve. Indicated this by setting the required for next state flag in metadata.
439    //If node is PreRoute, then the next state will be set to "SUBMIT" to indicate submit action, otherwise
440    //will be set to "APPROVED" to indicate approval action for node transition.
441  1 constraintMetadata.setRequiredForNextState(true);
442  1 if (DtoConstants.WORKFLOW_NODE_PRE_ROUTE.equals(workflowNode)){
443  0 constraintMetadata.setNextState(DtoState.SUBMITTED.toString());
444    } else {
445  1 constraintMetadata.setNextState(DtoState.APPROVED.toString());
446    }
447  1 constraintMetadata.setMinOccurs(0);
448  2 } else if (values.contains(workflowNode)){
449    //Field is required only for save
450  1 constraintMetadata.setRequiredForNextState(false);
451  1 constraintMetadata.setNextState(null);
452  1 constraintMetadata.setMinOccurs(1);
453    }
454    }
455    }
456    }
457    }
458   
459    /**
460    * @param values
461    * @param workflowNode
462    * @return true if workflowNode is first item in values, otherwise returns false
463    */
 
464  3 toggle private boolean isWorkflowNodeFirstConstraintValue(String workflowNode, List<Object> values){
465  3 if (values != null && !values.isEmpty()){
466  3 return values.get(0).equals(workflowNode);
467    } else {
468  0 return false;
469    }
470    }
471   
472    /**
473    * Processes a case constraint with field path of state.
474    */
 
475  11 toggle private void processStateCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String type, String state, String nextState, String workflowNode) {
476  11 List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint();
477   
478  11 if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) {
479  11 for (WhenConstraint whenConstraint : whenConstraints) {
480  22 List<Object> values = whenConstraint.getValues();
481  22 if (values != null) {
482  22 Constraint constraint = whenConstraint.getConstraint();
483   
484  22 if (constraint.getErrorLevel() == ErrorLevel.ERROR){
485    //NOTE: if the constraint has a nested constraint with fieldPath="lookup:proposal...",
486    //the required, requiredForNextState, and nextState values will be reset based on workflow node
487   
488    // Set the required for next state flag.
489  22 if (values.contains(nextState)) {
490  7 if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
491  7 constraintMetadata.setRequiredForNextState(true);
492  7 constraintMetadata.setNextState(nextState);
493    }
494    }
495   
496    // Update constraints based on state constraints
497  22 if (values.contains(state)) {
498  11 updateConstraintMetadata(constraintMetadata, constraint, type, state, nextState, workflowNode);
499    }
500    }
501    }
502    }
503    }
504    }
505   
506    /**
507    * Process a case constraint with fieldPath of type
508    */
 
509  0 toggle private void processTypeCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String type, String state, String nextState, String workflowNode) {
510  0 List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint();
511   
512  0 if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) {
513  0 for (WhenConstraint whenConstraint : whenConstraints) {
514  0 List<Object> values = whenConstraint.getValues();
515  0 if (values != null && values.contains(type)) {
516  0 Constraint constraint = whenConstraint.getConstraint();
517  0 updateConstraintMetadata(constraintMetadata, constraint, type, state, nextState, workflowNode);
518    }
519    }
520    }
521    }
522   
523   
524    /**
525    * Convert Object value to respective DataType. Method return null for object Value.
526    *
527    * @param dataType
528    * @param value
529    * @return
530    */
 
531  93 toggle protected Value convertDefaultValue(DataType dataType, Object value) {
532  93 Value v = null;
533  93 if (value instanceof String) {
534  0 String s = (String) value;
535  0 switch (dataType) {
536  0 case STRING:
537  0 v = new Data.StringValue(s);
538  0 break;
539  0 case BOOLEAN:
540  0 v = new Data.BooleanValue(Boolean.valueOf(s));
541  0 break;
542  0 case FLOAT:
543  0 v = new Data.FloatValue(Float.valueOf(s));
544  0 break;
545  0 case DATE:
546  0 DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
547  0 try {
548  0 v = new Data.DateValue(format.parse(s));
549    } catch (ParseException e) {
550  0 LOG.error("Unable to get default date value from metadata definition");
551    }
552  0 break;
553  0 case LONG:
554  0 if (!s.isEmpty()) {
555  0 v = new Data.LongValue(Long.valueOf(s));
556    }
557  0 break;
558  0 case DOUBLE:
559  0 v = new Data.DoubleValue(Double.valueOf(s));
560  0 break;
561  0 case INTEGER:
562  0 v = new Data.IntegerValue(Integer.valueOf(s));
563  0 break;
564    }
565    } else {
566  93 v = convertDefaultValue(value);
567    }
568   
569  93 return v;
570    }
571   
 
572  93 toggle protected Value convertDefaultValue(Object value) {
573  93 Value v = null;
574   
575  93 if (value instanceof String) {
576  0 v = new Data.StringValue((String) value);
577  93 } else if (value instanceof Boolean) {
578  0 v = new Data.BooleanValue((Boolean) value);
579  93 } else if (value instanceof Integer) {
580  0 v = new Data.IntegerValue((Integer) value);
581  93 } else if (value instanceof Double) {
582  0 v = new Data.DoubleValue((Double) value);
583  93 } else if (value instanceof Long) {
584  0 v = new Data.LongValue((Long) value);
585  93 } else if (value instanceof Short) {
586  0 v = new Data.ShortValue((Short) value);
587  93 } else if (value instanceof Float) {
588  0 v = new Data.FloatValue((Float) value);
589    }
590   
591  93 return v;
592    }
593   
 
594  93 toggle protected DataType convertDictionaryDataType(org.kuali.student.common.dictionary.dto.DataType dataType) {
595  93 switch (dataType) {
596  71 case STRING:
597  71 return DataType.STRING;
598  0 case BOOLEAN:
599  0 return DataType.BOOLEAN;
600  0 case INTEGER:
601  0 return DataType.INTEGER;
602  0 case FLOAT:
603  0 return DataType.FLOAT;
604  0 case COMPLEX:
605  0 return DataType.DATA;
606  11 case DATE:
607  11 return DataType.DATE;
608  11 case DOUBLE:
609  11 return DataType.DOUBLE;
610  0 case LONG:
611  0 return DataType.LONG;
612    }
613   
614  0 return null;
615    }
616   
 
617  2 toggle public void setUiLookupContext(String uiLookupContext) {
618  2 this.uiLookupContext = uiLookupContext;
619  2 init();
620   
621    }
622   
 
623  2 toggle @SuppressWarnings("unchecked")
624    private void init() {
625  2 ApplicationContext ac = new ClassPathXmlApplicationContext(uiLookupContext);
626   
627  2 Map<String, UILookupConfig> beansOfType = (Map<String, UILookupConfig>) ac.getBeansOfType(UILookupConfig.class);
628  2 lookupObjectStructures = new ArrayList<UILookupConfig>();
629  2 for (UILookupConfig objStr : beansOfType.values()) {
630  4 lookupObjectStructures.add(objStr);
631    }
632  2 System.out.println("UILookup loaded");
633    }
634   
 
635  26 toggle private String calcSimpleName(String objectKey) {
636  26 int lastDot = objectKey.lastIndexOf(".");
637  26 if (lastDot == -1) {
638  26 return objectKey;
639    }
640  0 return objectKey.substring(lastDot + 1);
641   
642    }
643   
 
644  26 toggle private boolean matchesObjectKey(String objectKey, String path) {
645  26 String simpleName = calcSimpleName(objectKey);
646  26 if (path.toLowerCase().startsWith(simpleName.toLowerCase())) {
647    // System.out.println ("matchesObjectKey: is TRUE for " + objectKey + " and " + path);
648  14 return true;
649    }
650    // System.out.println ("matchesObjectKey: is FALSE for " + objectKey + " and " + path);
651  12 return false;
652    }
653   
 
654  14 toggle private boolean matchesType(String paramType, String lookupType) {
655    // both null
656  14 if (paramType == null && lookupType == null) {
657  0 return true;
658    }
659    // not asking for type specific but the lookup defnition is type specific then
660    // no match
661  14 if (paramType == null && lookupType != null) {
662  4 return false;
663    }
664    // if looking for type specific but the lookup is not specific then
665    // take as default
666    // If configuration has both a null type (i.e. default) AND has a type
667    // specific one the type specific one has to be entered into the configuration
668    // file first so it is found first
669  10 if (paramType != null && lookupType == null) {
670  0 return true;
671    }
672  10 if (paramType.equalsIgnoreCase(lookupType)) {
673    // System.out.println ("matchesType: is TRUE for " + paramType + " and " + lookupType);
674  5 return true;
675    }
676    // System.out.println ("matchesType: is FALSE for " + paramType + " and " + lookupType);
677  5 return false;
678    }
679   
 
680  18 toggle private void addLookupstoMetadata(String objectKey, Metadata metadata, String type) {
681  18 if (lookupObjectStructures != null) {
682  13 for (UILookupConfig lookup : lookupObjectStructures) {
683  26 if (!matchesObjectKey(objectKey, lookup.getPath())) {
684  12 continue;
685    }
686  14 if (!matchesType(type, lookup.getType())) {
687  9 continue;
688    }
689    // TODO: figure out why path=courseInfo.creditOptions.type matches any structure that has a type on it so
690    // that lookup gets returned for all types
691  5 Map<String, Metadata> parsedMetadataMap = metadata.getProperties();
692  5 Metadata parsedMetadata = null;
693  5 String parsedMetadataKey = "";
694  5 String lookupFieldPath = lookup.getPath();
695  5 String[] lookupPathTokens = getPathTokens(lookupFieldPath);
696  10 for (int i = 1; i < lookupPathTokens.length; i++) {
697  5 if (parsedMetadataMap == null) {
698  0 break;
699    }
700  5 if (i == lookupPathTokens.length - 1) {
701    // get the metadata on the last path key token
702  5 parsedMetadata = parsedMetadataMap.get(lookupPathTokens[i]);
703  5 parsedMetadataKey = parsedMetadataKey + "." + lookupPathTokens[i];
704    }
705  5 if (parsedMetadataMap.get(lookupPathTokens[i]) != null) {
706  5 parsedMetadataMap = parsedMetadataMap.get(lookupPathTokens[i]).getProperties();
707  0 } else if (parsedMetadataMap.get("*") != null) {
708    // Lookup wildcard in case of unbounded elements in metadata.
709  0 parsedMetadataMap = parsedMetadataMap.get("*").getProperties();
710  0 i--;
711    }
712   
713    }
714  5 if (parsedMetadata != null) {
715    // System.out.println ("addLookupstoMetadata:" + parsedMetadataKey + " was found as a match for " +
716    // lookup.getPath ());
717  5 UILookupData initialLookup = lookup.getInitialLookup();
718  5 if (initialLookup != null) {
719  5 mapLookupDatatoMeta(initialLookup);
720  5 parsedMetadata.setInitialLookup(mapLookupDatatoMeta(lookup.getInitialLookup()));
721    }
722  5 List<LookupMetadata> additionalLookupMetadata = null;
723  5 if (lookup.getAdditionalLookups() != null) {
724  0 additionalLookupMetadata = new ArrayList<LookupMetadata>();
725  0 for (UILookupData additionallookup : lookup.getAdditionalLookups()) {
726  0 additionalLookupMetadata.add(mapLookupDatatoMeta(additionallookup));
727    }
728  0 parsedMetadata.setAdditionalLookups(additionalLookupMetadata);
729    }
730    }
731    }
732    }
733    }
734   
 
735  10 toggle private LookupMetadata mapLookupDatatoMeta(UILookupData lookupData) {
736  10 LookupMetadata lookupMetadata = new LookupMetadata();
737  10 List<LookupParamMetadata> paramsMetadata;
738  10 BeanUtils.copyProperties(lookupData, lookupMetadata, new String[]{"widget", "usage", "widgetOptions", "params"});
739  10 if (lookupData.getWidget() != null) {
740  10 lookupMetadata.setWidget(org.kuali.student.common.assembly.data.LookupMetadata.Widget.valueOf(lookupData.getWidget().toString()));
741    }
742  10 if (lookupData.getUsage() != null) {
743  10 lookupMetadata.setUsage(org.kuali.student.common.assembly.data.LookupMetadata.Usage.valueOf(lookupData.getUsage().toString()));
744    }
745  10 if (lookupData.getWidgetOptions () != null) {
746  0 lookupMetadata.setWidgetOptions (new HashMap<WidgetOption, String> ());
747  0 for (UILookupData.WidgetOption wo: lookupData.getWidgetOptions ().keySet ()) {
748  0 String value = lookupData.getWidgetOptions ().get (wo);
749  0 LookupMetadata.WidgetOption key = LookupMetadata.WidgetOption.valueOf(wo.toString());
750  0 lookupMetadata.getWidgetOptions ().put (key, value);
751    }
752    }
753  10 if (lookupData.getParams() != null) {
754  10 paramsMetadata = new ArrayList<LookupParamMetadata>();
755  10 for (CommonLookupParam param : lookupData.getParams()) {
756  50 paramsMetadata.add(mapLookupParamMetadata(param));
757    }
758  10 lookupMetadata.setParams(paramsMetadata);
759    }
760    // WidgetOptions is not used as of now. So not setting it into metadata.
761  10 return lookupMetadata;
762    }
763   
 
764  50 toggle private LookupParamMetadata mapLookupParamMetadata(CommonLookupParam param) {
765  50 LookupParamMetadata paramMetadata = new LookupParamMetadata();
766  50 BeanUtils.copyProperties(param, paramMetadata, new String[]{"childLookup", "dataType", "writeAccess", "usage", "widget"});
767  50 if (param.getChildLookup() != null) {
768  0 paramMetadata.setChildLookup(mapLookupDatatoMeta((UILookupData) param.getChildLookup()));
769    }
770  50 if (param.getDataType() != null) {
771  50 paramMetadata.setDataType(org.kuali.student.common.assembly.data.Data.DataType.valueOf(param.getDataType().toString()));
772    }
773  50 if (param.getWriteAccess() != null) {
774  40 paramMetadata.setWriteAccess(org.kuali.student.common.assembly.data.Metadata.WriteAccess.valueOf(param.getWriteAccess().toString()));
775    }
776  50 if (param.getUsage() != null) {
777  0 paramMetadata.setUsage(org.kuali.student.common.assembly.data.LookupMetadata.Usage.valueOf(param.getUsage().toString()));
778    }
779  50 if (param.getWidget() != null) {
780  0 paramMetadata.setWidget(org.kuali.student.common.assembly.data.LookupParamMetadata.Widget.valueOf(param.getWidget().toString()));
781    }
782   
783  50 return paramMetadata;
784    }
785   
 
786  5 toggle private static String[] getPathTokens(String fieldPath) {
787  5 return (fieldPath != null && fieldPath.contains(".") ? fieldPath.split("\\.") : new String[]{fieldPath});
788    }
789    }