FELIX-514 Updated compendium bundle to R4.1 OSGi API
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@681945 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java b/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java
new file mode 100644
index 0000000..51c5cae
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java
@@ -0,0 +1,907 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtData.java,v 1.8 2006/07/10 21:37:07 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package info.dmtree;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+
+// Possible enhancements to this class:
+// * new constructors and get/set methods for b64, to access the encoded value
+// * new constructors and get/set methods for date/time, for more convenient
+// Java access
+/**
+ * An immutable data structure representing the contents of a leaf or interior
+ * node. This structure represents only the value and the format property of the
+ * node, all other properties (like MIME type) can be set and read using the
+ * <code>DmtSession</code> interface.
+ * <p>
+ * Different constructors are available to create nodes with different formats.
+ * Nodes of <code>null</code> format can be created using the static
+ * {@link #NULL_VALUE} constant instance of this class.
+ * <p>
+ * {@link #FORMAT_RAW_BINARY} and {@link #FORMAT_RAW_STRING} enable the support
+ * of future data formats. When using these formats, the actual format name is
+ * specified as a <code>String</code>. The application is responsible for the
+ * proper encoding of the data according to the specified format.
+ */
+public final class DmtData {
+
+ /**
+ * The node holds an OMA DM <code>int</code> value.
+ */
+ public static final int FORMAT_INTEGER = 0x0001;
+
+ /**
+ * The node holds an OMA DM <code>float</code> value.
+ */
+ public static final int FORMAT_FLOAT = 0x0002;
+
+ /**
+ * The node holds an OMA DM <code>chr</code> value.
+ */
+ public static final int FORMAT_STRING = 0x0004;
+
+ /**
+ * The node holds an OMA DM <code>bool</code> value.
+ */
+ public static final int FORMAT_BOOLEAN = 0x0008;
+
+ /**
+ * The node holds an OMA DM <code>date</code> value.
+ */
+ public static final int FORMAT_DATE = 0x0010;
+
+ /**
+ * The node holds an OMA DM <code>time</code> value.
+ */
+ public static final int FORMAT_TIME = 0x0020;
+
+ /**
+ * The node holds an OMA DM <code>bin</code> value. The value of the node
+ * corresponds to the Java <code>byte[]</code> type.
+ */
+ public static final int FORMAT_BINARY = 0x0040;
+
+ /**
+ * The node holds an OMA DM <code>b64</code> value. Like
+ * {@link #FORMAT_BINARY}, this format is also represented by the Java
+ * <code>byte[]</code> type, the difference is only in the corresponding
+ * OMA DM format.
+ */
+ public static final int FORMAT_BASE64 = 0x0080;
+
+ /**
+ * The node holds an OMA DM <code>xml</code> value.
+ */
+ public static final int FORMAT_XML = 0x0100;
+
+ /**
+ * The node holds an OMA DM <code>null</code> value. This corresponds to
+ * the Java <code>null</code> type.
+ */
+ public static final int FORMAT_NULL = 0x0200;
+
+ /**
+ * Format specifier of an internal node. An interior node can hold a Java
+ * object as value (see {@link DmtData#DmtData(Object)} and
+ * {@link DmtData#getNode()}). This value can be used by Java programs that
+ * know a specific URI understands the associated Java type. This type is
+ * further used as a return value of the {@link MetaNode#getFormat} method
+ * for interior nodes.
+ */
+ public static final int FORMAT_NODE = 0x0400;
+
+ /**
+ * The node holds raw protocol data encoded as <code>String</code>. The
+ * {@link #getFormatName()} method can be used to get the actual format
+ * name.
+ */
+ public static final int FORMAT_RAW_STRING = 0x0800;
+
+ /**
+ * The node holds raw protocol data encoded in binary format. The
+ * {@link #getFormatName()} method can be used to get the actual format
+ * name.
+ */
+ public static final int FORMAT_RAW_BINARY = 0x1000;
+
+
+ private static final Hashtable FORMAT_NAMES = new Hashtable();
+
+ static {
+ FORMAT_NAMES.put(new Integer(FORMAT_BASE64), "b64");
+ FORMAT_NAMES.put(new Integer(FORMAT_BINARY), "bin");
+ FORMAT_NAMES.put(new Integer(FORMAT_BOOLEAN), "bool");
+ FORMAT_NAMES.put(new Integer(FORMAT_DATE), "date");
+ FORMAT_NAMES.put(new Integer(FORMAT_FLOAT), "float");
+ FORMAT_NAMES.put(new Integer(FORMAT_INTEGER), "int");
+ FORMAT_NAMES.put(new Integer(FORMAT_NODE), "node");
+ FORMAT_NAMES.put(new Integer(FORMAT_NULL), "null");
+ FORMAT_NAMES.put(new Integer(FORMAT_STRING), "chr");
+ FORMAT_NAMES.put(new Integer(FORMAT_TIME), "time");
+ FORMAT_NAMES.put(new Integer(FORMAT_XML), "xml");
+ }
+
+ /**
+ * Constant instance representing a leaf node of <code>null</code> format.
+ */
+ public static final DmtData NULL_VALUE = new DmtData();
+ // FORMAT_NAMES must be initialized by the time the constr. is called
+
+ private final String str;
+
+ private final int integer;
+
+ private final float flt;
+
+ private final boolean bool;
+
+ private final byte[] bytes;
+
+ private final int format;
+
+ private final String formatName;
+
+ private final Object complex;
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>null</code> format.
+ * This constructor is private and used only to create the public
+ * {@link #NULL_VALUE} constant.
+ */
+ private DmtData() {
+ format = FORMAT_NULL;
+ formatName = getFormatName(format);
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>chr</code> format
+ * with the given string value. The <code>null</code> string argument is
+ * valid.
+ *
+ * @param str the string value to set
+ */
+ public DmtData(String str) {
+ format = FORMAT_STRING;
+ formatName = getFormatName(format);
+ this.str = str;
+
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>node</code> format
+ * with the given object value. The value represents complex data associated
+ * with an interior node.
+ * <p>
+ * Certain interior nodes can support access to their subtrees through such
+ * complex values, making it simpler to retrieve or update all leaf nodes in
+ * a subtree.
+ * <p>
+ * The given value must be a non-<code>null</code> immutable object.
+ *
+ * @param complex the complex data object to set
+ */
+ public DmtData(Object complex) {
+ if(complex == null)
+ throw new NullPointerException("Complex data argument is null.");
+
+ format = FORMAT_NODE;
+ formatName = getFormatName(format);
+ this.complex = complex;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of the specified format and set
+ * its value based on the given string. Only the following string-based
+ * formats can be created using this constructor:
+ * <ul>
+ * <li>{@link #FORMAT_STRING} - value can be any string
+ * <li>{@link #FORMAT_XML} - value must contain an XML fragment (the
+ * validity is not checked by this constructor)
+ * <li>{@link #FORMAT_DATE} - value must be parseable to an ISO 8601
+ * calendar date in complete representation, basic format (pattern
+ * <tt>CCYYMMDD</tt>)
+ * <li>{@link #FORMAT_TIME} - value must be parseable to an ISO 8601 time
+ * of day in either local time, complete representation, basic format
+ * (pattern <tt>hhmmss</tt>) or Coordinated Universal Time, basic format
+ * (pattern <tt>hhmmssZ</tt>)
+ * </ul>
+ * The <code>null</code> string argument is only valid if the format is
+ * string or XML.
+ *
+ * @param value the string, XML, date or time value to set
+ * @param format the format of the <code>DmtData</code> instance to be
+ * created, must be one of the formats specified above
+ * @throws IllegalArgumentException if <code>format</code> is not one of
+ * the allowed formats, or <code>value</code> is not a valid
+ * string for the given format
+ * @throws NullPointerException if a date or time is constructed and
+ * <code>value</code> is <code>null</code>
+ */
+ public DmtData(String value, int format) {
+ switch (format) {
+ case FORMAT_DATE:
+ checkDateFormat(value);
+ break;
+ case FORMAT_TIME:
+ checkTimeFormat(value);
+ break;
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ break; // nothing to do, all string values are accepted
+ default:
+ throw new IllegalArgumentException(
+ "Invalid format in string constructor: " + format);
+ }
+ this.format = format;
+ this.formatName = getFormatName(format);
+ this.str = value;
+
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>int</code> format and
+ * set its value.
+ *
+ * @param integer the integer value to set
+ */
+ public DmtData(int integer) {
+ format = FORMAT_INTEGER;
+ formatName = getFormatName(format);
+ this.integer = integer;
+
+ this.str = null;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>float</code> format
+ * and set its value.
+ *
+ * @param flt the float value to set
+ */
+ public DmtData(float flt) {
+ format = FORMAT_FLOAT;
+ formatName = getFormatName(format);
+ this.flt = flt;
+
+ this.str = null;
+ this.integer = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>bool</code> format
+ * and set its value.
+ *
+ * @param bool the boolean value to set
+ */
+ public DmtData(boolean bool) {
+ format = FORMAT_BOOLEAN;
+ formatName = getFormatName(format);
+ this.bool = bool;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>bin</code> format and
+ * set its value.
+ *
+ * @param bytes the byte array to set, must not be <code>null</code>
+ * @throws NullPointerException if <code>bytes</code> is <code>null</code>
+ */
+ public DmtData(byte[] bytes) {
+ if (bytes == null)
+ throw new NullPointerException("Binary data argument is null.");
+
+ format = FORMAT_BINARY;
+ formatName = getFormatName(format);
+ this.bytes = bytes;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>bin</code> or
+ * <code>b64</code> format and set its value. The chosen format is
+ * specified by the <code>base64</code> parameter.
+ *
+ * @param bytes the byte array to set, must not be <code>null</code>
+ * @param base64 if <code>true</code>, the new instance will have
+ * <code>b64</code> format, if <code>false</code>, it will have
+ * <code>bin</code> format
+ * @throws NullPointerException if <code>bytes</code> is <code>null</code>
+ */
+ public DmtData(byte[] bytes, boolean base64) {
+ if (bytes == null)
+ throw new NullPointerException("Binary data argument is null.");
+
+ format = base64 ? FORMAT_BASE64 : FORMAT_BINARY;
+ formatName = getFormatName(format);
+ this.bytes = bytes;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance in {@link #FORMAT_RAW_STRING}
+ * format. The data is provided encoded as a <code>String</code>. The
+ * actual data format is specified in <code>formatName</code>. The
+ * encoding used in <code>data</code> must conform to this format.
+ *
+ * @param formatName the name of the format, must not be <code>null</code>
+ * @param data the data encoded according to the specified format, must not
+ * be <code>null</code>
+ * @throws NullPointerException if <code>formatName</code> or
+ * <code>data</code> is <code>null</code>
+ */
+ public DmtData(String formatName, String data) {
+ if(formatName == null)
+ throw new NullPointerException("Format name argument is null.");
+ if(data == null)
+ throw new NullPointerException("Data argument is null.");
+
+ format = FORMAT_RAW_STRING;
+ this.formatName = formatName;
+ this.str = data;
+
+ this.bytes = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance in {@link #FORMAT_RAW_BINARY}
+ * format. The data is provided encoded as binary. The actual data format is
+ * specified in <code>formatName</code>. The encoding used in
+ * <code>data</code> must conform to this format.
+ *
+ * @param formatName the name of the format, must not be <code>null</code>
+ * @param data the data encoded according to the specified format, must not
+ * be <code>null</code>
+ * @throws NullPointerException if <code>formatName</code> or
+ * <code>data</code> is <code>null</code>
+ */
+ public DmtData(String formatName, byte[] data) {
+ if(formatName == null)
+ throw new NullPointerException("Format name argument is null.");
+ if(data == null)
+ throw new NullPointerException("Data argument is null.");
+
+ format = FORMAT_RAW_BINARY;
+ this.formatName = formatName;
+ this.bytes = (byte[]) data.clone();
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Gets the value of a node with string (<code>chr</code>) format.
+ *
+ * @return the string value
+ * @throws DmtIllegalStateException if the format of the node is not string
+ */
+ public String getString() {
+ if (format == FORMAT_STRING)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not string.");
+ }
+
+ /**
+ * Gets the value of a node with date format. The returned date string is
+ * formatted according to the ISO 8601 definition of a calendar date in
+ * complete representation, basic format (pattern <tt>CCYYMMDD</tt>).
+ *
+ * @return the date value
+ * @throws DmtIllegalStateException if the format of the node is not date
+ */
+ public String getDate() {
+ if (format == FORMAT_DATE)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not date.");
+ }
+
+ /**
+ * Gets the value of a node with time format. The returned time string is
+ * formatted according to the ISO 8601 definition of the time of day. The
+ * exact format depends on the value the object was initialized with: either
+ * local time, complete representation, basic format (pattern
+ * <tt>hhmmss</tt>) or Coordinated Universal Time, basic format (pattern
+ * <tt>hhmmssZ</tt>).
+ *
+ * @return the time value
+ * @throws DmtIllegalStateException if the format of the node is not time
+ */
+ public String getTime() {
+ if (format == FORMAT_TIME)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not time.");
+ }
+
+ /**
+ * Gets the value of a node with <code>xml</code> format.
+ *
+ * @return the XML value
+ * @throws DmtIllegalStateException if the format of the node is not
+ * <code>xml</code>
+ */
+ public String getXml() {
+ if (format == FORMAT_XML)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not XML.");
+ }
+
+ /**
+ * Gets the value of a node with integer (<code>int</code>) format.
+ *
+ * @return the integer value
+ * @throws DmtIllegalStateException if the format of the node is not integer
+ */
+ public int getInt() {
+ if (format == FORMAT_INTEGER)
+ return integer;
+
+ throw new DmtIllegalStateException("DmtData value is not integer.");
+ }
+
+ /**
+ * Gets the value of a node with <code>float</code> format.
+ *
+ * @return the float value
+ * @throws DmtIllegalStateException if the format of the node is not
+ * <code>float</code>
+ */
+ public float getFloat() {
+ if (format == FORMAT_FLOAT)
+ return flt;
+
+ throw new DmtIllegalStateException("DmtData value is not float.");
+ }
+
+ /**
+ * Gets the value of a node with boolean (<code>bool</code>) format.
+ *
+ * @return the boolean value
+ * @throws DmtIllegalStateException if the format of the node is not boolean
+ */
+ public boolean getBoolean() {
+ if (format == FORMAT_BOOLEAN)
+ return bool;
+
+ throw new DmtIllegalStateException("DmtData value is not boolean.");
+ }
+
+ /**
+ * Gets the value of a node with binary (<code>bin</code>) format.
+ *
+ * @return the binary value
+ * @throws DmtIllegalStateException if the format of the node is not binary
+ */
+ public byte[] getBinary() {
+ if (format == FORMAT_BINARY) {
+ byte[] bytesCopy = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++)
+ bytesCopy[i] = bytes[i];
+
+ return bytesCopy;
+ }
+
+ throw new DmtIllegalStateException("DmtData value is not a byte array.");
+ }
+
+ /**
+ * Gets the value of a node in raw binary ({@link #FORMAT_RAW_BINARY})
+ * format.
+ *
+ * @return the data value in raw binary format
+ * @throws DmtIllegalStateException if the format of the node is not raw binary
+ */
+ public byte[] getRawBinary() {
+ if (format == FORMAT_RAW_BINARY)
+ return (byte[]) bytes.clone();
+
+ throw new DmtIllegalStateException(
+ "DmtData value is not in raw binary format.");
+ }
+
+ /**
+ * Gets the value of a node in raw <code>String</code>
+ * ({@link #FORMAT_RAW_STRING}) format.
+ *
+ * @return the data value in raw <code>String</code> format
+ * @throws DmtIllegalStateException if the format of the node is not raw
+ * <code>String</code>
+ */
+ public String getRawString() {
+ if (format == FORMAT_RAW_STRING)
+ return str;
+
+ throw new DmtIllegalStateException(
+ "DmtData value is not in raw string format.");
+ }
+
+ /**
+ * Gets the value of a node with base 64 (<code>b64</code>) format.
+ *
+ * @return the binary value
+ * @throws DmtIllegalStateException if the format of the node is not base 64.
+ */
+ public byte[] getBase64() {
+ if (format == FORMAT_BASE64) {
+ byte[] bytesCopy = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++)
+ bytesCopy[i] = bytes[i];
+
+ return bytesCopy;
+ }
+
+ throw new DmtIllegalStateException(
+ "DmtData value is not in base 64 format.");
+ }
+
+ /**
+ * Gets the complex data associated with an interior node (<code>node</code>
+ * format).
+ * <p>
+ * Certain interior nodes can support access to their subtrees through
+ * complex values, making it simpler to retrieve or update all leaf nodes in
+ * the subtree.
+ *
+ * @return the data object associated with an interior node
+ * @throws DmtIllegalStateException if the format of the data is not
+ * <code>node</code>
+ */
+ public Object getNode() {
+ if(format == FORMAT_NODE)
+ return complex;
+
+ throw new DmtIllegalStateException(
+ "DmtData does not contain interior node data.");
+ }
+
+ /**
+ * Get the node's format, expressed in terms of type constants defined in
+ * this class. Note that the 'format' term is a legacy from OMA DM, it is
+ * more customary to think of this as 'type'.
+ *
+ * @return the format of the node
+ */
+ public int getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns the format of this <code>DmtData</code> as <code>String</code>.
+ * For the predefined data formats this is the OMA DM defined name of the
+ * format. For {@link #FORMAT_RAW_STRING} and {@link #FORMAT_RAW_BINARY}
+ * this is the format specified when the object was created.
+ *
+ * @return the format name as <code>String</code>
+ */
+ public String getFormatName() {
+ return formatName;
+ }
+
+ /**
+ * Get the size of the data. The returned value depends on the format of
+ * data in the node:
+ * <ul>
+ * <li>{@link #FORMAT_STRING}, {@link #FORMAT_XML}, {@link #FORMAT_BINARY},
+ * {@link #FORMAT_BASE64}, {@link #FORMAT_RAW_STRING}, and
+ * {@link #FORMAT_RAW_BINARY}: the length of the stored data, or 0 if
+ * the data is <code>null</code>
+ * <li>{@link #FORMAT_INTEGER} and {@link #FORMAT_FLOAT}: 4
+ * <li>{@link #FORMAT_DATE} and {@link #FORMAT_TIME}: the length of the
+ * date or time in its string representation
+ * <li>{@link #FORMAT_BOOLEAN}: 1
+ * <li>{@link #FORMAT_NODE}: -1 (unknown)
+ * <li>{@link #FORMAT_NULL}: 0
+ * </ul>
+ *
+ * @return the size of the data stored by this object
+ */
+ public int getSize() {
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ case FORMAT_RAW_STRING:
+ return str == null ? 0 : str.length();
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ case FORMAT_RAW_BINARY:
+ return bytes.length;
+ case FORMAT_INTEGER:
+ case FORMAT_FLOAT:
+ return 4;
+ case FORMAT_BOOLEAN:
+ return 1;
+ case FORMAT_NODE:
+ return -1;
+ case FORMAT_NULL:
+ return 0;
+ }
+
+ return 0; // never reached
+ }
+
+ /**
+ * Gets the string representation of the <code>DmtData</code>. This
+ * method works for all formats.
+ * <p>
+ * For string format data - including {@link #FORMAT_RAW_STRING} - the
+ * string value itself is returned, while for XML, date, time, integer,
+ * float, boolean and node formats the string form of the value is returned.
+ * Binary - including {@link #FORMAT_RAW_BINARY} - and base64 data is
+ * represented by two-digit hexadecimal numbers for each byte separated by
+ * spaces. The {@link #NULL_VALUE} data has the string form of
+ * "<code>null</code>". Data of string or XML format containing the Java
+ * <code>null</code> value is represented by an empty string.
+ *
+ * @return the string representation of this <code>DmtData</code> instance
+ */
+ public String toString() {
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ case FORMAT_RAW_STRING:
+ return str == null ? "" : str;
+ case FORMAT_INTEGER:
+ return String.valueOf(integer);
+ case FORMAT_FLOAT:
+ return String.valueOf(flt);
+ case FORMAT_BOOLEAN:
+ return String.valueOf(bool);
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ case FORMAT_RAW_BINARY:
+ return getHexDump(bytes);
+ case FORMAT_NODE:
+ return complex.toString();
+ case FORMAT_NULL:
+ return "null";
+ }
+
+ return null; // never reached
+ }
+
+ /**
+ * Compares the specified object with this <code>DmtData</code> instance.
+ * Two <code>DmtData</code> objects are considered equal if their format
+ * is the same, and their data (selected by the format) is equal.
+ * <p>
+ * In case of {@link #FORMAT_RAW_BINARY} and {@link #FORMAT_RAW_STRING}
+ * the textual name of the data format - as returned by
+ * {@link #getFormatName()} - must be equal as well.
+ *
+ * @param obj the object to compare with this <code>DmtData</code>
+ * @return true if the argument represents the same <code>DmtData</code>
+ * as this object
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DmtData))
+ return false;
+
+ DmtData other = (DmtData) obj;
+
+ if (format != other.format)
+ return false;
+
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ return str == null ? other.str == null : str.equals(other.str);
+ case FORMAT_INTEGER:
+ return integer == other.integer;
+ case FORMAT_FLOAT:
+ return flt == other.flt;
+ case FORMAT_BOOLEAN:
+ return bool == other.bool;
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ return Arrays.equals(bytes, other.bytes);
+ case FORMAT_NODE:
+ return complex.equals(other.complex);
+ case FORMAT_NULL:
+ return true;
+ case FORMAT_RAW_BINARY:
+ return formatName.equals(other.formatName)
+ && Arrays.equals(bytes, other.bytes);
+ case FORMAT_RAW_STRING:
+ // in this case str cannot be null
+ return formatName.equals(other.formatName) && str.equals(other.str);
+ }
+
+ return false; // never reached
+ }
+
+ /**
+ * Returns the hash code value for this <code>DmtData</code> instance. The
+ * hash code is calculated based on the data (selected by the format) of
+ * this object.
+ *
+ * @return the hash code value for this object
+ */
+ public int hashCode() {
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ case FORMAT_RAW_STRING:
+ return str == null ? 0 : str.hashCode();
+ case FORMAT_INTEGER:
+ return new Integer(integer).hashCode();
+ case FORMAT_FLOAT:
+ return new Float(flt).hashCode();
+ case FORMAT_BOOLEAN:
+ return new Boolean(bool).hashCode();
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ case FORMAT_RAW_BINARY:
+ return new String(bytes).hashCode();
+ case FORMAT_NODE:
+ return complex.hashCode();
+ case FORMAT_NULL:
+ return 0;
+ }
+
+ return 0; // never reached
+ }
+
+ private static void checkDateFormat(String value) {
+ if(value.length() != 8)
+ throw new IllegalArgumentException("Date string '" + value +
+ "' does not follow the format 'CCYYMMDD'.");
+
+ int year = checkNumber(value, "Date", 0, 4, 0, 9999);
+ int month = checkNumber(value, "Date", 4, 2, 1, 12);
+ int day = checkNumber(value, "Date", 6, 2, 1, 31);
+
+ // Date checking is not prepared for all special rules (for example
+ // historical leap years), production code could contain a full check.
+
+ // Day 31 is invalid for April, June, September and November
+ if((month == 4 || month == 6 || month == 9 || month == 11) && day == 31)
+ throw new IllegalArgumentException("Date string '" + value +
+ "' contains an invalid date.");
+
+ // February 29 is invalid except for leap years, Feb. 30-31 are invalid
+ if(month == 2 && day > 28 &&
+ !(day == 29 && year%4 == 0 && (year%100 != 0 || year%400 == 0)))
+ throw new IllegalArgumentException("Date string '" + value +
+ "' contains an invalid date.");
+ }
+
+ private static void checkTimeFormat(String value) {
+ if(value.length() > 0 && value.charAt(value.length()-1) == 'Z')
+ value = value.substring(0, value.length()-1);
+
+ if(value.length() != 6)
+ throw new IllegalArgumentException("Time string '" + value +
+ "' does not follow the format 'hhmmss' or 'hhmmssZ'.");
+
+ // Time checking is not prepared for all special rules (for example
+ // leap seconds), production code could contain a full check.
+
+ // if hour is 24, only 240000 should be allowed
+ checkNumber(value, "Time", 0, 2, 0, 24);
+ checkNumber(value, "Time", 2, 2, 0, 59);
+ checkNumber(value, "Time", 4, 2, 0, 59);
+
+ if(value.startsWith("24") && !value.startsWith("240000"))
+ throw new IllegalArgumentException("Time string is out of range.");
+ }
+
+ private static int checkNumber(String value, String name, int from,
+ int length, int min, int max) {
+ String part = value.substring(from, from+length);
+ int number;
+ try {
+ number = Integer.parseInt(part);
+ } catch(NumberFormatException e) {
+ throw new IllegalArgumentException(name + " string '" + value +
+ "' contains a non-numeric part.");
+ }
+ if(number < min || number > max)
+ throw new IllegalArgumentException("A segment of the " + name +
+ " string '" + value + "' is out of range.");
+
+ return number;
+ }
+
+ // character array of hexadecimal digits, used for printing binary data
+ private static char[] hex = "0123456789ABCDEF".toCharArray();
+
+ // generates a hexadecimal dump of the given binary data
+ private static String getHexDump(byte[] bytes) {
+ if (bytes.length == 0)
+ return "";
+
+ StringBuffer buf = new StringBuffer();
+ appendHexByte(buf, bytes[0]);
+ for (int i = 1; i < bytes.length; i++)
+ appendHexByte(buf.append(' '), bytes[i]);
+
+ return buf.toString();
+ }
+
+ private static void appendHexByte(StringBuffer buf, byte b) {
+ buf.append(hex[(b & 0xF0) >> 4]).append(hex[b & 0x0F]);
+ }
+
+ private static String getFormatName(int format) {
+ return (String) FORMAT_NAMES.get(new Integer(format));
+ }
+}