001: /**
002: * com.mckoi.database.global.ObjectTransfer 20 Jul 2000
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.global;
024:
025: import java.io.*;
026: import java.util.Date;
027: import com.mckoi.util.BigNumber;
028: import java.math.BigInteger;
029:
030: /**
031: * Provides static methods for transfering different types of objects over
032: * a Data input/output stream.
033: *
034: * @author Tobias Downer
035: */
036:
037: public class ObjectTransfer {
038:
039: /**
040: * Makes an estimate of the size of the object. This is useful for making
041: * a guess for how much this will take up.
042: */
043: public static int size(Object ob) throws IOException {
044: if (ob == null) {
045: return 9;
046: } else if (ob instanceof StringObject) {
047: return (ob.toString().length() * 2) + 9;
048: } else if (ob instanceof BigNumber) {
049: return 15 + 9;
050: } else if (ob instanceof Date) {
051: return 8 + 9;
052: } else if (ob instanceof Boolean) {
053: return 2 + 9;
054: } else if (ob instanceof ByteLongObject) {
055: return ((ByteLongObject) ob).length() + 9;
056: } else if (ob instanceof StreamableObject) {
057: return 5 + 9;
058: } else {
059: throw new IOException("Unrecognised type: " + ob.getClass());
060: }
061: }
062:
063: /**
064: * Returns the exact size an object will take up when serialized.
065: */
066: public static int exactSize(Object ob) throws IOException {
067: if (ob == null) {
068: return 1;
069: } else if (ob instanceof StringObject) {
070: return (ob.toString().length() * 2) + 1 + 4;
071: } else if (ob instanceof BigNumber) {
072: BigNumber n = (BigNumber) ob;
073: if (n.canBeRepresentedAsInt()) {
074: return 4 + 1;
075: } else if (n.canBeRepresentedAsLong()) {
076: return 8 + 1;
077: }
078: byte[] buf = n.toByteArray();
079: return buf.length + 1 + 1 + 4 + 4;
080: } else if (ob instanceof Date) {
081: return 8 + 1;
082: } else if (ob instanceof Boolean) {
083: return 1 + 1;
084: } else if (ob instanceof ByteLongObject) {
085: return ((ByteLongObject) ob).length() + 1 + 8;
086: } else if (ob instanceof StreamableObject) {
087: return 1 + 1 + 4;
088: } else {
089: throw new IOException("Unrecognised type: " + ob.getClass());
090: }
091: }
092:
093: /**
094: * Writes an object to the data output stream.
095: */
096: public static void writeTo(DataOutput out, Object ob)
097: throws IOException {
098: if (ob == null) {
099: out.writeByte(1);
100: } else if (ob instanceof StringObject) {
101: String str = ob.toString();
102:
103: // All strings send as char array,
104: out.writeByte(18);
105: out.writeInt(str.length());
106: out.writeChars(str);
107:
108: } else if (ob instanceof BigNumber) {
109: BigNumber n = (BigNumber) ob;
110: if (n.canBeRepresentedAsInt()) {
111: out.writeByte(24);
112: out.writeInt(n.intValue());
113: } else if (n.canBeRepresentedAsLong()) {
114: out.writeByte(8);
115: out.writeLong(n.longValue());
116: } else {
117: out.writeByte(7);
118: out.writeByte(n.getState());
119: out.writeInt(n.getScale());
120: byte[] buf = n.toByteArray();
121: out.writeInt(buf.length);
122: out.write(buf);
123: }
124:
125: // out.writeByte(6);
126: // // NOTE: This method is only available in 1.2. This needs to be
127: // // compatible with 1.1 so we use a slower method,
128: //// BigInteger unscaled_val = n.unscaledValue();
129: // // NOTE: This can be swapped out eventually when we can guarentee
130: // // everything is 1.2 minimum.
131: // BigInteger unscaled_val = n.movePointRight(n.scale()).toBigInteger();
132: //
133: // byte[] buf = unscaled_val.toByteArray();
134: // out.writeInt(buf.length);
135: // out.write(buf);
136: } else if (ob instanceof Date) {
137: Date d = (Date) ob;
138: out.writeByte(9);
139: out.writeLong(d.getTime());
140: } else if (ob instanceof Boolean) {
141: Boolean b = (Boolean) ob;
142: out.writeByte(12);
143: out.writeBoolean(b.booleanValue());
144: } else if (ob instanceof ByteLongObject) {
145: ByteLongObject barr = (ByteLongObject) ob;
146: out.writeByte(15);
147: byte[] arr = barr.getByteArray();
148: out.writeLong(arr.length);
149: out.write(arr);
150: } else if (ob instanceof StreamableObject) {
151: StreamableObject ob_head = (StreamableObject) ob;
152: out.writeByte(16);
153: out.writeByte(ob_head.getType());
154: out.writeLong(ob_head.getSize());
155: out.writeLong(ob_head.getIdentifier());
156: } else {
157: throw new IOException("Unrecognised type: " + ob.getClass());
158: }
159: }
160:
161: /**
162: * Writes an object from the data input stream.
163: */
164: public static Object readFrom(DataInputStream in)
165: throws IOException {
166: byte type = in.readByte();
167:
168: switch (type) {
169: case (1):
170: return null;
171:
172: case (3):
173: String str = in.readUTF();
174: return StringObject.fromString(str);
175:
176: case (6): {
177: int scale = in.readInt();
178: int blen = in.readInt();
179: byte[] buf = new byte[blen];
180: in.readFully(buf);
181: return BigNumber.fromData(buf, scale, (byte) 0);
182: }
183:
184: case (7): {
185: byte state = in.readByte();
186: int scale = in.readInt();
187: int blen = in.readInt();
188: byte[] buf = new byte[blen];
189: in.readFully(buf);
190: return BigNumber.fromData(buf, scale, state);
191: }
192:
193: case (8): {
194: // 64-bit long numeric value
195: long val = in.readLong();
196: return BigNumber.fromLong(val);
197: }
198:
199: case (9):
200: long time = in.readLong();
201: return new Date(time);
202:
203: case (12):
204: return new Boolean(in.readBoolean());
205:
206: case (15): {
207: long size = in.readLong();
208: byte[] arr = new byte[(int) size];
209: in.readFully(arr, 0, (int) size);
210: return new ByteLongObject(arr);
211: }
212:
213: case (16): {
214: final byte h_type = in.readByte();
215: final long h_size = in.readLong();
216: final long h_id = in.readLong();
217: return new StreamableObject(h_type, h_size, h_id);
218: }
219:
220: case (18): {
221: // Handles strings > 64k
222: int len = in.readInt();
223: StringBuffer buf = new StringBuffer(len);
224: while (len > 0) {
225: buf.append(in.readChar());
226: --len;
227: }
228: return StringObject.fromString(new String(buf));
229: }
230:
231: case (24): {
232: // 32-bit int numeric value
233: long val = (long) in.readInt();
234: return BigNumber.fromLong(val);
235: }
236:
237: default:
238: throw new IOException("Unrecognised type: " + type);
239:
240: }
241: }
242:
243: }
|