001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
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 version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.kvem.jsr082.bluetooth;
027:
028: import java.io.IOException;
029: import java.util.Enumeration;
030: import java.util.Stack;
031: import javax.bluetooth.DataElement;
032: import javax.bluetooth.ServiceRecord;
033: import javax.bluetooth.UUID;
034:
035: /**
036: * Serializes and restores DataElement objects.
037: */
038: class DataElementSerializer {
039: /** NULL data header. */
040: private static final byte NULL_DATA = 0x00;
041:
042: /** Boolean data header. */
043: private static final byte BOOLEAN_DATA = 0x28;
044:
045: /** 1-byte signed integer header. */
046: private static final byte INT1_SIGNED = 0x10;
047:
048: /** 2-byte signed integer header. */
049: private static final byte INT2_SIGNED = 0x11;
050:
051: /** 4-byte signed integer header. */
052: private static final byte INT4_SIGNED = 0x12;
053:
054: /** 8-byte signed integer header. */
055: private static final byte INT8_SIGNED = 0x13;
056:
057: /** 16-byte signed integer header. */
058: private static final byte INT16_SIGNED = 0x14;
059:
060: /** 1-byte unsigned integer header. */
061: private static final byte INT1_UNSIGNED = 0x08;
062:
063: /** 2-byte unsigned integer header. */
064: private static final byte INT2_UNSIGNED = 0x09;
065:
066: /** 4-byte unsigned integer header. */
067: private static final byte INT4_UNSIGNED = 0x0a;
068:
069: /** 8-byte unsigned integer header. */
070: private static final byte INT8_UNSIGNED = 0x0b;
071:
072: /** 16-byte unsigned integer header. */
073: private static final byte INT16_UNSIGNED = 0x0c;
074:
075: /** 16-bit UUID header. */
076: private static final byte UUID_2 = 0x19;
077:
078: /** 32-bit UUID header. */
079: private static final byte UUID_4 = 0x1a;
080:
081: /** 128-bit UUID header. */
082: private static final byte UUID_16 = 0x1c;
083:
084: /** Mask to get type tag from header. */
085: private static final byte TYPE_MASK = ((byte) 0xf8);
086:
087: /** Mask to get size of data size field from header. */
088: private static final byte SIZE_MASK = 0x07;
089:
090: /** Tag for string type. */
091: private static final byte STRING_TYPE = 0x20;
092:
093: /** Tag for sequence type. */
094: private static final byte SEQUENCE_TYPE = 0x30;
095:
096: /** Tag for an alternative type. */
097: private static final byte ALTERNATIVE_TYPE = 0x38;
098:
099: /** Tag that identifies that size of data size field is 2 bytes. */
100: private static final byte SHORT_SIZE = 0x05;
101:
102: /** Tag that identifies that size of data size field is 4 bytes. */
103: private static final byte NORMAL_SIZE = 0x06;
104:
105: /** Tag that identifies that size of data size field is 8 bytes. */
106: private static final byte LONG_SIZE = 0x07;
107:
108: /** Destination buffer which collects binary data of a data element. */
109: protected byte[] writeBuffer = null;
110:
111: /** Source buffer which contains binary data of a data element. */
112: protected byte[] readBuffer = null;
113:
114: /** Current position at the destination buffer. */
115: protected long writePos = 0;
116:
117: /** Current position at the source buffer. */
118: protected long readPos = 0;
119:
120: /** Allows to store and retrieve positions at the source buffer. */
121: private Stack readPosStack = new Stack();
122:
123: /**
124: * Constructs the serializer object.
125: */
126: DataElementSerializer() {
127: }
128:
129: /**
130: * Serializes given DataElement object, i.e. creates an array of bytes
131: * representing DataElement as described in Bluetooth Specification
132: * Version 1.2, vol 3, page 127.
133: *
134: * @param data the data element to serialize
135: * @return an array containing the serialized data element
136: * @throws IOException if an I/O error occurs
137: */
138: public synchronized byte[] serialize(DataElement data)
139: throws IOException {
140: writeBuffer = new byte[(int) getDataSize(data)];
141: writePos = 0;
142: writeDataElement(data);
143: byte[] result = writeBuffer;
144: writeBuffer = null;
145: return result;
146: }
147:
148: /**
149: * Constructs DataElement from byte array containing the element in
150: * serialized form.
151: *
152: * @param data byte array containing the element in serialized form
153: * @return DataElement constructed from the binary data
154: * @throws IOException if an I/O error occurs
155: */
156: public synchronized DataElement restore(byte[] data)
157: throws IOException {
158: readBuffer = data;
159: readPos = 0;
160: DataElement result = readDataElement();
161: readBuffer = null;
162: return result;
163: }
164:
165: /**
166: * Returns the size of DataElement with service information
167: * to get the total size required to work with this DataElement.
168: *
169: * @param data the data element to get packet size for
170: * @return number of bytes needed to store given data element
171: */
172: long getDataSize(DataElement data) {
173: int type = data.getDataType();
174: long size = getPureDataSize(data);
175: if ((type == DataElement.NULL) || (type == DataElement.BOOL)
176: || (type == DataElement.INT_1)
177: || (type == DataElement.U_INT_1)
178: || (type == DataElement.INT_2)
179: || (type == DataElement.U_INT_2)
180: || (type == DataElement.INT_4)
181: || (type == DataElement.U_INT_4)
182: || (type == DataElement.INT_8)
183: || (type == DataElement.U_INT_8)
184: || (type == DataElement.INT_16)
185: || (type == DataElement.U_INT_16)
186: || (type == DataElement.UUID)) {
187: return size + 1;
188: } else if ((type == DataElement.DATSEQ)
189: || (type == DataElement.DATALT)
190: || (type == DataElement.STRING)
191: || (type == DataElement.URL)) {
192: if (size <= 0xffL) {
193: return size + 2;
194: } else if (size <= 0xffffL) {
195: return size + 3;
196: } else if (size <= 0xffffffffL) {
197: return size + 5;
198: } else {
199: throw new RuntimeException("Data size is too large.");
200: }
201: } else {
202: throw new RuntimeException("Unexpected data type.");
203: }
204: }
205:
206: /**
207: * Returns the size of DataElement without service information.
208: *
209: * @param data the data element to get pure data size for
210: * @return pure data size in bytes
211: */
212: long getPureDataSize(DataElement data) {
213: switch (data.getDataType()) {
214: case DataElement.NULL:
215: return 0;
216: case DataElement.BOOL:
217: case DataElement.INT_1:
218: case DataElement.U_INT_1:
219: return 1;
220: case DataElement.INT_2:
221: case DataElement.U_INT_2:
222: return 2;
223: case DataElement.INT_4:
224: case DataElement.U_INT_4:
225: return 4;
226: case DataElement.INT_8:
227: case DataElement.U_INT_8:
228: return 8;
229: case DataElement.INT_16:
230: case DataElement.U_INT_16:
231: return 16;
232: case DataElement.DATSEQ:
233: case DataElement.DATALT:
234: long size = 0;
235: Enumeration elements = (Enumeration) data.getValue();
236: while (elements.hasMoreElements()) {
237: size += getDataSize((DataElement) elements
238: .nextElement());
239: }
240: return size;
241: case DataElement.STRING:
242: case DataElement.URL:
243: return ((String) data.getValue()).length();
244: case DataElement.UUID:
245: return 16;
246: default:
247: throw new RuntimeException("Unknown data type.");
248: }
249: }
250:
251: /**
252: * Writes given data element into the write buffer.
253: *
254: * @param data the data element to write
255: * @throws IOException if an I/O error occurs
256: */
257: void writeDataElement(DataElement data) throws IOException {
258: long size = getPureDataSize(data);
259: int type = data.getDataType();
260: byte typeBits = 0x00;
261: if ((type == DataElement.NULL) || (type == DataElement.BOOL)
262: || (type == DataElement.INT_1)
263: || (type == DataElement.U_INT_1)
264: || (type == DataElement.INT_2)
265: || (type == DataElement.U_INT_2)
266: || (type == DataElement.INT_4)
267: || (type == DataElement.U_INT_4)
268: || (type == DataElement.INT_8)
269: || (type == DataElement.U_INT_8)
270: || (type == DataElement.INT_16)
271: || (type == DataElement.U_INT_16)) {
272: switch (type) {
273: case DataElement.NULL:
274: writeByte(NULL_DATA);
275: break;
276: case DataElement.BOOL:
277: writeByte(BOOLEAN_DATA);
278: writeBoolean(data.getBoolean());
279: break;
280: case DataElement.INT_1:
281: writeByte(INT1_SIGNED);
282: writeByte((byte) data.getLong());
283: break;
284: case DataElement.U_INT_1:
285: writeByte(INT1_UNSIGNED);
286: writeByte((byte) data.getLong());
287: break;
288: case DataElement.INT_2:
289: writeByte(INT2_SIGNED);
290: writeShort((short) data.getLong());
291: break;
292: case DataElement.U_INT_2:
293: writeByte(INT2_UNSIGNED);
294: writeShort((short) data.getLong());
295: break;
296: case DataElement.INT_4:
297: writeByte(INT4_SIGNED);
298: writeInteger((int) data.getLong());
299: break;
300: case DataElement.U_INT_4:
301: writeByte(INT4_UNSIGNED);
302: writeInteger((int) data.getLong());
303: break;
304: case DataElement.INT_8:
305: writeByte(INT8_SIGNED);
306: writeLong(data.getLong());
307: break;
308: case DataElement.U_INT_8:
309: writeByte(INT8_UNSIGNED);
310: writeBytes((byte[]) data.getValue());
311: break;
312: case DataElement.INT_16:
313: writeByte(INT16_SIGNED);
314: writeBytes((byte[]) data.getValue());
315: break;
316: case DataElement.U_INT_16:
317: writeByte(INT16_UNSIGNED);
318: writeBytes((byte[]) data.getValue());
319: break;
320: }
321: } else if ((type == DataElement.DATSEQ)
322: || (type == DataElement.DATALT)
323: || (type == DataElement.STRING)
324: || (type == DataElement.URL)) {
325: switch (type) {
326: case DataElement.DATSEQ:
327: typeBits = (TYPE_MASK & SEQUENCE_TYPE);
328: break;
329: case DataElement.DATALT:
330: typeBits = (TYPE_MASK & ALTERNATIVE_TYPE);
331: break;
332: case DataElement.STRING:
333: case DataElement.URL:
334: typeBits = (TYPE_MASK & STRING_TYPE);
335: break;
336: }
337: if (size <= 0xff) {
338: writeByte(typeBits | (SIZE_MASK & SHORT_SIZE));
339: writeByte((byte) size);
340: } else if (size <= 0xffff) {
341: writeByte(typeBits | (SIZE_MASK & NORMAL_SIZE));
342: writeShort((short) size);
343: } else {
344: writeByte(typeBits | (SIZE_MASK & LONG_SIZE));
345: writeInteger((int) size);
346: }
347: if ((type == DataElement.DATSEQ)
348: || (type == DataElement.DATALT)) {
349: Enumeration elements = (Enumeration) data.getValue();
350: while (elements.hasMoreElements()) {
351: writeDataElement((DataElement) elements
352: .nextElement());
353: }
354: } else {
355: writeBytes(((String) data.getValue()).getBytes());
356: }
357: } else if (type == DataElement.UUID) {
358: writeByte(UUID_16);
359: String uuid = ((UUID) data.getValue()).toString();
360: while (uuid.length() < 32) {
361: uuid = '0' + uuid;
362: }
363: for (int i = 0; i < 16; i++) {
364: writeByte(Integer.parseInt(uuid.substring(i * 2,
365: i * 2 + 2), 16));
366: }
367: } else {
368: throw new RuntimeException("Unknown data type.");
369: }
370: }
371:
372: /**
373: * Creates a data element from the binary data in the read buffer.
374: *
375: * @return <code>DataElement</code> read
376: * @throws IOException if an I/O error occurs
377: */
378: DataElement readDataElement() throws IOException {
379: byte header = readByte();
380: if ((header == NULL_DATA) || (header == BOOLEAN_DATA)
381: || (header == INT1_SIGNED) || (header == INT1_UNSIGNED)
382: || (header == INT2_SIGNED) || (header == INT2_UNSIGNED)
383: || (header == INT4_SIGNED) || (header == INT4_UNSIGNED)
384: || (header == INT8_SIGNED) || (header == INT8_UNSIGNED)
385: || (header == INT16_SIGNED)
386: || (header == INT16_UNSIGNED)) {
387: switch (header) {
388: case NULL_DATA:
389: return new DataElement(DataElement.NULL);
390: case BOOLEAN_DATA:
391: return new DataElement(readBoolean());
392: case INT1_SIGNED:
393: return new DataElement(DataElement.INT_1, readByte());
394: case INT1_UNSIGNED:
395: return new DataElement(DataElement.U_INT_1,
396: (readByte() & 0xffL));
397: case INT2_SIGNED:
398: return new DataElement(DataElement.INT_2, readShort());
399: case INT2_UNSIGNED:
400: return new DataElement(DataElement.U_INT_2,
401: (readShort() & 0xffffL));
402: case INT4_SIGNED:
403: return new DataElement(DataElement.INT_4, readInteger());
404: case INT4_UNSIGNED:
405: return new DataElement(DataElement.U_INT_4,
406: (readInteger() & 0xffffffffL));
407: case INT8_SIGNED:
408: return new DataElement(DataElement.INT_8, readLong());
409: case INT8_UNSIGNED:
410: return new DataElement(DataElement.U_INT_8,
411: readBytes(8));
412: case INT16_SIGNED:
413: return new DataElement(DataElement.INT_16,
414: readBytes(16));
415: case INT16_UNSIGNED:
416: return new DataElement(DataElement.U_INT_16,
417: readBytes(16));
418: }
419: } else if (((header & TYPE_MASK) == STRING_TYPE)
420: || ((header & TYPE_MASK) == SEQUENCE_TYPE)
421: || ((header & TYPE_MASK) == ALTERNATIVE_TYPE)) {
422: long size = 0;
423: if ((header & SIZE_MASK) == SHORT_SIZE) {
424: size = readByte() & 0xffL;
425: } else if ((header & SIZE_MASK) == NORMAL_SIZE) {
426: size = readShort() & 0xffffL;
427: } else if ((header & SIZE_MASK) == LONG_SIZE) {
428: size = readInteger() & 0xffffffffL;
429: } else {
430: System.err.println("Unknown size mask.");
431: }
432: if ((header & TYPE_MASK) == STRING_TYPE) {
433: return new DataElement(DataElement.STRING, new String(
434: readBytes((int) size)));
435: } else {
436: DataElement data = null;
437: DataElement dataElement = null;
438: long dataPos = 0;
439: if ((header & TYPE_MASK) == SEQUENCE_TYPE) {
440: data = new DataElement(DataElement.DATSEQ);
441: } else {
442: data = new DataElement(DataElement.DATALT);
443: }
444: while (dataPos < size) {
445: pushReadPos();
446: dataElement = readDataElement();
447: dataPos += readPos - popReadPos();
448: data.addElement(dataElement);
449: }
450: return data;
451: }
452: } else if (header == UUID_2) {
453: return new DataElement(DataElement.UUID, readUUID(2));
454: } else if (header == UUID_4) {
455: return new DataElement(DataElement.UUID, readUUID(4));
456: } else if (header == UUID_16) {
457: return new DataElement(DataElement.UUID, readUUID(16));
458: } else {
459: throw new RuntimeException("Unknown data type.");
460: }
461: return null;
462: }
463:
464: /**
465: * Writes boolean data to the buffer.
466: * Writes only value given itself. Note that boolean data header
467: * should be written before.
468: *
469: * @param data boolean value to write.
470: * @throws IOException if an I/O error occurs
471: */
472: void writeBoolean(boolean data) throws IOException {
473: writeByte(data ? 1 : 0);
474: }
475:
476: /**
477: * Writes 1-byte data to the buffer.
478: *
479: * @param data byte value to write.
480: * @throws IOException if an I/O error occurs
481: */
482: void writeByte(long data) throws IOException {
483: if (writePos < 0 || writePos >= writeBuffer.length) {
484: throw new IndexOutOfBoundsException();
485: }
486: writeBuffer[(int) writePos++] = (byte) data;
487: }
488:
489: /**
490: * Writes 2-byte data to the buffer.
491: *
492: * @param data 2-byte value to write.
493: * @throws IOException if an I/O error occurs
494: */
495: void writeShort(short data) throws IOException {
496: writeByte((byte) ((data >>> 8) & 0xff));
497: writeByte((byte) ((data >>> 0) & 0xff));
498: }
499:
500: /**
501: * Writes 4-byte data to the connection.
502: *
503: * @param data 4-byte value to write.
504: * @throws IOException if an I/O error occurs
505: */
506: void writeInteger(int data) throws IOException {
507: writeShort((short) ((data >>> 16) & 0xffff));
508: writeShort((short) ((data >>> 0) & 0xffff));
509: }
510:
511: /**
512: * Writes 8-byte data to the connection.
513: *
514: * @param data 8-byte value to write.
515: * @throws IOException if an I/O error occurs
516: */
517: void writeLong(long data) throws IOException {
518: writeInteger((int) ((data >>> 32) & 0xffffffff));
519: writeInteger((int) ((data >>> 0) & 0xffffffff));
520: }
521:
522: /**
523: * Writes given data to the connection.
524: *
525: * @param data bytes to write.
526: * @throws IOException if an I/O error occurs
527: */
528: void writeBytes(byte[] data) throws IOException {
529: if (writePos < 0 || writePos + data.length > writeBuffer.length) {
530: throw new IndexOutOfBoundsException();
531: }
532: System.arraycopy(data, 0, writeBuffer, (int) writePos,
533: data.length);
534: writePos += data.length;
535: }
536:
537: /**
538: * Reads boolean value from the connection.
539: *
540: * @return boolean value recieved.
541: * @throws IOException if an I/O error occurs
542: */
543: boolean readBoolean() throws IOException {
544: return (readByte() != 0);
545: }
546:
547: /**
548: * Reads 1-byte value from the connection.
549: *
550: * @return byte recieved.
551: * @throws IOException if an I/O error occurs
552: */
553: byte readByte() throws IOException {
554: if (readPos < 0 || readPos >= readBuffer.length) {
555: throw new IndexOutOfBoundsException();
556: }
557: return readBuffer[(int) readPos++];
558: }
559:
560: /**
561: * Reads 2-byte value from the connection.
562: *
563: * @return short which is the 2 bytes read.
564: * @throws IOException if an I/O error occurs
565: */
566: short readShort() throws IOException {
567: int data1 = ((int) readByte()) & 0xff;
568: int data2 = ((int) readByte()) & 0xff;
569: return (short) ((data1 << 8) + (data2 << 0));
570: }
571:
572: /**
573: * Reads 4-byte value from the connection.
574: *
575: * @return int which is the 4 bytes read.
576: * @throws IOException if an I/O error occurs
577: */
578: int readInteger() throws IOException {
579: int data1 = ((int) readShort()) & 0xffff;
580: int data2 = ((int) readShort()) & 0xffff;
581: return ((data1 << 16) + (data2 << 0));
582: }
583:
584: /**
585: * Reads 8-byte value from the connection.
586: *
587: * @return long which is the 8 bytes read.
588: * @throws IOException if an I/O error occurs
589: */
590: long readLong() throws IOException {
591: long data1 = ((long) readInteger()) & 0xffffffffL;
592: long data2 = ((long) readInteger()) & 0xffffffffL;
593: return ((data1 << 32) + (data2 << 0));
594: }
595:
596: /**
597: * Reads given number of bytes from the connection.
598: *
599: * @param size number of bytes to read.
600: * @return array of bytes read.
601: * @throws IOException if an I/O error occurs
602: */
603: byte[] readBytes(int size) throws IOException {
604: byte[] data = new byte[size];
605: int dataPos = 0;
606: if (readPos < 0 || readPos + data.length > readBuffer.length) {
607: throw new IndexOutOfBoundsException();
608: }
609: System.arraycopy(readBuffer, (int) readPos, data, 0,
610: data.length);
611: readPos += data.length;
612: return data;
613: }
614:
615: /**
616: * Reads UUID of a given size.
617: *
618: * @param len number of bytes to read
619: * @return UUID created from <code>len</code> bytes
620: * @throws IOException if an I/O error occurs
621: */
622: UUID readUUID(int len) throws IOException {
623: String uuid = "";
624: for (int i = 0; i < len; i++) {
625: String digit = Integer.toHexString(readByte() & 0xff);
626: if (digit.length() == 1)
627: digit = '0' + digit;
628: uuid += digit;
629: }
630: return new UUID(uuid, len < 16);
631: }
632:
633: /** Saves the current read position. */
634: private void pushReadPos() {
635: readPosStack.push(new Long(readPos));
636: }
637:
638: /** Extracts saved read position. */
639: private long popReadPos() {
640: return ((Long) readPosStack.pop()).longValue();
641: }
642:
643: }
|