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