KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > TType


1 /**
2  * com.mckoi.database.TType 26 Jul 2002
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import com.mckoi.database.global.SQLTypes;
28 import com.mckoi.database.global.ByteLongObject;
29 import com.mckoi.database.global.CastHelper;
30 import com.mckoi.util.BigNumber;
31 import com.mckoi.util.StringUtil;
32 import java.util.Date JavaDoc;
33 import java.util.List JavaDoc;
34
35 /**
36  * A TType object represents a type in a database engine. For example, an
37  * implementation might represent a STRING or a NUMBER. This is an
38  * immutable class. See implementations of this object for further examples.
39  *
40  * @author Tobias Downer
41  */

42
43 public abstract class TType implements java.io.Serializable JavaDoc {
44
45   static final long serialVersionUID = 5866230818579853961L;
46
47   /**
48    * The type as an SQL identifier from com.mckoi.database.global.SQLTypes.
49    */

50   private int sql_type;
51   
52   /**
53    * Constructs the type object.
54    */

55   protected TType(int sql_type) {
56     this.sql_type = sql_type;
57   }
58
59   /**
60    * Returns the SQL type of this.
61    */

62   public int getSQLType() {
63     return sql_type;
64   }
65
66   /**
67    * Returns this TType as a fully parsable declared SQL type. For example,
68    * if this represents a string we might return "VARCHAR(30) COLLATE 'jpJP'"
69    * This method is used for debugging and display purposes only and we would
70    * not expect to actually feed this back into an SQL parser.
71    */

72   public String JavaDoc asSQLString() {
73     return DataTableColumnDef.sqlTypeToString(getSQLType());
74   }
75
76
77   /**
78    * Returns true if the type of this object is logically comparable to the
79    * type of the given object. For example, VARCHAR and LONGVARCHAR are
80    * comparable types. DOUBLE and FLOAT are comparable types. DOUBLE and
81    * VARCHAR are not comparable types.
82    */

83   public abstract boolean comparableTypes(TType type);
84
85   /**
86    * Compares two objects that are logically comparable under this
87    * type. Returns 0 if the values are equal, >1 if ob1 is greater than
88    * ob2, and <1 if ob1 is less than ob2. It is illegal to pass NULL values
89    * for ob1 or ob2 into this method.
90    */

91   public abstract int compareObs(Object JavaDoc ob1, Object JavaDoc ob2);
92   
93   /**
94    * Calculates the approximate memory usage of an object of this type in
95    * bytes.
96    */

97   public abstract int calculateApproximateMemoryUse(Object JavaDoc ob);
98   
99   /**
100    * Returns the Java Class that is used to represent this type of object.
101    * For example, string types would return String.class.
102    */

103   public abstract Class JavaDoc javaClass();
104
105
106
107   // ----- Static methods for Encoding/Decoding TType to strings -----
108

109   /**
110    * Returns the value of a string that is quoted. For example, 'test' becomes
111    * test.
112    */

113   private static String JavaDoc parseQuotedString(String JavaDoc str) {
114     if (str.startsWith("'") && str.endsWith("'")) {
115       return str.substring(1, str.length() - 1);
116     }
117     else {
118       throw new RuntimeException JavaDoc("String is not quoted: " + str);
119     }
120   }
121   
122   /**
123    * Encodes a TType into a string which is a useful way to serialize a TType.
124    * The encoded string should be understandable when read.
125    */

126   public static String JavaDoc asEncodedString(TType type) {
127     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
128     if (type instanceof TBooleanType) {
129       buf.append("BOOLEAN(");
130       buf.append(type.getSQLType());
131       buf.append(')');
132     }
133     else if (type instanceof TStringType) {
134       TStringType str_type = (TStringType) type;
135       buf.append("STRING(");
136       buf.append(type.getSQLType());
137       buf.append(',');
138       buf.append(str_type.getMaximumSize());
139       buf.append(",'");
140       buf.append(str_type.getLocaleString());
141       buf.append("',");
142       buf.append(str_type.getStrength());
143       buf.append(',');
144       buf.append(str_type.getDecomposition());
145       buf.append(')');
146     }
147     else if (type instanceof TNumericType) {
148       TNumericType num_type = (TNumericType) type;
149       buf.append("NUMERIC(");
150       buf.append(type.getSQLType());
151       buf.append(',');
152       buf.append(num_type.getSize());
153       buf.append(',');
154       buf.append(num_type.getScale());
155       buf.append(')');
156     }
157     else if (type instanceof TBinaryType) {
158       TBinaryType bin_type = (TBinaryType) type;
159       buf.append("BINARY(");
160       buf.append(type.getSQLType());
161       buf.append(',');
162       buf.append(bin_type.getMaximumSize());
163       buf.append(')');
164     }
165     else if (type instanceof TDateType) {
166       buf.append("DATE(");
167       buf.append(type.getSQLType());
168       buf.append(')');
169     }
170     else if (type instanceof TNullType) {
171       buf.append("NULL(");
172       buf.append(type.getSQLType());
173       buf.append(')');
174     }
175     else if (type instanceof TJavaObjectType) {
176       buf.append("JAVAOBJECT(");
177       buf.append(type.getSQLType());
178       buf.append(",'");
179       buf.append(((TJavaObjectType) type).getJavaClassTypeString());
180       buf.append("')");
181     }
182     else {
183       throw new RuntimeException JavaDoc("Can not encode type: " + type);
184     }
185     return new String JavaDoc(buf);
186   }
187
188   /**
189    * Given an array of TType, returns a String that that is the encoded form
190    * of the array and that can be later decoded back into an array of TType.
191    * Useful for serializing a list of TType information.
192    */

193   public static String JavaDoc asEncodedString(TType[] types) {
194     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
195     for (int i = 0; i < types.length; ++i) {
196       buf.append(asEncodedString(types[i]));
197       if (i < types.length - 1) {
198         buf.append("!|");
199       }
200     }
201     return new String JavaDoc(buf);
202   }
203
204   /**
205    * Decodes a String that has been encoded with the 'asEncodedString' method
206    * and returns a TType that represented the type.
207    */

208   public static TType decodeString(String JavaDoc encoded_str) {
209     int param_s = encoded_str.indexOf('(');
210     int param_e = encoded_str.lastIndexOf(')');
211     String JavaDoc params = encoded_str.substring(param_s + 1, param_e);
212     List JavaDoc param_list = StringUtil.explode(params, ",");
213     int sql_type = Integer.parseInt((String JavaDoc) param_list.get(0));
214     
215     if (encoded_str.startsWith("BOOLEAN(")) {
216       return new TBooleanType(sql_type);
217     }
218     else if (encoded_str.startsWith("STRING(")) {
219       int size = Integer.parseInt((String JavaDoc) param_list.get(1));
220       String JavaDoc locale_str = parseQuotedString((String JavaDoc) param_list.get(2));
221       if (locale_str.length() == 0) {
222         locale_str = null;
223       }
224       int strength = Integer.parseInt((String JavaDoc) param_list.get(3));
225       int decomposition = Integer.parseInt((String JavaDoc) param_list.get(4));
226       return new TStringType(sql_type, size,
227                              locale_str, strength, decomposition);
228     }
229     else if (encoded_str.startsWith("NUMERIC(")) {
230       int size = Integer.parseInt((String JavaDoc) param_list.get(1));
231       int scale = Integer.parseInt((String JavaDoc) param_list.get(2));
232       return new TNumericType(sql_type, size, scale);
233     }
234     else if (encoded_str.startsWith("BINARY(")) {
235       int size = Integer.parseInt((String JavaDoc) param_list.get(1));
236       return new TBinaryType(sql_type, size);
237     }
238     else if (encoded_str.startsWith("DATE(")) {
239       return new TDateType(sql_type);
240     }
241     else if (encoded_str.startsWith("NULL(")) {
242       return new TNullType();
243     }
244     else if (encoded_str.startsWith("JAVAOBJECT(")) {
245       String JavaDoc class_str = parseQuotedString((String JavaDoc) param_list.get(1));
246       return new TJavaObjectType(class_str);
247     }
248     else {
249       throw new RuntimeException JavaDoc("Can not parse encoded string: " + encoded_str);
250     }
251   }
252   
253   /**
254    * Decodes a list (or array) of TType objects that was previously encoded
255    * with the 'asEncodedString(Type[])' method.
256    */

257   public static TType[] decodeTypes(String JavaDoc encoded_str) {
258     List JavaDoc items = StringUtil.explode(encoded_str, "!|");
259
260     // Handle the empty string (no args)
261
if (items.size() == 1) {
262       if (items.get(0).equals("")) {
263         return new TType[0];
264       }
265     }
266
267     int sz = items.size();
268     TType[] return_types = new TType[sz];
269     for (int i = 0; i < sz; ++i) {
270       String JavaDoc str = (String JavaDoc) items.get(i);
271       return_types[i] = decodeString(str);
272     }
273     return return_types;
274   }
275   
276   
277   // -----
278

279   /**
280    * Returns a TBinaryType constrained for the given class.
281    */

282   public static TType javaObjectType(String JavaDoc class_name) {
283     return new TJavaObjectType(class_name);
284   }
285
286   /**
287    * Returns a TStringType object of the given size and locale information.
288    * If locale is null then collation is lexicographical.
289    */

290   public static TType stringType(int sql_type, int size,
291                               String JavaDoc locale, int strength, int decomposition) {
292
293     return new TStringType(sql_type, size, locale, strength, decomposition);
294   }
295
296   /**
297    * Returns a TNumericType object of the given size and scale.
298    */

299   public static TType numericType(int sql_type, int size, int scale) {
300     return new TNumericType(sql_type, size, scale);
301   }
302
303   /**
304    * Returns a TBooleanType object.
305    */

306   public static TType booleanType(int sql_type) {
307     return new TBooleanType(sql_type);
308   }
309
310   /**
311    * Returns a TDateType object.
312    */

313   public static TType dateType(int sql_type) {
314     return new TDateType(sql_type);
315   }
316
317   /**
318    * Returns a TBinaryType object.
319    */

320   public static TType binaryType(int sql_type, int size) {
321     return new TBinaryType(sql_type, size);
322   }
323
324   // -----
325

326   /**
327    * Casts the given Java object to the given type. For example, given
328    * a BigNumber object and STRING_TYPE, this would return the number as a
329    * string.
330    */

331   public static Object JavaDoc castObjectToTType(Object JavaDoc ob, TType type) {
332     // Handle the null case
333
if (ob == null) {
334       return null;
335     }
336     
337     int size = -1;
338     int scale = -1;
339     int sql_type = type.getSQLType();
340
341     if (type instanceof TStringType) {
342       size = ((TStringType) type).getMaximumSize();
343     }
344     else if (type instanceof TNumericType) {
345       TNumericType num_type = (TNumericType) type;
346       size = num_type.getSize();
347       scale = num_type.getScale();
348     }
349     else if (type instanceof TBinaryType) {
350       size = ((TBinaryType) type).getMaximumSize();
351     }
352     
353     ob = CastHelper.castObjectToSQLType(ob, type.getSQLType(), size, scale,
354                                DataTableColumnDef.sqlTypeToString(sql_type));
355
356     return ob;
357   }
358
359   /**
360    * Given a java class, this will return a default TType object that can
361    * encapsulate Java objects of this type. For example, given
362    * java.lang.String, this will return a TStringType with no locale and
363    * maximum size.
364    * <p>
365    * Note that using this method is generally not recommended unless you
366    * really can't determine more type information than from the Java object
367    * itself.
368    */

369   public static TType fromClass(Class JavaDoc c) {
370     if (c == String JavaDoc.class) {
371       return STRING_TYPE;
372     }
373     else if (c == BigNumber.class) {
374       return NUMERIC_TYPE;
375     }
376     else if (c == java.util.Date JavaDoc.class) {
377       return DATE_TYPE;
378     }
379     else if (c == Boolean JavaDoc.class) {
380       return BOOLEAN_TYPE;
381     }
382     else if (c == ByteLongObject.class) {
383       return BINARY_TYPE;
384     }
385     else {
386       throw new Error JavaDoc("Don't know how to convert " + c + " to a TType.");
387     }
388
389   }
390
391   
392   /**
393    * Assuming that the two types are numeric types, this will return the
394    * 'widest' of the two types. For example, an INTEGER is a wider type than a
395    * SHORT, and a FLOAT is wider than an INTEGER.
396    * <p>
397    * Code by Jim McBeath.
398    */

399   public static TType getWidestType(TType t1, TType t2) {
400     int t1SQLType = t1.getSQLType();
401     int t2SQLType = t2.getSQLType();
402     if (t1SQLType == SQLTypes.DECIMAL) {
403       return t1;
404     }
405     if (t2SQLType == SQLTypes.DECIMAL) {
406       return t2;
407     }
408     if (t1SQLType == SQLTypes.NUMERIC) {
409       return t1;
410     }
411     if (t2SQLType == SQLTypes.NUMERIC) {
412       return t2;
413     }
414
415     if (t1SQLType == SQLTypes.BIT) {
416       return t2; // It can't be any smaller than a BIT
417
}
418     if (t2SQLType == SQLTypes.BIT) {
419       return t1;
420     }
421
422     int t1IntSize = getIntSize(t1SQLType);
423     int t2IntSize = getIntSize(t2SQLType);
424     if (t1IntSize > 0 && t2IntSize > 0) {
425       // Both are int types, use the largest size
426
return (t1IntSize > t2IntSize)?t1:t2;
427     }
428
429     int t1FloatSize = getFloatSize(t1SQLType);
430     int t2FloatSize = getFloatSize(t2SQLType);
431     if (t1FloatSize > 0 && t2FloatSize > 0) {
432       // Both are floating types, use the largest size
433
return (t1FloatSize > t2FloatSize)?t1:t2;
434     }
435
436     if (t1FloatSize > t2IntSize) {
437       return t1;
438     }
439     if (t2FloatSize > t1IntSize) {
440       return t2;
441     }
442     if (t1IntSize >= t2FloatSize || t2IntSize >= t1FloatSize) {
443       // Must be a long (8 bytes) and a real (4 bytes), widen to a double
444
return new TNumericType(SQLTypes.DOUBLE,8,-1);
445     }
446     // NOTREACHED - can't get here, the last three if statements cover
447
// all possibilities.
448
throw new Error JavaDoc("Widest type error.");
449   }
450
451   /**
452    * Get the number of bytes used by an integer type.
453    * <p>
454    * Code by Jim McBeath.
455    *
456    * @param sqlType The SQL type.
457    * @return The number of bytes required for data of that type, or 0
458    * if not an int type.
459    */

460   private static int getIntSize(int sqlType) {
461     switch (sqlType) {
462       case SQLTypes.TINYINT:
463         return 1;
464       case SQLTypes.SMALLINT:
465         return 2;
466       case SQLTypes.INTEGER:
467         return 4;
468       case SQLTypes.BIGINT:
469         return 8;
470       default:
471         return 0;
472     }
473   }
474
475   /**
476    * Get the number of bytes used by a floating type.
477    * <p>
478    * Code by Jim McBeath.
479    *
480    * @param sqlType The SQL type.
481    * @return The number of bytes required for data of that type, or 0
482    * if not an int type.
483    */

484   private static int getFloatSize(int sqlType) {
485     switch (sqlType) {
486       default:
487         return 0;
488       case SQLTypes.REAL:
489         return 4;
490       case SQLTypes.FLOAT:
491       case SQLTypes.DOUBLE:
492         return 8;
493     }
494   }
495
496   
497
498   // ------ Useful convenience statics ------
499

500   /**
501    * A default boolean (SQL BIT) type.
502    */

503   public static final TBooleanType BOOLEAN_TYPE = new TBooleanType(SQLTypes.BIT);
504
505   /**
506    * A default string (SQL VARCHAR) type of unlimited maximum size and null
507    * locale.
508    */

509   public static final TStringType STRING_TYPE = new TStringType(
510                                          SQLTypes.VARCHAR, -1, (String JavaDoc) null);
511
512   /**
513    * A default numeric (SQL NUMERIC) type of unlimited size and scale.
514    */

515   public static final TNumericType NUMERIC_TYPE =
516                           new TNumericType(SQLTypes.NUMERIC, -1, -1);
517
518   /**
519    * A default date (SQL TIMESTAMP) type.
520    */

521   public static final TDateType DATE_TYPE = new TDateType(SQLTypes.TIMESTAMP);
522
523   /**
524    * A default binary (SQL BLOB) type of unlimited maximum size.
525    */

526   public static final TBinaryType BINARY_TYPE =
527                                            new TBinaryType(SQLTypes.BLOB, -1);
528
529   /**
530    * A default NULL type.
531    */

532   public static final TNullType NULL_TYPE = new TNullType();
533
534   /**
535    * A type that represents a query plan (sub-select).
536    */

537   public static final TQueryPlanType QUERY_PLAN_TYPE = new TQueryPlanType();
538   
539   /**
540    * A type that represents an array.
541    */

542   public static final TArrayType ARRAY_TYPE = new TArrayType();
543   
544 }
545
Popular Tags