001 package org.kuali.student.common.dictionary.service.impl; 002 003 import java.beans.BeanInfo; 004 import java.beans.IntrospectionException; 005 import java.beans.Introspector; 006 import java.beans.PropertyDescriptor; 007 import java.io.File; 008 import java.io.FileNotFoundException; 009 import java.io.FileOutputStream; 010 import java.io.IOException; 011 import java.io.OutputStream; 012 import java.lang.reflect.ParameterizedType; 013 import java.util.Date; 014 import java.util.HashSet; 015 import java.util.List; 016 import java.util.Set; 017 018 import org.kuali.student.common.dictionary.dto.FieldDefinition; 019 import org.kuali.student.common.dto.Idable; 020 import org.kuali.student.common.dto.MetaInfo; 021 022 public class DictionaryCreator 023 { 024 025 private static final String OBJECT_STRUCTURE_CLASS = 026 "objectStructureDefinition"; 027 private static final String FIELD_DEFINITION_CLASS = 028 FieldDefinition.class.getSimpleName (); 029 private static final String ATTRIBUTES = "attributes"; 030 private static final String NAME = "name"; 031 private static final String HAS_META_DATA = "hasMetaData"; 032 private static final String DATA_OBJECT_STRUCTURE = "dataObjectStructure"; 033 private static final String BASE_INTEGER_REPEATING = "baseIntegerRepeating"; 034 private static final String BASE_LONG_REPEATING = "baseLongRepeating"; 035 private static final String BASE_DOUBLE_REPEATING = "baseDoubleRepeating"; 036 private static final String BASE_FLOAT_REPEATING = "baseFloatRepeating"; 037 private static final String BASE_BOOLEAN_REPEATING = "baseBooleanRepeating"; 038 private static final String BASE_DATE_REPEATING = "baseDateRepeating"; 039 private static final String BASE_STRING_REPEATING = "baseStringRepeating"; 040 private static final String BASE_COMPLEX_REPEATING = "baseComplexRepeating"; 041 private static final String BASE_INTEGER = "baseInteger"; 042 private static final String BASE_LONG = "baseLong"; 043 private static final String BASE_DOUBLE = "baseDouble"; 044 private static final String BASE_FLOAT = "baseFloat"; 045 private static final String BASE_BOOLEAN = "baseBoolean"; 046 private static final String BASE_DATE = "baseDate"; 047 private static final String BASE_STRING = "baseString"; 048 private static final String BASE_COMPLEX = "baseComplex"; 049 private static final String BASE_KUALI_ID = "baseKualiId"; 050 private static final String BASE_KUALI_ORG_ID = "baseKualiOrgId"; 051 private static final String BASE_KUALI_CLU_ID = "baseKualiCluId"; 052 private static final String BASE_KUALI_PERSON_ID = "baseKualiPersonId"; 053 private static final String BASE_KUALI_TYPE = "baseKualiType"; 054 private static final String BASE_KUALI_STATE = "baseKualiState"; 055 private static final String BASE_KUALI_EFFECTIVE_DATE = 056 "baseKualiEffectiveDate"; 057 private static final String BASE_KUALI_EXPIRATION_DATE = 058 "baseKualiExpirationDate"; 059 060 061 public void execute (Class<?> clazz, String outputFileName) 062 { 063 // Create base dictionary object structure for DTOs that map to entities 064 File file = new File (outputFileName); 065 OutputStream os; 066 try 067 { 068 os = new FileOutputStream (file); 069 } 070 catch (FileNotFoundException ex) 071 { 072 throw new IllegalArgumentException (ex); 073 } 074 StringBuffer s = new StringBuffer (); 075 addSpringHeaderOpen (s); 076 077 System.out.println (clazz.getName ()); 078 addObjectStructure (clazz, s, new HashSet<Class<?>> ()); 079 080 addSpringHeaderClose (s); 081 try 082 { 083 os.write (s.toString ().getBytes ()); 084 } 085 catch (IOException ex) 086 { 087 throw new IllegalArgumentException (ex); 088 } 089 } 090 091 private void addObjectStructure (Class<?> clazz, StringBuffer s, 092 Set<Class<?>> processed) 093 { 094 //Don't process if processed 095 if (processed.contains (clazz)) 096 { 097 return; 098 } 099 processed.add (clazz); 100 101 //Step 1, create the abstract structure 102 s.append ("\n\n<!-- " + clazz.getSimpleName () + "-->"); 103 s.append ("\n<bean id=\"" + clazz.getName () 104 + "-parent\" abstract=\"true\" parent=\"" + OBJECT_STRUCTURE_CLASS 105 + "\">"); 106 addProperty (NAME, clazz.getName (), s); 107 s.append ("\n<property name=\"" + ATTRIBUTES + "\">"); 108 s.append ("\n<list>"); 109 BeanInfo beanInfo; 110 try 111 { 112 beanInfo = Introspector.getBeanInfo (clazz); 113 } 114 catch (IntrospectionException ex) 115 { 116 throw new IllegalArgumentException (ex); 117 } 118 boolean hasMetaData = false; 119 for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors ()) 120 { 121 if ( ! MetaInfo.class.equals (pd.getPropertyType ()) && ! Class.class.equals ( 122 pd.getPropertyType ()) && ! ATTRIBUTES.equals (pd.getName ())) 123 { 124 String fieldName = clazz.getSimpleName ().substring (0, 1).toLowerCase () + clazz.getSimpleName ().substring ( 125 1) + "." + pd.getName (); 126 s.append ("\n<ref bean=\"" + fieldName + "\"/>"); 127 } 128 else 129 { 130 hasMetaData = true; 131 } 132 } 133 s.append ("\n</list>"); 134 s.append ("\n</property>"); 135 136 addProperty (HAS_META_DATA, String.valueOf (hasMetaData), s); 137 s.append ("\n</bean>"); 138 139 //Create the instance 140 s.append ("\n<bean id=\"" + clazz.getName () + "\" parent=\"" 141 + clazz.getName () + "-parent\"/>"); 142 //Step 2, loop through attributes 143 Set<Class<?>> dependantStructures = new HashSet<Class<?>> (); 144 for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors ()) 145 { 146 if ( ! MetaInfo.class.equals (pd.getPropertyType ()) && ! Class.class.equals ( 147 pd.getPropertyType ()) && ! ATTRIBUTES.equals (pd.getName ())) 148 { 149 dependantStructures.addAll (addField (clazz, pd, s, processed)); 150 } 151 } 152 //Step 3, process all dependant object structures 153 for (Class<?> dependantClass : dependantStructures) 154 { 155 addObjectStructure (dependantClass, s, processed); 156 } 157 158 } 159 160 private Set<Class<?>> addField (Class<?> clazz, PropertyDescriptor pd, 161 StringBuffer s, Set<Class<?>> processed) 162 { 163 Set<Class<?>> dependantStructures = new HashSet<Class<?>> (); 164 165 //Create the abstract field 166 String fieldName = clazz.getSimpleName ().substring (0, 1).toLowerCase () + clazz.getSimpleName ().substring ( 167 1) + "." + pd.getName (); 168 Class<?> pt = pd.getPropertyType (); 169 String parentField = FIELD_DEFINITION_CLASS; 170 boolean isComplex = false; 171 if (clazz.isAssignableFrom (Idable.class) && "id".equals (pd.getName ())) 172 { 173 parentField = BASE_KUALI_ID; 174 } 175 else if (pd.getName ().endsWith ("orgId")) 176 { 177 parentField = BASE_KUALI_ORG_ID; 178 } 179 else if (pd.getName ().endsWith ("personId")) 180 { 181 parentField = BASE_KUALI_PERSON_ID; 182 } 183 else if (pd.getName ().endsWith ("cluId")) 184 { 185 parentField = BASE_KUALI_CLU_ID; 186 } 187 else if (List.class.equals (pt)) 188 { 189 try 190 { 191 pt = 192 (Class<?>) ((ParameterizedType) clazz.getDeclaredField (pd.getName ()).getGenericType ()).getActualTypeArguments ()[0]; 193 } 194 catch (NoSuchFieldException ex) 195 { 196 throw new IllegalArgumentException (ex); 197 } 198 catch (SecurityException ex) 199 { 200 throw new IllegalArgumentException (ex); 201 } 202 if (int.class.equals (pt) || Integer.class.equals (pt)) 203 { 204 parentField = BASE_INTEGER_REPEATING; 205 } 206 else if (long.class.equals (pt) || Long.class.equals (pt)) 207 { 208 parentField = BASE_LONG_REPEATING; 209 } 210 else if (double.class.equals (pt) || Double.class.equals (pt)) 211 { 212 parentField = BASE_DOUBLE_REPEATING; 213 } 214 else if (float.class.equals (pt) || Float.class.equals (pt)) 215 { 216 parentField = BASE_FLOAT_REPEATING; 217 } 218 else if (boolean.class.equals (pt) || Boolean.class.equals (pt)) 219 { 220 parentField = BASE_BOOLEAN_REPEATING; 221 } 222 else if (Date.class.equals (pt)) 223 { 224 parentField = BASE_DATE_REPEATING; 225 } 226 else if (String.class.equals (pt)) 227 { 228 parentField = BASE_STRING_REPEATING; 229 } 230 else if (List.class.equals (pt)) 231 { 232 throw new RuntimeException ("Can't have a list of lists, List<List<?>> for property: " 233 + fieldName); 234 } 235 else 236 { 237 parentField = BASE_COMPLEX_REPEATING; 238 isComplex = true; 239 dependantStructures.add (pt); 240 } 241 } 242 else 243 { 244 if (int.class.equals (pt) || Integer.class.equals (pt)) 245 { 246 parentField = BASE_INTEGER; 247 } 248 else if (long.class.equals (pt) || Long.class.equals (pt)) 249 { 250 parentField = BASE_LONG; 251 } 252 else if (double.class.equals (pt) || Double.class.equals (pt)) 253 { 254 parentField = BASE_DOUBLE; 255 } 256 else if (float.class.equals (pt) || Float.class.equals (pt)) 257 { 258 parentField = BASE_FLOAT; 259 } 260 else if (boolean.class.equals (pt) || Boolean.class.equals (pt)) 261 { 262 parentField = BASE_BOOLEAN; 263 } 264 else if (Date.class.equals (pt)) 265 { 266 parentField = BASE_DATE; 267 } 268 else if (String.class.equals (pt)) 269 { 270 parentField = BASE_STRING; 271 } 272 else 273 { 274 parentField = BASE_COMPLEX; 275 isComplex = true; 276 dependantStructures.add (pt); 277 } 278 } 279 280 281 s.append ("\n\n<bean id=\"" + fieldName 282 + "-parent\" abstract=\"true\" parent=\"" + parentField + "\">"); 283 addProperty (NAME, pd.getName (), s); 284 if (isComplex) 285 { 286 addPropertyRef (DATA_OBJECT_STRUCTURE, pt.getName (), s); 287 } 288 289 s.append ("\n</bean>"); 290 291 //Create the instance 292 s.append ("\n<bean id=\"" + fieldName + "\" parent=\"" + fieldName 293 + "-parent\"/>"); 294 295 return dependantStructures; 296 } 297 298 private void addProperty (String propertyName, String propertyValue, 299 StringBuffer s) 300 { 301 s.append ("\n<property name=\"" + propertyName + "\" value=\"" + propertyValue 302 + "\"/>"); 303 } 304 305 private static void addPropertyRef (String propertyName, String propertyValue, 306 StringBuffer s) 307 { 308 s.append ("\n<property name=\"" + propertyName + "\" ref=\"" + propertyValue 309 + "\"/>"); 310 } 311 312 private void addSpringHeaderClose (StringBuffer s) 313 { 314 s.append ("\n</beans>"); 315 } 316 317 public void addSpringHeaderOpen (StringBuffer s) 318 { 319 s.append ("<beans xmlns=\"http://www.springframework.org/schema/beans\"" 320 + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" 321 + "xsi:schemaLocation=\"" 322 + "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" 323 + "\">"); 324 s.append ("\n<import resource=\"classpath:ks-base-dictionary-context.xml\"/>"); 325 } 326 }