001package gu.sql2java.generator;
002
003import java.sql.DatabaseMetaData;
004import java.sql.Types;
005import java.text.SimpleDateFormat;
006import java.util.Calendar;
007import java.util.Date;
008import java.util.List;
009import java.util.Random;
010import java.util.Vector;
011import java.util.regex.Matcher;
012import java.util.regex.Pattern;
013
014import org.apache.commons.lang.builder.EqualsBuilder;
015import org.apache.commons.lang.builder.ToStringBuilder;
016
017import com.google.common.base.Function;
018import com.google.common.base.Strings;
019import com.google.common.collect.ImmutableMap;
020import com.google.common.collect.Maps;
021import com.google.common.primitives.Primitives;
022
023import gu.sql2java.generator.CodeWriter;
024import gu.sql2java.generator.Database;
025import gu.sql2java.generator.StringUtilities;
026import gu.sql2java.generator.Table;
027
028public class Column implements Cloneable, Comparable<Column>,MappedType {
029        private String catalog;
030        private String schema;
031        private String tableName;
032        private String name;
033        private String remarks;
034        private String defaultValue;
035        private int size;
036        private int decDigits;
037        private int radix;
038        private int nullable;
039        private int ordinal;
040        private short type;
041        private boolean isPrimaryKey;
042        private String strCheckingType = "";
043        private String autoincrement;
044        private Database db;
045        private List<Column> foreignKeys = new Vector<Column>();
046        private List<Column> importedKeys = new Vector<Column>();
047        private String typeName = "";
048        private static Random rand = new Random();
049
050        @Override
051        public String toString() {
052                return new ToStringBuilder(this)
053                                .append("catalog",catalog)
054                                .append("schema",schema)
055                                .append("tableName",tableName)
056                                .append("name",name)
057                                .append("remarks",remarks)
058                                .append("defaultValue",defaultValue)
059                                .append("size",size)
060                                .append("decDigits",decDigits)
061                                .append("radix",radix)
062                                .append("nullable",nullable)
063                                .append("ordinal",ordinal)
064                                .append("type",type)
065                                .append("isPrimaryKey",isPrimaryKey)
066                                .append("autoincrement",autoincrement)
067                                .append("typeName",typeName)
068                                .toString();
069        }
070
071        @Override
072        public boolean equals(Object obj) {
073                if(super.equals(obj))return true;
074                if(!(obj instanceof Column))return false;
075                Column other = (Column)obj;
076                return new EqualsBuilder()
077                                .append(catalog,other.catalog)
078                                .append(schema,other.schema)
079                                .append(tableName,other.tableName)
080                                .append(name,other.name)
081                                .append(remarks,other.remarks)
082                                .append(defaultValue,other.defaultValue)
083                                .append(size,other.size)
084                                .append(decDigits,other.decDigits)
085                                .append(radix,other.radix)
086                                .append(nullable,other.nullable)
087                                .append(ordinal,other.ordinal)
088                                .append(type,other.type)
089                                .append(isPrimaryKey,other.isPrimaryKey)
090                                .append(autoincrement,other.autoincrement)
091                                .append(typeName,other.typeName)
092                                .isEquals();
093        }
094
095        public void setCheckingType(String strValue) {
096                this.strCheckingType = strValue;
097        }
098
099        public String getCheckingType() {
100                return this.strCheckingType;
101        }
102
103        public void setDatabase(Database db) {
104                this.db = db;
105        }
106
107        public void setCatalog(String catalog) {
108                this.catalog = catalog;
109        }
110
111        public void setSchema(String schema) {
112                this.schema = schema;
113        }
114
115        public void setTableName(String tableName) {
116                this.tableName = tableName;
117        }
118
119        public void setName(String name) {
120                this.name = null == name ? "" : name.replaceAll("\\W", "");
121        }
122
123        public void setType(short type) {
124                this.type = type;
125        }
126
127        public void setSize(int size) {
128                this.size = size;
129        }
130
131        public void setDecimalDigits(int decDigits) {
132                this.decDigits = decDigits;
133        }
134
135        public void setRadix(int radix) {
136                this.radix = radix;
137        }
138
139        public void setNullable(int nullable) {
140                this.nullable = nullable;
141        }
142
143        public void setRemarks(String remarks) {
144                if (remarks != null) {
145                        this.remarks = remarks.replaceAll("/\\*", "SLASH*").replaceAll("\\*/", "*SLASH");
146                }
147        }
148
149        public void setDefaultValue(String defaultValue) {
150                this.defaultValue = defaultValue;
151        }
152
153        public void setOrdinalPosition(int ordinal) {
154                this.ordinal = ordinal;
155        }
156
157        public void isPrimaryKey(boolean isKey) {
158                this.isPrimaryKey = isKey;
159        }
160
161        public String getCatalog() {
162                return this.catalog;
163        }
164
165        public String getSchema() {
166                return this.schema;
167        }
168
169        public String getTableName() {
170                return this.tableName;
171        }
172
173        public String getName() {
174                return this.name;
175        }
176
177        public short getType() {
178                return this.type;
179        }
180
181        public int getSize() {
182                return this.size;
183        }
184
185        public int getDecimalDigits() {
186                return this.decDigits;
187        }
188
189        public int getRadix() {
190                return this.radix;
191        }
192
193        public int getNullable() {
194                return this.nullable;
195        }
196
197        public String getNullableAsString() {
198                return this.getNullable() != 0 ? "nullable" : "null not allowed";
199        }
200
201        public int getOrdinalPosition() {
202                return this.ordinal;
203        }
204
205        public boolean isPrimaryKey() {
206                return this.isPrimaryKey;
207        }
208
209        public String getFullName() {
210                return this.tableName + "." + this.getName();
211        }
212
213        public String getConstName() {
214                return this.getName().toUpperCase();
215        }
216
217        public String getIDConstName() {
218                return (this.tableName + "_ID_" + this.getName()).toUpperCase();
219        }
220        public String getIDMaskConstName() {
221                return (this.tableName + "_ID_" + this.getName()).toUpperCase() + "_MASK";
222        }
223        public Object clone() throws CloneNotSupportedException {
224                return super.clone();
225        }
226
227        private void tuoe() {
228                throw new UnsupportedOperationException("Not supported yet: " + this.getTableName() + "." + this.getName() + " "
229                                + this.getJavaTypeAsTypeName());
230        }
231
232        private void tiae() {
233                throw new IllegalArgumentException("No primary type associated: " + this.getTableName() + "." + this.getName());
234        }
235
236        public int getMappedType() {
237                switch (this.getType()) {
238                        case Types.ARRAY : {
239                                return M_ARRAY;
240                        }
241                        case Types.BIGINT : {
242                                return M_LONG;
243                        }
244                        case Types.BINARY : {
245                                return M_BYTES;
246                        }
247                        case Types.BIT : {
248                                return M_BOOLEAN;
249                        }
250                        case Types.BLOB : {
251                                return M_BLOB;
252                        }
253                        case Types.BOOLEAN : {
254                                return M_BOOLEAN;
255                        }
256                        case Types.CHAR : {
257                                return M_STRING;
258                        }
259                        case Types.CLOB : {
260                                return M_CLOB;
261                        }
262                        case Types.DATALINK : {
263                                return M_URL;
264                        }
265                        case Types.DATE : {
266                                if ("java.util.Date".equals(CodeWriter.dateClassName)) {
267                                        return M_UTILDATE;
268                                }
269                                if ("java.sql.Date".equals(CodeWriter.dateClassName)) {
270                                        return M_SQLDATE;
271                                }
272                                if ("java.util.Calendar".equals(CodeWriter.dateClassName)) {
273                                        return M_CALENDAR;
274                                }
275                                this.tuoe();
276                        }
277                        case Types.DECIMAL : {
278                                return this.getDecimalDigits() > 0 ? M_BIGDECIMAL : M_LONG;
279                        }
280                        case Types.DISTINCT : {
281                                return M_OBJECT;
282                        }
283                        case Types.DOUBLE : {
284                                return M_DOUBLE;
285                        }
286                        case Types.FLOAT : {
287                                return M_DOUBLE;
288                        }
289                        case Types.INTEGER : {
290                                return this.getTypeName().equalsIgnoreCase("INT UNSIGNED") ? M_LONG : M_INTEGER;
291                        }
292                        case Types.JAVA_OBJECT : {
293                                return M_OBJECT;
294                        }
295                        case Types.LONGVARBINARY : {
296                                return M_BYTES;
297                        }
298                        case Types.LONGVARCHAR : {
299                                return M_STRING;
300                        }
301                        case Types.NUMERIC : {
302                                return this.getDecimalDigits() > 0 ? M_BIGDECIMAL : M_LONG;
303                        }
304                        case Types.OTHER : {
305                                return M_OBJECT;
306                        }
307                        case Types.REAL : {
308                                return M_FLOAT;
309                        }
310                        case Types.REF : {
311                                return M_REF;
312                        }
313                        case Types.SMALLINT : {
314                                return M_INTEGER;
315                        }
316                        case Types.STRUCT : {
317                                return M_OBJECT;
318                        }
319                        case Types.TIME : {
320                                if ("java.util.Date".equals(CodeWriter.timeClassName)) {
321                                        return M_UTILDATE;
322                                }
323                                if ("java.sql.Time".equals(CodeWriter.timeClassName)) {
324                                        return M_TIME;
325                                }
326                                if ("java.util.Calendar".equals(CodeWriter.timeClassName)) {
327                                        return M_CALENDAR;
328                                }
329                                this.tuoe();
330                        }
331                        case Types.TIMESTAMP : {
332                                if ("java.util.Date".equals(CodeWriter.timestampClassName)) {
333                                        return M_UTILDATE;
334                                }
335                                if ("java.sql.Timestamp".equals(CodeWriter.timestampClassName)) {
336                                        return M_TIMESTAMP;
337                                }
338                                if ("java.util.Calendar".equals(CodeWriter.timestampClassName)) {
339                                        return M_CALENDAR;
340                                }
341                                this.tuoe();
342                        }
343                        case Types.TINYINT : {
344                                return M_INTEGER;
345                        }
346                        case Types.VARBINARY : {
347                                return M_BYTES;
348                        }
349                        case Types.VARCHAR : {
350                                return M_STRING;
351                        }
352                }
353                this.tuoe();
354                return -1;
355        }
356
357        public String getQuerySetMethod() {
358                switch (this.getType()) {
359                        case Types.ARRAY : {
360                                return "setArray";
361                        }
362                        case Types.BIGINT : {
363                                return "setBigDecimal";
364                        }
365                        case Types.BINARY : {
366                                return "setBytes";
367                        }
368                        case Types.BIT : {
369                                return "setBoolean";
370                        }
371                        case Types.BLOB : {
372                                return "setBlob";
373                        }
374                        case Types.BOOLEAN : {
375                                return "setBoolean";
376                        }
377                        case Types.CHAR : {
378                                return "setString";
379                        }
380                        case Types.CLOB : {
381                                return "setClob";
382                        }
383                        case Types.DATALINK : {
384                                return "setURL";
385                        }
386                        case Types.DATE : {
387                                return "setDate";
388                        }
389                        case Types.DECIMAL : {
390                                return this.getDecimalDigits() > 0 ? "setBigDecimal" : "setLong";
391                        }
392                        case Types.DISTINCT : {
393                                return "setObject";
394                        }
395                        case Types.DOUBLE : {
396                                return "setDouble";
397                        }
398                        case Types.FLOAT : {
399                                return "setDouble";
400                        }
401                        case Types.INTEGER : {
402                                return this.getTypeName().equalsIgnoreCase("INT UNSIGNED") ? "setLong" : "setInt";
403                        }
404                        case Types.JAVA_OBJECT : {
405                                return "setObject";
406                        }
407                        case Types.LONGVARBINARY : {
408                                return "setBytes";
409                        }
410                        case Types.LONGVARCHAR : {
411                                return "setString";
412                        }
413                        case Types.NUMERIC : {
414                                return this.getDecimalDigits() > 0 ? "setBigDecimal" : "setLong";
415                        }
416                        case Types.OTHER : {
417                                return "setObject";
418                        }
419                        case Types.REAL : {
420                                return "setFloat";
421                        }
422                        case Types.REF : {
423                                return "setRef";
424                        }
425                        case Types.SMALLINT : {
426                                return "setInt";
427                        }
428                        case Types.STRUCT : {
429                                return "setObject";
430                        }
431                        case Types.TIME : {
432                                if ("java.util.Date".equals(CodeWriter.timeClassName)) {
433                                        return "setDate";
434                                }
435                                if ("java.sql.Time".equals(CodeWriter.timeClassName)) {
436                                        return "setTime";
437                                }
438                                this.tuoe();
439                        }
440                        case Types.TIMESTAMP : {
441                                if ("java.util.Date".equals(CodeWriter.timestampClassName)) {
442                                        return "setDate";
443                                }
444                                if ("java.sql.Timestamp".equals(CodeWriter.timestampClassName)) {
445                                        return "setTimestamp";
446                                }
447                                this.tuoe();
448                        }
449                        case Types.TINYINT : {
450                                return "setInt";
451                        }
452                        case Types.VARBINARY : {
453                                return "setBytes";
454                        }
455                        case Types.VARCHAR : {
456                                return "setString";
457                        }
458                }
459                this.tuoe();
460                return "setObject";
461        }
462
463        /**
464         * 返回对应的Java类型,除java语言内置类型(java.lang)外,其他类型返回全名
465         * @return
466         */
467        public String getJavaType() {
468                switch (this.getMappedType()) {
469                        case M_ARRAY : {
470                                return "java.sql.Array";
471                        }
472                        case M_BIGDECIMAL : {
473                                return "java.math.BigDecimal";
474                        }
475                        case M_BOOLEAN : {
476                                return "Boolean";
477                        }
478                        case M_BYTES : {
479                                return CodeWriter.binaryClassName;
480                        }
481                        case M_CLOB : {
482                                // map Clob to java.lang.String
483                                return "String";
484                        }
485                        case M_SQLDATE : {
486                                return "java.sql.Date";
487                        }
488                        case M_UTILDATE : {
489                                return "java.util.Date";
490                        }
491                        case M_DOUBLE : {
492                                return "Double";
493                        }
494                        case M_FLOAT : {
495                                return "Float";
496                        }
497                        case M_BLOB : {
498                                return CodeWriter.binaryClassName;
499                        }
500                        case M_INTEGER : {
501                                return "Integer";
502                        }
503                        case M_LONG : {
504                                return "Long";
505                        }
506                        case M_REF : {
507                                return "java.sql.Ref";
508                        }
509                        case M_STRING : {
510                                return "String";
511                        }
512                        case M_TIME : {
513                                return "java.sql.Time";
514                        }
515                        case M_TIMESTAMP : {
516                                return "java.sql.Timestamp";
517                        }
518                        case M_URL : {
519                                return "java.net.URL";
520                        }
521                        case M_OBJECT : {
522                                return "Object";
523                        }
524                        case M_CALENDAR : {
525                                return "java.util.Calendar";
526                        }
527                }
528                this.tiae();
529                return null;
530        }
531
532        public boolean hasPrimaryType() {
533                return this.getJavaPrimaryType() != null;
534        }
535
536        public String getJavaPrimaryType() throws IllegalArgumentException {
537                int decimalDigits = this.getDecimalDigits();
538                if ((this.type == Types.DECIMAL || this.type == Types.NUMERIC) && decimalDigits == 0) {
539                        if (this.size == 1) {
540                                return "boolean";
541                        }
542                        if (this.size < 3) {
543                                return "byte";
544                        }
545                        if (this.size < 5) {
546                                return "short";
547                        }
548                        if (this.size < 10) {
549                                return "int";
550                        }
551                        if (this.size < 19) {
552                                return "long";
553                        }
554                }
555                switch (this.getMappedType()) {
556                        case M_BOOLEAN : {
557                                return "boolean";
558                        }
559                        case M_SQLDATE : {
560                                return "long";
561                        }
562                        case M_UTILDATE : {
563                                return "long";
564                        }
565                        case M_DOUBLE : {
566                                return "double";
567                        }
568                        case M_FLOAT : {
569                                return "float";
570                        }
571                        case M_INTEGER : {
572                                return "int";
573                        }
574                        case M_LONG : {
575                                return "long";
576                        }
577                        case M_TIME : {
578                                return "long";
579                        }
580                        case M_TIMESTAMP : {
581                                return "long";
582                        }
583                }
584                return null;
585        }
586        
587        public String getNullInstead(){
588                if(isDate()){
589                        return "new "+getJavaType() + "(0L)";
590                }else if(isString()){
591                        return "\"\"";
592                }else{
593                        String primitiveName = getJavaPrimaryType();            
594                        if(null != primitiveName){
595                                ImmutableMap<String, Class<?>> primtypes = Maps.uniqueIndex(Primitives.allWrapperTypes(),new Function<Class<?>,String>(){
596                                        @Override
597                                        public String apply(Class<?> input) {
598                                                return Primitives.unwrap(input).getSimpleName();
599                                        }});
600                                Class<?> wrapType = primtypes.get(primitiveName);
601                                if(Number.class.isAssignableFrom(wrapType) || Character.class==wrapType){
602                                        return wrapType.getSimpleName()+".MIN_VALUE";
603                                }else if(Boolean.class == wrapType)
604                                        return wrapType.getSimpleName()+".FALSE";
605                                tuoe();
606                        }
607                }
608                return "null";
609        }
610        public String getJavaTypeAsTypeName() {
611                switch (this.getType()) {
612                        case Types.ARRAY : {
613                                return "Types.ARRAY";
614                        }
615                        case Types.BIGINT : {
616                                return "Types.BIGINT";
617                        }
618                        case Types.BINARY : {
619                                return "Types.BINARY";
620                        }
621                        case Types.BIT : {
622                                return "Types.BIT";
623                        }
624                        case Types.BLOB : {
625                                return "Types.BLOB";
626                        }
627                        case Types.BOOLEAN : {
628                                return "Types.BOOLEAN";
629                        }
630                        case Types.CHAR : {
631                                return "Types.CHAR";
632                        }
633                        case Types.CLOB : {
634                                return "Types.CLOB";
635                        }
636                        case Types.DATALINK : {
637                                return "Types.DATALINK";
638                        }
639                        case Types.DATE : {
640                                return "Types.DATE";
641                        }
642                        case Types.DECIMAL : {
643                                return "Types.DECIMAL";
644                        }
645                        case Types.DISTINCT : {
646                                return "Types.DISTINCT";
647                        }
648                        case Types.DOUBLE : {
649                                return "Types.DOUBLE";
650                        }
651                        case Types.FLOAT : {
652                                return "Types.FLOAT";
653                        }
654                        case Types.INTEGER : {
655                                return "Types.INTEGER";
656                        }
657                        case Types.JAVA_OBJECT : {
658                                return "Types.JAVA_OBJECT";
659                        }
660                        case Types.LONGVARBINARY : {
661                                return "Types.LONGVARBINARY";
662                        }
663                        case Types.LONGVARCHAR : {
664                                return "Types.LONGVARCHAR";
665                        }
666                        case Types.NULL : {
667                                return "Types.NULL";
668                        }
669                        case Types.NUMERIC : {
670                                return "Types.NUMERIC";
671                        }
672                        case Types.OTHER : {
673                                return "Types.OTHER";
674                        }
675                        case Types.REAL : {
676                                return "Types.REAL";
677                        }
678                        case Types.REF : {
679                                return "Types.REF";
680                        }
681                        case Types.SMALLINT : {
682                                return "Types.SMALLINT";
683                        }
684                        case Types.STRUCT : {
685                                return "Types.STRUCT";
686                        }
687                        case Types.TIME : {
688                                return "Types.TIME";
689                        }
690                        case Types.TIMESTAMP : {
691                                return "Types.TIMESTAMP";
692                        }
693                        case Types.TINYINT : {
694                                return "Types.TINYINT";
695                        }
696                        case Types.VARBINARY : {
697                                return "Types.VARBINARY";
698                        }
699                        case Types.VARCHAR : {
700                                return "Types.VARCHAR";
701                        }
702                }
703                return "unkown SQL type " + this.getType();
704        }
705
706        public boolean isColumnNumeric() {
707                switch (this.getMappedType()) {
708                        case M_BIGDECIMAL :
709                        case M_DOUBLE :
710                        case M_FLOAT :
711                        case M_INTEGER :
712                        case M_LONG : {
713                                return true;
714                        }
715                }
716                return false;
717        }
718
719        public boolean isString() {
720                return M_STRING == this.getMappedType();
721        }
722        public boolean isFloat() {
723                return M_FLOAT == this.getMappedType();
724        }
725        public boolean isDate() {
726                switch (this.getMappedType()) {
727                case M_SQLDATE: 
728                case M_UTILDATE :
729                case M_TIME :
730                case M_TIMESTAMP : 
731                        return true;
732                }
733                return false;
734        }
735        public boolean isBinary() {
736                switch (this.getMappedType()) {
737                case M_BYTES: 
738                case M_BLOB:
739                        return true;
740                }
741                return false;
742        }
743        public boolean isCalendar() {
744                return this.getMappedType() == M_CALENDAR;
745        }
746
747        public boolean hasCompareTo() throws Exception {
748                switch (this.getMappedType()) {
749                        case M_ARRAY : {
750                                return false;
751                        }
752                        case M_BIGDECIMAL : {
753                                return true;
754                        }
755                        case M_BOOLEAN : {
756                                return true;
757                        }
758                        case M_BYTES : {
759                                return CodeWriter.binaryIsByteBuffer();
760                        }
761                        case M_CLOB : {
762                                // Clob map to java.lang.String that has compareTo
763                                return true;
764                        }
765                        case M_SQLDATE : {
766                                return true;
767                        }
768                        case M_UTILDATE : {
769                                return true;
770                        }
771                        case M_DOUBLE : {
772                                return true;
773                        }
774                        case M_FLOAT : {
775                                return true;
776                        }
777                        case M_BLOB : {
778                                return CodeWriter.binaryIsByteBuffer();
779                        }
780                        case M_INTEGER : {
781                                return true;
782                        }
783                        case M_LONG : {
784                                return true;
785                        }
786                        case M_REF : {
787                                return false;
788                        }
789                        case M_STRING : {
790                                return true;
791                        }
792                        case M_TIME : {
793                                return true;
794                        }
795                        case M_TIMESTAMP : {
796                                return true;
797                        }
798                        case M_URL : {
799                                return false;
800                        }
801                        case M_OBJECT : {
802                                return false;
803                        }
804                        case M_CALENDAR : {
805                                return true;
806                        }
807                }
808                return false;
809        }
810
811        public boolean useEqualsInSetter() throws Exception {
812                // 优先使用equals方法
813                if(hasCompareTo())return true;
814                switch (this.getMappedType()) {
815                        case M_BOOLEAN : {
816                                return true;
817                        }
818                        case M_URL : {
819                                return true;
820                        }
821                }
822                return false;
823        }
824
825        public String getResultSetMethodObject(String pos) {
826                return this.getResultSetMethodObject("rs", pos);
827        }
828
829        public String getResultSetMethodObject(String resultSet, String pos) {
830                switch (this.getMappedType()) {
831                        case M_ARRAY : {
832                                return resultSet + ".getArray(" + pos + ")";
833                        }
834                        case M_LONG : {
835                                return CodeWriter.MGR_CLASS + ".getLong(" + resultSet + ", " + pos + ")";
836                        }
837                        case M_BYTES : {
838                                return CodeWriter.MGR_CLASS + ".getBytes(" + resultSet + ", " + pos + ")";
839                        }
840                        case M_BLOB : {
841                                return CodeWriter.MGR_CLASS + ".getBlob(" + resultSet + ", " + pos + ")";
842                        }
843                        case M_BOOLEAN : {
844                                return CodeWriter.MGR_CLASS + ".getBoolean(" + resultSet + ", " + pos + ")";
845                        }
846                        case M_STRING : {
847                                return resultSet + ".getString(" + pos + ")";
848                        }
849                        case M_CLOB : {
850                                return CodeWriter.MGR_CLASS + ".getClob(" + resultSet + ", " + pos + ")";
851                        }
852                        case M_URL : {
853                                return resultSet + ".getURL(" + pos + ")";
854                        }
855                        case M_BIGDECIMAL : {
856                                return resultSet + ".getBigDecimal(" + pos + ")";
857                        }
858                        case M_DOUBLE : {
859                                return CodeWriter.MGR_CLASS + ".getDouble(" + resultSet + ", " + pos + ")";
860                        }
861                        case M_FLOAT : {
862                                return CodeWriter.MGR_CLASS + ".getFloat(" + resultSet + ", " + pos + ")";
863                        }
864                        case M_INTEGER : {
865                                return CodeWriter.MGR_CLASS + ".getInteger(" + resultSet + ", " + pos + ")";
866                        }
867                        case M_OBJECT : {
868                                return resultSet + ".getObject(" + pos + ")";
869                        }
870                        case M_REF : {
871                                return resultSet + ".getRef(" + pos + ")";
872                        }
873                        case M_SQLDATE : {
874                                return resultSet + ".getDate(" + pos + ")";
875                        }
876                        case M_TIME : {
877                                return resultSet + ".getTime(" + pos + ")";
878                        }
879                        case M_TIMESTAMP : {
880                                return resultSet + ".getTimestamp(" + pos + ")";
881                        }
882                        case M_UTILDATE : {
883                                switch (this.getType()) {
884                                        case Types.TIME : {
885                                                return resultSet + ".getTime(" + pos + ")";
886                                        }
887                                        case Types.TIMESTAMP : {
888                                                return resultSet + ".getTimestamp(" + pos + ")";
889                                        }
890                                        case Types.DATE : {
891                                                return resultSet + ".getDate(" + pos + ")";
892                                        }
893                                }
894                                this.tuoe();
895                        }
896                        case M_CALENDAR : {
897                                return CodeWriter.MGR_CLASS + ".getCalendar(" + resultSet + ", " + pos + ")";
898                        }
899                }
900                this.tuoe();
901                return null;
902        }
903
904        public String getPreparedStatementMethod(String var, int pos) {
905                return this.getPreparedStatementMethod(var, String.valueOf(pos));
906        }
907
908        public String getPreparedStatementMethod(String var, String pos) {
909                StringBuffer sb = new StringBuffer();
910                StringBuffer end = new StringBuffer();
911                end.append(pos).append(", ").append(var).append(");");
912                String fillNullStart = Boolean.TRUE == CodeWriter.getFillNull() ? "" : "if(fillNull){";
913                String fillNullEnd = Boolean.TRUE == CodeWriter.getFillNull() ? "" : "}";
914        Pattern p = Pattern.compile("^((?:SQL_LIKE_WILDCARD\\s*\\+)*)([\\w\\. \\(\\)-]*)((?:\\+\\s*SQL_LIKE_WILDCARD)*)$");
915        
916        Matcher m = p.matcher(var);
917        if(!m.matches()){
918                throw new IllegalArgumentException(String.format("Not match found %s", var));
919        }
920                String v = m.group(2);
921                sb.append("if (").append(v).append(" == null) {"+fillNullStart+" ps.setNull(").append(pos).append(", ")
922                                .append(this.getJavaTypeAsTypeName()).append(");").append(fillNullEnd).append(" } else { ");
923                end.append(" }");
924                switch (this.getMappedType()) {
925                        case M_ARRAY : {
926                                return sb.append("ps.setArray(").append(end).toString();
927                        }
928                        case M_LONG : {
929                                return sb.append(CodeWriter.MGR_CLASS).append(".setLong(ps, ").append(end).toString();
930                        }
931                        case M_BYTES : {
932                                return sb.append(CodeWriter.MGR_CLASS).append(".setBytes("+this.getJavaTypeAsTypeName()+",ps, ").append(end).toString();
933                        }
934                        case M_BLOB : {
935                                return sb.append(CodeWriter.MGR_CLASS).append(".setBlob(ps, ").append(end).toString();
936                        }
937                        case M_BOOLEAN : {
938                                return sb.append(CodeWriter.MGR_CLASS).append(".setBoolean(ps, ").append(end).toString();
939                        }
940                        case M_STRING : {
941                                return sb.append("ps.setString(").append(end).toString();
942                        }
943                        case M_CLOB : {
944                                return sb.append(CodeWriter.MGR_CLASS).append(".setClob(ps, ").append(end).toString();
945                        }
946                        case M_URL : {
947                                return sb.append("ps.setURL(").append(end).toString();
948                        }
949                        case M_BIGDECIMAL : {
950                                return sb.append("ps.setBigDecimal(").append(end).toString();
951                        }
952                        case M_DOUBLE : {
953                                return sb.append(CodeWriter.MGR_CLASS).append(".setDouble(ps, ").append(end).toString();
954                        }
955                        case M_INTEGER : {
956                                return sb.append(CodeWriter.MGR_CLASS).append(".setInteger(ps, ").append(end).toString();
957                        }
958                        case M_OBJECT : {
959                                return sb.append("ps.setObject(").append(end).toString();
960                        }
961                        case M_FLOAT : {
962                                return sb.append(CodeWriter.MGR_CLASS).append(".setFloat(ps, ").append(end).toString();
963                        }
964                        case M_SQLDATE : {
965                                return sb.append("ps.setDate(").append(end).toString();
966                        }
967                        case M_TIME : {
968                                return sb.append("ps.setTime(").append(end).toString();
969                        }
970                        case M_TIMESTAMP : {
971                                return sb.append("ps.setTimestamp(").append(end).toString();
972                        }
973                        case M_UTILDATE : {
974                                switch (this.getType()) {
975                                        case Types.TIMESTAMP : {
976                                                return sb.append("ps.setTimestamp(").append(pos).append(", new java.sql.Timestamp(").append(var)
977                                                                .append(".getTime())); }").toString();
978                                        }
979                                        case Types.DATE : {
980                                                return sb.append("ps.setDate(").append(pos).append(", new java.sql.Date(").append(var)
981                                                                .append(".getTime())); }").toString();
982                                        }
983                                        case Types.TIME : {
984                                                return sb.append("ps.setTime(").append(pos).append(", new java.sql.Time(").append(var)
985                                                                .append(".getTime())); }").toString();
986                                        }
987                                }
988                                return null;
989                        }
990                        case M_CALENDAR : {
991                                return sb.append(CodeWriter.MGR_CLASS).append(".setCalendar(ps, ").append(end).toString();
992                        }
993                        case M_REF : {
994                                sb.setLength(0);
995                                sb.append("ps.setRef(").append(end);
996                                sb.setLength(sb.length() - 2);
997                                return sb.toString();
998                        }
999                }
1000                sb.setLength(0);
1001                sb.append("ps.setObject(").append(end);
1002                sb.setLength(sb.length() - 2);
1003                return sb.toString();
1004        }
1005
1006        public String getStringConvertionMethod() {
1007                switch (this.getMappedType()) {
1008                        case M_BIGDECIMAL : {
1009                                return "new java.math.BigDecimal";
1010                        }
1011                        case M_BOOLEAN : {
1012                                return "new Boolean";
1013                        }
1014                        case M_SQLDATE : {
1015                                return "new java.sql.Date";
1016                        }
1017                        case M_DOUBLE : {
1018                                return "new Double";
1019                        }
1020                        case M_FLOAT : {
1021                                return "new Float";
1022                        }
1023                        case M_INTEGER : {
1024                                return "new Integer";
1025                        }
1026                        case M_LONG : {
1027                                return "new Long";
1028                        }
1029                        case M_STRING : {
1030                                return "";
1031                        }
1032                        case M_UTILDATE :
1033                        case M_TIME :
1034                        case M_TIMESTAMP : {
1035                                if ("java.util.GregorianCalendar".equals(CodeWriter.dateClassName)) {
1036                                        return "GregorianDate";
1037                                }
1038                                return CodeWriter.MGR_CLASS + ".getDateFromString";
1039                        }
1040                }
1041                System.err.println(
1042                                " unknown mapped type " + this.getMappedType() + " (" + this.getType() + ") for " + this.getFullName());
1043                return "";
1044        }
1045
1046        public String getDefaultWidget() {
1047                if (this.isForeignKey()) {
1048                        return "SelectWidget";
1049                }
1050                if (this.isString() && (this.getSize() > 200 || this.getSize() == -1)) {
1051                        return "TextAreaWidget";
1052                }
1053                switch (this.getMappedType()) {
1054                        case M_BOOLEAN : {
1055                                return "BooleanWidget";
1056                        }
1057                        case M_SQLDATE :
1058                        case M_UTILDATE :
1059                        case M_TIME :
1060                        case M_TIMESTAMP : {
1061                                return "DateWidget";
1062                        }
1063                        case M_BIGDECIMAL :
1064                        case M_DOUBLE :
1065                        case M_FLOAT :
1066                        case M_INTEGER :
1067                        case M_LONG : {
1068                                return "NumericWidget";
1069                        }
1070                        case M_ARRAY :
1071                        case M_BYTES :
1072                        case M_CLOB :
1073                        case M_REF :
1074                        case M_STRING :
1075                        case M_URL :
1076                        case M_OBJECT : {
1077                                return "InputWidget";
1078                        }
1079                }
1080                System.err.println("type unknown for " + this.getFullName());
1081                return "";
1082        }
1083
1084        public boolean isVersion() {
1085                if (!CodeWriter.optimisticLockType.equalsIgnoreCase("timestamp")) {
1086                        return false;
1087                }
1088                if (!this.getName().equalsIgnoreCase(CodeWriter.optimisticLockColumn)) {
1089                        return false;
1090                }
1091                if (this.getMappedType() == M_LONG || this.getMappedType() == M_STRING) {
1092                        return true;
1093                }
1094                return false;
1095        }
1096
1097        public Table getTable() {
1098                return this.db.getTable(this.getTableName());
1099        }
1100
1101        public void addForeignKey(Column col, 
1102                        String fkName, 
1103                        short keySeq,
1104                        Table.ForeignKeyRule updateRule,
1105                        Table.ForeignKeyRule deleteRule) {
1106                this.foreignKeys.add(col);
1107                this.getTable().addForeignKey(this, fkName,keySeq, updateRule, deleteRule);
1108        }
1109
1110        public List<Column> getForeignKeys() {
1111                return this.foreignKeys;
1112        }
1113
1114        public void addImportedKey(Column col) {
1115                this.importedKeys.add(col);
1116                this.getTable().addImportedKey(col);
1117        }
1118
1119        public List<Column> getImportedKeys() {
1120                return this.importedKeys;
1121        }
1122
1123        public int countImportedKeys() {
1124                return this.importedKeys.size();
1125        }
1126
1127        public boolean isImportedKey() {
1128                if (this.countImportedKeys() > 0) {
1129                        return true;
1130                }
1131                return false;
1132        }
1133
1134        public Column getForeignColumn() {
1135                return (Column) this.foreignKeys.get(0);
1136        }
1137
1138        public int countForeignKeys() {
1139                return this.foreignKeys.size();
1140        }
1141
1142        public boolean isForeignKey() {
1143                if (this.countForeignKeys() > 0) {
1144                        return true;
1145                }
1146                return false;
1147        }
1148
1149        public String getPropertyTag() {
1150                return (this.getTableName() + "." + this.getName()).toLowerCase();
1151        }
1152
1153        public String getDefaultRules() {
1154                String rule = "";
1155                rule = this.getNullable() == 0 && !this.isPrimaryKey() ? rule + " nullnotallowed" : rule + " nullallowed";
1156                if (this.getType() == 91 || this.getType() == 93) {
1157                        rule = rule + " dateformat";
1158                }
1159                return rule;
1160        }
1161
1162        public boolean getDefaultIncludeFor(String webElement) {
1163                return true;
1164        }
1165        
1166        private static final String EMPTY_STRING = "";
1167        /**
1168         * 生成缺省值字符串
1169         * @param nullInstead 指示{@link #defaultValue}为 {@code null}时是否用字符串'null'代替
1170         * @return
1171         */
1172        public String getDefaultValue(boolean nullInstead) {
1173                String empty = nullInstead?"null":EMPTY_STRING;
1174                if(!CodeWriter.getPropertyBoolean("codewriter.generate.defaultvalue")){
1175                        return empty;
1176                }
1177                if (null != this.defaultValue) {
1178                        if (this.isColumnNumeric()) {
1179                                try {
1180                                        double value = Double.parseDouble(this.defaultValue);
1181                                        switch (this.getMappedType()) {
1182                                                case M_BIGDECIMAL :
1183                                                case M_INTEGER :
1184                                                case M_LONG : {
1185                                                        return this.generateNewNumeric(this.getJavaType(), this.defaultValue);
1186                                                }
1187                                                case M_DOUBLE :
1188                                                case M_FLOAT : {
1189                                                        return this.generateNewNumeric(this.getJavaType(), String.valueOf(value));
1190                                                }
1191                                        }
1192                                        return empty;
1193                                } catch (NumberFormatException nfe) {
1194                                        return empty;
1195                                }
1196                        }
1197                        if (this.isDate()) {
1198                                try {
1199                                        return generateDateDefaultValue(this.getJavaType(),this.defaultValue);
1200                                } catch (IllegalArgumentException pe) {
1201                                        return empty;
1202                                }
1203                        }
1204                        if (this.isString()) {
1205                                return "\"" + this.defaultValue + '\"';
1206                        }
1207                        if (M_BOOLEAN == this.getMappedType()) {
1208                                return "Boolean.valueOf(\"" + ("1".equals(this.defaultValue) ? "true" : "false")
1209                                                + "\").booleanValue()";
1210                        }
1211                }
1212                return this.defaultValue == null ? empty : this.defaultValue;
1213        }
1214        /** 兼容之前版本 */
1215        public String getDefaultValue() {
1216                return getDefaultValue(false);
1217        }
1218        /** 返回{@link #defaultValue}原始值 */
1219        public String getOriginalDefaultValue(){
1220                return this.defaultValue;
1221        }
1222        /** SQL 类型日期字符串转为java 日期对象 */
1223        private static Date parseSqlDate(String source){
1224                if(null == source)
1225                        throw new IllegalArgumentException();
1226                try{
1227                        return java.sql.Date.valueOf(source);
1228                }catch(IllegalArgumentException e){
1229                        try{
1230                                return java.sql.Time.valueOf(source);
1231                        }catch(IllegalArgumentException e2){
1232                                return java.sql.Timestamp.valueOf(source);
1233                        }
1234                }
1235        }
1236        /** 生成日期类型缺省值语句 */
1237        private String generateDateDefaultValue(String type, String parameter) {
1238                StringBuffer sb = new StringBuffer(100);
1239                Date parsedDate = parseSqlDate(parameter);
1240                String dateStr;
1241                switch(this.getMappedType()){
1242                case M_UTILDATE:{
1243                        String instanceName="";
1244                        if(parsedDate instanceof java.sql.Date){
1245                                instanceName = "new java.text.SimpleDateFormat(\"yyyy-MM-dd\")";
1246                        }else if(parsedDate instanceof java.sql.Time){
1247                                instanceName = "new java.text.SimpleDateFormat(\"HH:mm:ss\")";
1248                        }else if(parsedDate instanceof java.sql.Timestamp){
1249                                instanceName = "new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\")";
1250                        }else{
1251                                throw new IllegalStateException("invalid type");
1252                        }
1253                        sb.append(instanceName).append(".parse(\"").append(parsedDate.toString()).append("\",new java.text.ParsePosition(0))");
1254                        break;
1255                }
1256                case M_SQLDATE:{
1257                        dateStr = new java.sql.Date(parsedDate.getTime()).toString();
1258                        sb.append(type).append(".valueOf(\"").append(dateStr).append("\")");    
1259                        break;
1260                }
1261                case M_TIME:{
1262                        dateStr = new java.sql.Time(parsedDate.getTime()).toString();
1263                        sb.append(type).append(".valueOf(\"").append(dateStr).append("\")");    
1264                        break;
1265                }
1266                case M_TIMESTAMP:{
1267                        dateStr = new java.sql.Timestamp(parsedDate.getTime()).toString();
1268                        sb.append(type).append(".valueOf(\"").append(dateStr).append("\")");    
1269                        break;
1270                }
1271                default:
1272                        return EMPTY_STRING;
1273                }
1274                return sb.toString();
1275        }       
1276        
1277        private String generateNewNumeric(String type, String parameter) {
1278                StringBuffer sb = new StringBuffer(70);
1279                sb.append("new ").append(type);
1280                sb.append('(').append(parameter).append(')');
1281                return sb.toString();
1282        }
1283
1284        /** 生成缺省值({@link #defaultValue})的注释信息 */
1285        public String commentOfDefaultValue(){
1286                return Strings.isNullOrEmpty(defaultValue)?EMPTY_STRING: "/* DEFAULT:'"+defaultValue+"'*/";
1287        }
1288        /** 生成缺省值赋值语句 */
1289    public String getDefaultValueAssignment(boolean nullInstead){
1290        StringBuffer buffer = new StringBuffer();
1291        String value = getDefaultValue(nullInstead);
1292        if(!value.isEmpty())
1293                buffer.append(" = ").append(value);
1294        return buffer.toString();
1295    }
1296        public String getRemarks() {
1297                return this.remarks == null ? "" : this.remarks;
1298        }
1299
1300        public String getJavaName() {
1301                return this.convertName(this.getName());
1302        }
1303
1304        public String getSampleData() {
1305                if (this.getNullable() > 1 && rand.nextInt(20) == 10) {
1306                        return "";
1307                }
1308                if (this.isColumnNumeric()) {
1309                        return "" + rand.nextInt(100);
1310                }
1311                if (this.isDate()) {
1312                        Calendar rightNow = Calendar.getInstance();
1313                        rightNow.set(2000 + rand.nextInt(20), 1 + rand.nextInt(12), 1 + rand.nextInt(28), rand.nextInt(23),
1314                                        rand.nextInt(60), rand.nextInt(60));
1315                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
1316                        return dateFormat.format(rightNow.getTime());
1317                }
1318                return StringUtilities.getSampleString((int) this.getSize());
1319        }
1320
1321        private String escape(String s) {
1322                return StringUtilities.escape((String) s);
1323        }
1324
1325        private String escape() {
1326                return this.escape(this.getName());
1327        }
1328
1329        public String convertName(String columnName) {
1330                return StringUtilities.convertName((String) columnName, true);
1331        }
1332
1333        public String convertName(Column col) {
1334                return this.convertName(col.getName());
1335        }
1336
1337        public String convertName() {
1338                return this.convertName(this.name);
1339        }
1340
1341        public String getImportedKeyVarName() {
1342                return this.convertName(this.escape() + "_collection");
1343        }
1344
1345        public String getGetMethod() {
1346                return this.convertName("get_" + this.escape());
1347        }
1348
1349        public String getSetMethod() {
1350                return this.convertName("set_" + this.escape());
1351        }
1352        public String getReadMethod() {
1353                return this.convertName("read_" + this.escape());
1354        }
1355        public String getWriteMethod() {
1356                return this.convertName("write_" + this.escape());
1357        }
1358        public String getModifiedMethod() {
1359                return this.convertName("check_" + this.escape() + "_modified");
1360        }
1361
1362        public String getInitializedMethod() {
1363                return this.convertName("check_" + this.escape() + "_initialized");
1364        }
1365        public String getGetCacheMethod() {
1366                return this.convertName("get_bean_by_" + this.escape());
1367        }
1368        public String getPutCacheMethod() {
1369                return this.convertName("put_by_" + this.escape());
1370        }
1371        public String getPutIfAbsentCacheMethod() {
1372                return this.convertName("put_If_absent_by_" + this.escape());
1373        }
1374        public String getReplaceCacheMethod() {
1375                return this.convertName("replace_by_" + this.escape());
1376        }
1377
1378        public String bitAndExpression(String varName){
1379                if(this.getTable().countColumns()>32){
1380                        int pos = getOrdinalPosition()-1;
1381                        return String.format("(%s[%d] & (1 << %d))",varName,pos>>6,pos & 0x3f);
1382                }else{
1383                        return String.format("(%s & %s)", varName,getIDMaskConstName());
1384                }
1385        }
1386        
1387        public String bitORAssignExpression(String varName){
1388                if(this.getTable().countColumns()>32){
1389                        int pos = getOrdinalPosition()-1;
1390                        return String.format("%s[%d] |= (1 << %d)",varName,pos>>6,pos & 0x3f);
1391                }else{
1392                        return String.format("%s |= %s", varName,getIDMaskConstName());
1393                }
1394        }       
1395        public String bitResetAssignExpression(String varName){
1396                if(this.getTable().countColumns()>32){
1397                        int pos = getOrdinalPosition()-1;
1398                        return String.format("%s[%d] &= (~(1 << %d))",varName,pos>>6,pos & 0x3f);
1399                }else{
1400                        return String.format("%s &= (~%s)", varName,getIDMaskConstName());
1401                }
1402        }
1403        public String getWidgetMethod() {
1404                return this.convertName("get_" + this.escape() + "_widget");
1405        }
1406
1407        public String getVarName() {
1408                return this.convertName(this.escape());
1409        }
1410        public String getCacheVarName() {
1411                return this.convertName(this.escape() + "_cacher");
1412        }
1413        public String getFullVarName() {
1414                return this.convertName(this.name +"_of_" + this.getTable().getBasename(true));
1415        }
1416        public String getModifiedVarName() {
1417                return this.convertName(this.escape() + "_is_modified");
1418        }
1419
1420        public String getInitializedVarName() {
1421                return this.convertName(this.escape() + "_is_initialized");
1422        }
1423
1424        public String getImportedKeyModifiedVarName() {
1425                return this.convertName(this.escape() + "_collection_is_modified");
1426        }
1427
1428        public String getImportedKeyInitializedVarName() {
1429                return this.convertName(this.escape() + "_collection_is_initialized");
1430        }
1431
1432        public String getImportedKeyInitializedMethod() {
1433                return this.convertName("is_" + this.escape() + "_collection_initialized");
1434        }
1435
1436        public String getImportedKeyGetMethod() {
1437                return this.convertName("get_" + this.escape() + "_collection");
1438        }
1439
1440        public String getImportedKeyAddMethod() {
1441                return this.convertName("add_" + this.escape() + "");
1442        }
1443
1444        public String getImportedKeySetMethod() {
1445                return this.convertName("set_" + this.escape() + "_collection");
1446        }
1447
1448        public String getImportedKeyModifiedMethod() {
1449                return this.convertName("is_" + this.escape() + "_collection_modified");
1450        }
1451
1452        public String getForeignKeyVarName() {
1453                return this.convertName(this.escape() + "_object");
1454        }
1455
1456        public String getForeignKeyModifiedVarName() {
1457                return this.convertName(this.escape() + "_object_is_modified");
1458        }
1459
1460        public String getForeignKeyInitializedVarName() {
1461                return this.convertName(this.escape() + "_object_is_initialized");
1462        }
1463
1464        public String getForeignKeyInitializedMethod() {
1465                return this.convertName("is_" + this.escape() + "_object_initialized");
1466        }
1467
1468        public String getForeignKeyGetMethod(String col) {
1469                return this.convertName("get_" + this.escape() + "_object");
1470        }
1471
1472        public String getForeignKeySetMethod(String col) {
1473                return this.convertName("set_" + this.escape() + "_object");
1474        }
1475
1476        public String getForeignKeyModifiedMethod(String col) {
1477                return this.convertName("is_" + this.escape() + "_object_modified");
1478        }
1479        
1480        public String getTypeName() {
1481                return this.typeName;
1482        }
1483
1484        public void setTypeName(String typeName) {
1485                this.typeName = typeName;
1486        }
1487
1488        public int compareTo(Column obj) {
1489                return  this.ordinal - obj.ordinal;
1490        }
1491
1492        public String getAutoincrement() {
1493                return autoincrement;
1494        }
1495
1496        public void setAutoincrement(String autoincrement) {
1497                this.autoincrement = autoincrement;
1498        }
1499        public boolean isAutoincrement(){
1500                return "YES".equals(this.autoincrement);
1501        }
1502        public boolean isNotNull(){
1503                return DatabaseMetaData.columnNoNulls  == this.nullable ;
1504        }
1505}