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