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    }