1   
2   
3   
4   
5   
6   
7   
8   
9   package org.kuali.student.r1.common.assembly.dictionary;
10  
11  import org.apache.log4j.Logger;
12  import org.kuali.student.r1.common.assembly.data.ConstraintMetadata;
13  import org.kuali.student.r1.common.assembly.data.Data;
14  import org.kuali.student.r1.common.assembly.data.Data.DataType;
15  import org.kuali.student.r1.common.assembly.data.Data.Value;
16  import org.kuali.student.r1.common.assembly.data.LookupMetadata;
17  import org.kuali.student.r1.common.assembly.data.LookupMetadata.WidgetOption;
18  import org.kuali.student.r1.common.assembly.data.LookupParamMetadata;
19  import org.kuali.student.r1.common.assembly.data.Metadata;
20  import org.kuali.student.r1.common.assembly.data.Metadata.WriteAccess;
21  import org.kuali.student.r1.common.assembly.data.UILookupConfig;
22  import org.kuali.student.r1.common.assembly.data.UILookupData;
23  import org.kuali.student.r1.common.dictionary.dto.CaseConstraint;
24  import org.kuali.student.r1.common.dictionary.dto.CommonLookupParam;
25  import org.kuali.student.r1.common.dictionary.dto.Constraint;
26  import org.kuali.student.r1.common.dictionary.dto.FieldDefinition;
27  import org.kuali.student.r1.common.dictionary.dto.ObjectStructureDefinition;
28  import org.kuali.student.r1.common.dictionary.dto.WhenConstraint;
29  import org.kuali.student.r1.common.dictionary.service.DictionaryService;
30  import org.kuali.student.r2.common.dto.DtoConstants;
31  import org.kuali.student.r2.common.dto.DtoConstants.DtoState;
32  import org.kuali.student.r2.common.infc.ValidationResult.ErrorLevel;
33  import org.kuali.student.r2.common.util.date.DateFormatters;
34  import org.springframework.beans.BeanUtils;
35  import org.springframework.context.ApplicationContext;
36  import org.springframework.context.support.ClassPathXmlApplicationContext;
37  
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  
44  
45  
46  
47  
48  
49  
50  public class MetadataServiceImpl {
51      final Logger LOG = Logger.getLogger(MetadataServiceImpl.class);
52  
53      
54      private List<DictionaryService> dictionaryServices;
55      private List<UILookupConfig> lookupObjectStructures;
56      private String uiLookupContext;
57  
58      private static class RecursionCounter {
59          public static final int MAX_DEPTH = 4;
60  
61          private Map<String, Integer> recursions = new HashMap<String, Integer>();
62  
63          public int increment(String objectName) {
64              Integer hits = recursions.get(objectName);
65  
66              if (hits == null) {
67                  hits = new Integer(1);
68              } else {
69                  hits++;
70              }
71              recursions.put(objectName, hits);
72              return hits;
73          }
74  
75          public int decrement(String objectName) {
76              Integer hits = recursions.get(objectName);
77              if (hits >= 1) {
78                  hits--;
79              }
80  
81              recursions.put(objectName, hits);
82              return hits;
83          }
84      }
85  
86  	public MetadataServiceImpl() {
87  		super();
88  	}
89  
90      
91  
92  
93  
94  
95      public MetadataServiceImpl(DictionaryService... dictionaryServices) {
96      	setDictionaryServices(Arrays.asList(dictionaryServices));
97      }
98  
99      public synchronized void setDictionaryServices(List<DictionaryService> dictionaryServices) {
100         this.dictionaryServices = dictionaryServices;
101     	
102 
103 
104 
105 
106 
107 
108 
109 
110 	}
111 
112 
113 	
114 
115 
116 
117 
118 
119 
120 
121 
122 
123     public Metadata getMetadata(String objectKey, String type, String state, String nextState, String documentTypeName) {
124     	nextState = (nextState == null || nextState.length() <=0 ? DtoState.getNextStateAsString(state):nextState);
125     	state = state==null?null:state.toUpperCase();
126     	nextState = nextState==null?null:nextState.toUpperCase();
127     	
128     	return getMetadataFromDictionaryService(objectKey, type, state, nextState, null, documentTypeName);
129     }
130 
131 	
132 
133 
134 
135 
136     public Metadata getMetadataByWorkflowNode(String objectKey, String workflowNode, String documentTypeName) {
137     	
138     	return getMetadataFromDictionaryService(objectKey, null, DtoState.DRAFT.toString(), null, workflowNode, documentTypeName);
139     }
140 
141     
142 
143 
144 
145 
146 
147 
148 
149     public Metadata getMetadata(String objectKey, String type, String state) {
150     	state = (state == null ? DtoState.DRAFT.toString():state);
151     	String nextState = DtoState.getNextStateAsString(state);
152     	
153     	return getMetadata(objectKey, type, state.toUpperCase(), nextState, null);
154     }
155 
156 
157     
158 
159 
160 
161 
162 
163 
164     public Metadata getMetadata(String objectKey, String state) {
165     	return getMetadata(objectKey, null, state);
166     }
167 
168     
169 
170 
171 
172 
173 
174 
175     public Metadata getMetadata(String objectKey) {
176         return getMetadata(objectKey, null, null);
177     }
178 
179     
180 
181 
182 
183 
184 
185 
186 
187 
188 
189     protected Metadata getMetadataFromDictionaryService(String objectKey, String type, String state, String nextState, String workflowNode, String documentTypeName) {
190         
191         Metadata metadata = new Metadata();
192 
193         ObjectStructureDefinition objectStructure = getObjectStructure(objectKey);
194 		
195         metadata.setProperties(getProperties(objectStructure, type, state, nextState, workflowNode, new RecursionCounter(), documentTypeName));
196 
197         metadata.setWriteAccess(WriteAccess.ALWAYS);
198         metadata.setDataType(DataType.DATA);
199         addLookupstoMetadata(objectKey, metadata, type);
200         return metadata;
201     }
202 
203     
204 
205 
206 
207 
208 
209 
210 
211 
212 
213 
214 
215     private Map<String, Metadata> getProperties(ObjectStructureDefinition objectStructure, String type, String state, String nextState, String workflowNode, RecursionCounter counter, String documentTypeName) {
216         String objectName = objectStructure.getName();
217         int hits = counter.increment(objectName);
218 
219         Map<String, Metadata> properties = null;
220 
221         if (hits < RecursionCounter.MAX_DEPTH) {
222             properties = new HashMap<String, Metadata>();
223 
224             List<FieldDefinition> attributes = objectStructure.getAttributes();
225             for (FieldDefinition fd : attributes) {
226 
227                 Metadata metadata = new Metadata();
228 
229                 
230                 metadata.setWriteAccess(WriteAccess.ALWAYS);
231                 metadata.setDataType(convertDictionaryDataType(fd.getDataType()));
232                 
233                 metadata.setConstraints(getConstraints(fd, type, state, nextState, workflowNode, documentTypeName));
234                 metadata.setCanEdit(!fd.isReadOnly());
235                 metadata.setCanUnmask(!fd.isMask());
236                 metadata.setCanView(!fd.isHide());
237                 metadata.setDynamic(fd.isDynamic());
238                 metadata.setLabelKey(fd.getLabelKey());
239                 metadata.setDefaultValue(convertDefaultValue(metadata.getDataType(), fd.getDefaultValue()));
240                 metadata.setDefaultValuePath(fd.getDefaultValuePath());
241                 
242 	           	if (fd.isPartialMask()){
243 	           		metadata.setPartialMaskFormatter(fd.getPartialMaskFormatter());
244 	           	}
245 	           	
246 	           	if (fd.isMask()){
247 	           		metadata.setMaskFormatter(fd.getMaskFormatter());
248 	           	}
249 
250                 
251                 Map<String, Metadata> nestedProperties = null;
252                 if (fd.getDataType() == org.kuali.student.r1.common.dictionary.dto.DataType.COMPLEX && fd.getDataObjectStructure() != null) {
253                     nestedProperties = getProperties(fd.getDataObjectStructure(), type, state, nextState, workflowNode, counter, documentTypeName);
254                 }
255 
256                 
257                 if (isRepeating(fd)) {
258                     Metadata repeatingMetadata = new Metadata();
259                     metadata.setDataType(DataType.LIST);
260 
261                     repeatingMetadata.setWriteAccess(WriteAccess.ALWAYS);
262                     repeatingMetadata.setOnChangeRefreshMetadata(false);
263                     repeatingMetadata.setDataType(convertDictionaryDataType(fd.getDataType()));
264 
265                     if (nestedProperties != null) {
266                         repeatingMetadata.setProperties(nestedProperties);
267                     }
268 
269                     Map<String, Metadata> repeatingProperty = new HashMap<String, Metadata>();
270                     repeatingProperty.put("*", repeatingMetadata);
271                     metadata.setProperties(repeatingProperty);
272                 } else if (nestedProperties != null) {
273                     metadata.setProperties(nestedProperties);
274                 }
275 
276                 properties.put(fd.getName(), metadata);
277 
278             }
279         }
280 
281         counter.decrement(objectName);
282         return properties;
283     }
284 
285     
286 
287 
288 
289 
290 
291     protected boolean isRepeating(FieldDefinition fd) {
292         boolean isRepeating = false;
293         try {
294             int maxOccurs = Integer.parseInt(fd.getMaxOccurs());
295             isRepeating = maxOccurs > 1;
296         } catch (NumberFormatException nfe) {
297             isRepeating = FieldDefinition.UNBOUNDED.equals(fd.getMaxOccurs());
298         }
299 
300         return isRepeating;
301     }
302 
303     
304 
305 
306 
307 
308 
309     protected ObjectStructureDefinition getObjectStructure(String objectKey) {
310         
311         
312         DictionaryService dictionaryService = null;
313         for (DictionaryService service : this.dictionaryServices){
314             List<String> objectTypes = service.getObjectTypes();
315             for (String objectType : objectTypes) {
316                 if (objectType.equals(objectKey)){
317                     dictionaryService = service;
318                 }
319             }
320         }
321 
322         if (dictionaryService == null) {
323             throw new RuntimeException("Dictionary service not provided for objectKey=[" + objectKey + "].");
324         }
325 
326         return dictionaryService.getObjectStructure(objectKey);
327     }
328 
329 	
330     protected List<ConstraintMetadata> getConstraints(FieldDefinition fd, String type, String state, String nextState, String workflowNode, String documentTypeName) {
331         List<ConstraintMetadata> constraints = new ArrayList<ConstraintMetadata>();
332 
333         ConstraintMetadata constraintMetadata = new ConstraintMetadata();
334 
335         updateConstraintMetadata(constraintMetadata, (Constraint) fd, type, state, nextState, workflowNode);
336         constraints.add(constraintMetadata);
337 
338         return constraints;
339     }
340 
341     
342 
343 
344 
345 
346 
347     protected void updateConstraintMetadata(ConstraintMetadata constraintMetadata, Constraint constraint, String type, String state, String nextState, String workflowNode) {
348         
349         
350 
351         
352         if (constraint.getMinLength() != null) {
353             constraintMetadata.setMinLength(constraint.getMinLength());
354         }
355 
356         
357         try {
358             if (constraint.getMaxLength() != null) {
359                 constraintMetadata.setMaxLength(Integer.parseInt(constraint.getMaxLength()));
360             }
361             
362         } catch (NumberFormatException nfe) {
363             
364             
365             constraintMetadata.setMaxLength(9999);
366         }
367 
368         
369         if (constraint.getMinOccurs() != null) {
370             constraintMetadata.setMinOccurs(constraint.getMinOccurs());
371         }
372 
373         
374         String maxOccurs = constraint.getMaxOccurs();
375         if (maxOccurs != null) {
376             try {
377                 constraintMetadata.setMaxOccurs(Integer.parseInt(maxOccurs));
378                 if (!FieldDefinition.SINGLE.equals(maxOccurs)) {
379                     constraintMetadata.setId("repeating");
380                 }
381             } catch (NumberFormatException nfe) {
382                 
383                 if (FieldDefinition.UNBOUNDED.equals(maxOccurs)) {
384                     constraintMetadata.setId("repeating");
385                     constraintMetadata.setMaxOccurs(9999);
386                 }
387             }
388         }
389 
390         
391         if (constraint.getExclusiveMin() != null) {
392             constraintMetadata.setMinValue(constraint.getExclusiveMin());
393         }
394 
395         
396         if (constraint.getInclusiveMax() != null) {
397             constraintMetadata.setMaxValue(constraint.getInclusiveMax());
398         }
399 
400         if (constraint.getValidChars() != null) {
401             constraintMetadata.setValidChars(constraint.getValidChars().getValue());
402             constraintMetadata.setValidCharsMessageId(constraint.getValidChars().getLabelKey());
403         }
404 
405         
406         if (constraint.getCaseConstraint() != null) {
407             processCaseConstraint(constraintMetadata, constraint.getCaseConstraint(), type, state, nextState, workflowNode);
408         }
409     }
410 
411     
412 
413 
414 
415 
416     protected void processCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String type, String state, String nextState, String workflowNode) {
417         String fieldPath = caseConstraint.getFieldPath();
418         fieldPath = (fieldPath != null ? fieldPath.toUpperCase() : fieldPath);
419         
420         if (workflowNode != null && fieldPath != null && fieldPath.startsWith("PROPOSAL/WORKFLOWNODE")){
421         	processRequiredByNodeCaseConstraint(constraintMetadata, caseConstraint, workflowNode);
422           
423         } else if ("STATEKEY".equals(fieldPath) || "STATE".equals(fieldPath)) {
424         	processStateCaseConstraint(constraintMetadata, caseConstraint, type, state, nextState, workflowNode);
425           
426         } else if ("TYPEKEY".equals(fieldPath) || "TYPE".equals(fieldPath)) {
427         	processTypeCaseConstraint(constraintMetadata, caseConstraint, type, state, nextState, workflowNode);
428         }
429     }
430         
431     
432 
433 
434 
435 
436 
437 
438 
439     private void processRequiredByNodeCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint,  String workflowNode) {
440         List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint();
441         
442     	if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) {
443             for (WhenConstraint whenConstraint : whenConstraints) {
444                 List<Object> values = whenConstraint.getValues();
445                 Constraint constraint = whenConstraint.getConstraint();
446 
447                 if (constraint.getErrorLevel() == ErrorLevel.ERROR && constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0){
448                     
449                 	
450                 	
451                 	
452                		
453                 	if (isWorkflowNodeFirstConstraintValue(workflowNode, values)) {
454                 		
455                 		
456                 		
457                			constraintMetadata.setRequiredForNextState(true);
458                			if (DtoConstants.WORKFLOW_NODE_PRE_ROUTE.equals(workflowNode)){
459                				constraintMetadata.setNextState(DtoState.SUBMITTED.toString());
460                			} else {
461                				constraintMetadata.setNextState(DtoState.APPROVED.toString());
462                			}
463                			constraintMetadata.setMinOccurs(0);
464                     } else if (values.contains(workflowNode)){
465                     	
466                			constraintMetadata.setRequiredForNextState(false);
467                			constraintMetadata.setNextState(null);
468                			constraintMetadata.setMinOccurs(1);
469                     }
470                 }
471             }
472         }
473     }
474     
475     
476 
477 
478 
479 
480     private boolean isWorkflowNodeFirstConstraintValue(String workflowNode, List<Object> values){
481     	if (values != null && !values.isEmpty()){
482     		return values.get(0).equals(workflowNode);
483     	} else {
484     		return false;
485     	}
486     }
487 
488 	
489 
490 
491     private void processStateCaseConstraint(ConstraintMetadata constraintMetadata,	CaseConstraint caseConstraint, String type, String state, String nextState, String workflowNode) {
492         List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint();
493         
494         if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) {
495             for (WhenConstraint whenConstraint : whenConstraints) {
496                 List<Object> values = whenConstraint.getValues();
497                 if (values != null) {
498                     Constraint constraint = whenConstraint.getConstraint();
499 
500                     if (constraint.getErrorLevel() == ErrorLevel.ERROR){
501                     	
502                     	
503                     	
504                     	
505                         if (values.contains(nextState)) {
506                             if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
507                                 constraintMetadata.setRequiredForNextState(true);
508                                 constraintMetadata.setNextState(nextState);
509                             }
510                         }
511 
512                         
513                         if (values.contains(state)) {
514                             updateConstraintMetadata(constraintMetadata, constraint, type, state, nextState, workflowNode);
515                         }
516                     }
517                 }
518             }
519         }		
520 	}
521     
522     
523 
524 
525     private void processTypeCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String type, String state,	String nextState, String workflowNode) {
526         List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint();
527     	
528         if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) {
529             for (WhenConstraint whenConstraint : whenConstraints) {
530                 List<Object> values = whenConstraint.getValues();
531                 if (values != null && values.contains(type)) {
532                     Constraint constraint = whenConstraint.getConstraint();
533                     updateConstraintMetadata(constraintMetadata, constraint, type, state, nextState, workflowNode);
534                 }
535             }
536         }		
537 	}
538     
539 
540 	
541 
542 
543 
544 
545 
546 
547     protected Value convertDefaultValue(DataType dataType, Object value) {
548         Value v = null;
549         if (value instanceof String) {
550             String s = (String) value;
551             switch (dataType) {
552                 case STRING:
553                     v = new Data.StringValue(s);
554                     break;
555                 case BOOLEAN:
556                     v = new Data.BooleanValue(Boolean.valueOf(s));
557                     break;
558                 case FLOAT:
559                     v = new Data.FloatValue(Float.valueOf(s));
560                     break;
561                 case DATE:
562                     try {
563                         v = new Data.DateValue(DateFormatters.DEFAULT_DATE_FORMATTER.parse(s));
564                     } catch (IllegalArgumentException e) {
565                         LOG.error("Unable to get default date value from metadata definition");
566                     }
567                     break;
568                 case LONG:
569                     if (!s.isEmpty()) {
570                         v = new Data.LongValue(Long.valueOf(s));
571                     }
572                     break;
573                 case DOUBLE:
574                     v = new Data.DoubleValue(Double.valueOf(s));
575                     break;
576                 case INTEGER:
577                     v = new Data.IntegerValue(Integer.valueOf(s));
578                     break;
579             }
580         } else {
581             v = convertDefaultValue(value);
582         }
583 
584         return v;
585     }
586 
587     protected Value convertDefaultValue(Object value) {
588         Value v = null;
589 
590         if (value instanceof String) {
591             v = new Data.StringValue((String) value);
592         } else if (value instanceof Boolean) {
593             v = new Data.BooleanValue((Boolean) value);
594         } else if (value instanceof Integer) {
595             v = new Data.IntegerValue((Integer) value);
596         } else if (value instanceof Double) {
597             v = new Data.DoubleValue((Double) value);
598         } else if (value instanceof Long) {
599             v = new Data.LongValue((Long) value);
600         } else if (value instanceof Short) {
601             v = new Data.ShortValue((Short) value);
602         } else if (value instanceof Float) {
603             v = new Data.FloatValue((Float) value);
604         }
605 
606         return v;
607     }
608 
609     protected DataType convertDictionaryDataType(org.kuali.student.r1.common.dictionary.dto.DataType dataType) {
610         switch (dataType) {
611             case STRING:
612                 return DataType.STRING;
613             case BOOLEAN:
614                 return DataType.BOOLEAN;
615             case INTEGER:
616                 return DataType.INTEGER;
617             case FLOAT:
618                 return DataType.FLOAT;
619             case COMPLEX:
620                 return DataType.DATA;
621             case DATE:
622                 return DataType.DATE;
623             case DOUBLE:
624                 return DataType.DOUBLE;
625             case LONG:
626                 return DataType.LONG;
627         }
628 
629         return null;
630     }
631 
632     public void setUiLookupContext(String uiLookupContext) {
633         this.uiLookupContext = uiLookupContext;
634         init();
635 
636     }
637 
638     @SuppressWarnings("unchecked")
639     private void init() {
640         ApplicationContext ac = new ClassPathXmlApplicationContext(uiLookupContext);
641 
642         Map<String, UILookupConfig> beansOfType = (Map<String, UILookupConfig>) ac.getBeansOfType(UILookupConfig.class);
643         lookupObjectStructures = new ArrayList<UILookupConfig>();
644         for (UILookupConfig objStr : beansOfType.values()) {
645             lookupObjectStructures.add(objStr);
646         }
647         System.out.println("UILookup loaded");
648     }
649 
650     private String calcSimpleName(String objectKey) {
651         int lastDot = objectKey.lastIndexOf(".");
652         if (lastDot == -1) {
653             return objectKey;
654         }
655         return objectKey.substring(lastDot + 1);
656 
657     }
658 
659     private boolean matchesObjectKey(String objectKey, String path) {
660         String simpleName = calcSimpleName(objectKey);
661         if (path.toLowerCase().startsWith(simpleName.toLowerCase())) {
662             
663             return true;
664         }
665         
666         return false;
667     }
668 
669     private boolean matchesType(String paramType, String lookupType) {
670         
671         if (paramType == null && lookupType == null) {
672             return true;
673         }
674         
675         
676         if (paramType == null && lookupType != null) {
677             return false;
678         }
679         
680         
681         
682         
683         
684         if (paramType != null && lookupType == null) {
685             return true;
686         }
687         if (paramType.equalsIgnoreCase(lookupType)) {
688             
689             return true;
690         }
691         
692         return false;
693     }
694 
695     private void addLookupstoMetadata(String objectKey, Metadata metadata, String type) {
696         if (lookupObjectStructures != null) {
697             for (UILookupConfig lookup : lookupObjectStructures) {
698                 if (!matchesObjectKey(objectKey, lookup.getPath())) {
699                     continue;
700                 }
701                 if (!matchesType(type, lookup.getType())) {
702                     continue;
703                 }
704                 
705                 
706                 Map<String, Metadata> parsedMetadataMap = metadata.getProperties();
707                 Metadata parsedMetadata = null;
708                 String parsedMetadataKey = "";
709                 String lookupFieldPath = lookup.getPath();
710                 String[] lookupPathTokens = getPathTokens(lookupFieldPath);
711                 for (int i = 1; i < lookupPathTokens.length; i++) {
712                     if (parsedMetadataMap == null) {
713                         break;
714                     }
715                     if (i == lookupPathTokens.length - 1) {
716                         
717                         parsedMetadata = parsedMetadataMap.get(lookupPathTokens[i]);
718                         parsedMetadataKey = parsedMetadataKey + "." + lookupPathTokens[i];
719                     }
720                     if (parsedMetadataMap.get(lookupPathTokens[i]) != null) {
721                         parsedMetadataMap = parsedMetadataMap.get(lookupPathTokens[i]).getProperties();
722                     } else if (parsedMetadataMap.get("*") != null) {
723                         
724                         parsedMetadataMap = parsedMetadataMap.get("*").getProperties();
725                         i--;
726                     }
727 
728                 }
729                 if (parsedMetadata != null) {
730                     
731                     
732                     UILookupData initialLookup = lookup.getInitialLookup();
733                     if (initialLookup != null) {
734                         mapLookupDatatoMeta(initialLookup);
735                         parsedMetadata.setInitialLookup(mapLookupDatatoMeta(lookup.getInitialLookup()));
736                     }
737                     List<LookupMetadata> additionalLookupMetadata = null;
738                     if (lookup.getAdditionalLookups() != null) {
739                         additionalLookupMetadata = new ArrayList<LookupMetadata>();
740                         for (UILookupData additionallookup : lookup.getAdditionalLookups()) {
741                             additionalLookupMetadata.add(mapLookupDatatoMeta(additionallookup));
742                         }
743                         parsedMetadata.setAdditionalLookups(additionalLookupMetadata);
744                     }
745                 }
746             }
747         }
748     }
749 
750     private LookupMetadata mapLookupDatatoMeta(UILookupData lookupData) {
751         LookupMetadata lookupMetadata = new LookupMetadata();
752         List<LookupParamMetadata> paramsMetadata;
753         BeanUtils.copyProperties(lookupData, lookupMetadata, new String[]{"widget", "usage", "widgetOptions", "params"});
754         if (lookupData.getWidget() != null) {
755             lookupMetadata.setWidget(org.kuali.student.r1.common.assembly.data.LookupMetadata.Widget.valueOf(lookupData.getWidget().toString()));
756         }
757         if (lookupData.getUsage() != null) {
758             lookupMetadata.setUsage(org.kuali.student.r1.common.assembly.data.LookupMetadata.Usage.valueOf(lookupData.getUsage().toString()));
759         }
760         if (lookupData.getWidgetOptions () != null) {
761          lookupMetadata.setWidgetOptions (new HashMap<WidgetOption, String> ());
762          for (UILookupData.WidgetOption wo: lookupData.getWidgetOptions ().keySet ()) {
763           String value = lookupData.getWidgetOptions ().get (wo);
764           LookupMetadata.WidgetOption key = LookupMetadata.WidgetOption.valueOf(wo.toString());
765           lookupMetadata.getWidgetOptions ().put (key, value);
766          }
767         }
768         if (lookupData.getParams() != null) {
769             paramsMetadata = new ArrayList<LookupParamMetadata>();
770             for (CommonLookupParam param : lookupData.getParams()) {
771                 paramsMetadata.add(mapLookupParamMetadata(param));
772             }
773             lookupMetadata.setParams(paramsMetadata);
774         }
775         
776         return lookupMetadata;
777     }
778 
779     private LookupParamMetadata mapLookupParamMetadata(CommonLookupParam param) {
780         LookupParamMetadata paramMetadata = new LookupParamMetadata();
781         BeanUtils.copyProperties(param, paramMetadata, new String[]{"childLookup", "dataType", "writeAccess", "usage", "widget"});
782         if (param.getChildLookup() != null) {
783             paramMetadata.setChildLookup(mapLookupDatatoMeta((UILookupData) param.getChildLookup()));
784         }
785         if (param.getDataType() != null) {
786             paramMetadata.setDataType(org.kuali.student.r1.common.assembly.data.Data.DataType.valueOf(param.getDataType().toString()));
787         }
788         if (param.getWriteAccess() != null) {
789             paramMetadata.setWriteAccess(org.kuali.student.r1.common.assembly.data.Metadata.WriteAccess.valueOf(param.getWriteAccess().toString()));
790         }
791         if (param.getUsage() != null) {
792             paramMetadata.setUsage(org.kuali.student.r1.common.assembly.data.LookupMetadata.Usage.valueOf(param.getUsage().toString()));
793         }
794         if (param.getWidget() != null) {
795             paramMetadata.setWidget(org.kuali.student.r1.common.assembly.data.LookupParamMetadata.Widget.valueOf(param.getWidget().toString()));
796         }
797 
798         return paramMetadata;
799     }
800 
801     private static String[] getPathTokens(String fieldPath) {
802         return (fieldPath != null && fieldPath.contains(".") ? fieldPath.split("\\.") : new String[]{fieldPath});
803     }
804 }