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