001: /**
002: * com.mckoi.database.TObject 26 Jul 2002
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.database;
024:
025: import java.io.IOException;
026: import java.io.ObjectOutputStream;
027: import java.util.Locale;
028: import com.mckoi.util.BigNumber;
029: import com.mckoi.database.global.ByteLongObject;
030: import com.mckoi.database.global.BlobRef;
031: import com.mckoi.database.global.ClobRef;
032: import com.mckoi.database.global.SQLTypes;
033: import com.mckoi.database.global.StringObject;
034:
035: /**
036: * A TObject is a strongly typed object in a database engine. A TObject must
037: * maintain type information (eg. STRING, NUMBER, etc) along with the
038: * object value being represented itself.
039: *
040: * @author Tobias Downer
041: */
042:
043: public final class TObject implements java.io.Serializable {
044:
045: static final long serialVersionUID = -5129157457207765079L;
046:
047: /**
048: * The type of this object.
049: */
050: private TType type;
051:
052: /**
053: * The Java representation of the object.
054: */
055: private Object ob;
056:
057: /**
058: * Constructs the TObject as the given type.
059: */
060: public TObject(TType type, Object ob) {
061: this .type = type;
062: if (ob instanceof String) {
063: this .ob = StringObject.fromString((String) ob);
064: } else {
065: this .ob = ob;
066: }
067: }
068:
069: /**
070: * Returns the type of this object.
071: */
072: public TType getTType() {
073: return type;
074: }
075:
076: /**
077: * Returns true if the object is null. Note that we must still be able to
078: * determine type information for an object that is NULL.
079: */
080: public boolean isNull() {
081: return (getObject() == null);
082: }
083:
084: /**
085: * Returns a java.lang.Object that is the data behind this object.
086: */
087: public Object getObject() {
088: return ob;
089: }
090:
091: /**
092: * Returns the approximate memory use of this object in bytes. This is used
093: * when the engine is caching objects and we need a general indication of how
094: * much space it takes up in memory.
095: */
096: public int approximateMemoryUse() {
097: return getTType().calculateApproximateMemoryUse(getObject());
098: }
099:
100: /**
101: * Returns true if the type of this object is logically comparable to the
102: * type of the given object. For example, VARCHAR and LONGVARCHAR are
103: * comparable types. DOUBLE and FLOAT are comparable types. DOUBLE and
104: * VARCHAR are not comparable types.
105: */
106: public boolean comparableTypes(TObject ob) {
107: return getTType().comparableTypes(ob.getTType());
108: }
109:
110: /**
111: * Returns the BigNumber of this object if this object is a numeric type. If
112: * the object is not a numeric type or is NULL then a null object is
113: * returned. This method can not be used to cast from a type to a number.
114: */
115: public BigNumber toBigNumber() {
116: if (getTType() instanceof TNumericType) {
117: return (BigNumber) getObject();
118: }
119: return null;
120: }
121:
122: /**
123: * Returns the Boolean of this object if this object is a boolean type. If
124: * the object is not a boolean type or is NULL then a null object is
125: * returned. This method must not be used to cast from a type to a boolean.
126: */
127: public Boolean toBoolean() {
128: if (getTType() instanceof TBooleanType) {
129: return (Boolean) getObject();
130: }
131: return null;
132: }
133:
134: /**
135: * Returns the String of this object if this object is a string type. If
136: * the object is not a string type or is NULL then a null object is
137: * returned. This method must not be used to cast from a type to a string.
138: */
139: public String toStringValue() {
140: if (getTType() instanceof TStringType) {
141: return getObject().toString();
142: }
143: return null;
144: }
145:
146: public static final TObject BOOLEAN_TRUE = new TObject(
147: TType.BOOLEAN_TYPE, Boolean.TRUE);
148: public static final TObject BOOLEAN_FALSE = new TObject(
149: TType.BOOLEAN_TYPE, Boolean.FALSE);
150: public static final TObject BOOLEAN_NULL = new TObject(
151: TType.BOOLEAN_TYPE, null);
152:
153: public static final TObject NULL_OBJECT = new TObject(
154: TType.NULL_TYPE, null);
155:
156: /**
157: * Returns a TObject of boolean type that is either true or false.
158: */
159: public static TObject booleanVal(boolean b) {
160: if (b) {
161: return BOOLEAN_TRUE;
162: }
163: return BOOLEAN_FALSE;
164: }
165:
166: /**
167: * Returns a TObject of numeric type that represents the given int value.
168: */
169: public static TObject intVal(int val) {
170: return bigNumberVal(BigNumber.fromLong(val));
171: }
172:
173: /**
174: * Returns a TObject of numeric type that represents the given long value.
175: */
176: public static TObject longVal(long val) {
177: return bigNumberVal(BigNumber.fromLong(val));
178: }
179:
180: /**
181: * Returns a TObject of numeric type that represents the given double value.
182: */
183: public static TObject doubleVal(double val) {
184: return bigNumberVal(BigNumber.fromDouble(val));
185: }
186:
187: /**
188: * Returns a TObject of numeric type that represents the given BigNumber
189: * value.
190: */
191: public static TObject bigNumberVal(BigNumber val) {
192: return new TObject(TType.NUMERIC_TYPE, val);
193: }
194:
195: /**
196: * Returns a TObject of VARCHAR type that represents the given StringObject
197: * value.
198: */
199: public static TObject stringVal(StringObject str) {
200: return new TObject(TType.STRING_TYPE, str);
201: }
202:
203: /**
204: * Returns a TObject of VARCHAR type that represents the given String value.
205: */
206: public static TObject stringVal(String str) {
207: return new TObject(TType.STRING_TYPE, StringObject
208: .fromString(str));
209: }
210:
211: /**
212: * Returns a TObject of DATE type that represents the given time value.
213: */
214: public static TObject dateVal(java.util.Date d) {
215: return new TObject(TType.DATE_TYPE, d);
216: }
217:
218: /**
219: * Returns a TObject of NULL type that represents a null value.
220: */
221: public static TObject nullVal() {
222: return NULL_OBJECT;
223: }
224:
225: /**
226: * Returns a TObject from the given Java value.
227: */
228: public static TObject objectVal(Object ob) {
229: if (ob == null) {
230: return nullVal();
231: } else if (ob instanceof BigNumber) {
232: return bigNumberVal((BigNumber) ob);
233: } else if (ob instanceof StringObject) {
234: return stringVal((StringObject) ob);
235: } else if (ob instanceof Boolean) {
236: return booleanVal(((Boolean) ob).booleanValue());
237: } else if (ob instanceof java.util.Date) {
238: return dateVal((java.util.Date) ob);
239: } else if (ob instanceof ByteLongObject) {
240: return new TObject(TType.BINARY_TYPE, (ByteLongObject) ob);
241: } else if (ob instanceof byte[]) {
242: return new TObject(TType.BINARY_TYPE, new ByteLongObject(
243: (byte[]) ob));
244: } else if (ob instanceof BlobRef) {
245: return new TObject(TType.BINARY_TYPE, (BlobRef) ob);
246: } else if (ob instanceof ClobRef) {
247: return new TObject(TType.STRING_TYPE, (ClobRef) ob);
248: } else {
249: throw new Error("Don't know how to convert object type "
250: + ob.getClass());
251: }
252: }
253:
254: /**
255: * Compares this object with the given object (which is of a logically
256: * comparable type). Returns 0 if the value of the objects are equal, < 0
257: * if this object is smaller than the given object, and > 0 if this object
258: * is greater than the given object.
259: * <p>
260: * This can not be used to compare null values so it assumes that checks
261: * for null have already been made.
262: */
263: public int compareToNoNulls(TObject tob) {
264: TType type = getTType();
265: // Strings must be handled as a special case.
266: if (type instanceof TStringType) {
267: // We must determine the locale to compare against and use that.
268: TStringType stype = (TStringType) type;
269: // If there is no locale defined for this type we use the locale in the
270: // given type.
271: if (stype.getLocale() == null) {
272: type = tob.getTType();
273: }
274: }
275: return type.compareObs(getObject(), tob.getObject());
276: }
277:
278: /**
279: * Compares this object with the given object (which is of a logically
280: * comparable type). Returns 0 if the value of the objects are equal, < 0
281: * if this object is smaller than the given object, and > 0 if this object
282: * is greater than the given object.
283: * <p>
284: * This compares NULL values before non null values, and null values are
285: * equal.
286: */
287: public int compareTo(TObject tob) {
288: // If this is null
289: if (isNull()) {
290: // and value is null return 0 return less
291: if (tob.isNull()) {
292: return 0;
293: } else {
294: return -1;
295: }
296: } else {
297: // If this is not null and value is null return +1
298: if (tob.isNull()) {
299: return 1;
300: } else {
301: // otherwise both are non null so compare normally.
302: return compareToNoNulls(tob);
303: }
304: }
305: }
306:
307: /**
308: * Equality test. This will throw an exception if it is used. The reason
309: * for this is because it's not clear what we would be testing the equality
310: * of with this method. Equality of the object + the type or equality of the
311: * objects only?
312: */
313: public boolean equals(Object ob) {
314: throw new Error("equals method should not be used.");
315: }
316:
317: /**
318: * Equality test. Returns true if this object is equivalent to the given
319: * TObject. This means the types are the same, and the object itself is the
320: * same.
321: */
322: public boolean valuesEqual(TObject ob) {
323: if (this == ob) {
324: return true;
325: }
326: if (getTType().comparableTypes(ob.getTType())) {
327: return compareTo(ob) == 0;
328: }
329: return false;
330: }
331:
332: // ---------- Object operators ----------
333:
334: /**
335: * Bitwise OR operation of this object with the given object. If either
336: * numeric value has a scale of 1 or greater then it returns null. If this
337: * or the given object is not a numeric type then it returns null. If either
338: * this object or the given object is NULL, then the NULL object is returned.
339: */
340: public TObject operatorOr(TObject val) {
341: BigNumber v1 = toBigNumber();
342: BigNumber v2 = val.toBigNumber();
343: TType result_type = TType.getWidestType(getTType(), val
344: .getTType());
345:
346: if (v1 == null || v2 == null) {
347: return new TObject(result_type, null);
348: }
349:
350: return new TObject(result_type, v1.bitWiseOr(v2));
351: }
352:
353: /**
354: * Mathematical addition of this object to the given object. If this or
355: * the given object is not a numeric type then it returns null.
356: * If either this object or the given object is NULL, then the NULL object
357: * is returned.
358: */
359: public TObject operatorAdd(TObject val) {
360: BigNumber v1 = toBigNumber();
361: BigNumber v2 = val.toBigNumber();
362: TType result_type = TType.getWidestType(getTType(), val
363: .getTType());
364:
365: if (v1 == null || v2 == null) {
366: return new TObject(result_type, null);
367: }
368:
369: return new TObject(result_type, v1.add(v2));
370: }
371:
372: /**
373: * Mathematical subtraction of this object to the given object. If this or
374: * the given object is not a numeric type then it returns null.
375: * If either this object or the given object is NULL, then the NULL object
376: * is returned.
377: */
378: public TObject operatorSubtract(TObject val) {
379: BigNumber v1 = toBigNumber();
380: BigNumber v2 = val.toBigNumber();
381: TType result_type = TType.getWidestType(getTType(), val
382: .getTType());
383:
384: if (v1 == null || v2 == null) {
385: return new TObject(result_type, null);
386: }
387:
388: return new TObject(result_type, v1.subtract(v2));
389: }
390:
391: /**
392: * Mathematical multiply of this object to the given object. If this or
393: * the given object is not a numeric type then it returns null.
394: * If either this object or the given object is NULL, then the NULL object
395: * is returned.
396: */
397: public TObject operatorMultiply(TObject val) {
398: BigNumber v1 = toBigNumber();
399: BigNumber v2 = val.toBigNumber();
400: TType result_type = TType.getWidestType(getTType(), val
401: .getTType());
402:
403: if (v1 == null || v2 == null) {
404: return new TObject(result_type, null);
405: }
406:
407: return new TObject(result_type, v1.multiply(v2));
408: }
409:
410: /**
411: * Mathematical division of this object to the given object. If this or
412: * the given object is not a numeric type then it returns null.
413: * If either this object or the given object is NULL, then the NULL object
414: * is returned.
415: */
416: public TObject operatorDivide(TObject val) {
417: BigNumber v1 = toBigNumber();
418: BigNumber v2 = val.toBigNumber();
419: TType result_type = TType.getWidestType(getTType(), val
420: .getTType());
421:
422: if (v1 == null || v2 == null) {
423: return new TObject(result_type, null);
424: }
425:
426: return new TObject(result_type, v1.divide(v2));
427: }
428:
429: /**
430: * String concat of this object to the given object. If this or the given
431: * object is not a string type then it returns null. If either this object
432: * or the given object is NULL, then the NULL object is returned.
433: * <p>
434: * This operator always returns an object that is a VARCHAR string type of
435: * unlimited size with locale inherited from either this or val depending
436: * on whether the locale information is defined or not.
437: */
438: public TObject operatorConcat(TObject val) {
439:
440: // If this or val is null then return the null value
441: if (isNull()) {
442: return this ;
443: } else if (val.isNull()) {
444: return val;
445: }
446:
447: TType tt1 = getTType();
448: TType tt2 = val.getTType();
449:
450: if (tt1 instanceof TStringType && tt2 instanceof TStringType) {
451: // Pick the first locale,
452: TStringType st1 = (TStringType) tt1;
453: TStringType st2 = (TStringType) tt2;
454:
455: Locale str_locale = null;
456: int str_strength = 0;
457: int str_decomposition = 0;
458:
459: if (st1.getLocale() != null) {
460: str_locale = st1.getLocale();
461: str_strength = st1.getStrength();
462: str_decomposition = st1.getDecomposition();
463: } else if (st2.getLocale() != null) {
464: str_locale = st2.getLocale();
465: str_strength = st2.getStrength();
466: str_decomposition = st2.getDecomposition();
467: }
468:
469: TStringType dest_type = st1;
470: if (str_locale != null) {
471: dest_type = new TStringType(SQLTypes.VARCHAR, -1,
472: str_locale, str_strength, str_decomposition);
473: }
474:
475: return new TObject(dest_type, StringObject
476: .fromString(toStringValue() + val.toStringValue()));
477:
478: }
479:
480: // Return null if LHS or RHS are not strings
481: return new TObject(tt1, null);
482: }
483:
484: /**
485: * Comparison of this object and the given object. The compared objects
486: * must be the same type otherwise it returns false. This
487: * is able to compare null values.
488: */
489: public TObject operatorIs(TObject val) {
490: if (isNull() && val.isNull()) {
491: return BOOLEAN_TRUE;
492: }
493: if (comparableTypes(val)) {
494: return booleanVal(compareTo(val) == 0);
495: }
496: // Not comparable types so return false
497: return BOOLEAN_FALSE;
498: }
499:
500: /**
501: * Comparison of this object and the given object. The compared objects
502: * must be the same type otherwise it returns null (doesn't know). If either
503: * this object or the given object is NULL then NULL is returned.
504: */
505: public TObject operatorEquals(TObject val) {
506: // Check the types are comparable
507: if (comparableTypes(val) && !isNull() && !val.isNull()) {
508: return booleanVal(compareToNoNulls(val) == 0);
509: }
510: // Not comparable types so return null
511: return BOOLEAN_NULL;
512: }
513:
514: /**
515: * Comparison of this object and the given object. The compared objects
516: * must be the same type otherwise it returns null (doesn't know). If either
517: * this object or the given object is NULL then NULL is returned.
518: */
519: public TObject operatorNotEquals(TObject val) {
520: // Check the types are comparable
521: if (comparableTypes(val) && !isNull() && !val.isNull()) {
522: return booleanVal(compareToNoNulls(val) != 0);
523: }
524: // Not comparable types so return null
525: return BOOLEAN_NULL;
526: }
527:
528: /**
529: * Comparison of this object and the given object. The compared objects
530: * must be the same type otherwise it returns null (doesn't know). If either
531: * this object or the given object is NULL then NULL is returned.
532: */
533: public TObject operatorGreater(TObject val) {
534: // Check the types are comparable
535: if (comparableTypes(val) && !isNull() && !val.isNull()) {
536: return booleanVal(compareToNoNulls(val) > 0);
537: }
538: // Not comparable types so return null
539: return BOOLEAN_NULL;
540: }
541:
542: /**
543: * Comparison of this object and the given object. The compared objects
544: * must be the same type otherwise it returns null (doesn't know). If either
545: * this object or the given object is NULL then NULL is returned.
546: */
547: public TObject operatorGreaterEquals(TObject val) {
548: // Check the types are comparable
549: if (comparableTypes(val) && !isNull() && !val.isNull()) {
550: return booleanVal(compareToNoNulls(val) >= 0);
551: }
552: // Not comparable types so return null
553: return BOOLEAN_NULL;
554: }
555:
556: /**
557: * Comparison of this object and the given object. The compared objects
558: * must be the same type otherwise it returns null (doesn't know). If either
559: * this object or the given object is NULL then NULL is returned.
560: */
561: public TObject operatorLess(TObject val) {
562: // Check the types are comparable
563: if (comparableTypes(val) && !isNull() && !val.isNull()) {
564: return booleanVal(compareToNoNulls(val) < 0);
565: }
566: // Not comparable types so return null
567: return BOOLEAN_NULL;
568: }
569:
570: /**
571: * Comparison of this object and the given object. The compared objects
572: * must be the same type otherwise it returns null (doesn't know). If either
573: * this object or the given object is NULL then NULL is returned.
574: */
575: public TObject operatorLessEquals(TObject val) {
576: // Check the types are comparable
577: if (comparableTypes(val) && !isNull() && !val.isNull()) {
578: return booleanVal(compareToNoNulls(val) <= 0);
579: }
580: // Not comparable types so return null
581: return BOOLEAN_NULL;
582: }
583:
584: /**
585: * Performs a logical NOT on this value.
586: */
587: public TObject operatorNot() {
588: // If type is null
589: if (isNull()) {
590: return this ;
591: }
592: Boolean b = toBoolean();
593: if (b != null) {
594: return booleanVal(!b.booleanValue());
595: }
596: return BOOLEAN_NULL;
597: }
598:
599: // ---------- Casting methods -----------
600:
601: /**
602: * Returns a TObject of the given type and with the given Java object. If
603: * the object is not of the right type then it is cast to the correct type.
604: */
605: public static TObject createAndCastFromObject(TType type, Object ob) {
606: return new TObject(type, TType.castObjectToTType(ob, type));
607: }
608:
609: /**
610: * Casts this object to the given type and returns a new TObject.
611: */
612: public TObject castTo(TType cast_to_type) {
613: Object ob = getObject();
614: return createAndCastFromObject(cast_to_type, ob);
615: }
616:
617: public String toString() {
618: if (isNull()) {
619: return "NULL";
620: } else {
621: return getObject().toString();
622: }
623: }
624:
625: // // ------ Default casting objects ----------
626: //
627: // /**
628: // * Casts this object to a number. If this object is NULL then the returned
629: // * object is a numeric typed NULL.
630: // */
631: // public TObject castToNumber() {
632: // if (getTType().isString()) {
633: // try {
634: // return new BigDecimal((String) ob);
635: // }
636: // catch (Throwable e) {
637: // return BD_ZERO;
638: // }
639: // }
640: // if (getTType().isBoolean()) {
641: // if (((Boolean) ob).booleanValue() == true) {
642: // return BD_ONE;
643: // }
644: // else {
645: // return BD_ZERO;
646: // }
647: // }
648: // if (getTType().isDate()) {
649: // return new BigDecimal(((Date) ob).getTime());
650: // }
651: // return (BigDecimal) ob;
652: // }
653: //
654: //
655: // // ---------- Convenience statics ----------
656: //
657: // private final static BigDecimal BD_ZERO = new BigDecimal(0);
658: // private final static BigDecimal BD_ONE = new BigDecimal(1);
659:
660: /**
661: * Writes the state of this object to the object stream. This method is
662: * implemented because GCJ doesn't like it if you implement readObject
663: * without writeObject.
664: */
665: private void writeObject(ObjectOutputStream out) throws IOException {
666: out.defaultWriteObject();
667: }
668:
669: /**
670: * Serialization overwritten method. We overwrite this method because of a
671: * change with how strings are stored. In 0.93 we stored strings in this
672: * object as java.lang.String and in 0.94 we stored strings as
673: * java.lang.StringObject. This performs a conversion between the old and
674: * new format.
675: */
676: private void readObject(java.io.ObjectInputStream in)
677: throws IOException, ClassNotFoundException {
678: in.defaultReadObject();
679: // HACK: We convert old TObject that used String to represent a string object
680: // to StringObject
681: if (ob instanceof String) {
682: ob = StringObject.fromString((String) ob);
683: }
684: }
685:
686: }
|