/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.io.device.bluetooth; import totalcross.sys.Vm; import totalcross.util.Vector; /** * This class is defined by the JSR-82 specification <em>Java™ APIs for Bluetooth™ Wireless Technology, * Version 1.1.</em> */ public class DataElement { public static final int NULL = 0x0000; public static final int U_INT_1 = 0x0008; public static final int U_INT_2 = 0x0009; public static final int U_INT_4 = 0x000A; public static final int U_INT_8 = 0x000B; public static final int U_INT_16 = 0x000C; public static final int INT_1 = 0x0010; public static final int INT_2 = 0x0011; public static final int INT_4 = 0x0012; public static final int INT_8 = 0x0013; public static final int INT_16 = 0x0014; public static final int URL = 0x0040; public static final int UUID = 0x0018; public static final int BOOL = 0x0028; public static final int STRING = 0x0020; public static final int DATSEQ = 0x0030; public static final int DATALT = 0x0038; /** Keeps the specified or derived type of the element. */ private int valueType; /** Keeps the boolean value for the type BOOL. */ private boolean booleanValue; /** Keeps the long value for the types *INT*. */ private long longValue; /** * Keeps the misc type value for the rest of types. * * This field also keeps the value for the type DATALT and DATSEQ. In this case it's a Vector. The access to the * Vector elements is synchronized in cldc (according the source code). But, this is not documented, so we make a * synchronize access to this field to fit any cldc implementation. */ private Object miscValue; public DataElement(int valueType) { switch (valueType) { case NULL: /* miscValue = null in this case. */ break; case DATALT: /* falls through */ case DATSEQ: this.miscValue = new Vector(); break; default: throw new IllegalArgumentException("Invalid valueType for this constructor: " + valueType); } this.valueType = valueType; } public DataElement(boolean bool) { valueType = BOOL; booleanValue = bool; } public DataElement(int valueType, long value) { long min = 0; long max = 0; switch (valueType) { case U_INT_1: max = 0xffL; break; case U_INT_2: max = 0xffffL; break; case U_INT_4: max = 0xffffffffL; break; case INT_1: min = Byte.MIN_VALUE; max = Byte.MAX_VALUE; break; case INT_2: min = -0x8000L; max = 0x7fffL; break; case INT_4: min = Integer.MIN_VALUE; max = Integer.MAX_VALUE; break; case INT_8: min = Long.MIN_VALUE; max = Long.MAX_VALUE; break; default: throw new IllegalArgumentException("Invalid 'valueType' for this constructor: " + valueType); } // check if value in the valid range for this type if (value < min || value > max) throw new IllegalArgumentException("Invalid 'value' (" + value + ") for the specified type (" + valueType + ")"); this.valueType = valueType; this.longValue = value; } public DataElement(int valueType, Object value) { boolean isCorrectValue = true; switch (valueType) { case URL: /* falls through */ case STRING: isCorrectValue = value instanceof String; break; case UUID: isCorrectValue = value instanceof UUID; break; case INT_16: /* falls through */ case U_INT_16: isCorrectValue = value instanceof byte[] && ((byte[]) value).length == 16; break; case U_INT_8: isCorrectValue = value instanceof byte[] && ((byte[]) value).length == 8; break; default: throw new IllegalArgumentException("Invalid 'valueType' for this constructor: " + valueType); } // check if value in the valid range for this type if (!isCorrectValue) throw new IllegalArgumentException("Invalid 'value' for specified type: " + value); this.valueType = valueType; this.miscValue = value; } public synchronized void addElement(DataElement elem) { /* * We can't optimize this by invoking the this.insertElementAt(elem, getSize()), because the ClassCastException * may be thrown from getSize() which gives us improper stack trace. */ if (valueType != DATSEQ && valueType != DATALT) throw new ClassCastException("Invalid element type for this method: " + valueType); if (elem == null) throw new NullPointerException("Specified element is null"); ((Vector) miscValue).addElement(elem); } public synchronized void insertElementAt(DataElement elem, int index) { if (valueType != DATSEQ && valueType != DATALT) throw new ClassCastException("Invalid element type for this method: " + valueType); if (elem == null) throw new NullPointerException("Specified element is null"); /* * We can't use the Vector.insertElementAt check for out of bounds, because Vector throws * ArrayIndexOutOfBoundsException in this case. */ if (index < 0 || index > ((Vector) miscValue).size()) throw new IndexOutOfBoundsException("Specified index is out of range"); ((Vector) miscValue).insertElementAt(elem, index); } public synchronized int getSize() { if (valueType != DATSEQ && valueType != DATALT) throw new ClassCastException("Invalid element type for this method: " + valueType); return ((Vector) miscValue).size(); } public boolean removeElement(DataElement elem) { if (valueType != DATSEQ && valueType != DATALT) throw new ClassCastException("Invalid element type for this method: " + valueType); if (elem == null) throw new NullPointerException("Specified element is null"); /* * The Bluetooth spec says the two DataElement equals if their references are equal. According to cldc1.1 ref impl * sources, the Vector uses 'equals' call, and the Object.equls uses a references compare, so we may not care * about doing this here. */ return ((Vector) miscValue).removeElement(elem); } public int getDataType() { return valueType; } public long getLong() { switch (valueType) { case U_INT_1: /* falls through */ case U_INT_2: /* falls through */ case U_INT_4: /* falls through */ case INT_1: /* falls through */ case INT_2: /* falls through */ case INT_4: /* falls through */ case INT_8: break; default: throw new ClassCastException("Invalid element type for this method: " + valueType); } return longValue; } public boolean getBoolean() { if (valueType != BOOL) throw new ClassCastException("Invalid element type for this method: " + valueType); return booleanValue; } public synchronized Object getValue() { Object retValue = miscValue; /* * According to cldc & bluetooth specifications, the String and UUID are immutable, so we may not return a clone * object to safe the stored one. * * The Vector.elements() returns an Enumeration, which does not allow to break the Vector either. * * The array may be modified by reference, so we have to return a clone. */ switch (valueType) { case URL: /* falls through */ case STRING: /* falls through */ case UUID: break; case DATALT: /* falls through */ case DATSEQ: { retValue = new DataElement[((Vector) miscValue).size()]; ((Vector) miscValue).copyInto((Object[]) retValue); } break; case U_INT_8: /* falls through */ case U_INT_16: /* falls through */ case INT_16: int length = ((byte[]) miscValue).length; retValue = new byte[length]; Vm.arrayCopy(miscValue, 0, retValue, 0, length); break; default: throw new ClassCastException("Invalid element type for this method: " + valueType); } return retValue; } }