001package gu.sql2java.generator;
002
003import java.security.MessageDigest;
004import java.security.NoSuchAlgorithmException;
005import java.sql.DatabaseMetaData;
006import java.util.ArrayList;
007import java.util.Arrays;
008import java.util.Collection;
009import java.util.Collections;
010import java.util.Date;
011import java.util.HashMap;
012import java.util.HashSet;
013import java.util.Hashtable;
014import java.util.Iterator;
015import java.util.LinkedHashMap;
016import java.util.LinkedHashSet;
017import java.util.List;
018import java.util.Map.Entry;
019import java.util.Random;
020import java.util.Vector;
021import org.apache.commons.lang.builder.EqualsBuilder;
022import org.apache.commons.lang.builder.HashCodeBuilder;
023import org.apache.commons.lang.builder.ToStringBuilder;
024
025import com.google.common.base.Function;
026import com.google.common.base.Joiner;
027import com.google.common.base.Predicate;
028import com.google.common.base.Strings;
029import com.google.common.collect.Collections2;
030import com.google.common.collect.ImmutableList;
031import com.google.common.collect.Iterables;
032import com.google.common.collect.Iterators;
033import com.google.common.collect.Lists;
034import com.google.common.collect.Maps;
035import com.google.common.collect.Ordering;
036import com.google.common.collect.Sets;
037import com.google.common.primitives.Longs;
038
039import gu.sql2java.generator.CodeWriter;
040import gu.sql2java.generator.Column;
041import gu.sql2java.generator.Database;
042import gu.sql2java.generator.Index;
043import gu.sql2java.generator.Procedure;
044import gu.sql2java.generator.StringUtilities;
045
046import static com.google.common.base.Preconditions.checkNotNull;
047import static com.google.common.base.Preconditions.checkArgument;
048
049public class Table {
050        private Hashtable<String,Column> colHash = new Hashtable<String,Column>();
051        private Vector<Column> cols = new Vector<Column>();
052        private Hashtable<String,Index> indHash = new Hashtable<String,Index>();
053        private Vector<Index> indices = new Vector<Index>();
054        private Hashtable<String,Index> indUniqueHash = new Hashtable<String,Index>();
055        private Vector<Index> uniqueIndices = new Vector<Index>();
056        private Hashtable<String,Index> indNonUniHash = new Hashtable<String,Index>();
057        private Vector<Index> nonUniqueIndices = new Vector<Index>();
058        private Vector<Column> priKey = new Vector<Column>();
059        private Column autoincrement;
060        private String catalog;
061        private String schema;
062        private String name;
063        private String type;
064        private String remarks;
065        private Database database;
066        private Vector<Column> foreignKeys = new Vector<Column>();
067        private Vector<Column> importedKeys = new Vector<Column>();
068        /** FK_NAME 为索引保存所有 foreign keys */
069        private LinkedHashMap<String, ForeignKey> fkNameMap = new LinkedHashMap<String,ForeignKey>();
070        private List<Procedure> procedures = new ArrayList<Procedure>();
071        private HashMap<String,Procedure> procHash = new HashMap<String,Procedure>();
072        private Random aleatorio = new Random(new Date().getTime());
073
074        @Override
075        public String toString() {
076                return new ToStringBuilder(this)
077                                .append("catalog",catalog)
078                                .append("schema", schema)
079                                .append("name", name)
080                                .append("type", type)
081                                .append("remarks", remarks)                             
082                                .toString();
083        }
084
085        @Override
086        public int hashCode() {
087                return new HashCodeBuilder()
088                                .append(catalog)
089                                .append(indices)
090                                .append(name)
091                                .append(schema)
092                                .toHashCode();
093        }
094
095        @Override
096        public boolean equals(Object obj) {
097                if(super.equals(obj))return true;
098                if(!(obj instanceof Table))return false;
099                Table other = (Table)obj;
100                return new EqualsBuilder()
101                        .append(catalog, other.catalog)
102                        .append(indices, other.indices)
103                        .append(name, other.name)
104                        .append(schema, other.schema)
105                        .append(type, other.type)
106                        .isEquals();
107        }
108
109        public boolean isRelationTable() {
110                return this.foreignKeys.size() == 2;
111        }
112        
113        /**
114         * 代替{@link #isRelationTable()},判断当前表是否为联接表
115         * @return
116         */
117        public boolean isJunctionTable() {
118                if(fkNameMap.size() ==2){
119                        HashSet<Column> fkColumns = Sets.<Column>newHashSet();
120                        for(ForeignKey fks:fkNameMap.values()){
121                                fkColumns.addAll(fks.columns);
122                        }
123                        Vector<Column> pkColumns = getPrimaryKeysAsList();
124                        if(pkColumns.size() == fkColumns.size()){
125                                fkColumns.retainAll(getPrimaryKeysAsList());
126                                return fkColumns.size() ==pkColumns.size();     
127                        }                       
128                }                       
129                return false;
130        }
131        /**
132         * 判断是否为联接表,且主键为两个字段
133         * @return
134         */
135        public boolean isSampleJunctionTable() {
136                return isJunctionTable() && 2 == countPrimaryKeys();
137        }
138        /**
139         * 判断外键是否为自引用
140         * @return
141         */
142        public boolean isSelfRef(ForeignKey fk) {
143                if(null == fk)return false;
144                return fk.getForeignTable().equals(this);
145        }
146        /** 返回所有自引用外键 */
147        public List<ForeignKey> getSelfRefKeys(){
148                return ImmutableList.copyOf(Collections2.filter(this.fkNameMap.values(), new Predicate<ForeignKey>(){
149                        @Override
150                        public boolean apply(ForeignKey input) {
151                                return isSelfRef(input);
152                        }}));
153        }
154        public boolean relationConnectsTo(Table otherTable) {
155                if (this.equals(otherTable)) {
156                        return false;
157                }
158                int nbImported = this.importedKeys.size();
159                for (int i = 0; i < nbImported; ++i) {
160                        Column c = (Column) this.importedKeys.get(i);
161                        if (!c.getTableName().equals(otherTable.getName()))
162                                continue;
163                        return true;
164                }
165                return false;
166        }
167
168        public Table[] linkedTables(Database pDatabase, Table pTable) {
169                Vector<Table> vector = new Vector<Table>();
170                int nbImported = this.importedKeys.size();
171                for (int iIndex = 0; iIndex < nbImported; ++iIndex) {
172                        Table pTableToAdd;
173                        Column pColumn = (Column) this.importedKeys.get(iIndex);
174                        if (pColumn.getTableName().equals(pTable.getName())
175                                        || vector.contains(pTableToAdd = pDatabase.getTable(pColumn.getTableName())))
176                                continue;
177                        vector.add(pTableToAdd);
178                }
179                return vector.toArray(new Table[vector.size()]);
180        }
181        /**
182         * 代替 {@link #linkedTables(Database, Table)}<br>
183         * 对于联接表(junction)返回{@code pTable}联接的表<br>
184         * @param pTable
185         * @return 当前对象不是连接表或{@code pTable}不属于联接表时返回{@code null}
186         */
187        public Table tableOfJunction(Table pTable) {
188                if(this.isJunctionTable()){                     
189                        for(ForeignKey fk:this.fkNameMap.values()){
190                                Table jtable = fk.getForeignTable();
191                                if(!jtable.equals(pTable))
192                                        return jtable; 
193                        }
194                }
195                return null;
196        }
197        public Column getForeignKeyFor(Table pTable) {
198                int nbImported = this.importedKeys.size();
199                for (int iIndex = 0; iIndex < nbImported; ++iIndex) {
200                        Column pColumn = (Column) this.importedKeys.get(iIndex);
201                        if (!pColumn.getTableName().equals(pTable.getName()))
202                                continue;
203                        return pColumn;
204                }
205                return null;
206        }
207
208        /** 返回当前对象的关联表 */
209        public List<Table> getJunctionTables() {
210                List<Table> tabs = this.getDatabase().getJunctionTables();
211                if(isJunctionTable()){
212                        // 当前表是关联表,则返回空
213                        tabs.clear();
214                        return tabs;
215                }
216                for(Iterator<Table> itor = tabs.iterator();itor.hasNext();){
217                        Table table = itor.next();
218                        if(!table.getForeignTablesAsList().contains(this)){
219                                itor.remove();
220                        }
221                }
222                return tabs;
223        }
224        public void setCatalog(String catalog) {
225                this.catalog = catalog;
226        }
227
228        public void setSchema(String schema) {
229                this.schema = schema;
230        }
231
232        public void setName(String name) {
233                this.name = name;
234        }
235
236        public void setType(String type) {
237                this.type = type;
238        }
239
240        public void setRemarks(String remarks) {
241                if (remarks != null) {
242                        this.remarks = remarks.replaceAll("/\\*", "SLASH*").replaceAll("\\*/", "*SLASH");
243                }
244        }
245
246        public String getCatalog() {
247                return this.catalog;
248        }
249
250        public String getSchema() {
251                return this.schema;
252        }
253
254        public String getName() {
255                return this.name;
256        }
257
258        public String getType() {
259                return this.type;
260        }
261
262        public Column[] getColumns() {
263                return this.getColumnsAsList().toArray(new Column[this.cols.size()]);
264        }
265        public Vector<Column> getColumnsAsList() {
266                Collections.sort(this.cols);
267                return this.cols;
268        }
269        public List<Column> getColumnsExceptPrimaryAsList() {
270                return Lists.newArrayList(Collections2.filter(getColumnsAsList(), new Predicate<Column>(){
271                        @Override
272                        public boolean apply(Column input) {
273                                return !input.isPrimaryKey();
274                        }}));
275        }
276        public Column[] getColumnsExceptPrimary() {
277                return getColumnsExceptPrimaryAsList().toArray(new Column[0]);
278        }
279        public Column getColumn(String columnName) {
280                return (Column) this.colHash.get(columnName.toLowerCase());
281        }
282
283        public void addColumn(Column column) {
284                this.colHash.put(column.getName().toLowerCase(), column);
285                this.cols.addElement(column);
286        }
287
288        public void removeColumn(Column column) {
289                this.cols.removeElement((Object) column);
290                this.colHash.remove(column.getName().toLowerCase());
291        }
292
293        public Index[] getUniqueIndices() {
294                return this.uniqueIndices.toArray(new Index[this.uniqueIndices.size()]);
295        }
296
297        public Index[] getNonUniqueIndices() {
298                return this.nonUniqueIndices.toArray( new Index[this.nonUniqueIndices.size()]);
299        }
300
301        public int countIndices() {
302                return this.indices.size();
303        }
304
305        public Index[] getIndices() {
306                return this.indices.toArray( new Index[this.indices.size()]);
307        }
308        public List<Index> getIndicesAsList(Boolean unique) {
309                Iterator<Index>itor = this.indices.iterator();
310                if(Boolean.TRUE == unique)
311                        itor = Iterators.filter(itor, new Predicate<Index>(){
312                                @Override
313                                public boolean apply(Index arg0) {
314                                        return arg0.isUnique();
315                                }});
316                return ImmutableList.copyOf(itor);
317        }
318        public List<Index> getIndicesAsList(){
319                return getIndicesAsList(false);
320        } 
321        public List<Index> getUniqueIndicesAsList(){
322                return getIndicesAsList(true);
323        } 
324        public Index getIndex(String indName) {
325                return (Index) this.indHash.get(indName.toLowerCase());
326        }
327
328        public void addIndex(Index index) {
329                this.indHash.put(index.getName().toLowerCase(), index);
330                this.indices.add(index);
331                if (index.isUnique()) {
332                        this.indUniqueHash.put(index.getName().toLowerCase(), index);
333                        this.uniqueIndices.add(index);
334                } else {
335                        this.indNonUniHash.put(index.getName().toLowerCase(), index);
336                        this.nonUniqueIndices.add(index);
337                }
338        }
339
340        public void removeIndex(Index index) {
341                this.indices.remove((Object) index);
342                this.indHash.remove(index.getName().toLowerCase());
343                if (index.isUnique()) {
344                        this.uniqueIndices.remove((Object) index);
345                        this.indUniqueHash.remove(index.getName().toLowerCase());
346                } else {
347                        this.nonUniqueIndices.remove((Object) index);
348                        this.indNonUniHash.remove(index.getName().toLowerCase());
349                }
350        }
351
352        public Column[] getPrimaryKeys() {
353                return this.priKey.toArray( new Column[this.priKey.size()]);
354        }
355        public Vector<Column> getPrimaryKeysAsList() {
356                return new Vector<Column>(this.priKey);
357        }
358        public boolean hasCompositeKey() {
359                if (this.priKey.size() > 1) {
360                        return true;
361                }
362                return false;
363        }
364
365        public Column getPrimaryKey() throws RuntimeException {
366                if (this.priKey.size() != 1) {
367                        throw new RuntimeException("Table " + this.getName() + " has a composite key, not a unique primary key");
368                }
369                return (Column) this.priKey.get(0);
370        }
371
372        public void addPrimaryKey(Column column) {
373                this.priKey.addElement(column);
374                column.isPrimaryKey(true);
375        }
376
377        public Column[] getImportedKeys() {
378                return this.importedKeys.toArray( new Column[this.importedKeys.size()]);
379        }
380
381        public void addImportedKey(Column column) {
382                if (!this.importedKeys.contains((Object) column)) {
383                        this.importedKeys.addElement(column);
384                }
385        }
386
387        public int countColumns() {
388                return this.cols.size();
389        }
390
391        public int countPrimaryKeys() {
392                return this.priKey.size();
393        }
394
395        public boolean hasPrimaryKey() {
396                return this.countPrimaryKeys() > 0;
397        }
398
399        public int countImportedKeys() {
400                return this.importedKeys.size();
401        }
402
403        public boolean hasImportedKeys() {
404                return this.countImportedKeys() > 0;
405        }
406
407        public int countForeignKeys() {
408                return this.foreignKeys.size();
409        }
410
411        public boolean hasForeignKeys() {
412                return this.countForeignKeys() > 0;
413        }
414
415        public void addForeignKey(Column col, 
416                        String fkName, 
417                        short keySeq,
418                        Table.ForeignKeyRule updateRule,
419                        Table.ForeignKeyRule deleteRule) {
420                checkNotNull(col);
421                checkArgument(!Strings.isNullOrEmpty(fkName));
422                checkArgument(keySeq>0,"the argument 'keySeq' must >0");
423
424                if (!this.foreignKeys.contains(col)) {
425                        this.foreignKeys.add(col);
426                }
427                if(!this.fkNameMap.containsKey(fkName))
428                        this.fkNameMap.put(fkName, new ForeignKey(fkName,updateRule,deleteRule,col));
429                Vector<Column> fkCols = fkNameMap.get(fkName).columns;
430                if(keySeq > fkCols.size()){
431                        fkCols.setSize(keySeq);
432                }
433                fkCols.set(keySeq-1, col);
434        }
435        
436        /**
437         * 返回所有 foreign key name ( FK_NAME )
438         * @return
439         */
440        public Vector<String> getFkMapNames() {         
441                return new Vector<String>(this.fkNameMap.keySet());
442        }
443
444        /**
445         * 检索外键引用指定表(tableName)的所有 FK_NAME<br>
446         * 没有结果则返回空数组
447         * @param tableName
448         * @return
449         */
450        public Vector<String> getFkMapNames(String tableName) {
451                Vector<String> names=new Vector<String>();
452                for(ForeignKey fk:this.fkNameMap.values()){
453                        for(Column col:fk.columns.get(0).getForeignKeys()){
454                                if(col.getTableName().equals(tableName)){
455                                        names.add(fk.fkName);
456                                        break;
457                                }
458                        }
459                }
460                Collections.sort(names);
461                
462                return names;
463        }
464
465        /**
466         * 检索指定 FK_NAME 包含的所有字段<br>
467         * 没有结果则返回空数组
468         * @param fkName
469         * @return
470         */
471        public Vector<Column> getForeignKeysByFkName(String fkName) {           
472                ForeignKey keys=this.fkNameMap.get(fkName);
473                return null==keys?new Vector<Column>():new Vector<Column>(keys.columns);
474        }
475        
476        /**
477         * 检索指定 FK_NAME 的{@link ForeignKey}对象<br>
478         * @param fkName
479         * @return
480         * @see java.util.concurrent.ConcurrentHashMap#get(java.lang.Object)
481         */
482        public ForeignKey getForeignKey(String fkName) {
483                return fkNameMap.get(fkName);
484        }
485        
486        /**
487         * 返回{@code table}对应的所有{@link ForeignKey}对象
488         * @param table
489         * @return
490         */
491        public List<ForeignKey> getForeignKeys(final Table table){              
492                return Lists.newArrayList(Collections2.filter(this.fkNameMap.values(), new Predicate<ForeignKey>(){
493                        @Override
494                        public boolean apply(ForeignKey input) {
495                                return input.getForeignTable().equals(table);
496                        }}));
497        }
498        public List<ForeignKey> getImportedFoeignKeysAsList(){
499                ArrayList<ForeignKey> list = new ArrayList<ForeignKey>();
500                for(Table table:getImportedTablesAsList()){
501                        list.addAll(table.getForeignKeys(this));
502                }
503                return list;
504        }
505        public List<ForeignKey> getForeignKeysAsList(){
506                return ImmutableList.copyOf(this.fkNameMap.values());
507        }
508        /**
509         * 返回 所有需要输出foreign key listener的 {@link ForeignKey}对象
510         * @return
511         */
512        public List<ForeignKey> getForeignKeysForListener(){
513                Collection<ForeignKey> c = Maps.filterEntries(fkNameMap, 
514                                new Predicate<Entry<String, ForeignKey>>(){
515                                        @Override
516                                        public boolean apply(Entry<String, ForeignKey> input) {
517                                                ForeignKey fk = input.getValue();
518                                                return fk.updateRule.isNoAction() 
519                                                                && !Strings.isNullOrEmpty(fk.deleteRule.getEventOfDeleteRule());
520                                        }}).values();
521                // 排序输出
522                return Ordering.natural().onResultOf(new Function<ForeignKey,String>(){
523                        @Override
524                        public String apply(ForeignKey input) {
525                                return input.fkName;
526                        }}).sortedCopy(c);
527        }
528        /**
529         * 判断 FK_NAME 包含的所有字段是否都允许为null
530         * @param fkName
531         * @return
532         */
533        public boolean isNullable(String fkName){
534                for(Column column: getForeignKeysByFkName(fkName)){
535                        if(column.isNotNull())return false;
536                }
537                return true;
538        }
539        /**
540         * 返回 FK_NAME 包含的所有字段中不允许为null的所有字段
541         * @param fkName
542         * @return
543         */
544        public Vector<Column> noNullableColumns(String fkName){
545                Vector<Column> keys = getForeignKeysByFkName(fkName);
546                for(Iterator<Column> itor = keys.iterator();itor.hasNext();){
547                        Column column = itor.next();
548                        if(DatabaseMetaData.columnNullable !=column.getNullable())continue;
549                        itor.remove();
550                }
551                return keys;
552        }
553        private String toUniversalFkName(String fkName) {
554                ForeignKey key = this.fkNameMap.get(fkName);
555                if(null!=key){
556                        return key.getUniversalName();
557                }
558                return "";
559        }
560        
561        public Table getForeignTableByFkName(String fkName){
562                Vector<Column> keys = this.getForeignKeysByFkName(fkName);
563                return 0==keys.size()? null:keys.get(0).getForeignColumn().getTable();          
564        }
565        public Column[] getForeignKeys() {
566                return this.foreignKeys.toArray( new Column[this.foreignKeys.size()]);
567        }
568
569        public boolean isForeignKey(Column col) {
570                return this.foreignKeys.contains((Object) col);
571        }
572
573        public int countManyToManyTables() {
574                return this.getManyToManyTables().length;
575        }
576
577        public boolean hasManyToManyTables() {
578                return this.countManyToManyTables() > 0;
579        }
580
581        public Table[] getManyToManyTables() {
582                Vector<Table> vector = new Vector<Table>();
583                Table[] linkedTables = this.getImportedTables();
584                System.out.println(this.getName() + "  getManyToManyTables, linked tables = " + linkedTables.length);
585                for (int iIndex = 0; iIndex < linkedTables.length; ++iIndex) {
586                        System.out.println(this.getName() + "    " + linkedTables[iIndex].getName() + " relation table ?");
587                        if (!linkedTables[iIndex].isRelationTable())
588                                continue;
589                        Table[] relationLinkedTable = linkedTables[iIndex].getForeignTables();
590                        System.out.println(this.getName() + "      " + linkedTables[iIndex].getName() + " has "
591                                        + relationLinkedTable.length + " foreign table");
592                        for (int i = 0; i < relationLinkedTable.length; ++i) {
593                                System.out.println(this.getName() + "          " + i + " " + relationLinkedTable[i].getName()
594                                                + " is relation table");
595                                if (relationLinkedTable[i].equals(this) || vector.contains(relationLinkedTable[i]))
596                                        continue;
597                                vector.add(relationLinkedTable[i]);
598                        }
599                }
600                return vector.toArray(new Table[vector.size()]);
601        }
602
603        public int countLinkedTables() {
604                return this.getLinkedTables().length;
605        }
606
607        public boolean hasLinkedTables() {
608                return this.countLinkedTables() > 0;
609        }
610
611        public Table[] getLinkedTables() {
612                Vector<Table> vector = new Vector<Table>();
613                int nbImported = this.importedKeys.size();
614                for (int iIndex = 0; iIndex < nbImported; ++iIndex) {
615                        Table pTableToAdd;
616                        Column column = (Column) this.importedKeys.get(iIndex);
617                        if (column.getTableName().equals(this.getName()) || vector.contains(pTableToAdd = column.getTable()))
618                                continue;
619                        vector.add(pTableToAdd);
620                }
621                int nbForeign = this.foreignKeys.size();
622                for (int iIndex2 = 0; iIndex2 < nbForeign; ++iIndex2) {
623                        Table pTableToAdd;
624                        Column column = (Column) this.foreignKeys.get(iIndex2);
625                        if ((column = column.getForeignColumn()).getTableName().equals(this.getName())
626                                        || vector.contains(pTableToAdd = column.getTable()))
627                                continue;
628                        vector.add(pTableToAdd);
629                }
630                for(Table jtable:getJunctionTables()){
631                        Table pTableToAdd = jtable.tableOfJunction(this);
632                        if(vector.contains(pTableToAdd)){
633                                continue;
634                        }
635                        vector.add(pTableToAdd);
636                }
637                return vector.toArray(new Table[vector.size()]);
638        }
639
640        public int countImportedTables() {
641                return this.getImportedTables().length;
642        }
643
644        public boolean hasImportedTables() {
645                return this.countImportedTables() > 0;
646        }
647
648        public List<Table> getImportedTablesAsList() {
649                LinkedHashSet<Table> tables = Sets.newLinkedHashSet(Lists.transform(this.importedKeys, new Function<Column,Table>(){
650                        @Override
651                        public Table apply(Column t) {
652                                return t.getTable();
653                        }}));
654                return Lists.newArrayList(tables);
655        }
656        public Table[] getImportedTables() {
657                return getImportedTablesAsList().toArray(new Table[0]);
658        }
659        public int countForeignTables() {
660                return this.getForeignTables().length;
661        }
662
663        public boolean hasForeignTables() {
664                return this.countForeignTables() > 0;
665        }
666
667        public List<Table> getForeignTablesAsList() {
668                LinkedHashSet<Table> tables = Sets.newLinkedHashSet(Lists.transform(this.foreignKeys, new Function<Column,Table>(){
669                        @Override
670                        public Table apply(Column t) {
671                                return t.getForeignColumn().getTable();
672                        }}));
673                return Lists.newArrayList(tables);
674        }
675        public Table[] getForeignTables() {
676                return getForeignTablesAsList().toArray(new Table[0]);
677        }
678        public Table getRelationTable(Table targetTable) {
679                System.out.println("getRelationTable " + this.getName() + "<->" + targetTable.getName() + ")");
680                Table[] importedTables = this.getImportedTables();
681                for (int iIndex = 0; iIndex < importedTables.length; ++iIndex) {
682                        Table[] foreignTables = importedTables[iIndex].getForeignTables();
683                        for (int iIndex2 = 0; iIndex2 < foreignTables.length; ++iIndex2) {
684                                if (!foreignTables[iIndex2].getName().equalsIgnoreCase(this.getName()))
685                                        continue;
686                                return importedTables[iIndex];
687                        }
688                }
689                return targetTable;
690        }
691
692        public int countProcedures() {
693                return this.procedures.size();
694        }
695
696        public boolean hasProcedures() {
697                return this.countProcedures() > 0;
698        }
699
700        public Procedure[] getProcedures() {
701                return this.procedures.toArray( new Procedure[this.procedures.size()]);
702        }
703
704        public void addProcedure(Procedure procedure) {
705                if (null == this.procHash.get(procedure.getName())) {
706                        this.procedures.add(procedure);
707                        this.procHash.put(procedure.getName(), procedure);
708                }
709        }
710
711        public String[] getLinkedPackages() {
712                Vector<String> vector = new Vector<String>();
713                Table[] linkedTables = this.getLinkedTables();
714                for (int iIndex = 0; iIndex < linkedTables.length; ++iIndex) {
715                        if (vector.contains(linkedTables[iIndex].getPackage()))
716                                continue;
717                        vector.add(linkedTables[iIndex].getPackage());
718                }
719                return vector.toArray(new String[vector.size()]);
720        }
721
722        public String getPackage() {
723                String basePackage = CodeWriter.getProperty((String) "codewriter.package");
724                return checkNotNull(basePackage,"NOT DEFINED codewriter.package");
725        }
726
727        public String getPackagePath() {
728                return this.getPackage().replace('.', '/') + "/";
729        }
730
731        public Column getFirstColumn() {
732                return (Column) this.cols.get(0);
733        }
734
735        public String getRemarks() {
736                return this.remarks == null ? "" : this.remarks;
737        }
738
739        public boolean hasRemarks(){
740                return !getRemarks().isEmpty();
741        }
742        public String getJavaName() {
743                return this.convertName("");
744        }
745        
746        public String getBasename(Boolean nsp){
747                String basename =Boolean.TRUE == nsp
748                                ? this.getName().replaceFirst(this.getDatabase().getSamePrefix(), "")
749                                : this.getName();
750                return  "".equals(CodeWriter.getClassPrefix())
751                                ? basename
752                                : CodeWriter.getClassPrefix() + "_" + basename;
753        }
754
755        public String convertName(String value,Boolean nsp) {
756                String basename = getBasename(nsp);
757                if ("".equals(value)) {
758                        return StringUtilities.convertName((String) basename, false);
759                }
760                return StringUtilities.convertName((String) (basename + "_" + value), false);
761        }
762        
763        public String convertName(String value) {
764                return convertName(value,false);
765        }
766        
767        public String convertNameNSP(String value) {
768                return convertName(value,true);
769        }
770        
771        public String asClass(String suffix) {
772                return this.convertName(suffix);
773        }
774
775        public String asCoreClass() {
776                return this.convertName("");
777        }
778
779        public String asCoreClassNSP() {
780                return this.convertNameNSP("");
781        }
782        public String asCoreClass(Boolean nsp) {
783                return this.convertName("",nsp);
784        }
785        public String asBeanClass() {
786                return this.convertName("Bean");
787        }
788        
789        public String asFullBeanClass() {
790                return this.getPackage() + "." + this.asBeanClass();
791        }
792        
793        public String asBeanClassNSP() {
794                return this.convertNameNSP("Bean");     
795        }
796        
797        public String asBeanClass(Boolean nsp) {
798                return convertName("Bean",nsp);
799        }
800        public String asConstClass() {
801                return this.convertName("Const");
802        }
803        public String asConstClass(boolean nsp) {
804                return this.convertName("Const",nsp);
805        }
806        public String asConstClassNSP() {
807                return asConstClass(true);
808        }
809        public String asCacheClass() {
810                return this.convertName("Cache");
811        }
812        public String asCacheClass(boolean nsp) {
813                return this.convertName("Cache",nsp);
814        }
815        public String asRelationnalBeanClass() {
816                return this.convertName("Relationnal_Bean");
817        }
818
819        public String asHibernateManagerClass() {
820                return this.convertName("Hibernate_Manager");
821        }
822
823        public String asIteratorClass() {
824                return this.convertName("Iterator");
825        }
826
827        public String asFactoryClass() {
828                return this.convertName("Factory");
829        }
830
831        public String asHttpFactoryClass() {
832                return this.convertName("Http_Factory");
833        }
834
835        public String asComparatorClass() {
836                return this.convertName("Comparator");
837        }
838        
839        public String asComparatorClass(Boolean nsp) {
840                return convertName("Comparator",nsp);
841        }
842        public String asListenerClass() {
843                return this.convertName("Listener");
844        }
845
846        public String asListenerClassNSP() {
847                return this.convertNameNSP("Listener");
848        }
849        
850        public String asRendererClass() {
851                return this.convertName("Renderer");
852        }
853
854        public String asExceptionClass() {
855                return this.convertName("Exception");
856        }
857
858        public String asWidgetClass() {
859                return this.convertName("Widget");
860        }
861
862        public String asWidgetFactoryClass() {
863                return this.convertName("Widget_Factory");
864        }
865
866        public String asActionClass() {
867                return this.convertName("Action");
868        }
869
870        public String asActionTestClass() {
871                return this.convertName("Action_Test");
872        }
873
874        public String asControllerClass() {
875                return this.convertName("Controller");
876        }
877
878        public String asControllerTestClass() {
879                return this.convertName("Controller_Test");
880        }
881
882        public String asFormControllerClass() {
883                return this.convertName("Form_Controller");
884        }
885
886        public String asFormControllerTestClass() {
887                return this.convertName("Form_Controller_Test");
888        }
889
890        public String asDAOClass() {
891                return this.convertName("D_A_O");
892        }
893
894        public String asDAOTestClass() {
895                return this.convertName("D_A_O_Test");
896        }
897
898        public String asDAOHibernateClass() {
899                return this.convertName("D_A_O_Hibernate");
900        }
901
902        public String asManagerClass() {
903                return this.convertName("Manager");
904        }
905        public String asManagerClass(Boolean nsp) {
906                return this.convertName("Manager",nsp);
907        }
908        public String asManagerClassNSP() {
909                return this.convertNameNSP("Manager");
910        }
911        public String asManagerInterfaceNSP() {
912                return "I"+asManagerClassNSP();
913        }
914        public String asManagerImplClass() {
915                return this.convertName("Manager_Impl");
916        }
917        public String asMetaDataClassNSP() {
918                return asCoreClass(true) + "MetaData";
919        }
920        public String asManagerTestClass() {
921                return this.convertName("Manager_Test");
922        }
923        public String asCacheManagerClass() {
924                return this.convertName("cache_manager");
925        }
926        public String asCacheManagerClassNSP() {
927                return this.convertNameNSP("cache_manager");
928        }
929        public String asCacheManagerClass(boolean nsp) {
930                return this.convertName("cache_manager",nsp);
931        }
932        public String asVar(String prefix,String suffix){
933                return StringUtilities.convertName( prefix + getBasename(true) +  suffix,true);
934        }
935        public String asVar(String prefix){
936                return asVar(prefix,"");
937        }
938        public String asVar(){
939                return asVar("","");
940        }
941        public String asVarBean(){
942                return asVar("","_Bean");
943        }
944        public String asVarManager(){
945                return asVar("","_Manager");
946        }
947        public String asConverterVar(){
948                return "converter" + asBeanClassNSP() ;
949        }
950        public String asConverterConstVar(){            
951                return  "converter_".concat(asBeanClassNSP()).toUpperCase();
952        }
953        public String asCacheVarName(){
954                return StringUtilities.convertName( getBasename(true) +  "_cache",true);
955        }
956        public String asCacheVarSetMethod(){
957                return StringUtilities.convertName("set_" +  getBasename(true) +  "_cache",true);
958        }
959        public String asCacheVarGetMethod(){
960                return StringUtilities.convertName("get_" +  getBasename(true) +  "_cache",true);
961        }
962        public String asInstanceMethod(Boolean nsp){
963                return "instanceOf" + asManagerClass(nsp);
964        }
965        public String asModelClass() {
966                return this.convertName("Model");
967        }
968
969        public String asPKClass() {
970                return this.convertName("P_K");
971        }
972
973        public String asTblClass() {
974                return this.convertName("Tbl");
975        }
976
977        public Column getVersionColumn() {
978                int nbCols = this.cols.size();
979                for (int i = 0; i < nbCols; ++i) {
980                        Column c = (Column) this.cols.get(i);
981                        if (!c.isVersion())
982                                continue;
983                        return c;
984                }
985                throw new IllegalArgumentException("No version column for table " + this.getName());
986        }
987
988        public boolean hasVersionColumn() {
989                try {
990                        this.getVersionColumn();
991                        return true;
992                } catch (IllegalArgumentException e) {
993                        return false;
994                }
995        }
996
997        public long getSerialVersionUID() {
998                return this.aleatorio.nextLong();
999        }
1000        /**
1001         * 生成MD5校验码
1002         * 
1003         * @param source
1004         * @return
1005         */
1006        static public byte[] getMD5(byte[] source) {
1007                if (null==source)
1008                        return null;
1009                try {
1010                        MessageDigest md = MessageDigest.getInstance("MD5");
1011                        return md.digest(source);
1012                } catch (NoSuchAlgorithmException e) {
1013                        throw new RuntimeException(e);
1014                }
1015        }
1016        /**
1017         * 将字节数组转为long<br>
1018         * 如果input为null,或offset指定的剩余数组长度不足8字节则抛出异常
1019         * @param input 
1020         * @param offset 起始偏移量
1021         * @param littleEndian 输入数组是否小端模式
1022         * @return
1023         */
1024        public static long longFrom8Bytes(byte[] input, int offset, boolean littleEndian){
1025                if(offset <0 || offset+8>input.length)
1026                        throw new IllegalArgumentException(String.format("less than 8 bytes from index %d  is insufficient for long",offset));
1027                long value=0;
1028                for(int  count=0;count<8;++count){
1029                        int shift=(littleEndian?count:(7-count))<<3;
1030                        value |=((long)0xff<< shift) & ((long)input[offset+count] << shift);
1031                }
1032                return value;
1033        }
1034        /**
1035         * 根据输入的String返回唯一的UID(long)
1036         * @param input
1037         * @return
1038         */
1039        public long getSerialVersionUID(String input){
1040                byte[] md5 = getMD5(input.getBytes());
1041                return longFrom8Bytes(md5,0, false)  ^ longFrom8Bytes(md5,8, false);
1042        }       
1043        public String asFkVar(String fkName){
1044                return StringUtilities.convertName(toUniversalFkName(fkName),true);
1045        }
1046        public String asIKVar(String fkName){
1047                Table foreignTable = this.getForeignTableByFkName(fkName);
1048                return null==foreignTable
1049                                ? ""
1050                                : StringUtilities.convertName(toUniversalFkName(fkName) + "_of_" +getBasename(true), true);
1051        }
1052
1053        public String asFKConst(String fkName){
1054                return (this.name+"_FK_" + toUniversalFkName(fkName)).toUpperCase();
1055        }
1056
1057        public String asIKConst(String fkName){
1058                Table foreignTable = this.getForeignTableByFkName(fkName);
1059                return (null==foreignTable?"":foreignTable.getName() + "_IK_" + this.name +"_" + toUniversalFkName(fkName)).toUpperCase();
1060        }
1061        
1062        public String asRefArg(String fkName){
1063                return StringUtilities.convertName("ref_"+ this.getForeignTableByFkName(fkName).asCoreClassNSP() +"_by_" + toUniversalFkName(fkName),true);
1064        }
1065        
1066        public String asImpArg(String fkName){
1067                return StringUtilities.convertName("imp_"+ this.asCoreClassNSP() +"_by_" + toUniversalFkName(fkName),true);
1068        }
1069
1070        public String getReferencedVarName(String fkName){
1071                return StringUtilities.convertName("referenced_by_" + toUniversalFkName(fkName),true);
1072        }
1073        
1074        public String getReferencedVarGetMethod(String fkName) {
1075                return StringUtilities.convertName("get_" + "referenced_by_" + toUniversalFkName(fkName),true);
1076        }
1077        
1078        public String getReferencedVarSetMethod(String fkName){
1079                return StringUtilities.convertName("set_" + "referenced_by_" + toUniversalFkName(fkName),true);
1080        }
1081        
1082        public String getImportedBeansGetMethod(String fkName) {                
1083                return "get" + this.asBeanClassNSP() + "s" + StringUtilities.convertName("By_" + toUniversalFkName(fkName),false);
1084        }
1085        public String getImportedBeansSetMethod(String fkName) {                
1086                return "set" + this.asBeanClassNSP() + "s" + StringUtilities.convertName("By_" + toUniversalFkName(fkName),false);
1087        }
1088        public String getImportedBeansDelMethod(String fkName) {                
1089                return "delete" + this.asBeanClassNSP() + "s" + StringUtilities.convertName("By_" + toUniversalFkName(fkName),false);
1090        }
1091        
1092        public String getForeignKeyListenerVar(String fkName) {         
1093                return "foreignKeyListener" + StringUtilities.convertName("By_" + toUniversalFkName(fkName),false);
1094        }
1095        public String getBindMethod(String fkName) {
1096                return StringUtilities.convertName(
1097                                Joiner.on('_').join("bind",
1098                                                toUniversalFkName(fkName),"listener",
1099                                                "to",
1100                                                getForeignTableByFkName(fkName).getBasename(false),"Manager"),
1101                                true);
1102        }
1103        public boolean stateVarTypeIsArray(){
1104                return countColumns()>CodeWriter.getBitStateClassSize();
1105        }       
1106        public String stateVarType(){
1107                return stateVarTypeIsArray() ? CodeWriter.getBitStateClassName() + "[]" : CodeWriter.getBitStateClassName();
1108        }
1109        /** 生成全0L的modified初始值  */
1110        public String maskInitializeWithZero(){
1111                if(stateVarTypeIsArray()){
1112                        int len = (countColumns() + CodeWriter.getBitStateClassSize() - 1)/CodeWriter.getBitStateClassSize();
1113                        StringBuffer sb = new StringBuffer();
1114                        for(int i=0;i<len;++i){
1115                                if(i>0)sb.append(",");
1116                                sb.append("0" + CodeWriter.getBitStateConstSuffix());           
1117                        }
1118                        return String.format("new " + CodeWriter.getBitStateClassName() + "[]{%s}",sb.toString());
1119                }else{
1120                        return "0" + CodeWriter.getBitStateConstSuffix();
1121                }
1122        }
1123
1124        /** 根据字段是否有default value生成initialized字段初始值  */
1125        public String maskInitializeWithDefaultValue(){
1126                if(stateVarTypeIsArray()){
1127                        final long[] array = new long[(countColumns()+CodeWriter.getBitStateClassSize() - 1)/CodeWriter.getBitStateClassSize()];
1128                        Arrays.fill(array, 0L);
1129                        Collections2.filter(getColumnsAsList(), new Predicate<Column>(){
1130                                @Override
1131                                public boolean apply(Column input) {
1132                                        if(!input.getDefaultValue().isEmpty()){
1133                                                int index = input.getOrdinalPosition()-1;
1134                                                array[index/CodeWriter.getBitStateClassSize()] |= (1L << (index & CodeWriter.getBitStateMask()));
1135                                        }
1136                                        return false;
1137                                }});
1138                        String initValue = Joiner.on(',').join(Lists.transform(Longs.asList(array),new Function<Long,String>(){
1139                                @Override
1140                                public String apply(Long input) {
1141                                        return "0B" + Long.toBinaryString(input.longValue()) + CodeWriter.getBitStateConstSuffix();
1142                                }}));
1143                        return String.format("new " + CodeWriter.getBitStateClassName() + "[]{%s}",initValue);
1144                }else{
1145                        Collection<Column> defCols = Collections2.filter(getColumnsAsList(), new Predicate<Column>(){
1146                                @Override
1147                                public boolean apply(Column input) {
1148                                        return !input.getDefaultValue().isEmpty();
1149                                }});
1150                        String mask = Joiner.on(" | ").join(Collections2.transform(defCols,
1151                                        new Function<Column,String>(){
1152                                                @Override
1153                                                public String apply(Column input) {
1154                                                        return input.getIDMaskConstName();
1155                                                }}));
1156                        return mask.isEmpty()?"0" + CodeWriter.getBitStateConstSuffix():"("+mask+")";
1157                }
1158        }
1159
1160        public String stateVarAssignStatement(String src,String dst){
1161                if(stateVarTypeIsArray()){
1162                return "if( null != ${src} && ${dst}.length != ${src}.length )System.arraycopy(${src},0,${dst},0,${dst}.length)"
1163                                .replace("${src}", src).replace("${dst}", dst);
1164                }else{
1165                        return dst + " = " + src;
1166                }
1167        }
1168        public String getLoadMethodOfJunction(){
1169                if(this.isJunctionTable()){
1170                        return "load" + "Via" + this.asCoreClass(true);
1171                }
1172                return "";
1173        }
1174        protected Database getDatabase() {
1175                return database;
1176        }
1177
1178        protected void setDatabase(Database database) {
1179                this.database = database;
1180        }
1181
1182        public Column getAutoincrement() {
1183                return autoincrement;
1184        }
1185
1186        public void setAutoincrement(Column autoincrement) {
1187                this.autoincrement = autoincrement;
1188        }
1189   
1190        public int countForeignKeyNames(){
1191                return this.getFkMapNames().size();
1192        }
1193        
1194        public int countImportedKeyNames(){
1195                int count = 0;
1196                for(Table table:this.getImportedTables()){
1197                        count += table.getFkMapNames(this.getName()).size();
1198                }
1199                return count;
1200        }
1201        public String bitResetAssignExpression(Column[]columns,String varName,String indent){
1202                if(null == columns || 0 == columns.length)return "// columns is null or empty";
1203                if(null == indent)indent = "";
1204                if(stateVarTypeIsArray()){
1205                        StringBuffer buffer = new StringBuffer();
1206                        for(Column column : columns){
1207                                int pos = column.getOrdinalPosition()-1;
1208                                buffer.append(indent).append(String.format("%s[%d] &= (~(1L << %d));\n",varName,pos>>6,pos & 0x3f));                            
1209                        }
1210                        return buffer.toString();
1211                }else{
1212                        StringBuffer buffer = new StringBuffer("(");
1213                        for(int i=0;i<columns.length;++i){
1214                                if(i > 0)
1215                                        buffer.append(" |\n").append(indent);
1216                                buffer.append(columns[i].getIDMaskConstName());
1217                        }
1218                        buffer.append(")");
1219                        return String.format("%s &= (~%s)", varName,buffer.toString());
1220                }
1221        }
1222        /**
1223         * 包含foreign key信息的数据对象 
1224         * @author guyadong
1225         *
1226         */
1227        public static class ForeignKey{
1228                /** foreign key name */
1229                final String fkName;
1230                /** columns of foreign key ,in KEY_SEQ order*/
1231                final Vector<Column> columns = new Vector<Column>();
1232                /** UPDATE_RULE */
1233                final Table.ForeignKeyRule updateRule;
1234                /**  DELETE_RULE */
1235                final Table.ForeignKeyRule deleteRule;
1236                public ForeignKey(String fkName, 
1237                                Table.ForeignKeyRule updateRule, 
1238                                Table.ForeignKeyRule deleteRule, 
1239                                Column columns) {
1240                        checkArgument(!Strings.isNullOrEmpty(fkName));
1241                        checkNotNull(columns);
1242                        this.fkName = fkName;
1243                        this.columns.add(columns);
1244                        this.updateRule = checkNotNull(updateRule);
1245                        this.deleteRule = checkNotNull(deleteRule);
1246                }
1247                public Table getForeignTable(){
1248                        return columns.get(0).getForeignColumn().getTable();
1249                }
1250                public Table getTable(){
1251                        return columns.get(0).getTable();
1252                }
1253                /** 返回主键{@code primaryColumn}对应的字段 */
1254                public Column foreignColumnOf(Column primaryColumn){
1255                        for(Column column:columns){
1256                                if(column.getForeignColumn().equals(primaryColumn))return column;
1257                        }
1258                        return null;
1259                }
1260                @Override
1261                public boolean equals(Object obj) {
1262                        if(super.equals(obj))return true;
1263                        if(!(obj instanceof ForeignKey))return false;
1264                        ForeignKey other = (ForeignKey)obj;
1265                        return new EqualsBuilder()
1266                                .append(fkName, other.fkName)
1267                                .append(columns, other.columns)
1268                                .append(updateRule, other.updateRule)
1269                                .append(deleteRule, other.deleteRule)
1270                                .isEquals();
1271                }
1272                @Override
1273                public String toString() {                      
1274                        return new ToStringBuilder(this)
1275                                        .append("fkName",fkName)
1276                                        .append("columns", columns)
1277                                        .append("updateRule", updateRule)
1278                                        .append("deleteRule", deleteRule)
1279                                        .toString();
1280                }
1281                @Override
1282                public int hashCode() {
1283                        return new HashCodeBuilder()
1284                                        .append(fkName)
1285                                        .append(columns)
1286                                        .append(updateRule)
1287                                        .append(deleteRule)
1288                                        .toHashCode();
1289                }
1290                public String getFkName() {
1291                        return fkName;
1292                }
1293                public Vector<Column> getColumns() {
1294                        return columns;
1295                }
1296                public Table.ForeignKeyRule getUpdateRule() {
1297                        return updateRule;
1298                }
1299                public Table.ForeignKeyRule getDeleteRule() {
1300                        return deleteRule;
1301                }
1302                public String asFkVar(){
1303                        return StringUtilities.convertName(getUniversalName(),true);
1304                }
1305                public String asIkVar(){
1306                        return StringUtilities.convertName(getUniversalName() + "_of_" +getTable().getBasename(true), true);
1307                }
1308                public String getUniversalName() {
1309                        return Joiner.on('_').join(Lists.transform(
1310                                        columns, 
1311                                        new Function<Column,String>(){
1312                                                @Override
1313                                                        public String apply(Column input) {
1314                                                                return input.getName();
1315                                                        }}));
1316                }
1317        }
1318        public static enum ForeignKeyRule{
1319                CASCADE("DELETE"),RESTRICT,SET_NULL("UPDATE"),NO_ACTION,SET_DEFAULT("UPDATE");
1320                private final String eventOfDeleteRule;
1321
1322                private ForeignKeyRule(){
1323                        this("");
1324                }
1325                private ForeignKeyRule(String event) {
1326                        this.eventOfDeleteRule = event;
1327                }
1328                public boolean isNoAction(){
1329                        return this == NO_ACTION || this == RESTRICT;
1330                }
1331                public boolean equals(String value){
1332                        try{
1333                                if(Strings.isNullOrEmpty(value))return false;
1334                                return this == ForeignKeyRule.valueOf(value.toUpperCase());
1335                        }catch(Exception e){
1336                                return false;
1337                        }
1338                }
1339                public String getEventOfDeleteRule() {
1340                        return eventOfDeleteRule;
1341                }
1342        }
1343        public String getCyeleTestMethod(ForeignKey fk) {
1344                return getSelftMethod(fk,"is_cycle_on_");
1345        }
1346        public String getTopMethod(ForeignKey fk) {
1347                return getSelftMethod(fk,"top_of_");
1348        }
1349        public String getLevelMethod(ForeignKey fk) {
1350                return getSelftMethod(fk,"level_of_");
1351        }
1352        public String getListMethod(ForeignKey fk) {
1353                return getSelftMethod(fk,"list_of_");
1354        }
1355        public String getCheckNotCycleMethod(ForeignKey fk) {
1356                return getSelftMethod(fk,"check_cycle_of_");
1357        }
1358        public String getChildListMethod(ForeignKey fk) {
1359                return getSelftMethod(fk,"child_list_by_");
1360        }
1361        private  String getSelftMethod(ForeignKey fk,String prefix) {
1362                if(null == fk )return null;
1363                return StringUtilities.convertName(
1364                                prefix + getSelfFkSuffix(fk),
1365                                true);
1366        }
1367        public  String getSelfFkSuffix(ForeignKey fk) {
1368                if(null == fk )return null;
1369                return 
1370                                Joiner.on('_').join(Lists.transform(fk.columns, new Function<Column,String>(){
1371                                        @Override
1372                                        public String apply(Column input) {
1373                                                return input.getName();
1374                                        }}));
1375        }
1376        public String getGetManagerMethod(){
1377                return "get" +  asManagerClassNSP();            
1378        }
1379        
1380        public boolean isHasMaxSize(){
1381                return Iterables.tryFind(cols, new Predicate<Column>(){
1382
1383                        @Override
1384                        public boolean apply(Column input) {
1385                                return input.isMaxSize();
1386                        }}).isPresent();
1387        }
1388        public boolean isHasNotNullNoDef(){
1389                return Iterables.tryFind(cols, new Predicate<Column>(){
1390
1391                        @Override
1392                        public boolean apply(Column input) {
1393                                return input.isNotNull() && (input.getOriginalDefaultValue() == null);
1394                        }}).isPresent();
1395        }
1396        public boolean isHasCrossableDefaultvalue(){
1397                return Iterables.tryFind(cols, new Predicate<Column>(){
1398
1399                        @Override
1400                        public boolean apply(Column input) {
1401                                return input.isCrossableDefaultvalue() ;
1402                        }}).isPresent();
1403        }
1404        public boolean isNeedInvalidValueAnn(){
1405                return Iterables.tryFind(cols, new Predicate<Column>(){
1406
1407                        @Override
1408                        public boolean apply(Column input) {
1409                                        return !input.getInvalidValueAnn().isEmpty();                                   
1410                        }}).isPresent();
1411        }
1412
1413}