Coverage Report - org.kuali.student.common.assembly.transform.DefaultDataBeanMapper
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultDataBeanMapper
68%
125/183
55%
87/156
8.6
 
 1  
 package org.kuali.student.common.assembly.transform;
 2  
 
 3  
 import java.beans.BeanInfo;
 4  
 import java.beans.Introspector;
 5  
 import java.beans.PropertyDescriptor;
 6  
 import java.lang.reflect.Field;
 7  
 import java.lang.reflect.ParameterizedType;
 8  
 import java.lang.reflect.Type;
 9  
 import java.sql.Time;
 10  
 import java.sql.Timestamp;
 11  
 import java.util.ArrayList;
 12  
 import java.util.Collection;
 13  
 import java.util.Date;
 14  
 import java.util.HashMap;
 15  
 import java.util.HashSet;
 16  
 import java.util.Iterator;
 17  
 import java.util.List;
 18  
 import java.util.Map;
 19  
 import java.util.Map.Entry;
 20  
 import java.util.Set;
 21  
 
 22  
 import org.apache.log4j.Logger;
 23  
 import org.kuali.student.common.assembly.data.Data;
 24  
 import org.kuali.student.common.assembly.data.Data.DataType;
 25  
 import org.kuali.student.common.assembly.data.Data.DataValue;
 26  
 import org.kuali.student.common.assembly.data.Data.Key;
 27  
 import org.kuali.student.common.assembly.data.Data.Property;
 28  
 import org.kuali.student.common.assembly.data.Data.StringKey;
 29  
 import org.kuali.student.common.assembly.data.Metadata;
 30  
 
 31  
 
 32  2
 public class DefaultDataBeanMapper implements DataBeanMapper {
 33  1
         public static DataBeanMapper INSTANCE = new DefaultDataBeanMapper();
 34  2
         final Logger LOG = Logger.getLogger(DefaultDataBeanMapper.class);        
 35  
         
 36  
         /* (non-Javadoc)
 37  
          * @see org.kuali.student.core.assembly.data.DataBeanMapper#convertFromBean(java.lang.Object)
 38  
          */
 39  
         public Data convertFromBean(Object value, Metadata metadata) throws Exception{
 40  2
                  Data result = new Data();
 41  
                 
 42  2
                 if (value != null) {
 43  2
                 BeanInfo info = Introspector.getBeanInfo(value.getClass());
 44  2
                 PropertyDescriptor[] properties = info.getPropertyDescriptors();
 45  
                  
 46  24
                 for (PropertyDescriptor pd : properties) {
 47  22
                     String propKey = pd.getName();
 48  22
                     Object propValue = pd.getReadMethod().invoke(value, (Object[]) null);
 49  
                     
 50  22
                     if ("attributes".equals(propKey)){
 51  1
                             setDataAttributes(result, propValue, metadata);
 52  
                     } else {
 53  21
                             setDataValue(result, propKey, propValue, metadata);
 54  
                     }
 55  
                 }
 56  
                 }        
 57  
                 
 58  2
                 return result;
 59  
         }
 60  
         
 61  
         /* (non-Javadoc)
 62  
          * @see org.kuali.student.core.assembly.data.DataBeanMapper#convertFromData(org.kuali.student.core.assembly.data.Data, java.lang.Class)
 63  
          * 
 64  
          * FIXME: Probably a good idea to change "clazz" parameter to be "String objectType" to be consistent with convertFromBean method
 65  
          */
 66  
         public Object convertFromData(Data data, Class<?> clazz, Metadata metadata) throws Exception{
 67  2
                 Object result = null;
 68  
                 
 69  2
                 result = clazz.newInstance();
 70  2
         BeanInfo info = Introspector.getBeanInfo(result.getClass());
 71  2
         PropertyDescriptor[] properties = info.getPropertyDescriptors();
 72  
 
 73  2
         PropertyDescriptor attrProperty = null;
 74  
         
 75  2
         Set<Key> staticProperties = new HashSet<Key>();
 76  24
                 for (PropertyDescriptor pd : properties) {
 77  22
                         if ("attributes".equals(pd.getName())){
 78  
                                 //Dynamic attributes will be handled later
 79  1
                                 attrProperty = pd;
 80  
                         } else {
 81  21
                     StringKey propKey = new StringKey(pd.getName());
 82  21
                     Object propValue = data.get(propKey);
 83  
         
 84  
                     //Process a nested object structure or list
 85  21
                     if (propValue instanceof Data){
 86  1
                             clazz.getFields();
 87  1
                             if(metadata!=null){
 88  1
                                     if(DataType.LIST.equals(metadata.getDataType())){
 89  0
                                             propValue = convertNestedData((Data)propValue, clazz.getDeclaredField(propKey.toString()),metadata.getProperties().get("*"));
 90  
                                     }else{
 91  1
                                             propValue = convertNestedData((Data)propValue, clazz.getDeclaredField(propKey.toString()),metadata.getProperties().get(propKey.toString()));
 92  
                                     }
 93  
                             }
 94  
                             else{
 95  0
                                     propValue = convertNestedData((Data)propValue, clazz.getDeclaredField(propKey.toString()),null);
 96  
                             }
 97  20
                     }else if(metadata!=null&&propValue==null){
 98  1
                             Metadata fieldMetadata = metadata.getProperties().get(propKey.toString());
 99  1
                             if(fieldMetadata != null && fieldMetadata.getDefaultValue() != null){
 100  0
                                     propValue = fieldMetadata.getDefaultValue().get();        
 101  
                             }
 102  
                     }
 103  
                     
 104  
                             //Set the bean property
 105  21
                             if(pd.getWriteMethod() != null & propValue != null){
 106  18
                                     if(!(propValue instanceof List) && pd.getPropertyType().isAssignableFrom(List.class)){
 107  0
                                             ArrayList<Object> list = new ArrayList<Object>(1);
 108  0
                                             list.add(propValue);
 109  0
                                             pd.getWriteMethod().invoke(result, list);
 110  0
                                     }else{
 111  18
                                             pd.getWriteMethod().invoke(result, new Object[] {propValue});
 112  
                                     }
 113  
                     }
 114  
                     
 115  
                             //Hold onto the property so we know it is not dynamic
 116  21
                             staticProperties.add(propKey);
 117  
                         }
 118  
                 }
 119  
                 
 120  
                 //Any fields not processed above doesn't exist as properties for the bean and 
 121  
                 //will be set as dynamic attributes.
 122  2
                 Set<Key> keySet = data.keySet();
 123  2
                 if (keySet != null && attrProperty != null){
 124  1
                         Map<String,String> attributes = new HashMap<String,String>();
 125  1
             for (Key k : keySet) {
 126  11
                                 String keyString = k.toString();
 127  
                                 //Obtain the dynamic flag from the dictionary
 128  11
                                 if(metadata==null){
 129  11
                                         if (!staticProperties.contains(k) && data.get(k) != null && !keyString.startsWith("_run")){
 130  2
                                                 attributes.put((String)k.get(),data.get(k).toString());
 131  
                                         }
 132  
                                 }
 133  
                                 else {
 134  0
                                     if ((! staticProperties.contains(k)) &&
 135  
                                         (null != data.get(k)) &&
 136  
                                         (! keyString.startsWith("_run")) &&
 137  
                                         (null != metadata.getProperties().get(keyString)) &&
 138  
                                         (metadata.getProperties().get(keyString).isDynamic()))
 139  
                                     {
 140  0
                                             if (data.get(k) instanceof Data){
 141  0
                                                     attributes.put((String) k.get(), convertDataValueToStringValue((Data)data.get(k)));
 142  
                                             } else {
 143  0
                                                     attributes.put((String) k.get(), data.get(k).toString());
 144  
                                             }
 145  
                                         }
 146  
                                 }
 147  11
                         }
 148  1
             if (attrProperty.getWriteMethod() != null) {
 149  1
                 attrProperty.getWriteMethod().invoke(result, new Object[] {attributes});
 150  
             }
 151  
                 }
 152  
 
 153  2
                 return result;
 154  
         }
 155  
         
 156  
         
 157  
         /**
 158  
          * Processes a nested data map, it checks to see if the data should be converted to 
 159  
          * a list structure or simply be processed as a nested complex object structure.
 160  
          * 
 161  
          * @param data
 162  
          * @param propField
 163  
          * @return
 164  
          * @throws Exception
 165  
          */
 166  
         protected Object convertNestedData(Data data, Field propField, Metadata metadata) throws Exception{
 167  1
                 Object result = null;
 168  
 
 169  1
                 Class<?> propClass = propField.getType();
 170  1
                 if ("java.util.List".equals(propClass.getName())){
 171  
                         //Get the generic type for the list
 172  1
                         ParameterizedType propType = (ParameterizedType)propField.getGenericType();
 173  1
                         Type itemType = propType.getActualTypeArguments()[0];
 174  
                         
 175  
                         
 176  1
                         List<Object> resultList = new ArrayList<Object>();
 177  1
                         for(Iterator<Property> listIter = data.realPropertyIterator(); listIter.hasNext();){
 178  1
                                 Property listItem = listIter.next();
 179  1
                                 Object listItemValue = listItem.getValue();
 180  1
                                 if (listItemValue instanceof Data ){
 181  1
                                         Data listItemData = (Data)listItemValue;
 182  1
                                         Boolean isDeleted = listItemData.query("_runtimeData/deleted");
 183  1
                                         if (isDeleted == null || !isDeleted){
 184  1
                                                 if(metadata!=null){
 185  0
                                                         listItemValue = convertFromData((Data)listItemValue, (Class<?>)itemType, metadata.getProperties().get("*"));
 186  
                                                 }else{
 187  1
                                                         listItemValue = convertFromData((Data)listItemValue, (Class<?>)itemType, null);
 188  
                                                 }
 189  1
                                                 resultList.add(listItemValue);
 190  
                                         }
 191  1
                                 } else {
 192  0
                                         resultList.add(listItemValue);
 193  
                                 }
 194  1
                         }
 195  
 
 196  1
                         result = resultList;
 197  1
                 } else {
 198  0
                         result = convertFromData(data, propClass,metadata);
 199  
                 }
 200  
                 
 201  1
                 return result;
 202  
         }
 203  
         
 204  
         /**
 205  
          * Used to set a simple property value into the data object. 
 206  
          * @param data
 207  
          * @param propertyKey
 208  
          * @param value
 209  
          * 
 210  
          */
 211  
         protected void setDataValue(Data data, String propertyKey, Object value, Metadata metadata) throws Exception{
 212  21
                 if (isPropertyValid(value)){
 213  18
                         if (value instanceof Boolean){
 214  0
                                 data.set(propertyKey, (Boolean)value);
 215  18
                         } else if (value instanceof Date){
 216  1
                                 data.set(propertyKey, (Date)value);
 217  17
                         } else if (value instanceof Integer){
 218  0
                                 data.set(propertyKey, (Integer)value);
 219  17
                         } else if (value instanceof Double){
 220  1
                                 data.set(propertyKey, (Double)value);
 221  16
                         } else if (value instanceof Float){
 222  0
                                 data.set(propertyKey, (Float)value);
 223  16
                         } else if (value instanceof Long) {
 224  0
                                 data.set(propertyKey, (Long)value);
 225  16
                         } else if (value instanceof Short){
 226  0
                                 data.set(propertyKey, (Short)value);                        
 227  16
                         } else if (value instanceof String){
 228  15
                                 data.set(propertyKey, (String)value);
 229  1
                         } else if (value instanceof Timestamp){
 230  0
                                 data.set(propertyKey, (Timestamp)value);
 231  1
                         } else if (value instanceof Time){
 232  0
                                 data.set(propertyKey, (Time)value);
 233  1
                         } else if (value instanceof Collection){
 234  
                                 //TODO: Determine correct metadata to pass in getCollectionData() instead of null
 235  1
                                 data.set(propertyKey, getCollectionData(value, null));
 236  
                         } else {
 237  
                                 //TODO: Determine correct metadata to pass into convertFromBean() instead of null
 238  0
                                 Data dataValue = convertFromBean(value, null);
 239  0
                                 data.set(propertyKey, dataValue);
 240  
                         }
 241  
                 }
 242  
                 
 243  21
         }
 244  
         
 245  
         /**
 246  
          * This process the attributes map and sets the attribute key/value pairs into the Data map
 247  
          * 
 248  
          * @param data
 249  
          * @param value
 250  
          */
 251  
         protected void setDataAttributes(Data data, Object value, Metadata metadata) {
 252  
                 @SuppressWarnings("unchecked")
 253  1
                 Map<String, String> attributes = (Map<String, String>)value;
 254  
                 
 255  1
                 for (Entry<String, String> entry:attributes.entrySet()){
 256  2
                 Metadata fieldMetadata = null;
 257  2
                 if (metadata != null) {
 258  0
                         fieldMetadata = metadata.getProperties().get(entry.getKey());
 259  
                 } else {
 260  
                         //FIXME: Fix this so this warning never happens !!
 261  2
                         LOG.warn("Metadata was null while processing property " + entry.getKey());
 262  
                 }
 263  2
                 if (fieldMetadata != null && DataType.LIST.equals(fieldMetadata.getDataType())){
 264  0
                         data.set(entry.getKey(), convertStringToDataValue(entry.getValue()));
 265  2
                 } else if ("false".equals(entry.getValue())||"true".equals(entry.getValue())){
 266  0
                                 data.set(entry.getKey(), Boolean.valueOf(entry.getValue()));
 267  
                         }else{
 268  2
                                 data.set(entry.getKey(), entry.getValue());
 269  
                         }
 270  2
                 }
 271  1
         }
 272  
 
 273  
         /**
 274  
          * Converts a list represented by a comma delimited string so to a DataValue
 275  
          * 
 276  
          * @param stringValue a comma separated list of values
 277  
          * @return DataValue representation of stringValue
 278  
          */
 279  
         protected Data convertStringToDataValue(String stringValue) {
 280  0
                 Data data = null;
 281  
                 
 282  0
                 if (stringValue != null){
 283  0
                         data = new Data();
 284  0
                         String[] stringValues = stringValue.split(",");
 285  0
                         for (String value:stringValues){
 286  0
                                 data.add(value);
 287  
                         }
 288  
                 }
 289  
                                 
 290  0
                 return data;
 291  
         }
 292  
 
 293  
         /**
 294  
          * Converts a list represented by DataValue to a list of values as a comma delimited StringValue
 295  
 
 296  
          * @param dataValue DataValue representing a list object
 297  
          * @return the list converted to a comma delimited StringValue
 298  
          */
 299  
         protected String convertDataValueToStringValue(Data data) {
 300  0
                 StringBuffer sbValue = new StringBuffer();
 301  
                 
 302  0
                 Iterator<Property> propertyIterator = data.realPropertyIterator();
 303  0
                 while(propertyIterator.hasNext()){
 304  0
                         Property property = propertyIterator.next();
 305  0
                         String propertyValue = property.getValue();
 306  0
                         sbValue.append(",");
 307  0
                         sbValue.append(propertyValue);
 308  0
                 }
 309  
                                 
 310  0
                 if (sbValue.toString().isEmpty()){
 311  0
                         return "";
 312  
                 } else {
 313  0
                         return sbValue.toString().substring(1);
 314  
                 }
 315  
         }
 316  
 
 317  
         /**
 318  
          * Used to set a list item value into the data object.
 319  
          * 
 320  
          * @param data
 321  
          * @param value
 322  
          */
 323  
         protected void setDataListValue(Data data, Object value, Metadata metadata) throws Exception{
 324  1
                 if (isPropertyValid(value)){
 325  1
                         if (value instanceof Boolean){
 326  0
                                 data.add((Boolean)value);
 327  1
                         } else if (value instanceof Date){
 328  0
                                 data.add((Date)value);
 329  1
                         } else if (value instanceof Integer){
 330  0
                                 data.add((Integer)value);
 331  1
                         } else if (value instanceof Double){
 332  0
                                 data.add((Double)value);
 333  1
                         } else if (value instanceof Float){
 334  0
                                 data.add((Float)value);
 335  1
                         } else if (value instanceof Long) {
 336  0
                                 data.add((Long)value);
 337  1
                         } else if (value instanceof Short){
 338  0
                                 data.add((Short)value);                        
 339  1
                         } else if (value instanceof String){
 340  0
                                 data.add((String)value);
 341  1
                         } else if (value instanceof Timestamp){
 342  0
                                 data.add((Timestamp)value);
 343  1
                         } else if (value instanceof Time){
 344  0
                                 data.add((Time)value);
 345  1
                         } else if (value instanceof Collection){
 346  
                                 //TODO: Find correct metadata to pass in 
 347  0
                                 data.add(getCollectionData(value, null));
 348  
                         } else {
 349  
                                 //TODO: Find correct metadata to pass in                                
 350  1
                                 Data dataValue = convertFromBean(value, null);
 351  1
                                 data.add(dataValue);                        
 352  
                         }
 353  
                 }
 354  1
         }
 355  
         
 356  
         /**
 357  
          * Returns a data map object representing a collection
 358  
          * 
 359  
          * @param value
 360  
          * @return
 361  
          * @throws Exception
 362  
          */
 363  
         protected Data getCollectionData(Object value, Metadata metadata) throws Exception{
 364  1
                 Data result = new Data();
 365  
                 
 366  1
                 if (value instanceof List){
 367  1
                         List<?> valueList = (List<?>)value;
 368  2
                         for (int i=0;i<valueList.size();i++){
 369  1
                                 Object itemValue = valueList.get(i); 
 370  1
                                 setDataListValue(result, itemValue, metadata);
 371  
                         }
 372  1
                 } else {
 373  0
                         Collection<?> valueCollection = (Collection<?>)value;
 374  0
                         for (Object o:valueCollection){
 375  0
                                 setDataListValue(result, o, metadata);
 376  
                         }
 377  
                 }
 378  
                 
 379  1
                 return result;
 380  
         }
 381  
         
 382  
         protected boolean isPropertyValid(Object value){
 383  22
                 boolean isValid = false;
 384  
                 
 385  22
                 if (value != null){
 386  21
                         Class<?> clazz = value.getClass();
 387  21
                         isValid = !(clazz.isArray() || clazz.isAnnotation() || value instanceof Class);
 388  
                 }
 389  
                 
 390  22
                 return isValid;
 391  
         }
 392  
 }