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