Coverage Report - org.javagen.revgen.mapping.Db2JavaMapper
 
Classes in this File Line Coverage Branch Coverage Complexity
Db2JavaMapper
77% 
81% 
0
 
 1  
 /*
 2  
  * Copyright 2006 Outsource Cafe, Inc.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 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.javagen.revgen.mapping;
 17  
 
 18  
 import org.javagen.agile.core.util.RegexRenamer;
 19  
 import org.javagen.agile.core.util.StringUtil;
 20  
 import org.javagen.agile.db.model.Cardinality;
 21  
 import org.javagen.agile.db.model.Column;
 22  
 import org.javagen.agile.db.model.FkConstraint;
 23  
 import org.javagen.agile.db.model.FkEnum;
 24  
 import org.javagen.agile.db.model.Table;
 25  
 import org.javagen.agile.oo.model.Class;
 26  
 import org.javagen.agile.oo.naming.Collections;
 27  
 import org.javagen.agile.oo.naming.JavaNamingService;
 28  
 import org.javagen.agile.oo.naming.OONamingService;
 29  
 import org.javagen.agile.oo.util.OOUtil;
 30  
 
 31  
 /**
 32  
  * Maps DB elements to Java types and names.
 33  
  * <p>
 34  
  * TODO with a little more refactoring this class could be non-Java specific and renamed DefaultDb2OOMapper.
 35  
  * 
 36  
  * @author Richard Easterling
 37  
  */
 38  
 public class Db2JavaMapper implements Db2OOMapper {
 39  
         
 40  
     public static final String DEFAULT_TYPE = "String";
 41  
     
 42  
         protected OONamingService ooNamingService;
 43  
     private Collections collections;
 44  2
     private String defaultType = DEFAULT_TYPE;
 45  2
     private ReferenceNamingStrategy referenceNamingStrategy = ReferenceNamingStrategy.useTargetClass;
 46  
     private RegexRenamer regexRenamer;
 47  2
     private boolean pluralTableNames = false;
 48  
     
 49  4
         public Db2JavaMapper() {}
 50  
         
 51  
     ///////////////////////////////////////////////////////////////////////////
 52  
     // Db2JavaMapper interface methods:
 53  
     ///////////////////////////////////////////////////////////////////////////
 54  
 
 55  
     /**
 56  
      * Uses camel-back naming conventions to convert table name to class name converting
 57  
      * plural to singular names as specified in the <code>OONamingService</code>.
 58  
      * <p>Applies regular expression renaming if a RegexRenamer is set.
 59  
      */
 60  
     public String classNameSingular(Table table) {
 61  1
         String name = OOUtil.camelBackJavaClass(table.getName());
 62  1
         if (pluralTableNames)
 63  1
             name = getOONamingService().toSingular(name);
 64  1
         return regexRename(name);
 65  
     }
 66  
 
 67  
     /**
 68  
      * Uses camel-back naming conventions to convert column name to property name.
 69  
      * <p>Applies regular expression renaming if a RegexRenamer is set.
 70  
      */
 71  
     public String propertyNameSingular(Column column) {
 72  3
         String propeName = OOUtil.camelBackJavaClass(column.getName());
 73  3
         if (pluralTableNames)
 74  3
             propeName = getOONamingService().toSingular(propeName);
 75  3
         String varName = OOUtil.varFromClassName(propeName);
 76  3
         return regexRename(varName);
 77  
     }
 78  
 
 79  
     /**
 80  
      * Tries to find best Java type for column type.  The following rules are applied in order:
 81  
      * <ol>
 82  
      * <li>if column size is 1 char and default is 'Y', 'N', 'T' or 'F' then convert to a boolean.</li>
 83  
      * <li>If NUMERIC OR DECIMAL use the smallest possible Java type for the specified precision.</li>
 84  
      * <li>If column is primary or foreign key, use wrapper types for numbers.</li>
 85  
      * <li>For non-null simple types use primitives (byte, int, float), otherwise use the wrapper types (Byte, Integer, Float).</li>
 86  
      * <li>Otherwise use default JDBC mapping types.</li>
 87  
      * <li>If still no match is found use the <code>defaultType</code>, set by default to a String.</li>
 88  
      * </ol>
 89  
      */
 90  
     public String propertyType(Column column) {
 91  20
         java.lang.Class<?> booleanType = Db2JavaUtil.booleanTypeCanidate(column);
 92  20
         if (booleanType!=null) {
 93  2
             return booleanType.getName();
 94  
         } else {
 95  18
             java.lang.Class<?> minimalNumericType = Db2JavaUtil.minimalJavaType(column);
 96  18
             if (minimalNumericType!=null) {
 97  15
                 java.lang.Class<?> primitiveType = Db2JavaUtil.primitiveMappingCanidate(minimalNumericType, column);
 98  15
                 if (primitiveType!=null) {
 99  9
                     return primitiveType.getName();
 100  
                 } else {
 101  6
                     return minimalNumericType.getName();
 102  
                 }
 103  
             } 
 104  
         }
 105  3
         java.lang.Class<?> type = Db2JavaUtil.javaType(column.getDbType());
 106  3
         if (type==null) {
 107  1
             return defaultType;
 108  
         } else {
 109  2
             return type.getName();
 110  
         }
 111  
         }
 112  
  
 113  
     /**
 114  
      * Given a foreign key constraint assign the property type based on the target class,
 115  
      * including special handling needed for relations containing link tables.
 116  
      */
 117  
         public Class referenceType(FkConstraint fkConstraint) {
 118  5
         Class type = null;
 119  5
         Table targetTable = fkConstraint.getTargetTable();
 120  5
         if (Boolean.TRUE.equals(targetTable.isLinkTable())) {
 121  0
             for(FkConstraint linkFkColumn : fkConstraint.getTargetTable().getFkColumns()) {
 122  0
                 if (fkConstraint.getParentTable() != linkFkColumn.getTargetTable())
 123  0
                     targetTable = linkFkColumn.getTargetTable();
 124  0
             }
 125  
         }
 126  5
         type = Db2JavaUtil.getClassFromTable(targetTable);
 127  5
                 return type;
 128  
         }
 129  
 
 130  
         /**
 131  
          * Name reference based on ReferenceNamingStrategy.
 132  
          * Name will be plural or singular based on cardinality.
 133  
      * <p>Applies regular expression renaming if a RegexRenamer is set.
 134  
          * @param fkConstraint
 135  
          * @return reference name for resulting Java property.
 136  
          */
 137  
         public String referenceName(FkConstraint fkConstraint) {
 138  5
         String referencePropName = null;
 139  5
         ReferenceNamingStrategy strategy = referenceNamingStrategy;
 140  5
         if (strategy == ReferenceNamingStrategy.useFkColumn && (fkConstraint.columnReferencesSize()>1 || FkEnum.EXPORTED == fkConstraint.getFkType())) {
 141  
             //useFkColumn naming has limited applicability, fallback when it won't work
 142  1
             strategy = ReferenceNamingStrategy.useTargetClass;
 143  
         }
 144  1
         switch (strategy) {
 145  
             case useTargetClass:
 146  4
                 Class targetClass = referenceType(fkConstraint);
 147  4
                 boolean toMany = isManyCardinality(fkConstraint);
 148  4
                 referencePropName = toMany 
 149  
                     ? getOONamingService().toPlural(targetClass.getName()) 
 150  
                     : getOONamingService().toSingular(targetClass.getName());
 151  4
                 break;
 152  
             case useFkColumn:
 153  
                 //these are IMPORTED constraints holding the fkColumn
 154  1
                 Column fkColumn = fkConstraint.getColumnReferences().get(0).getLocalColumn();
 155  1
                 referencePropName = propertyNameSingular(fkColumn);
 156  1
                 return referencePropName; //already regexRenamed and case-correct
 157  
             case useFkConstraint:
 158  0
                 referencePropName = OOUtil.camelBackJavaClass(fkConstraint.getName());
 159  
         }
 160  4
                 String result = StringUtil.makeFirstLetterLowerCase( referencePropName );
 161  4
         return this.regexRename(result);
 162  
         }
 163  
 
 164  
     /**
 165  
      * This method calls <code>determineCardinality</code> as a side effect
 166  
      * @return true if target table cardinality is to-many.
 167  
      */
 168  
     public boolean isManyCardinality(FkConstraint fkConstraint) {
 169  10
         Cardinality cardinality = determineCardinality(fkConstraint);
 170  10
         return Cardinality.Side.MANY.equals( Cardinality.otherSide(cardinality) );
 171  
     }
 172  
     
 173  
     /**
 174  
      * Determines the cardinality based on foreign key constraints as follows:
 175  
      * <ol>
 176  
      * <li>If cardinality is already set just return it.</li>
 177  
      * <li>if a link table is referenced, return <code>MANY_TO_MANY</code>.</li>
 178  
      * <li>If foreign key is unique, return <code>ONE_TO_ONE</code>.</li>
 179  
      * <li>Otherwise, return <code>MANY_TO_ONE</code> for the side that contains the foreign 
 180  
      * key and <code>ONE_TO_MANY</code> for the opposite side.</li>
 181  
      * </ol>
 182  
      */
 183  
     public Cardinality determineCardinality(FkConstraint fkConstraint) {
 184  10
         Cardinality cardinality = fkConstraint.getCardinality();
 185  10
         if (cardinality==null) {
 186  1
             boolean isLinkTable = Boolean.TRUE.equals(fkConstraint.getTargetTable().isLinkTable());
 187  1
             if (isLinkTable) {
 188  0
                 cardinality = Cardinality.MANY_TO_MANY;
 189  0
             } else if (fkConstraint.unique()) {
 190  0
                 cardinality = Cardinality.ONE_TO_ONE;
 191  0
             } else {
 192  1
                 cardinality = FkEnum.IMPORTED.equals(fkConstraint.getFkType()) ? Cardinality.MANY_TO_ONE : Cardinality.ONE_TO_MANY;
 193  
             }
 194  1
             fkConstraint.setCardinality(cardinality);
 195  
         }
 196  10
         return cardinality;
 197  
     }
 198  
     
 199  
     /**
 200  
      * Given a foreign key constraint with a many-sided mapping, return container interface.
 201  
      * @return name of type (LIST, SET, MAP, BAG) or null if to-one relationship
 202  
      */
 203  
         public String containerType(FkConstraint fkConstraint) {      
 204  2
                 return isManyCardinality(fkConstraint) ? getCollections().getDefaultCollectionKey() : null;
 205  
         }
 206  
 
 207  
     public void setReferenceNamingStrategy(ReferenceNamingStrategy referenceNamingStrategy) {
 208  3
         this.referenceNamingStrategy = referenceNamingStrategy;
 209  3
     }
 210  
 
 211  
     ///////////////////////////////////////////////////////////////////////////
 212  
     // support methods:
 213  
     ///////////////////////////////////////////////////////////////////////////
 214  
 
 215  
     public void setReferenceNamingStrategyString(String referenceNamingStrategy) {
 216  0
         this.setReferenceNamingStrategy( ReferenceNamingStrategy.valueOf(referenceNamingStrategy) );
 217  0
     }
 218  
 
 219  
 //    public ReferenceNamingStrategy getReferenceNamingStrategy() {
 220  
 //        return referenceNamingStrategy;
 221  
 //    }
 222  
 //
 223  
     public OONamingService getOONamingService() {
 224  8
         if (ooNamingService==null)
 225  0
             ooNamingService = new JavaNamingService();
 226  8
         return ooNamingService;
 227  
     }
 228  
 
 229  
     public void setOONamingService(OONamingService ooNamingService) {
 230  2
         this.ooNamingService = ooNamingService;
 231  2
     }
 232  
         
 233  
     public Collections getCollections() {
 234  1
         if (collections==null)
 235  1
             collections = new Collections();
 236  1
         return collections;
 237  
     }
 238  
 
 239  
     public void setCollections(Collections collections) {
 240  0
         this.collections = collections;
 241  0
     }
 242  
 
 243  
     public String getDefaultType() {
 244  1
         return defaultType;
 245  
     }
 246  
 
 247  
     public void setDefaultType(String defaultType) {
 248  0
         this.defaultType = defaultType;
 249  0
     }
 250  
 
 251  
     public RegexRenamer getRegexRenamer() {
 252  0
         return regexRenamer;
 253  
     }
 254  
 
 255  
     public void setRegexRenamer(RegexRenamer regexRenamer) {
 256  0
         this.regexRenamer = regexRenamer;
 257  0
     }
 258  
     
 259  
     /**
 260  
      * Property file friendly means of adding regex-replacePattern pairs as comma-delineated list.
 261  
      * @see RegexRenamer#setRegexReplacePairs(String)
 262  
      */
 263  
     public void setRegexReplacePairs(String replacePatterns) {
 264  1
         if (regexRenamer==null)
 265  1
             regexRenamer = new RegexRenamer();
 266  1
         regexRenamer.setRegexReplacePairs(replacePatterns);
 267  1
     }
 268  
 
 269  
     private String regexRename(String name) {
 270  8
         if (regexRenamer==null || regexRenamer.isEmpty()) {
 271  8
           return name;  
 272  
         } else {
 273  0
             String newName = regexRenamer.rename(name);
 274  0
             return newName==null ? name : newName;
 275  
         }
 276  
     }
 277  
 
 278  
     public boolean getPluralTableNames() {
 279  0
         return pluralTableNames;
 280  
     }
 281  
 
 282  
 
 283  
     public void setPluralTableNames(boolean pluralTableNames) {
 284  2
         this.pluralTableNames = pluralTableNames;
 285  2
     }
 286  
 
 287  
 }