Coverage Report - org.melati.poem.TableFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
TableFactory
96%
148/153
80%
80/100
6.5
TableFactory$Prop
100%
13/13
N/A
6.5
 
 1  
 /*
 2  
  * $Source$
 3  
  * $Revision$
 4  
  *
 5  
  * Copyright (C) 2007 Tim Pizey
 6  
  *
 7  
  * Part of Melati (http://melati.org), a framework for the rapid
 8  
  * development of clean, maintainable web applications.
 9  
  *
 10  
  * Melati is free software; Permission is granted to copy, distribute
 11  
  * and/or modify this software under the terms either:
 12  
  *
 13  
  * a) the GNU General Public License as published by the Free Software
 14  
  *    Foundation; either version 2 of the License, or (at your option)
 15  
  *    any later version,
 16  
  *
 17  
  *    or
 18  
  *
 19  
  * b) any version of the Melati Software License, as published
 20  
  *    at http://melati.org
 21  
  *
 22  
  * You should have received a copy of the GNU General Public License and
 23  
  * the Melati Software License along with this program;
 24  
  * if not, write to the Free Software Foundation, Inc.,
 25  
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
 26  
  * GNU General Public License and visit http://melati.org to obtain the
 27  
  * Melati Software License.
 28  
  *
 29  
  * Feel free to contact the Developers of Melati (http://melati.org),
 30  
  * if you would like to work out a different arrangement than the options
 31  
  * outlined here.  It is our intention to allow Melati to be used by as
 32  
  * wide an audience as possible.
 33  
  *
 34  
  * This program is distributed in the hope that it will be useful,
 35  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 36  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 37  
  * GNU General Public License for more details.
 38  
  *
 39  
  * Contact details for copyright holder:
 40  
  *
 41  
  *     Tim Pizey <timp At paneris.org>
 42  
  *     http://paneris.org/~timp
 43  
  */
 44  
 package org.melati.poem;
 45  
 
 46  
 import java.lang.reflect.Method;
 47  
 import java.lang.reflect.Modifier;
 48  
 import java.util.Collection;
 49  
 import java.util.Enumeration;
 50  
 import java.util.Hashtable;
 51  
 import java.util.Map;
 52  
 
 53  
 import org.melati.poem.util.ClassUtils;
 54  
 import org.melati.poem.util.StringUtils;
 55  
 
 56  
 
 57  
 /**
 58  
  * Given an Object or class create a set of Tables to represent the graph 
 59  
  * it is the starting node of, and populate it for an Object.
 60  
  * 
 61  
  * The rules are a sub-set of the Hibernate rules: 
 62  
  * Only public types can be persisted.
 63  
  * Only members with a public setter and getter will be persisted.
 64  
  * If the object has a field called Id then the Persistent troid column will be called 
 65  
  * <tt>poemId</tt>.  
 66  
  *  
 67  
  * @author timp
 68  
  * @since 12 Jun 2007
 69  
  * 
 70  
  */
 71  
 public final class TableFactory {
 72  
 
 73  
   /**
 74  
    * Disable instantiation.
 75  
    */
 76  0
   private TableFactory() {
 77  0
   }
 78  
 
 79  
   /**
 80  
    * @param db
 81  
    *          the database to create table in and lookup existing tables
 82  
    * @param pojo
 83  
    *          class to introspect
 84  
    * @return A new or existing table
 85  
    */
 86  
   public static Table<?> fromInstance(Database db, Object pojo) {
 87  16
     return fromClass(db, pojo.getClass());
 88  
   }
 89  
 
 90  
   /**
 91  
    * @param db
 92  
    *          the database to create table in and lookup existing tables
 93  
    * @param clazz
 94  
    *          class to introspect
 95  
    * @return A new or existing table
 96  
    */
 97  
   public static Table<?> fromClass(Database db, Class<?> clazz) {
 98  21
     if (clazz.isPrimitive())
 99  1
       throw new IllegalArgumentException(
 100  1
       "Cannot create a Table for a primitive type: " + clazz.getName());
 101  20
     if (clazz.isInterface())
 102  0
       throw new IllegalArgumentException(
 103  0
       "Cannot create a Table for an interface: " + clazz.getName());
 104  20
     if(!Modifier.isPublic(clazz.getModifiers())) 
 105  1
       throw new IllegalArgumentException(
 106  1
         "Cannot create a Table for a non public type: "+ clazz.getName());
 107  19
     if (clazz.isArray() && !(clazz == byte[].class))
 108  1
       throw new IllegalArgumentException("Cannot create a Table for an array: " + clazz.getName());
 109  18
     String name = clazz.getName();
 110  18
     String simpleName = name.substring(name.lastIndexOf('.') + 1);
 111  
     try {
 112  18
       return db.getTable(simpleName);
 113  12
     } catch (NoSuchTablePoemException e) {
 114  12
       System.err.println("Creating a table for : " + name);
 115  
     }
 116  
     // We will have to create one
 117  12
     TableInfo tableInfo = (TableInfo)db.getTableInfoTable().newPersistent();
 118  12
     tableInfo.setName(simpleName);
 119  12
     tableInfo.setDisplayname(simpleName);
 120  12
     tableInfo.setDescription(simpleName + " introspected table");
 121  12
     tableInfo.setDisplayorder(13);
 122  12
     tableInfo.setSeqcached(Boolean.FALSE);
 123  12
     tableInfo.setCategory(db.getTableCategoryTable().NORMAL);
 124  12
     tableInfo.setCachelimit(555);
 125  12
     tableInfo.makePersistent();
 126  
 
 127  12
     Table<Persistent> table = new JdbcTable<Persistent>(db, simpleName, DefinitionSource.runtime);
 128  12
     String troidName = "id";
 129  12
     if (ClassUtils.getNoArgMethod(clazz, "getId") != null &&
 130  3
         !(Persistent.class.isAssignableFrom(clazz)))
 131  3
       troidName = "poemId";
 132  24
     table.defineColumn(new ExtraColumn<Integer>(table, troidName, TroidPoemType.it,
 133  12
             DefinitionSource.runtime, table.getNextExtrasIndex()));
 134  12
     table.setTableInfo(tableInfo);
 135  12
     table.unifyWithColumnInfo();
 136  12
     table.unifyWithDB(null,troidName);
 137  
 
 138  12
     PoemThread.commit();
 139  12
     db.defineTable(table);
 140  12
     Hashtable<String,Prop> props = new Hashtable<String,Prop>();
 141  
 
 142  12
     Method[] methods = clazz.getMethods();
 143  
 
 144  252
     for (int i = 0; i < methods.length; i++) {
 145  240
       if (Modifier.isPublic(methods[i].getModifiers())) {
 146  240
         if (methods[i].getName().startsWith("set") 
 147  68
             && ! methods[i].getName().equals("set")
 148  68
             && Character.isUpperCase(methods[i].getName().toCharArray()[3])
 149  68
             && methods[i].getParameterTypes().length == 1
 150  68
             && (methods[i].getParameterTypes()[0] == byte[].class || 
 151  67
                 ! methods[i].getParameterTypes()[0].isArray())
 152  68
             && !methods[i].getParameterTypes()[0].isInterface()
 153  68
             && ! Collection.class.isAssignableFrom(methods[i].getParameterTypes()[0])
 154  68
             && ! Map.class.isAssignableFrom(methods[i].getParameterTypes()[0])
 155  
             ) {
 156  68
           String propName = methods[i].getName().substring(3);
 157  68
           propName = StringUtils.uncapitalised(propName);
 158  68
           Prop p = (Prop)props.get(propName);
 159  68
           Class<?> setClass = methods[i].getParameterTypes()[0];
 160  68
           if (p == null) {
 161  16
             p = new Prop(propName);
 162  16
             p.setSet(setClass);
 163  
           } else { 
 164  52
             if (p.getGot() != null) { 
 165  52
               if(p.getGot() == setClass)
 166  48
                 p.setSet(setClass);
 167  
             } else { 
 168  0
               p.setSet(setClass);                
 169  
             }
 170  
           }
 171  68
           props.put(propName, p);
 172  
         }
 173  
         
 174  240
         if (methods[i].getParameterTypes().length == 0
 175  136
             && ! methods[i].getReturnType().isInterface()
 176  136
             && (methods[i].getReturnType() == byte[].class || 
 177  135
                 !methods[i].getReturnType().isArray())
 178  136
             && !Collection.class.isAssignableFrom(methods[i].getReturnType())
 179  136
             && !Map.class.isAssignableFrom(methods[i].getReturnType())
 180  
         ) {
 181  136
           String propName = null;
 182  136
           if ((methods[i].getName().startsWith("get")) && 
 183  72
                Character.isUpperCase(methods[i].getName().toCharArray()[3]))
 184  72
             propName = methods[i].getName().substring(3);
 185  64
           else if (methods[i].getName().startsWith("is") && 
 186  4
                    Character.isUpperCase(methods[i].getName().toCharArray()[2])) 
 187  4
             propName = methods[i].getName().substring(2);
 188  136
           if (propName != null) { 
 189  76
             propName = StringUtils.uncapitalised(propName);
 190  76
             Prop p = (Prop)props.get(propName);
 191  76
             Class<?> gotClass = methods[i].getReturnType();
 192  76
             if (p == null) {
 193  60
               p = new Prop(propName);
 194  60
               p.setGot(gotClass);
 195  
             } else { 
 196  16
               p.setGot(gotClass);
 197  
             }
 198  76
             props.put(propName, p);
 199  
           }
 200  
         }
 201  
       }
 202  
     }
 203  12
     Enumeration<Prop> propsEn = props.elements();
 204  88
     while (propsEn.hasMoreElements()) {
 205  76
       Prop p = (Prop)propsEn.nextElement();
 206  76
       if (p.getGot() != null && 
 207  76
           p.getGot() == p.getSet()) { 
 208  64
         addColumn(table, p.getName(), p.getGot(), p.getGot() == p.getSet());
 209  
       }
 210  76
     }
 211  12
     return table;
 212  
   }
 213  
 
 214  
   private static void addColumn(Table<?> table, String name, Class<?> fieldClass,
 215  
           boolean hasSetter) {
 216  64
     ColumnInfo columnInfo = (ColumnInfo)table.getDatabase()
 217  64
             .getColumnInfoTable().newPersistent();
 218  64
     columnInfo.setTableinfo(table.getInfo());
 219  64
     columnInfo.setName(name);
 220  
     
 221  64
     columnInfo.setDisplayname(name);
 222  64
     columnInfo.setDisplayorder(99);
 223  64
     columnInfo.setSearchability(Searchability.yes);
 224  64
     columnInfo.setIndexed(false);
 225  64
     columnInfo.setUnique(false);
 226  64
     columnInfo.setDescription("An introspected member");
 227  64
     columnInfo.setUsercreateable(hasSetter);
 228  64
     columnInfo.setUsereditable(hasSetter);
 229  64
     columnInfo.setSize(8);
 230  64
     columnInfo.setWidth(20);
 231  64
     columnInfo.setHeight(1);
 232  64
     columnInfo.setPrecision(0);
 233  64
     columnInfo.setScale(0);
 234  64
     columnInfo.setNullable(true);
 235  64
     columnInfo.setDisplaylevel(DisplayLevel.record);
 236  64
     if (fieldClass == java.lang.Boolean.class) {
 237  4
       columnInfo.setTypefactory(PoemTypeFactory.BOOLEAN);
 238  4
       columnInfo.setSize(1);
 239  4
       columnInfo.setWidth(10);
 240  60
     } else if (fieldClass == boolean.class) {
 241  4
       columnInfo.setTypefactory(PoemTypeFactory.BOOLEAN);
 242  4
       columnInfo.setSize(1);
 243  4
       columnInfo.setWidth(10);
 244  56
     } else if (fieldClass == java.lang.Integer.class) {
 245  5
       columnInfo.setTypefactory(PoemTypeFactory.INTEGER);
 246  51
     } else if (fieldClass == int.class) {
 247  4
       columnInfo.setTypefactory(PoemTypeFactory.INTEGER);
 248  47
     } else if (fieldClass == java.lang.Double.class) {
 249  4
       columnInfo.setTypefactory(PoemTypeFactory.DOUBLE);
 250  43
     } else if (fieldClass == double.class) {
 251  4
       columnInfo.setTypefactory(PoemTypeFactory.DOUBLE);
 252  39
     } else if (fieldClass == java.lang.Long.class) {
 253  4
       columnInfo.setTypefactory(PoemTypeFactory.LONG);
 254  35
     } else if (fieldClass == long.class) {
 255  4
       columnInfo.setTypefactory(PoemTypeFactory.LONG);
 256  31
     } else if (fieldClass == java.math.BigDecimal.class) {
 257  4
       columnInfo.setTypefactory(PoemTypeFactory.BIGDECIMAL);
 258  4
       columnInfo.setPrecision(22);
 259  4
       columnInfo.setScale(2);
 260  27
     } else if (fieldClass == java.lang.String.class) {
 261  14
       columnInfo.setTypefactory(PoemTypeFactory.STRING);
 262  14
       columnInfo.setSize(-1);
 263  13
     } else if (fieldClass == java.sql.Date.class) {
 264  4
       columnInfo.setTypefactory(PoemTypeFactory.DATE);
 265  9
     } else if (fieldClass == java.sql.Timestamp.class) {
 266  4
       columnInfo.setTypefactory(PoemTypeFactory.TIMESTAMP);
 267  5
     } else if (fieldClass == byte[].class) {
 268  1
       columnInfo.setTypefactory(PoemTypeFactory.BINARY);
 269  
     }
 270  
     else {
 271  4
       Table<?> referredTable = fromClass(table.getDatabase(), fieldClass);
 272  8
       columnInfo.setTypefactory(PoemTypeFactory.forCode(table.getDatabase(),
 273  4
               referredTable.getInfo().troid().intValue()));
 274  
     }
 275  64
     columnInfo.makePersistent();
 276  64
     table.addColumnAndCommit(columnInfo);
 277  64
   }
 278  
 
 279  
   /**
 280  
    * Details of a member variable.
 281  
    */
 282  
   static final class Prop {
 283  76
     String name = null;
 284  76
     Class<?> set = null;
 285  76
     Class<?> got = null;
 286  
     
 287  76
     Prop(String name) {
 288  76
       this.name = name;
 289  76
     }
 290  
 
 291  
     /**
 292  
      * @return the name
 293  
      */
 294  
     public String getName() {
 295  64
       return name;
 296  
     }
 297  
 
 298  
     /**
 299  
      * @return the got
 300  
      */
 301  
     public Class<?> getGot() {
 302  384
       return got;
 303  
     }
 304  
 
 305  
     /**
 306  
      * @param got
 307  
      *          the Class of the getter
 308  
      */
 309  
     public void setGot(Class<?> got) {
 310  76
       this.got = got;
 311  76
     }
 312  
 
 313  
     /**
 314  
      * @return the set
 315  
      */
 316  
     public Class<?> getSet() {
 317  140
       return set;
 318  
     }
 319  
 
 320  
     /**
 321  
      * @param set
 322  
      *          the Class of the setter
 323  
      */
 324  
     public void setSet(Class<?> set) {
 325  64
       this.set = set;
 326  64
     }
 327  
 
 328  
   }
 329  
 }