1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 private String defaultType = DEFAULT_TYPE;
45 private ReferenceNamingStrategy referenceNamingStrategy = ReferenceNamingStrategy.useTargetClass;
46 private RegexRenamer regexRenamer;
47 private boolean pluralTableNames = false;
48
49 public Db2JavaMapper() {}
50
51
52
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 String name = OOUtil.camelBackJavaClass(table.getName());
62 if (pluralTableNames)
63 name = getOONamingService().toSingular(name);
64 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 String propeName = OOUtil.camelBackJavaClass(column.getName());
73 if (pluralTableNames)
74 propeName = getOONamingService().toSingular(propeName);
75 String varName = OOUtil.varFromClassName(propeName);
76 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 java.lang.Class<?> booleanType = Db2JavaUtil.booleanTypeCanidate(column);
92 if (booleanType!=null) {
93 return booleanType.getName();
94 } else {
95 java.lang.Class<?> minimalNumericType = Db2JavaUtil.minimalJavaType(column);
96 if (minimalNumericType!=null) {
97 java.lang.Class<?> primitiveType = Db2JavaUtil.primitiveMappingCanidate(minimalNumericType, column);
98 if (primitiveType!=null) {
99 return primitiveType.getName();
100 } else {
101 return minimalNumericType.getName();
102 }
103 }
104 }
105 java.lang.Class<?> type = Db2JavaUtil.javaType(column.getDbType());
106 if (type==null) {
107 return defaultType;
108 } else {
109 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 Class type = null;
119 Table targetTable = fkConstraint.getTargetTable();
120 if (Boolean.TRUE.equals(targetTable.isLinkTable())) {
121 for(FkConstraint linkFkColumn : fkConstraint.getTargetTable().getFkColumns()) {
122 if (fkConstraint.getParentTable() != linkFkColumn.getTargetTable())
123 targetTable = linkFkColumn.getTargetTable();
124 }
125 }
126 type = Db2JavaUtil.getClassFromTable(targetTable);
127 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 String referencePropName = null;
139 ReferenceNamingStrategy strategy = referenceNamingStrategy;
140 if (strategy == ReferenceNamingStrategy.useFkColumn && (fkConstraint.columnReferencesSize()>1 || FkEnum.EXPORTED == fkConstraint.getFkType())) {
141
142 strategy = ReferenceNamingStrategy.useTargetClass;
143 }
144 switch (strategy) {
145 case useTargetClass:
146 Class targetClass = referenceType(fkConstraint);
147 boolean toMany = isManyCardinality(fkConstraint);
148 referencePropName = toMany
149 ? getOONamingService().toPlural(targetClass.getName())
150 : getOONamingService().toSingular(targetClass.getName());
151 break;
152 case useFkColumn:
153
154 Column fkColumn = fkConstraint.getColumnReferences().get(0).getLocalColumn();
155 referencePropName = propertyNameSingular(fkColumn);
156 return referencePropName;
157 case useFkConstraint:
158 referencePropName = OOUtil.camelBackJavaClass(fkConstraint.getName());
159 }
160 String result = StringUtil.makeFirstLetterLowerCase( referencePropName );
161 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 Cardinality cardinality = determineCardinality(fkConstraint);
170 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 Cardinality cardinality = fkConstraint.getCardinality();
185 if (cardinality==null) {
186 boolean isLinkTable = Boolean.TRUE.equals(fkConstraint.getTargetTable().isLinkTable());
187 if (isLinkTable) {
188 cardinality = Cardinality.MANY_TO_MANY;
189 } else if (fkConstraint.unique()) {
190 cardinality = Cardinality.ONE_TO_ONE;
191 } else {
192 cardinality = FkEnum.IMPORTED.equals(fkConstraint.getFkType()) ? Cardinality.MANY_TO_ONE : Cardinality.ONE_TO_MANY;
193 }
194 fkConstraint.setCardinality(cardinality);
195 }
196 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 return isManyCardinality(fkConstraint) ? getCollections().getDefaultCollectionKey() : null;
205 }
206
207 public void setReferenceNamingStrategy(ReferenceNamingStrategy referenceNamingStrategy) {
208 this.referenceNamingStrategy = referenceNamingStrategy;
209 }
210
211
212
213
214
215 public void setReferenceNamingStrategyString(String referenceNamingStrategy) {
216 this.setReferenceNamingStrategy( ReferenceNamingStrategy.valueOf(referenceNamingStrategy) );
217 }
218
219
220
221
222
223 public OONamingService getOONamingService() {
224 if (ooNamingService==null)
225 ooNamingService = new JavaNamingService();
226 return ooNamingService;
227 }
228
229 public void setOONamingService(OONamingService ooNamingService) {
230 this.ooNamingService = ooNamingService;
231 }
232
233 public Collections getCollections() {
234 if (collections==null)
235 collections = new Collections();
236 return collections;
237 }
238
239 public void setCollections(Collections collections) {
240 this.collections = collections;
241 }
242
243 public String getDefaultType() {
244 return defaultType;
245 }
246
247 public void setDefaultType(String defaultType) {
248 this.defaultType = defaultType;
249 }
250
251 public RegexRenamer getRegexRenamer() {
252 return regexRenamer;
253 }
254
255 public void setRegexRenamer(RegexRenamer regexRenamer) {
256 this.regexRenamer = regexRenamer;
257 }
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 if (regexRenamer==null)
265 regexRenamer = new RegexRenamer();
266 regexRenamer.setRegexReplacePairs(replacePatterns);
267 }
268
269 private String regexRename(String name) {
270 if (regexRenamer==null || regexRenamer.isEmpty()) {
271 return name;
272 } else {
273 String newName = regexRenamer.rename(name);
274 return newName==null ? name : newName;
275 }
276 }
277
278 public boolean getPluralTableNames() {
279 return pluralTableNames;
280 }
281
282
283 public void setPluralTableNames(boolean pluralTableNames) {
284 this.pluralTableNames = pluralTableNames;
285 }
286
287 }