View Javadoc

1   /*
2    * joey-gen and its relative products are published under the terms
3    * of the Apache Software License.
4    * 
5    * Created on 2004/08/14 20:48:18
6    */
7   package org.asyrinx.joey.gen.command.rdb2java.standard;
8   
9   import java.util.HashMap;
10  import java.util.Iterator;
11  import java.util.Map;
12  
13  import org.apache.commons.logging.Log;
14  import org.apache.commons.logging.LogFactory;
15  import org.asyrinx.brownie.core.lang.StringUtils;
16  import org.asyrinx.joey.gen.command.java.JavaCheckCommands;
17  import org.asyrinx.joey.gen.command.java.JavaCommand;
18  import org.asyrinx.joey.gen.command.rdb.RdbCommand;
19  import org.asyrinx.joey.gen.command.rdb.StandardCommands;
20  import org.asyrinx.joey.gen.command.rdb2java.NamingStrategy;
21  import org.asyrinx.joey.gen.command.rdb2java.PackagingStrategy;
22  import org.asyrinx.joey.gen.command.rdb2java.Rdb2JavaBuilder;
23  import org.asyrinx.joey.gen.command.rdb2java.TypeMappingStrategy;
24  import org.asyrinx.joey.gen.hibernate.Cascade;
25  import org.asyrinx.joey.gen.model.Element;
26  import org.asyrinx.joey.gen.model.EnumerationEntry;
27  import org.asyrinx.joey.gen.model.command.Command;
28  import org.asyrinx.joey.gen.model.java.AppDomain;
29  import org.asyrinx.joey.gen.model.java.Entity;
30  import org.asyrinx.joey.gen.model.java.EntityKey;
31  import org.asyrinx.joey.gen.model.java.EntityKeyEntry;
32  import org.asyrinx.joey.gen.model.java.EntityKeyType;
33  import org.asyrinx.joey.gen.model.java.JavaEnumeration;
34  import org.asyrinx.joey.gen.model.java.Property;
35  import org.asyrinx.joey.gen.model.java.Reference;
36  import org.asyrinx.joey.gen.model.java.ReferenceEntry;
37  import org.asyrinx.joey.gen.model.java.ReferenceType;
38  import org.asyrinx.joey.gen.model.java.Type;
39  import org.asyrinx.joey.gen.model.java.classes.EmbeddedClassUtils;
40  import org.asyrinx.joey.gen.model.java.classes.JoeyRuntimeClass;
41  import org.asyrinx.joey.gen.model.rdb.Column;
42  import org.asyrinx.joey.gen.model.rdb.Databases;
43  import org.asyrinx.joey.gen.model.rdb.ForeignKey;
44  import org.asyrinx.joey.gen.model.rdb.ForeignKeyEntry;
45  import org.asyrinx.joey.gen.model.rdb.ForeignKeyType;
46  import org.asyrinx.joey.gen.model.rdb.Index;
47  import org.asyrinx.joey.gen.model.rdb.IndexEntry;
48  import org.asyrinx.joey.gen.model.rdb.RdbEnumeration;
49  import org.asyrinx.joey.gen.model.rdb.Table;
50  import org.asyrinx.joey.gen.model.rdb.Unique;
51  import org.asyrinx.joey.gen.model.rdb.visitor.RdbTopDownVisitor;
52  import org.asyrinx.joey.gen.model.rdb.visitor.RdbVisitorMock;
53  
54  /***
55   * @author akima
56   */
57  public class BasicBuilder implements Rdb2JavaBuilder {
58  
59      /***
60       *  
61       */
62      public BasicBuilder() {
63          super();
64      }
65  
66      private final JavaElementBuilder elementBuilder = new JavaElementBuilder();
67  
68      private final JavaTypeResolver resolver = new JavaTypeResolver();
69  
70      private final JavaReferenceBuilder referenceBuilder = new JavaReferenceBuilder();
71  
72      public AppDomain execute(Databases dbs) {
73  
74          final AppDomain result = new AppDomain();
75          //
76          final Command dbCommand = new StandardCommands();
77          dbCommand.execute(dbs);
78          //まずクラスとか基本要素を作る
79          elementBuilder.execute(dbs, result, this.resolver);
80          //CaptionPropertyを解決
81          new CaptionPropertyResolver().execute(dbs, elementBuilder.getRdb2java());
82          //パッケージ名を設定
83          new PackagePreparer().execute(result, new BasicPackaging(getProperties()));
84          //primary keyに対応するEntityKeyを生成。
85          new JavaPrimaryKeyBuilder().execute(dbs, elementBuilder.getRdb2java());
86          //IndexとUniqueに対応するEntityKeyを生成。
87          new JavaIndexKeyBuilder().execute(dbs, elementBuilder.getRdb2java());
88          //クラスのTypeとしての参照を解決
89          resolver.execute(result);
90          //プロパティのデフォルト値を設定
91          new DefaultValueBuilder().execute(result);
92          //プロパティのEnumとの関係を設定
93          new EnumPropertyBuilder().execute(dbs, elementBuilder.getRdb2java(), this.getNaming());
94          //継承関係を解決
95          new ExtendsResolver().execute(dbs, elementBuilder.getRdb2java());
96          //派生クラスを解決
97          new AssignableEntityResolver().execute(result);
98          //クラス間の関係をFKを元に構築
99          referenceBuilder.execute(dbs, elementBuilder.getRdb2java());
100         //importを設定
101         new ImportBuilder().execute(result);
102         //
103         //Java関係のチェック
104         new JavaCheckCommands().execute(result);
105         return result;
106     }
107 
108     /***
109      * @return
110      */
111     public NamingStrategy getNaming() {
112         return elementBuilder.getNaming();
113     }
114 
115     /***
116      * @return
117      */
118     public TypeMappingStrategy getTypeMapping() {
119         return elementBuilder.getTypeMapping();
120     }
121 
122     /***
123      * @param naming
124      */
125     public void setNaming(NamingStrategy naming) {
126         elementBuilder.setNaming(naming);
127     }
128 
129     /***
130      * @param typeMapping
131      */
132     public void setTypeMapping(TypeMappingStrategy typeMapping) {
133         elementBuilder.setTypeMapping(typeMapping);
134     }
135 
136     public Map getRdb2Java() {
137         return elementBuilder.getRdb2java();
138     }
139 
140     /***
141      * @return
142      */
143     public Map getJava2rdb() {
144         return elementBuilder.getJava2rdb();
145     }
146 
147     private Map properties = null;
148 
149     public Map getProperties() {
150         return properties;
151     }
152 
153     public void setProperties(Map properties) {
154         this.properties = properties;
155     }
156 }
157 
158 class JavaElementBuilder extends RdbVisitorMock {
159 
160     private AppDomain appDomain = null;
161 
162     private JavaTypeResolver resolver = null;
163 
164     final Log log = LogFactory.getLog(this.getClass());
165 
166     final Map java2rdb = new HashMap() {
167         public Object put(Object key, Object value) {
168             if (this.containsKey(key))
169                 log.warn("java2rdb: key'" + key + "' was overriden.");
170             return super.put(key, value);
171         }
172     };
173 
174     private final Map rdb2java = new HashMap() {
175         private String toName(Object obj) {
176             if (obj instanceof Element)
177                 return ((Element) obj).getFullName();
178             else
179                 return String.valueOf(obj);
180         }
181 
182         public Object remove(Object key) {
183             final Object value = super.remove(key);
184             if (value != null)
185                 JavaElementBuilder.this.java2rdb.remove(value);
186             return value;
187         }
188 
189         public Object put(Object key, Object value) {
190             if (key == null)
191                 log.warn("rdb2java: key was null. value =" + toName(value));
192             if (value == null)
193                 log.warn("rdb2java: value was null. key =" + toName(key));
194             if (this.containsKey(key))
195                 log.warn("rdb2java: key'" + key + "' was overriden.");
196             JavaElementBuilder.this.java2rdb.put(value, key);
197             return super.put(key, value);
198         }
199 
200     };
201 
202     private NamingStrategy naming = new BasicNaming();
203 
204     private TypeMappingStrategy typeMapping = new BasicTypeMapping();
205 
206     public JavaElementBuilder() {
207         super();
208     }
209 
210     private void prepareEmbedded() {
211         rdb2java.put(JoeyRuntimeClass.RDB_BOOLEAN_ENUM, JoeyRuntimeClass.JAVA_BOOLEAN_ENUM);
212         resolver.addType(JoeyRuntimeClass.JAVA_BOOLEAN_ENUM.getFullName(),
213                 JoeyRuntimeClass.JAVA_BOOLEAN_ENUM);
214     }
215 
216     public void execute(Databases databases, AppDomain domain, JavaTypeResolver typeResolver) {
217         this.appDomain = domain;
218         this.resolver = typeResolver;
219         prepareEmbedded();
220         final RdbTopDownVisitor topDownVisitor = new RdbTopDownVisitor(this);
221         topDownVisitor.visit(databases);
222     }
223 
224     public void visit(RdbEnumeration enum) {
225         final JavaEnumeration result = new JavaEnumeration(this.appDomain, enum.getName(), enum
226                 .getValueType());
227         result.setOriginal(enum);
228         //
229         result.setLabel(enum.getLabel());
230         //この時点ではtemplate相当のデータが入っているかもしれない。あとでPackagePreparerで直す
231         //result.setPackageName(packaging.toPackageName(enum));
232         result.setDescription(enum.getDescription());
233         result.setValueTypeObj(typeMapping.toJavaType(enum.getValueType()));
234         result.getOptions().putAll(enum.getOptions());
235         //
236         for (Iterator i = enum.iterator(); i.hasNext();) {
237             final EnumerationEntry entry = (EnumerationEntry) i.next();
238             try {
239                 result.add((EnumerationEntry) entry.clone());
240             } catch (CloneNotSupportedException e) {
241                 e.printStackTrace();
242                 throw new UnsupportedOperationException(e.getMessage());
243             }
244         }
245         rdb2java.put(enum, result);
246         resolver.addType(result.getFullName(), result);
247     }
248 
249     public void visit(Table table) {
250         final Entity result = new Entity(this.appDomain);
251         result.setOriginal(table);
252         //
253         result.setName(naming.toClassName(table));
254         //result.setPackageTemplate(packaging.toPackageName(table));
255         result.setDescription(table.getDescription());
256         result.setLabel(table.getLabel());
257         result.getOptions().putAll(table.getOptions());
258         result.setInterfaces(StringUtils.defaultString(table.getOptions().get("interfaces")));
259         //
260         rdb2java.put(table, result);
261         resolver.addType(result.getFullName(), result);
262     }
263 
264     public void visit(Column column) {
265         final Entity owner = (Entity) rdb2java.get(column.getParent());
266         final Property result = new Property(owner);
267         result.setOriginal(column);
268         //
269         result.setName(naming.toPropertyName(column));
270         result.setType(typeMapping.toJavaType(column));
271         result.setPrimaryKey(column.isPrimaryKey());
272         if (result.getType() == null) {
273             JavaEnumeration enumeration = appDomain.getEnumerations().getEnumeration(
274                     column.getType());
275             if (enumeration != null)
276                 result.setType(enumeration);
277         }
278         result.setDefaultValue(column.getDefaultValue());
279         result.setDescription(column.getDescription());
280         result.setLabel(column.getLabel());
281         result.setRequired(column.isRequired());
282         result.setMaxLength(column.getSizeAsInt());
283         result.setExtended(column.isExtended());
284         result.getOptions().putAll(column.getOptions());
285         //
286         rdb2java.put(column, result);
287     }
288 
289     /***
290      * @return Returns the naming.
291      */
292     public NamingStrategy getNaming() {
293         return naming;
294     }
295 
296     /***
297      * @param naming
298      *            The naming to set.
299      */
300     public void setNaming(NamingStrategy naming) {
301         this.naming = naming;
302     }
303 
304     /***
305      * @return Returns the typeMapping.
306      */
307     public TypeMappingStrategy getTypeMapping() {
308         return typeMapping;
309     }
310 
311     /***
312      * @param typeMapping
313      *            The typeMapping to set.
314      */
315     public void setTypeMapping(TypeMappingStrategy typeMapping) {
316         this.typeMapping = typeMapping;
317     }
318 
319     /***
320      * @return Returns the rdb2java.
321      */
322     public Map getRdb2java() {
323         return rdb2java;
324     }
325 
326     /***
327      * @return Returns the java2rdb.
328      */
329     public Map getJava2rdb() {
330         return java2rdb;
331     }
332 }
333 
334 class CaptionPropertyResolver extends RdbCommand {
335 
336     private Map rdb2java = null;
337 
338     public void execute(Databases databases, Map rdb2javaMap) {
339         this.rdb2java = rdb2javaMap;
340         new RdbTopDownVisitor(this).visit(databases);
341     }
342 
343     public void visit(Table table) {
344         if (table.getCaptionColumn() == null)
345             return;
346         final Property property = (Property) rdb2java.get(table.getCaptionColumn());
347         if (property == null)
348             addError(table.getCaptionColumn(), "property was not found");
349         final Entity ownerClass = (Entity) rdb2java.get(table);
350         ownerClass.setCaptionProperty(property);
351     }
352 
353 }
354 
355 class JavaTypeResolver extends JavaCommand {
356     private final Map javaName2Type = new HashMap();
357 
358     public void addType(String typeName, Type type) {
359         this.javaName2Type.put(typeName, type);
360     }
361 
362     public Type getType(String typeName) {
363         final Type result = (Type) this.javaName2Type.get(typeName);
364         return (result != null) ? result : EmbeddedClassUtils.get(typeName);
365     }
366 
367     public void visit(JavaEnumeration enum) {
368         if (enum.getValueTypeObj() != null)
369             return;
370         final Type type = getType(enum.getValueType());
371         if (type == null) {
372             addError(enum, "type[" + enum.getValueType() + "] is not found.");
373             return;
374         }
375         enum.setValueTypeObj(type);
376     }
377 
378     public void visit(Property property) {
379         if (property.getType() != null)
380             return;
381         final Type type = getType(property.getTypeName());
382         if (type == null) {
383             addError(property, "type[" + property.getTypeName() + "] is not found.");
384             return;
385         }
386         property.setType(type);
387     }
388 }
389 
390 class JavaPrimaryKeyBuilder extends RdbVisitorMock {
391     private Map rdb2java = null;
392 
393     public void execute(Databases databases, Map rdb2javaMap) {
394         this.rdb2java = rdb2javaMap;
395         new RdbTopDownVisitor(this).visit(databases);
396     }
397 
398     public void visit(Table table) {
399         final Entity ownerClass = (Entity) rdb2java.get(table);
400         if (table.getExtendsTable() != null)
401             return;
402         final EntityKey pk = new EntityKey(ownerClass, "pk");
403         pk.setKeyType(EntityKeyType.PK);
404         pk.setName("primaryKey");
405         for (Iterator i = table.getColumns().iterator(); i.hasNext();) {
406             final Column column = (Column) i.next();
407             if (column.isPrimaryKey()) {
408                 final Property property = (Property) rdb2java.get(column);
409                 new EntityKeyEntry(pk, property);
410             }
411         }
412     }
413 }
414 
415 class JavaIndexKeyBuilder extends RdbVisitorMock {
416     private Map rdb2java = null;
417 
418     public void execute(Databases databases, Map rdb2javaMap) {
419         this.rdb2java = rdb2javaMap;
420         new RdbTopDownVisitor(this).visit(databases);
421     }
422 
423     public void visit(Index index) {
424         final EntityKey key = buildEntityKey(index);
425         key.setKeyType(EntityKeyType.INDEX);
426     }
427 
428     public void visit(Unique unique) {
429         final EntityKey key = buildEntityKey(unique);
430         key.setKeyType(EntityKeyType.UNIQUE);
431     }
432 
433     /***
434      * @param index
435      */
436     private EntityKey buildEntityKey(Index index) {
437         final Table table = index.getParent();
438         final Entity ownerClass = (Entity) rdb2java.get(table);
439         final EntityKey result = new EntityKey(ownerClass);
440         result.setOriginal(index);
441         for (Iterator i = index.iterator(); i.hasNext();) {
442             final IndexEntry entry = (IndexEntry) i.next();
443             final Column column = entry.getColumn();
444             final Property property = (Property) rdb2java.get(column);
445             new EntityKeyEntry(result, property);
446         }
447         result.setName("key" + concatEntryNames(index));
448         return result;
449     }
450 
451     private String concatEntryNames(Index index) {
452         final StringBuffer result = new StringBuffer();
453         for (Iterator i = index.iterator(); i.hasNext();) {
454             final IndexEntry entry = (IndexEntry) i.next();
455             final Column column = entry.getColumn();
456             final Property property = (Property) rdb2java.get(column);
457             result.append(StringUtils.capitalize(property.getName()));
458         }
459         return result.toString();
460     }
461 
462 }
463 
464 class JavaReferenceBuilder extends RdbCommand {
465     private Map rdb2java = null;
466 
467     public void execute(Databases databases, Map rdb2javaMap) {
468         this.rdb2java = rdb2javaMap;
469         super.execute(databases);
470     }
471 
472     public void visit(ForeignKey foreignKey) {
473         if (foreignKey.getType() == ForeignKeyType.EXTENDS) {
474             rdb2java.remove(foreignKey);
475             return;
476         }
477         final Table localTable = foreignKey.getParent();
478         final Table foreignTable = foreignKey.getForeignTable();
479         final Entity ownerClass = (Entity) rdb2java.get(localTable);
480         final Entity referenceClass = (Entity) rdb2java.get(foreignTable);
481         final Reference result = new Reference(ownerClass);
482         result.setOriginal(foreignKey);
483         result.setType(ReferenceType.get(foreignKey.getType().getName()));
484         result.setCascade(Cascade.get(foreignKey.getCascade()));
485         result.setLabel(foreignKey.getLabel());
486         result.setReferenceClass(referenceClass);
487         for (Iterator i = foreignKey.iterator(); i.hasNext();) {
488             final ForeignKeyEntry entry = (ForeignKeyEntry) i.next();
489             final Property local = (Property) rdb2java.get(entry.getLocalColumn());
490             final Property foreign = (Property) rdb2java.get(entry.getForeignColumn());
491             if (local == null) {
492                 addError(foreignKey, "property for column '" + entry.getLocal() + "'"
493                         + entry.getLocalColumn() + " not found.");
494             }
495             if (foreign == null)
496                 addError(foreignKey, "column '" + entry.getForeign() + "' not found.");
497             new ReferenceEntry(result, local, foreign);
498         }
499         rdb2java.put(foreignKey, result);
500     }
501 
502 }
503 
504 class DefaultValueBuilder extends JavaCommand {
505     public void visit(Property property) {
506         if (property.getDefaultValue() != null)
507             return;
508         if (property.getType() == null) {
509             addError(property, "type [" + property.getTypeName() + "] is not found", false);
510             return;
511         }
512         property.setDefaultValue(property.getType().getCategory().getDefaultValue());
513     }
514 }
515 
516 class EnumPropertyBuilder extends RdbCommand {
517     private Map rdb2java = null;
518 
519     private NamingStrategy naming = null;
520 
521     public void execute(Databases databases, Map rdb2javaMap, NamingStrategy namingStrategy) {
522         this.rdb2java = rdb2javaMap;
523         this.naming = namingStrategy;
524         new RdbTopDownVisitor(this).visit(databases);
525     }
526 
527     public void visit(Column column) {
528         final Property property = (Property) rdb2java.get(column);
529         if (StringUtils.isEmpty(column.getEnum()))
530             return;
531         property.setEnumPropertyName(naming.toEnumPropertyName(property.getName()));
532         property.setEnumType(findEnum(column));
533     }
534 
535     protected JavaEnumeration findEnum(Column column) {
536         final RdbEnumeration rdbENum = findRdbEnum(column);
537         if (rdbENum == null) {
538             addError(column, "enum [" + column.getEnum() + "] is not found.");
539             return null;
540         }
541         final Object javaEnum = rdb2java.get(rdbENum);
542         if (javaEnum == null) {
543             addError(column, "rdbENum [" + rdbENum.getFullName() + "] has no mapped java class.");
544             return null;
545         }
546         if (javaEnum instanceof JavaEnumeration) {
547             return (JavaEnumeration) javaEnum;
548         } else {
549             addError(column, "enum [" + column.getEnum() + "] is not found.");
550             return null;
551         }
552     }
553 
554     /***
555      * @param column
556      * @return
557      */
558     private RdbEnumeration findRdbEnum(Column column) {
559         final RdbEnumeration rdbENum = column.getParent().getParent().getEnumerations()
560                 .getEnumeration(column.getEnum());
561         if (rdbENum != null)
562             return rdbENum;
563         return JoeyRuntimeClass.getRdbEnum(column.getEnum());
564     }
565 }
566 
567 class PackagePreparer extends JavaCommand {
568 
569     private PackagingStrategy packaging = null;
570 
571     public void execute(AppDomain appDomain, PackagingStrategy packagingStrategy) {
572         this.packaging = packagingStrategy;
573         execute(appDomain);
574     }
575 
576     public void visit(Entity entity) {
577         packaging.preparePackageNames(entity);
578     }
579 
580     public void visit(JavaEnumeration enum) {
581         packaging.preparePackageName(enum);
582     }
583 
584 }
585 
586 class ImportBuilder extends JavaCommand {
587     public void visit(Entity entity) {
588         for (Entity source = entity; source != null; source = source.getSuperClass()) {
589             addImportByEntity(entity, source);
590         }
591     }
592 
593     /***
594      * @param entity
595      */
596     private void addImportByEntity(Entity entity, Entity source) {
597         if (source.getSuperClass() != null) {
598             addImport(entity, source.getSuperClass());
599         }
600         for (Iterator i = source.getProperties().iterator(); i.hasNext();) {
601             final Property property = (Property) i.next();
602             addImport(entity, property.getEnumType());
603         }
604         for (Iterator i = source.getReferences().iterator(); i.hasNext();) {
605             final Reference reference = (Reference) i.next();
606             addImport(entity, reference.getReferenceClass());
607         }
608         for (Iterator i = source.getReferreds().iterator(); i.hasNext();) {
609             final Reference reference = (Reference) i.next();
610             addImport(entity, reference.getParent());
611         }
612     }
613 
614     /***
615      * @param entity
616      * @param type
617      */
618     private void addImport(Entity entity, final Type type) {
619         if (type == null)
620             return;
621         if (!type.getPackage().equals(entity.getPackage()))
622             entity.getImports().add(type.getFqn());
623     }
624 }
625 
626 class ExtendsResolver extends RdbCommand {
627 
628     private Map rdb2java = null;
629 
630     public void execute(Databases databases, Map rdb2javaMap) {
631         this.rdb2java = rdb2javaMap;
632         new RdbTopDownVisitor(this).visit(databases);
633     }
634 
635     public void visit(Table table) {
636         if (table.getExtendsTable() == null)
637             return;
638         final Entity subclass = (Entity) rdb2java.get(table);
639         final Entity superclass = (Entity) rdb2java.get(table.getExtendsTable());
640         subclass.setSuperClass(superclass);
641     }
642 }
643 
644 class AssignableEntityResolver extends JavaCommand {
645 
646     public void visit(Entity javaClass) {
647         addToAssignableEntities(javaClass);
648     }
649 
650     private void addToAssignableEntities(Entity entity) {
651         for (Entity current = entity; current != null; current = current.getSuperClass()) {
652             current.getAssignableEntities().add(entity);
653         }
654     }
655 }