001 /** 002 * Copyright 2010 The Kuali Foundation Licensed under the Educational Community License, Version 2.0 (the "License"); you may 003 * not use this file except in compliance with the License. You may obtain a copy of the License at 004 * http://www.osedu.org/licenses/ECL-2.0 Unless required by applicable law or agreed to in writing, software distributed 005 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 006 * implied. See the License for the specific language governing permissions and limitations under the License. 007 */ 008 009 package org.kuali.student.common.assembly.dictionary; 010 011 import java.text.DateFormat; 012 import java.text.ParseException; 013 import java.text.SimpleDateFormat; 014 import java.util.ArrayList; 015 import java.util.HashMap; 016 import java.util.List; 017 import java.util.Map; 018 019 import org.apache.log4j.Logger; 020 import org.kuali.student.common.assembly.data.ConstraintMetadata; 021 import org.kuali.student.common.assembly.data.Data; 022 import org.kuali.student.common.assembly.data.LookupMetadata; 023 import org.kuali.student.common.assembly.data.LookupParamMetadata; 024 import org.kuali.student.common.assembly.data.Metadata; 025 import org.kuali.student.common.assembly.data.UILookupConfig; 026 import org.kuali.student.common.assembly.data.UILookupData; 027 import org.kuali.student.common.assembly.data.Data.DataType; 028 import org.kuali.student.common.assembly.data.Data.Value; 029 import org.kuali.student.common.assembly.data.Metadata.WriteAccess; 030 import org.kuali.student.common.dictionary.dto.CaseConstraint; 031 import org.kuali.student.common.dictionary.dto.CommonLookupParam; 032 import org.kuali.student.common.dictionary.dto.Constraint; 033 import org.kuali.student.common.dictionary.dto.FieldDefinition; 034 import org.kuali.student.common.dictionary.dto.ObjectStructureDefinition; 035 import org.kuali.student.common.dictionary.dto.WhenConstraint; 036 import org.kuali.student.common.dictionary.service.DictionaryService; 037 import org.kuali.student.common.dto.DtoConstants.DtoState; 038 import org.springframework.beans.BeanUtils; 039 import org.springframework.context.ApplicationContext; 040 import org.springframework.context.support.ClassPathXmlApplicationContext; 041 042 /** 043 * This class provides metadata lookup for service dto objects. 044 * 045 * @author Kuali Student Team 046 */ 047 public class MetadataServiceImpl { 048 final Logger LOG = Logger.getLogger(MetadataServiceImpl.class); 049 050 private Map<String, DictionaryService> dictionaryServiceMap; 051 private List<UILookupConfig> lookupObjectStructures; 052 private String uiLookupContext; 053 054 private static class RecursionCounter { 055 public static final int MAX_DEPTH = 4; 056 057 private Map<String, Integer> recursions = new HashMap<String, Integer>(); 058 059 public int increment(String objectName) { 060 Integer hits = recursions.get(objectName); 061 062 if (hits == null) { 063 hits = new Integer(1); 064 } else { 065 hits++; 066 } 067 recursions.put(objectName, hits); 068 return hits; 069 } 070 071 public int decrement(String objectName) { 072 Integer hits = recursions.get(objectName); 073 if (hits >= 1) { 074 hits--; 075 } 076 077 recursions.put(objectName, hits); 078 return hits; 079 } 080 } 081 082 /** 083 * Create a metadata service initializing it with all known dictionary services 084 * 085 * @param dictionaryServices 086 */ 087 public MetadataServiceImpl(DictionaryService... dictionaryServices) { 088 if (dictionaryServices != null) { 089 this.dictionaryServiceMap = new HashMap<String, DictionaryService>(); 090 for (DictionaryService d : dictionaryServices) { 091 List<String> objectTypes = d.getObjectTypes(); 092 for (String objectType : objectTypes) { 093 dictionaryServiceMap.put(objectType, d); 094 } 095 } 096 } 097 } 098 099 /** 100 * This method gets the metadata for the given object key, type, state and nextState 101 * 102 * @param objectKey 103 * @param type The type of the object (value can be null) 104 * @param state The state for which to retrieve object constraints (value can be null) 105 * @param nextState The state to to check requiredForNextState indicators (value can be null) 106 * @return 107 */ 108 public Metadata getMetadata(String objectKey, String type, String state, String nextState) { 109 return getMetadataFromDictionaryService(objectKey, type, state, nextState); 110 } 111 112 /** 113 * This method gets the metadata for the given object key, type and state 114 * 115 * @param objectKey 116 * @param type The type of the object (value can be null) 117 * @param state The state for which to retrieve object constraints (value can be null) 118 * @return 119 */ 120 public Metadata getMetadata(String objectKey, String type, String state) { 121 return getMetadata(objectKey, type, state, null); 122 } 123 124 125 /** 126 * This method gets the metadata for the given object key and state 127 * 128 * @param objectKey 129 * @param type The type of the object (value can be null) 130 */ 131 public Metadata getMetadata(String objectKey, String state) { 132 return getMetadata(objectKey, null, state); 133 } 134 135 /** 136 * This method gets the metadata for the given object key for state DRAFT. 137 * 138 * @see MetadataServiceImpl#getMetadata(String, String) 139 * @param objectKey 140 * @return 141 */ 142 public Metadata getMetadata(String objectKey) { 143 return getMetadata(objectKey, null, null); 144 } 145 146 /** 147 * This invokes the appropriate dictionary service to get the object structure and then converts it to a metadata 148 * structure. 149 * 150 * @param objectKey 151 * @param type 152 * @param state 153 * @return 154 */ 155 protected Metadata getMetadataFromDictionaryService(String objectKey, String type, String state, String nextState) { 156 Metadata metadata = new Metadata(); 157 158 ObjectStructureDefinition objectStructure = getObjectStructure(objectKey); 159 160 metadata.setProperties(getProperties(objectStructure, type, state, nextState, new RecursionCounter())); 161 162 metadata.setWriteAccess(WriteAccess.ALWAYS); 163 metadata.setDataType(DataType.DATA); 164 addLookupstoMetadata(objectKey, metadata, type); 165 return metadata; 166 } 167 168 /** 169 * This method is used to convert a list of dictionary fields into metadata properties 170 * 171 * @param fields 172 * @param type 173 * @param state 174 * @return 175 */ 176 private Map<String, Metadata> getProperties(ObjectStructureDefinition objectStructure, String type, String state, String nextState, RecursionCounter counter) { 177 String objectId = objectStructure.getName(); 178 int hits = counter.increment(objectId); 179 180 Map<String, Metadata> properties = null; 181 182 if (hits < RecursionCounter.MAX_DEPTH) { 183 properties = new HashMap<String, Metadata>(); 184 185 List<FieldDefinition> attributes = objectStructure.getAttributes(); 186 for (FieldDefinition fd : attributes) { 187 188 Metadata metadata = new Metadata(); 189 190 // Set constraints, authz flags, default value 191 metadata.setWriteAccess(WriteAccess.ALWAYS); 192 metadata.setDataType(convertDictionaryDataType(fd.getDataType())); 193 metadata.setConstraints(getConstraints(fd, type, state, nextState)); 194 metadata.setCanEdit(!fd.isReadOnly()); 195 metadata.setCanUnmask(!fd.isMask()); 196 metadata.setCanView(!fd.isHide()); 197 metadata.setDynamic(fd.isDynamic()); 198 metadata.setLabelKey(fd.getLabelKey()); 199 metadata.setDefaultValue(convertDefaultValue(metadata.getDataType(), fd.getDefaultValue())); 200 metadata.setDefaultValuePath(fd.getDefaultValuePath()); 201 202 if (fd.isPartialMask()){ 203 metadata.setPartialMaskFormatter(fd.getPartialMaskFormatter()); 204 } 205 206 if (fd.isMask()){ 207 metadata.setMaskFormatter(fd.getMaskFormatter()); 208 } 209 210 // Get properties for nested object structure 211 Map<String, Metadata> nestedProperties = null; 212 if (fd.getDataType() == org.kuali.student.common.dictionary.dto.DataType.COMPLEX && fd.getDataObjectStructure() != null) { 213 nestedProperties = getProperties(fd.getDataObjectStructure(), type, state, nextState, counter); 214 } 215 216 // For repeating field, create a LIST with wildcard in metadata structure 217 if (isRepeating(fd)) { 218 Metadata repeatingMetadata = new Metadata(); 219 metadata.setDataType(DataType.LIST); 220 221 repeatingMetadata.setWriteAccess(WriteAccess.ALWAYS); 222 repeatingMetadata.setOnChangeRefreshMetadata(false); 223 repeatingMetadata.setDataType(convertDictionaryDataType(fd.getDataType())); 224 225 if (nestedProperties != null) { 226 repeatingMetadata.setProperties(nestedProperties); 227 } 228 229 Map<String, Metadata> repeatingProperty = new HashMap<String, Metadata>(); 230 repeatingProperty.put("*", repeatingMetadata); 231 metadata.setProperties(repeatingProperty); 232 } else if (nestedProperties != null) { 233 metadata.setProperties(nestedProperties); 234 } 235 236 properties.put(fd.getName(), metadata); 237 238 } 239 } 240 241 counter.decrement(objectId); 242 return properties; 243 } 244 245 /** 246 * This method determines if a field is repeating 247 * 248 * @param fd 249 * @return 250 */ 251 protected boolean isRepeating(FieldDefinition fd) { 252 boolean isRepeating = false; 253 try { 254 int maxOccurs = Integer.parseInt(fd.getMaxOccurs()); 255 isRepeating = maxOccurs > 1; 256 } catch (NumberFormatException nfe) { 257 isRepeating = FieldDefinition.UNBOUNDED.equals(fd.getMaxOccurs()); 258 } 259 260 return isRepeating; 261 } 262 263 /** 264 * This method gets the object structure for given objectKey from a dictionaryService 265 * 266 * @param objectKey 267 * @return 268 */ 269 protected ObjectStructureDefinition getObjectStructure(String objectKey) { 270 DictionaryService dictionaryService = dictionaryServiceMap.get(objectKey); 271 272 if (dictionaryService == null) { 273 throw new RuntimeException("Dictionary service not provided for objectKey=[" + objectKey + "]."); 274 } 275 276 return dictionaryService.getObjectStructure(objectKey); 277 } 278 279 protected List<ConstraintMetadata> getConstraints(FieldDefinition fd, String type, String state, String nextState) { 280 List<ConstraintMetadata> constraints = new ArrayList<ConstraintMetadata>(); 281 282 ConstraintMetadata constraintMetadata = new ConstraintMetadata(); 283 284 updateConstraintMetadata(constraintMetadata, (Constraint) fd, type, state, nextState); 285 constraints.add(constraintMetadata); 286 287 return constraints; 288 } 289 290 /** 291 * This updates the constraintMetadata with defintions from the dictionary constraint field. 292 * 293 * @param constraintMetadata 294 * @param constraint 295 */ 296 protected void updateConstraintMetadata(ConstraintMetadata constraintMetadata, Constraint constraint, String type, String state, String nextState) { 297 // For now ignoring the serverSide flag and making determination of which constraints 298 // should be passed up to the UI via metadata. 299 300 // Min Length 301 if (constraint.getMinLength() != null) { 302 constraintMetadata.setMinLength(constraint.getMinLength()); 303 } 304 305 // Max Length 306 try { 307 if (constraint.getMaxLength() != null) { 308 constraintMetadata.setMaxLength(Integer.parseInt(constraint.getMaxLength())); 309 } 310 // Do we need to add another constraint and label it required if minOccurs = 1 311 } catch (NumberFormatException nfe) { 312 // Ignoring an unbounded length, cannot be handled in metadata structure, maybe change Metadata to string or set 313 // to -1 314 constraintMetadata.setMaxLength(9999); 315 } 316 317 // Min Occurs 318 if (constraint.getMinOccurs() != null) { 319 constraintMetadata.setMinOccurs(constraint.getMinOccurs()); 320 } 321 322 // Max Occurs 323 String maxOccurs = constraint.getMaxOccurs(); 324 if (maxOccurs != null) { 325 try { 326 constraintMetadata.setMaxOccurs(Integer.parseInt(maxOccurs)); 327 if (!FieldDefinition.SINGLE.equals(maxOccurs)) { 328 constraintMetadata.setId("repeating"); 329 } 330 } catch (NumberFormatException nfe) { 331 // Setting unbounded to a value of 9999, since unbounded not handled by metadata 332 if (FieldDefinition.UNBOUNDED.equals(maxOccurs)) { 333 constraintMetadata.setId("repeating"); 334 constraintMetadata.setMaxOccurs(9999); 335 } 336 } 337 } 338 339 // Min Value 340 if (constraint.getExclusiveMin() != null) { 341 constraintMetadata.setMinValue(constraint.getExclusiveMin()); 342 } 343 344 // Max Value 345 if (constraint.getInclusiveMax() != null) { 346 constraintMetadata.setMaxValue(constraint.getInclusiveMax()); 347 } 348 349 if (constraint.getValidChars() != null) { 350 constraintMetadata.setValidChars(constraint.getValidChars().getValue()); 351 constraintMetadata.setValidCharsMessageId(constraint.getValidChars().getLabelKey()); 352 } 353 354 // Case constraints 355 if (constraint.getCaseConstraint() != null) { 356 processCaseConstraint(constraintMetadata, constraint.getCaseConstraint(), type, state, nextState); 357 } 358 } 359 360 protected void processCaseConstraint(ConstraintMetadata constraintMetadata, CaseConstraint caseConstraint, String type, String state, String nextState) { 361 String fieldPath = caseConstraint.getFieldPath(); 362 List<WhenConstraint> whenConstraints = caseConstraint.getWhenConstraint(); 363 364 fieldPath = (fieldPath != null ? fieldPath.toUpperCase() : fieldPath); 365 if ("STATE".equals(fieldPath)) { 366 // Process a state constraint 367 368 // Defaults for state and nextState 369 state = (state == null ? DtoState.DRAFT.toString():state); 370 nextState = (nextState == null ? DtoState.getNextStateAsString(state):nextState); 371 372 if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) { 373 for (WhenConstraint whenConstraint : whenConstraints) { 374 List<Object> values = whenConstraint.getValues(); 375 if (values != null) { 376 Constraint constraint = whenConstraint.getConstraint(); 377 378 // Set the required for next state flag 379 if (values.contains(nextState)) { 380 if (constraint.getMinOccurs() > 0) { 381 constraintMetadata.setRequiredForNextState(true); 382 constraintMetadata.setNextState(nextState); 383 } 384 } 385 386 // Update constraints based on state constraints 387 if (values.contains(state.toUpperCase())) { 388 updateConstraintMetadata(constraintMetadata, constraint, type, state, nextState); 389 } 390 } 391 } 392 } 393 } else if ("TYPE".equals(fieldPath)) { 394 // Process a type constraint 395 396 if ("EQUALS".equals(caseConstraint.getOperator()) && whenConstraints != null) { 397 for (WhenConstraint whenConstraint : whenConstraints) { 398 List<Object> values = whenConstraint.getValues(); 399 if (values != null && values.contains(type)) { 400 Constraint constraint = whenConstraint.getConstraint(); 401 updateConstraintMetadata(constraintMetadata, constraint, type, state, nextState); 402 } 403 } 404 } 405 } 406 } 407 408 /** 409 * Convert Object value to respective DataType. Method return null for object Value. 410 * 411 * @param dataType 412 * @param value 413 * @return 414 */ 415 protected Value convertDefaultValue(DataType dataType, Object value) { 416 Value v = null; 417 if (value instanceof String) { 418 String s = (String) value; 419 switch (dataType) { 420 case STRING: 421 v = new Data.StringValue(s); 422 break; 423 case BOOLEAN: 424 v = new Data.BooleanValue(Boolean.valueOf(s)); 425 break; 426 case FLOAT: 427 v = new Data.FloatValue(Float.valueOf(s)); 428 break; 429 case DATE: 430 DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 431 try { 432 v = new Data.DateValue(format.parse(s)); 433 } catch (ParseException e) { 434 LOG.error("Unable to get default date value from metadata definition"); 435 } 436 break; 437 case LONG: 438 if (!s.isEmpty()) { 439 v = new Data.LongValue(Long.valueOf(s)); 440 } 441 break; 442 case DOUBLE: 443 v = new Data.DoubleValue(Double.valueOf(s)); 444 break; 445 case INTEGER: 446 v = new Data.IntegerValue(Integer.valueOf(s)); 447 break; 448 } 449 } else { 450 v = convertDefaultValue(value); 451 } 452 453 return v; 454 } 455 456 protected Value convertDefaultValue(Object value) { 457 Value v = null; 458 459 if (value instanceof String) { 460 v = new Data.StringValue((String) value); 461 } else if (value instanceof Boolean) { 462 v = new Data.BooleanValue((Boolean) value); 463 } else if (value instanceof Integer) { 464 v = new Data.IntegerValue((Integer) value); 465 } else if (value instanceof Double) { 466 v = new Data.DoubleValue((Double) value); 467 } else if (value instanceof Long) { 468 v = new Data.LongValue((Long) value); 469 } else if (value instanceof Short) { 470 v = new Data.ShortValue((Short) value); 471 } else if (value instanceof Float) { 472 v = new Data.FloatValue((Float) value); 473 } 474 475 return v; 476 } 477 478 protected DataType convertDictionaryDataType(org.kuali.student.common.dictionary.dto.DataType dataType) { 479 switch (dataType) { 480 case STRING: 481 return DataType.STRING; 482 case BOOLEAN: 483 return DataType.BOOLEAN; 484 case INTEGER: 485 return DataType.INTEGER; 486 case FLOAT: 487 return DataType.FLOAT; 488 case COMPLEX: 489 return DataType.DATA; 490 case DATE: 491 return DataType.DATE; 492 case DOUBLE: 493 return DataType.DOUBLE; 494 case LONG: 495 return DataType.LONG; 496 } 497 498 return null; 499 } 500 501 public void setUiLookupContext(String uiLookupContext) { 502 this.uiLookupContext = uiLookupContext; 503 init(); 504 505 } 506 507 @SuppressWarnings("unchecked") 508 private void init() { 509 ApplicationContext ac = new ClassPathXmlApplicationContext(uiLookupContext); 510 511 Map<String, UILookupConfig> beansOfType = (Map<String, UILookupConfig>) ac.getBeansOfType(UILookupConfig.class); 512 lookupObjectStructures = new ArrayList<UILookupConfig>(); 513 for (UILookupConfig objStr : beansOfType.values()) { 514 lookupObjectStructures.add(objStr); 515 } 516 System.out.println("UILookup loaded"); 517 } 518 519 private String calcSimpleName(String objectKey) { 520 int lastDot = objectKey.lastIndexOf("."); 521 if (lastDot == -1) { 522 return objectKey; 523 } 524 return objectKey.substring(lastDot + 1); 525 526 } 527 528 private boolean matchesObjectKey(String objectKey, String path) { 529 String simpleName = calcSimpleName(objectKey); 530 if (path.toLowerCase().startsWith(simpleName.toLowerCase())) { 531 // System.out.println ("matchesObjectKey: is TRUE for " + objectKey + " and " + path); 532 return true; 533 } 534 // System.out.println ("matchesObjectKey: is FALSE for " + objectKey + " and " + path); 535 return false; 536 } 537 538 private boolean matchesType(String paramType, String lookupType) { 539 // both null 540 if (paramType == null && lookupType == null) { 541 return true; 542 } 543 // not asking for type specific but the lookup defnition is type specific then 544 // no match 545 if (paramType == null && lookupType != null) { 546 return false; 547 } 548 // if looking for type specific but the lookup is not specific then 549 // take as default 550 // If configuration has both a null type (i.e. default) AND has a type 551 // specific one the type specific one has to be entered into the configuration 552 // file first so it is found first 553 if (paramType != null && lookupType == null) { 554 return true; 555 } 556 if (paramType.equalsIgnoreCase(lookupType)) { 557 // System.out.println ("matchesType: is TRUE for " + paramType + " and " + lookupType); 558 return true; 559 } 560 // System.out.println ("matchesType: is FALSE for " + paramType + " and " + lookupType); 561 return false; 562 } 563 564 private void addLookupstoMetadata(String objectKey, Metadata metadata, String type) { 565 if (lookupObjectStructures != null) { 566 for (UILookupConfig lookup : lookupObjectStructures) { 567 if (!matchesObjectKey(objectKey, lookup.getPath())) { 568 continue; 569 } 570 if (!matchesType(type, lookup.getType())) { 571 continue; 572 } 573 // TODO: figure out why path=courseInfo.creditOptions.type matches any structure that has a type on it so 574 // that lookup gets returned for all types 575 Map<String, Metadata> parsedMetadataMap = metadata.getProperties(); 576 Metadata parsedMetadata = null; 577 String parsedMetadataKey = ""; 578 String lookupFieldPath = lookup.getPath(); 579 String[] lookupPathTokens = getPathTokens(lookupFieldPath); 580 for (int i = 1; i < lookupPathTokens.length; i++) { 581 if (parsedMetadataMap == null) { 582 break; 583 } 584 if (i == lookupPathTokens.length - 1) { 585 // get the metadata on the last path key token 586 parsedMetadata = parsedMetadataMap.get(lookupPathTokens[i]); 587 parsedMetadataKey = parsedMetadataKey + "." + lookupPathTokens[i]; 588 } 589 if (parsedMetadataMap.get(lookupPathTokens[i]) != null) { 590 parsedMetadataMap = parsedMetadataMap.get(lookupPathTokens[i]).getProperties(); 591 } else if (parsedMetadataMap.get("*") != null) { 592 // Lookup wildcard in case of unbounded elements in metadata. 593 parsedMetadataMap = parsedMetadataMap.get("*").getProperties(); 594 i--; 595 } 596 597 } 598 if (parsedMetadata != null) { 599 // System.out.println ("addLookupstoMetadata:" + parsedMetadataKey + " was found as a match for " + 600 // lookup.getPath ()); 601 UILookupData initialLookup = lookup.getInitialLookup(); 602 if (initialLookup != null) { 603 mapLookupDatatoMeta(initialLookup); 604 parsedMetadata.setInitialLookup(mapLookupDatatoMeta(lookup.getInitialLookup())); 605 } 606 List<LookupMetadata> additionalLookupMetadata = null; 607 if (lookup.getAdditionalLookups() != null) { 608 additionalLookupMetadata = new ArrayList<LookupMetadata>(); 609 for (UILookupData additionallookup : lookup.getAdditionalLookups()) { 610 additionalLookupMetadata.add(mapLookupDatatoMeta(additionallookup)); 611 } 612 parsedMetadata.setAdditionalLookups(additionalLookupMetadata); 613 } 614 } 615 } 616 } 617 } 618 619 private LookupMetadata mapLookupDatatoMeta(UILookupData lookupData) { 620 LookupMetadata lookupMetadata = new LookupMetadata(); 621 List<LookupParamMetadata> paramsMetadata; 622 BeanUtils.copyProperties(lookupData, lookupMetadata, new String[]{"widget", "usage", "widgetOptions", "params"}); 623 if (lookupData.getWidget() != null) { 624 lookupMetadata.setWidget(org.kuali.student.common.assembly.data.LookupMetadata.Widget.valueOf(lookupData.getWidget().toString())); 625 } 626 if (lookupData.getUsage() != null) { 627 lookupMetadata.setUsage(org.kuali.student.common.assembly.data.LookupMetadata.Usage.valueOf(lookupData.getUsage().toString())); 628 } 629 if (lookupData.getWidgetOptions () != null) { 630 lookupMetadata.setWidgetOptions (new HashMap ()); 631 for (UILookupData.WidgetOption wo: lookupData.getWidgetOptions ().keySet ()) { 632 String value = lookupData.getWidgetOptions ().get (wo); 633 LookupMetadata.WidgetOption key = LookupMetadata.WidgetOption.valueOf(wo.toString()); 634 lookupMetadata.getWidgetOptions ().put (key, value); 635 } 636 } 637 if (lookupData.getParams() != null) { 638 paramsMetadata = new ArrayList<LookupParamMetadata>(); 639 for (CommonLookupParam param : lookupData.getParams()) { 640 paramsMetadata.add(mapLookupParamMetadata(param)); 641 } 642 lookupMetadata.setParams(paramsMetadata); 643 } 644 // WidgetOptions is not used as of now. So not setting it into metadata. 645 return lookupMetadata; 646 } 647 648 private LookupParamMetadata mapLookupParamMetadata(CommonLookupParam param) { 649 LookupParamMetadata paramMetadata = new LookupParamMetadata(); 650 BeanUtils.copyProperties(param, paramMetadata, new String[]{"childLookup", "dataType", "writeAccess", "usage", "widget"}); 651 if (param.getChildLookup() != null) { 652 paramMetadata.setChildLookup(mapLookupDatatoMeta((UILookupData) param.getChildLookup())); 653 } 654 if (param.getDataType() != null) { 655 paramMetadata.setDataType(org.kuali.student.common.assembly.data.Data.DataType.valueOf(param.getDataType().toString())); 656 } 657 if (param.getWriteAccess() != null) { 658 paramMetadata.setWriteAccess(org.kuali.student.common.assembly.data.Metadata.WriteAccess.valueOf(param.getWriteAccess().toString())); 659 } 660 if (param.getUsage() != null) { 661 paramMetadata.setUsage(org.kuali.student.common.assembly.data.LookupMetadata.Usage.valueOf(param.getUsage().toString())); 662 } 663 if (param.getWidget() != null) { 664 paramMetadata.setWidget(org.kuali.student.common.assembly.data.LookupParamMetadata.Widget.valueOf(param.getWidget().toString())); 665 } 666 667 return paramMetadata; 668 } 669 670 private static String[] getPathTokens(String fieldPath) { 671 return (fieldPath != null && fieldPath.contains(".") ? fieldPath.split("\\.") : new String[]{fieldPath}); 672 } 673 }