Coverage Report - org.kuali.student.datadictionary.util.DictionaryCreator
 
Classes in this File Line Coverage Branch Coverage Complexity
DictionaryCreator
84%
158/186
77%
73/94
6.9
DictionaryCreator$1
100%
1/1
N/A
6.9
DictionaryCreator$PropertyDescriptorComparator
93%
31/33
92%
26/28
6.9
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 1.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl1.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.student.datadictionary.util;
 17  
 
 18  
 import java.beans.BeanInfo;
 19  
 import java.beans.IntrospectionException;
 20  
 import java.beans.Introspector;
 21  
 import java.beans.PropertyDescriptor;
 22  
 import java.io.File;
 23  
 import java.io.FileNotFoundException;
 24  
 import java.io.FileOutputStream;
 25  
 import java.io.IOException;
 26  
 import java.io.OutputStream;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Arrays;
 29  
 import java.util.Collections;
 30  
 import java.util.Comparator;
 31  
 import java.util.HashSet;
 32  
 import java.util.List;
 33  
 import java.util.Set;
 34  
 import org.kuali.rice.kns.datadictionary.DataObjectEntry;
 35  
 
 36  
 import org.kuali.rice.kns.datadictionary.validation.DataType;
 37  
 
 38  25
 public class DictionaryCreator {
 39  
 
 40  
     private static String initLower(String str) {
 41  676
         if (str == null) {
 42  0
             return null;
 43  
         }
 44  676
         if (str.length() == 0) {
 45  0
             return str;
 46  
         }
 47  676
         if (str.length() == 1) {
 48  0
             return str.toLowerCase();
 49  
         }
 50  676
         return str.substring(0, 1).toLowerCase() + str.substring(1);
 51  
     }
 52  
 
 53  
     public void execute(Class<?> clazz) {
 54  25
         String outputFileName = "target/ks-" + clazz.getSimpleName() + "-dictinoary.xml";
 55  25
         execute(clazz, outputFileName);
 56  25
     }
 57  
 
 58  
     public void execute(Class<?> clazz, String outputFileName) {
 59  
         // Create base dictionary object structure for DTOs that map to entities
 60  25
         File file = new File(outputFileName);
 61  
         OutputStream os;
 62  
         try {
 63  25
             os = new FileOutputStream(file);
 64  0
         } catch (FileNotFoundException ex) {
 65  0
             throw new IllegalArgumentException(ex);
 66  25
         }
 67  25
         StringBuffer s = new StringBuffer();
 68  25
         addSpringHeaderOpen(s);
 69  
 
 70  25
         System.out.println(clazz.getName());
 71  25
         addObjectStructure(clazz, s, new HashSet<Class<?>>());
 72  
 
 73  25
         addSpringHeaderClose(s);
 74  
         try {
 75  25
             os.write(s.toString().getBytes());
 76  25
             os.close();
 77  0
         } catch (IOException ex) {
 78  0
             throw new IllegalArgumentException(ex);
 79  25
         }
 80  25
     }
 81  
 
 82  
     private void addObjectStructure(Class<?> clazz, StringBuffer s,
 83  
             Set<Class<?>> processed) {
 84  
         //Don't process if processed
 85  25
         if (processed.contains(clazz)) {
 86  0
             return;
 87  
         }
 88  25
         processed.add(clazz);
 89  
 
 90  
         BeanInfo beanInfo;
 91  
         try {
 92  25
             beanInfo = Introspector.getBeanInfo(clazz);
 93  0
         } catch (IntrospectionException ex) {
 94  0
             throw new IllegalArgumentException(ex);
 95  25
         }
 96  
 
 97  
 
 98  
         //Step 1, create the abstract structure
 99  25
         s.append("\n\n<!-- " + clazz.getSimpleName() + "-->");
 100  25
         s.append("\n<bean id=\"" + initLower(clazz.getSimpleName())
 101  
                 + "-parent\" abstract=\"true\" parent=\"" + initLower(DataObjectEntry.class.getSimpleName())
 102  
                 + "\">");
 103  25
         addProperty("name", initLower(clazz.getSimpleName()), s);
 104  25
         addProperty("objectClass", clazz.getName(), s);
 105  25
         addProperty("objectLabel", "", s);
 106  25
         addProperty("objectDescription", "", s);
 107  25
         String titleAttribute = calcTitleAttribute(clazz, beanInfo);
 108  25
         if (titleAttribute != null) {
 109  11
             addProperty("titleAttribute", titleAttribute, s);
 110  
         }
 111  25
         s.append("\n<property name=\"primaryKeys\">");
 112  25
         List<String> pks = calcPrimaryKeys(clazz, beanInfo);
 113  25
         if (pks != null && !pks.isEmpty()) {
 114  15
             s.append("\n<list>");
 115  15
             for (String pk : pks) {
 116  15
                 addValue(pk, s);
 117  
             }
 118  15
             s.append("\n</list>");
 119  
         }
 120  25
         s.append("\n</property>");
 121  25
         s.append("\n<property name=\"attributes\">");
 122  25
         s.append("\n<list>");
 123  
 
 124  25
         for (PropertyDescriptor pd : getFilteredSortedProperties(beanInfo)) {
 125  175
             String fieldName = initLower(clazz.getSimpleName() + "." + initLower(pd.getName()));
 126  175
             s.append("\n<ref bean=\"" + fieldName + "\"/>");
 127  175
         }
 128  25
         s.append("\n</list>");
 129  25
         s.append("\n</property>");
 130  25
         s.append("\n</bean>");
 131  
 
 132  
         //Create the instance
 133  25
         s.append("\n<bean id=\"" + initLower(clazz.getSimpleName()) + "\" parent=\""
 134  
                 + initLower(clazz.getSimpleName()) + "-parent\"/>");
 135  
 
 136  
         //Step 2, loop through attributes
 137  25
         Set<Class<?>> dependantStructures = new HashSet<Class<?>>();
 138  25
         for (PropertyDescriptor pd : getFilteredSortedProperties(beanInfo)) {
 139  175
             dependantStructures.addAll(addAttributeDefinition(clazz, pd, s, processed));
 140  
         }
 141  
         //Step 3, process all dependant object structures
 142  25
         for (Class<?> dependantClass : dependantStructures) {
 143  0
             addObjectStructure(dependantClass, s, processed);
 144  
         }
 145  25
     }
 146  
 
 147  
     private List<PropertyDescriptor> getFilteredSortedProperties(BeanInfo beanInfo) {
 148  50
         List<PropertyDescriptor> list = new ArrayList(beanInfo.getPropertyDescriptors().length);
 149  450
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 150  400
             if (pd.getPropertyType().equals(Class.class)) {
 151  50
                 continue;
 152  
             }
 153  350
             list.add(pd);
 154  
         }
 155  50
         Collections.sort(list, new PropertyDescriptorComparator());
 156  50
         return list;
 157  
     }
 158  
 
 159  25
     private static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
 160  
 
 161  
         @Override
 162  
         public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
 163  816
             return calcRank(o1).compareTo(calcRank(o2));
 164  
         }
 165  
 
 166  
         private String calcRank(PropertyDescriptor pd) {
 167  
 
 168  1632
             String name = pd.getName();
 169  1632
             String lowerName = pd.getName().toLowerCase();
 170  1632
             if (lowerName.equals("id")) {
 171  40
                 return "00" + name;
 172  
             }
 173  1592
             if (lowerName.equals("key")) {
 174  92
                 return "00" + name;
 175  
             }
 176  1500
             if (lowerName.equals("typekey")) {
 177  154
                 return "01" + name;
 178  
             }
 179  1346
             if (lowerName.equals("statekey")) {
 180  118
                 return "02" + name;
 181  
             }
 182  
             // to handle r1 services
 183  1228
             if (lowerName.equals("type")) {
 184  0
                 return "01" + name;
 185  
             }
 186  1228
             if (lowerName.equals("state")) {
 187  0
                 return "02" + name;
 188  
             }
 189  1228
             if (lowerName.equals("name")) {
 190  108
                 return "03" + name;
 191  
             }
 192  1120
             if (lowerName.equals("descr")) {
 193  152
                 return "04" + name;
 194  
             }
 195  968
             if (lowerName.equals("effectivedate")) {
 196  74
                 return "10" + name;
 197  
             }
 198  894
             if (lowerName.equals("expirationdate")) {
 199  62
                 return "11" + name;
 200  
             }
 201  832
             if (lowerName.equals("attributes")) {
 202  180
                 return "80" + name;
 203  
             }
 204  652
             if (lowerName.equals("metainfo")) {
 205  130
                 return "90" + name;
 206  
             }
 207  522
             if (lowerName.equals("startdate")) {
 208  74
                 return "48" + name;
 209  
             }
 210  448
             if (lowerName.equals("enddate")) {
 211  68
                 return "49" + name;
 212  
             }
 213  380
             return "50" + name;
 214  
         }
 215  
     }
 216  
 
 217  
     private String calcTitleAttribute(Class<?> clazz, BeanInfo beanInfo) {
 218  185
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 219  171
             if (pd.getName().equalsIgnoreCase("name")) {
 220  11
                 return initLower(pd.getName());
 221  
             }
 222  
         }
 223  94
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 224  80
             if (pd.getName().equalsIgnoreCase("title")) {
 225  0
                 return initLower(pd.getName());
 226  
             }
 227  
         }
 228  94
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 229  80
             if (pd.getName().toLowerCase().endsWith("name")) {
 230  0
                 return initLower(pd.getName());
 231  
             }
 232  
         }
 233  94
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 234  80
             if (pd.getName().toLowerCase().endsWith("title")) {
 235  0
                 return initLower(pd.getName());
 236  
             }
 237  
         }
 238  14
         return null;
 239  
     }
 240  
 
 241  
     private List<String> calcPrimaryKeys(Class<?> clazz, BeanInfo beanInfo) {
 242  197
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 243  177
             if (pd.getName().equalsIgnoreCase("id")) {
 244  5
                 return Arrays.asList(initLower(pd.getName()));
 245  
             }
 246  
         }
 247  118
         for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
 248  108
             if (pd.getName().equalsIgnoreCase("key")) {
 249  10
                 return Arrays.asList(initLower(pd.getName()));
 250  
             }
 251  
         }
 252  10
         return null;
 253  
     }
 254  
 
 255  
     private Set<Class<?>> addAttributeDefinition(Class<?> clazz, PropertyDescriptor pd,
 256  
             StringBuffer s, Set<Class<?>> processed) {
 257  175
         Set<Class<?>> dependantStructures = new HashSet<Class<?>>();
 258  
 
 259  
         //Create the abstract field
 260  175
         String name = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(
 261  
                 1) + "." + pd.getName();
 262  175
         Class<?> actualClass = Bean2DictionaryConverter.calcActualClass(clazz, pd);
 263  175
         DataType dt = calcDataType(clazz, pd);
 264  175
         String parentField = calcBaseKualiType(actualClass, pd, dt);
 265  
 
 266  175
         s.append("\n\n<bean id=\"" + name
 267  
                 + "-parent\" abstract=\"true\" parent=\"" + parentField + "\">");
 268  175
         addProperty("name", initLower(pd.getName()), s);
 269  175
         if (isList(pd)) {
 270  18
             addProperty("maxOccurs", "" + DictionaryConstants.UNBOUNDED, s);
 271  
         }
 272  175
         s.append("\n</bean>");
 273  
 
 274  
         //Create the instance
 275  175
         s.append("\n<bean id=\"" + name + "\" parent=\"" + name
 276  
                 + "-parent\"/>");
 277  175
         return dependantStructures;
 278  
     }
 279  
 
 280  
     private DataType calcDataType(Class<?> clazz, PropertyDescriptor pd) {
 281  175
         Class<?> actualClass = Bean2DictionaryConverter.calcActualClass(clazz, pd);
 282  175
         DataType dataType = Bean2DictionaryConverter.calcDataType(clazz, actualClass);
 283  175
         return dataType;
 284  
     }
 285  
 
 286  
     private boolean isList(PropertyDescriptor pd) {
 287  175
         if (pd.getPropertyType().equals(List.class)) {
 288  18
             return true;
 289  
         }
 290  157
         return false;
 291  
     }
 292  
 
 293  
     private boolean isComplex(Class<?> clazz, PropertyDescriptor pd) {
 294  0
         return isComplex(calcDataType(clazz, pd));
 295  
     }
 296  
 
 297  
     private boolean isComplex(DataType dt) {
 298  0
         if (dt.equals(DataType.COMPLEX)) {
 299  0
             return true;
 300  
         }
 301  0
         return false;
 302  
     }
 303  
 
 304  
     private String calcBaseKualiType(Class<?> clazz, PropertyDescriptor pd, DataType dt) {
 305  
 
 306  175
         String name = pd.getName();
 307  175
         String lowerName = pd.getName().toLowerCase();
 308  175
         if (lowerName.equals("id")) {
 309  5
             return "baseKualiId";
 310  
         }
 311  170
         if (lowerName.equals("key")) {
 312  11
             return "baseKualiKey";
 313  
         }
 314  159
         if (lowerName.equals("typeKey")) {
 315  0
             return "baseKualiTypeKey";
 316  
         }
 317  159
         if (lowerName.equals("stateKey")) {
 318  0
             return "baseKualiStateKey";
 319  
         }
 320  
         // to handle r1 services
 321  159
         if (lowerName.equals("type")) {
 322  0
             return "baseKualiTypeKey";
 323  
         }
 324  159
         if (lowerName.equals("state")) {
 325  0
             return "baseKualiStateKey";
 326  
         }
 327  159
         if (lowerName.equals("effectivedate")) {
 328  7
             return "baseKualiEffectiveDate";
 329  
         }
 330  152
         if (lowerName.equals("expirationdate")) {
 331  7
             return "baseKualiExpirationDate";
 332  
         }
 333  145
         if (lowerName.endsWith("orgid")) {
 334  0
             return "baseKualiOrgId";
 335  
         }
 336  145
         if (lowerName.endsWith("personid")) {
 337  1
             return "baseKualiPersonId";
 338  
         }
 339  144
         if (lowerName.endsWith("principalid")) {
 340  1
             return "baseKualiPrincipalId";
 341  
         }
 342  143
         if (lowerName.endsWith("cluid")) {
 343  1
             return "baseKualiCluId";
 344  
         }
 345  142
         if (lowerName.endsWith("luiid")) {
 346  3
             return "baseKualiCluId";
 347  
         }
 348  139
         if (lowerName.endsWith("code")) {
 349  1
             return "baseKualiCode";
 350  
         }
 351  1
         switch (dt) {
 352  
             case STRING:
 353  68
                 return "baseKualiString";
 354  
             case DATE:
 355  24
                 return "baseKualiDateTime";
 356  
             case TRUNCATED_DATE:
 357  0
                 return "baseKualiDate";
 358  
             case BOOLEAN:
 359  6
                 return "baseKualiBoolean";
 360  
             case INTEGER:
 361  
             case LONG:
 362  5
                 return "baseKualiInteger";
 363  
             case FLOAT:
 364  
             case DOUBLE:
 365  0
                 return "baseKualiCurrency";
 366  
             case COMPLEX:
 367  35
                 return "baseKualiComplex";
 368  
             default:
 369  0
                 return "baseKualiString";
 370  
         }
 371  
     }
 372  
 
 373  
     private void addValue(String value, StringBuffer s) {
 374  15
         s.append("\n<value>" + value + "</value>");
 375  15
     }
 376  
 
 377  
     private void addProperty(String propertyName, String propertyValue,
 378  
             StringBuffer s) {
 379  304
         s.append("\n<property name=\"" + propertyName + "\" value=\"" + propertyValue
 380  
                 + "\"/>");
 381  304
     }
 382  
 
 383  
     private static void addPropertyRef(String propertyName, String propertyValue,
 384  
             StringBuffer s) {
 385  0
         s.append("\n<property name=\"" + propertyName + "\" ref=\"" + propertyValue
 386  
                 + "\"/>");
 387  0
     }
 388  
 
 389  
     private void addSpringHeaderClose(StringBuffer s) {
 390  25
         s.append("\n</beans>").append("\n");
 391  25
     }
 392  
 
 393  
     private void addSpringHeaderOpen(StringBuffer s) {
 394  25
         s.append("<!--").append("\n");
 395  25
         s.append(" Copyright 2011 The Kuali Foundation").append("\n");
 396  25
         s.append("").append("\n");
 397  25
         s.append(" Licensed under the Educational Community License, Version 2.0 (the \"License\");").append("\n");
 398  25
         s.append(" you may not use this file except in compliance with the License.").append("\n");
 399  25
         s.append(" You may obtain a copy of the License at").append("\n");
 400  25
         s.append("").append("\n");
 401  25
         s.append(" http://www.opensource.org/licenses/ecl2.php").append("\n");
 402  25
         s.append("").append("\n");
 403  25
         s.append(" Unless required by applicable law or agreed to in writing, software").append("\n");
 404  25
         s.append(" distributed under the License is distributed on an \"AS IS\" BASIS,").append("\n");
 405  25
         s.append(" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.").append("\n");
 406  25
         s.append(" See the License for the specific language governing permissions and").append("\n");
 407  25
         s.append(" limitations under the License.").append("\n");
 408  25
         s.append("-->").append("\n");
 409  25
         s.append("<beans xmlns=\"http://www.springframework.org/schema/beans\"").append("\n");
 410  25
         s.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"").append("\n");
 411  25
         s.append("xsi:schemaLocation=\"").append("\n");
 412  25
         s.append("http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd").append("\n");
 413  25
         s.append("\">").append("\n");
 414  25
         s.append("\n<import resource=\"classpath:ks-base-dictionary.xml\"/>");
 415  25
     }
 416  
 }
 417