/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying * information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2006 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.asn1;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import org.opends.server.api.ProtocolElement;
import org.opends.server.types.ByteString;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ProtocolMessages.*;
import static org.opends.server.protocols.asn1.ASN1Constants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines the data structures and methods to use when interacting
* with generic ASN.1 elements. Subclasses may provide more specific
* functionality for individual element types.
*/
public class ASN1Element
implements ProtocolElement, Serializable
{
/**
* The fully-qualified name of this class for debugging purposes.
*/
private static final String CLASS_NAME =
"org.opends.server.protocols.asn1.ASN1Element";
/**
* The serial version identifier required to satisfy the compiler because this
* class implements the java.io.Serializable interface. This
* value was generated using the serialver command-line utility
* included with the Java SDK.
*/
private static final long serialVersionUID = -6085322427222358963L;
// The BER type for this element.
private byte type;
// The encoded value for this element.
private byte[] value;
/**
* Creates a new ASN.1 element with the specified type and no value.
*
* @param type The BER type for this ASN.1 element.
*/
public ASN1Element(byte type)
{
assert debugConstructor(CLASS_NAME, byteToHex(type));
this.type = type;
this.value = NO_VALUE;
}
/**
* Creates a new ASN.1 element with the specified type and value.
*
* @param type The BER type for this ASN.1 element.
* @param value The encoded value for this ASN.1 element.
*/
public ASN1Element(byte type, byte[] value)
{
assert debugConstructor(CLASS_NAME, byteToHex(type), bytesToHex(value));
this.type = type;
if (value == null)
{
this.value = NO_VALUE;
}
else
{
this.value = value;
}
}
/**
* Retrieves the BER type for this ASN.1 element.
*
* @return The BER type for this ASN.1 element.
*/
public byte getType()
{
assert debugEnter(CLASS_NAME, "getType");
return type;
}
/**
* Specifies the BER type for this ASN.1 element.
*
* @param type The BER type for this ASN.1 element.
*/
public void setType(byte type)
{
assert debugEnter(CLASS_NAME, "setType", byteToHex(type));
this.type = type;
}
/**
* Indicates whether this ASN.1 element is in the universal class.
*
* @return true if this ASN.1 element is in the universal class,
* or false if not.
*/
public boolean isUniversal()
{
assert debugEnter(CLASS_NAME, "isUniversal");
return ((type & TYPE_MASK_ALL_BUT_CLASS) == TYPE_MASK_UNIVERSAL);
}
/**
* Indicates whether this ASN.1 element is in the application-specific class.
*
* @return true if this ASN.1 element is in the
* application-specific class, or false if not.
*/
public boolean isApplicationSpecific()
{
assert debugEnter(CLASS_NAME, "isApplicationSpecific");
return ((type & TYPE_MASK_ALL_BUT_CLASS) == TYPE_MASK_APPLICATION);
}
/**
* Indicates whether this ASN.1 element is in the context-specific class.
*
* @return true if this ASN.1 element is in the context-specific
* class, or false if not.
*/
public boolean isContextSpecific()
{
assert debugEnter(CLASS_NAME, "isContextSpecific");
return ((type & TYPE_MASK_ALL_BUT_CLASS) == TYPE_MASK_CONTEXT);
}
/**
* Indicates whether this ASN.1 element is in the private class.
*
* @return true if this ASN.1 element is in the private class,
* or false if not.
*/
public boolean isPrivate()
{
assert debugEnter(CLASS_NAME, "isPrivate");
return ((type & TYPE_MASK_ALL_BUT_CLASS) == TYPE_MASK_PRIVATE);
}
/**
* Indicates whether this ASN.1 element has a primitive value.
*
* @return true if this ASN.1 element has a primitive value, or
* false if it is constructed.
*/
public boolean isPrimitive()
{
assert debugEnter(CLASS_NAME, "isPrimitive");
return ((type & TYPE_MASK_ALL_BUT_PC) == TYPE_MASK_PRIMITIVE);
}
/**
* Indicates whether this ASN.1 element has a constructed value.
*
* @return true if this ASN.1 element has a constructed value,
* or false if it is primitive.
*/
public boolean isConstructed()
{
assert debugEnter(CLASS_NAME, "isConstructed");
return ((type & TYPE_MASK_ALL_BUT_PC) == TYPE_MASK_CONSTRUCTED);
}
/**
* Retrieves the encoded value for this ASN.1 element.
*
* @return The encoded value for this ASN.1 element.
*/
public byte[] value()
{
assert debugEnter(CLASS_NAME, "value");
return value;
}
/**
* Specifies the encoded value for this ASN.1 element.
*
* @param value The encoded value for this ASN.1 element.
*
* @throws ASN1Exception If the provided value is not appropriate for this
* type of ASN.1 element.
*/
public void setValue(byte[] value)
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "setValue", bytesToHex(value));
if (value == null)
{
this.value = NO_VALUE;
}
else
{
this.value = value;
}
}
/**
* Specifies the value to use for this ASN.1 element, but without performing
* any validity checks. This should only be used by subclasses and they must
* ensure that it is non-null and conforms to the appropriate requirements of
* the underlying type.
*
* @param value The encoded value for this ASN.1 element.
*/
protected final void setValueInternal(byte[] value)
{
assert debugEnter(CLASS_NAME, "setValueInternal", bytesToHex(value));
this.value = value;
}
/**
* Encodes the provided value for use as the length of an ASN.1 element.
*
* @param length The length to encode for use in an ASN.1 element.
*
* @return The byte array containing the encoded length.
*/
public static byte[] encodeLength(int length)
{
assert debugEnter(CLASS_NAME, "encodeLength", String.valueOf(length));
if (length < 128)
{
return new byte[] { (byte) length };
}
if ((length & 0x000000FF) == length)
{
return new byte[]
{
(byte) 0x81,
(byte) (length & 0xFF)
};
}
else if ((length & 0x0000FFFF) == length)
{
return new byte[]
{
(byte) 0x82,
(byte) ((length >> 8) & 0xFF),
(byte) (length & 0xFF)
};
}
else if ((length & 0x00FFFFFF) == length)
{
return new byte[]
{
(byte) 0x83,
(byte) ((length >> 16) & 0xFF),
(byte) ((length >> 8) & 0xFF),
(byte) (length & 0xFF)
};
}
else
{
return new byte[]
{
(byte) 0x84,
(byte) ((length >> 24) & 0xFF),
(byte) ((length >> 16) & 0xFF),
(byte) ((length >> 8) & 0xFF),
(byte) (length & 0xFF)
};
}
}
/**
* Encodes this ASN.1 element to a byte array.
*
* @return The byte array containing the encoded ASN.1 element.
*/
public byte[] encode()
{
assert debugEnter(CLASS_NAME, "encode");
if (value.length == 0)
{
return new byte[] { type, 0x00 };
}
else if (value.length < 128)
{
byte[] encodedElement = new byte[value.length + 2];
encodedElement[0] = type;
encodedElement[1] = (byte) value.length;
System.arraycopy(value, 0, encodedElement, 2, value.length);
return encodedElement;
}
else
{
byte[] encodedLength = encodeLength(value.length);
byte[] encodedElement = new byte[1 + value.length + encodedLength.length];
encodedElement[0] = type;
System.arraycopy(encodedLength, 0, encodedElement, 1,
encodedLength.length);
System.arraycopy(value, 0, encodedElement, 1+encodedLength.length,
value.length);
return encodedElement;
}
}
/**
* Retrieves a byte array containing the encoded representation of the
* provided boolean value.
*
* @param booleanValue The boolean value to encode.
*
* @return A byte array containing the encoded representation of the provided
* boolean value.
*/
public static byte[] encodeValue(boolean booleanValue)
{
assert debugEnter(CLASS_NAME, "encodeValue", String.valueOf(booleanValue));
return (booleanValue ? BOOLEAN_VALUE_TRUE : BOOLEAN_VALUE_FALSE);
}
/**
* Retrieves a byte array containing the encoded representation of the
* provided integer value.
*
* @param intValue The integer value to encode.
*
* @return A byte array containing the encoded representation of the provided
* integer value.
*/
public static byte[] encodeValue(int intValue)
{
assert debugEnter(CLASS_NAME, "encodeValue", String.valueOf(intValue));
if ((intValue & 0x0000007F) == intValue)
{
return new byte[]
{
(byte) (intValue & 0xFF)
};
}
else if ((intValue & 0x00007FFF) == intValue)
{
return new byte[]
{
(byte) ((intValue >> 8) & 0xFF),
(byte) (intValue & 0xFF)
};
}
else if ((intValue & 0x007FFFFF) == intValue)
{
return new byte[]
{
(byte) ((intValue >> 16) & 0xFF),
(byte) ((intValue >> 8) & 0xFF),
(byte) (intValue & 0xFF)
};
}
else
{
return new byte[]
{
(byte) ((intValue >> 24) & 0xFF),
(byte) ((intValue >> 16) & 0xFF),
(byte) ((intValue >> 8) & 0xFF),
(byte) (intValue & 0xFF)
};
}
}
/**
* Retrieves a byte array containing the encoded representation of the
* provided long value.
*
* @param longValue The long value to encode.
*
* @return A byte array containing the encoded representation of the provided
* long value.
*/
public static byte[] encodeLongValue(long longValue)
{
assert debugEnter(CLASS_NAME, "encodeLongValue", String.valueOf(longValue));
if ((longValue & 0x000000000000007FL) == longValue)
{
return new byte[]
{
(byte) (longValue & 0xFF)
};
}
else if ((longValue & 0x0000000000007FFFL) == longValue)
{
return new byte[]
{
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
else if ((longValue & 0x00000000007FFFFFL) == longValue)
{
return new byte[]
{
(byte) ((longValue >> 16) & 0xFF),
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
else if ((longValue & 0x000000007FFFFFFFL) == longValue)
{
return new byte[]
{
(byte) ((longValue >> 24) & 0xFF),
(byte) ((longValue >> 16) & 0xFF),
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
else if ((longValue & 0x0000007FFFFFFFFFL) == longValue)
{
return new byte[]
{
(byte) ((longValue >> 32) & 0xFF),
(byte) ((longValue >> 24) & 0xFF),
(byte) ((longValue >> 16) & 0xFF),
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
else if ((longValue & 0x00007FFFFFFFFFFFL) == longValue)
{
return new byte[]
{
(byte) ((longValue >> 40) & 0xFF),
(byte) ((longValue >> 32) & 0xFF),
(byte) ((longValue >> 24) & 0xFF),
(byte) ((longValue >> 16) & 0xFF),
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
else if ((longValue & 0x007FFFFFFFFFFFFFL) == longValue)
{
return new byte[]
{
(byte) ((longValue >> 48) & 0xFF),
(byte) ((longValue >> 40) & 0xFF),
(byte) ((longValue >> 32) & 0xFF),
(byte) ((longValue >> 24) & 0xFF),
(byte) ((longValue >> 16) & 0xFF),
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
else
{
return new byte[]
{
(byte) ((longValue >> 56) & 0xFF),
(byte) ((longValue >> 48) & 0xFF),
(byte) ((longValue >> 40) & 0xFF),
(byte) ((longValue >> 32) & 0xFF),
(byte) ((longValue >> 24) & 0xFF),
(byte) ((longValue >> 16) & 0xFF),
(byte) ((longValue >> 8) & 0xFF),
(byte) (longValue & 0xFF)
};
}
}
/**
* Retrieves a byte array containing the encoded representation of the
* provided set of ASN.1 elements.
*
* @param elements The set of ASN.1 elements to encode into the value.
*
* @return A byte array containing the encoded representation of the
* provided set of ASN.1 elements.
*/
public static byte[] encodeValue(ArrayList elements)
{
assert debugEnter(CLASS_NAME, "encodeValue", String.valueOf(elements));
if (elements == null)
{
return NO_VALUE;
}
int totalLength = 0;
byte[][] encodedElements = new byte[elements.size()][];
for (int i=0; i < encodedElements.length; i++)
{
encodedElements[i] = elements.get(i).encode();
totalLength += encodedElements[i].length;
}
byte[] encodedValue = new byte[totalLength];
int startPos = 0;
for (byte[] b : encodedElements)
{
System.arraycopy(b, 0, encodedValue, startPos, b.length);
startPos += b.length;
}
return encodedValue;
}
/**
* Decodes the contents of the provided byte array as an ASN.1 element.
*
* @param encodedElement The byte array containing the ASN.1 element to
* decode.
*
* @return The decoded ASN.1 element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode the
* byte array as an ASN.1 element.
*/
public static ASN1Element decode(byte[] encodedElement)
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decode", bytesToHex(encodedElement));
// First make sure that the array is not null and long enough to contain
// a valid ASN.1 element.
if (encodedElement == null)
{
int msgID = MSGID_ASN1_NULL_ELEMENT;
String message = getMessage(msgID);
throw new ASN1Exception(msgID, message);
}
else if (encodedElement.length < 2)
{
int msgID = MSGID_ASN1_SHORT_ELEMENT;
String message = getMessage(msgID, encodedElement.length);
throw new ASN1Exception(msgID, message);
}
// Next, decode the length. This allows multi-byte lengths with up to four
// bytes used to indicate how many bytes are in the length.
byte type = encodedElement[0];
int length = (encodedElement[1] & 0x7F);
int valueStartPos = 2;
if (length != encodedElement[1])
{
int numLengthBytes = length;
if (numLengthBytes > 4)
{
int msgID = MSGID_ASN1_INVALID_NUM_LENGTH_BYTES;
String message = getMessage(msgID, numLengthBytes);
throw new ASN1Exception(msgID, message);
}
else if (encodedElement.length < (2 + numLengthBytes))
{
int msgID = MSGID_ASN1_TRUNCATED_LENGTH;
String message = getMessage(msgID, numLengthBytes);
throw new ASN1Exception(msgID, message);
}
length = 0x00;
valueStartPos = 2 + numLengthBytes;
for (int i=0; i < numLengthBytes; i++)
{
length = (length << 8) | (encodedElement[i+2] & 0xFF);
}
}
// Make sure that the number of bytes left is equal to the number of bytes
// in the value.
if ((encodedElement.length - valueStartPos) != length)
{
int msgID = MSGID_ASN1_LENGTH_MISMATCH;
String message = getMessage(msgID, length,
(encodedElement.length - valueStartPos));
throw new ASN1Exception(msgID, message);
}
// Copy the value and construct the element to return.
byte[] value = new byte[length];
System.arraycopy(encodedElement, valueStartPos, value, 0, length);
return new ASN1Element(type, value);
}
/**
* Decodes the specified portion of the provided byte array as an ASN.1
* element.
*
* @param encodedElement The byte array containing the ASN.1 element to
* decode.
* @param startPos The position in the provided array at which to
* start decoding.
* @param length The number of bytes in the set of data to decode as
* an ASN.1 element.
*
* @return The decoded ASN.1 element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode the
* byte array as an ASN.1 element.
*/
public static ASN1Element decode(byte[] encodedElement, int startPos,
int length)
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decode", bytesToHex(encodedElement),
String.valueOf(startPos), String.valueOf(length));
// First make sure that the array is not null and long enough to contain
// a valid ASN.1 element.
if (encodedElement == null)
{
int msgID = MSGID_ASN1_NULL_ELEMENT;
String message = getMessage(msgID);
throw new ASN1Exception(msgID, message);
}
else if ((startPos < 0) || (startPos+length > encodedElement.length) ||
(length < 2))
{
int msgID = MSGID_ASN1_SHORT_ELEMENT;
String message = getMessage(msgID, encodedElement.length);
throw new ASN1Exception(msgID, message);
}
// Next, decode the length. This allows multi-byte lengths with up to four
// bytes used to indicate how many bytes are in the length.
byte type = encodedElement[startPos];
int elementLength = (encodedElement[startPos+1] & 0x7F);
int valueStartPos = startPos + 2;
if (elementLength != encodedElement[startPos+1])
{
int numLengthBytes = elementLength;
if (numLengthBytes > 4)
{
int msgID = MSGID_ASN1_INVALID_NUM_LENGTH_BYTES;
String message = getMessage(msgID, numLengthBytes);
throw new ASN1Exception(msgID, message);
}
else if (startPos+length < (2 + numLengthBytes))
{
int msgID = MSGID_ASN1_TRUNCATED_LENGTH;
String message = getMessage(msgID, numLengthBytes);
throw new ASN1Exception(msgID, message);
}
elementLength = 0x00;
valueStartPos = startPos + 2 + numLengthBytes;
for (int i=0; i < numLengthBytes; i++)
{
elementLength = (elementLength << 8) | (encodedElement[i+2] & 0xFF);
}
}
// Make sure that the number of bytes left is equal to the number of bytes
// in the value.
if ((startPos+length - valueStartPos) != elementLength)
{
int msgID = MSGID_ASN1_LENGTH_MISMATCH;
String message = getMessage(msgID, elementLength,
(startPos+length - valueStartPos));
throw new ASN1Exception(msgID, message);
}
// Copy the value and construct the element to return.
byte[] value = new byte[elementLength];
System.arraycopy(encodedElement, valueStartPos, value, 0, elementLength);
return new ASN1Element(type, value);
}
/**
* Decodes this ASN.1 element as an ASN.1 Boolean element.
*
* @return The ASN.1 Boolean element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 Boolean element.
*/
public ASN1Boolean decodeAsBoolean()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsBoolean");
return ASN1Boolean.decodeAsBoolean(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 enumerated element.
*
* @return The ASN.1 enumerated element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 enumerated element.
*/
public ASN1Enumerated decodeAsEnumerated()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsEnumerated");
return ASN1Enumerated.decodeAsEnumerated(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 integer element.
*
* @return The ASN.1 integer element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 integer element.
*/
public ASN1Integer decodeAsInteger()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsInteger");
return ASN1Integer.decodeAsInteger(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 long element.
*
* @return The ASN.1 long element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 long element.
*/
public ASN1Long decodeAsLong()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsLong");
return ASN1Long.decodeAsLong(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 null element.
*
* @return The ASN.1 null element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 null element.
*/
public ASN1Null decodeAsNull()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsNull");
return ASN1Null.decodeAsNull(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 octet string element.
*
* @return The ASN.1 octet string element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 octet string element.
*/
public ASN1OctetString decodeAsOctetString()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsOctetString");
return ASN1OctetString.decodeAsOctetString(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 sequence element.
*
* @return The ASN.1 sequence element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 sequence element.
*/
public ASN1Sequence decodeAsSequence()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsSequence");
return ASN1Sequence.decodeAsSequence(this);
}
/**
* Decodes this ASN.1 element as an ASN.1 set element.
*
* @return The ASN.1 set element decoded from this element.
*
* @throws ASN1Exception If a problem occurs while attempting to decode this
* element as an ASN.1 set element.
*/
public ASN1Set decodeAsSet()
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeAsSet");
return ASN1Set.decodeAsSet(this);
}
/**
* Decodes the provided byte array as a collection of ASN.1 elements as would
* be found in the value of a sequence or set.
*
* @param encodedElements The byte array containing the data to decode.
*
* @return The set of decoded ASN.1 elements.
*
* @throws ASN1Exception If a problem occurs while attempting to decode the
* set of ASN.1 elements from the provided byte array.
*/
public static ArrayList decodeElements(byte[] encodedElements)
throws ASN1Exception
{
assert debugEnter(CLASS_NAME, "decodeElements",
bytesToHex(encodedElements));
// Make sure that the element array is not null.
if (encodedElements == null)
{
int msgID = MSGID_ASN1_ELEMENT_SET_NULL;
String message = getMessage(msgID);
throw new ASN1Exception(msgID, message);
}
// Iterate through the array and keep reading elements until the end is
// reached.
ArrayList elements = new ArrayList();
int startPos = 0;
while (startPos < encodedElements.length)
{
byte type = encodedElements[startPos++];
if (startPos >= encodedElements.length)
{
int msgID = MSGID_ASN1_ELEMENT_SET_NO_LENGTH;
String message = getMessage(msgID);
throw new ASN1Exception(msgID, message);
}
byte firstLengthByte = encodedElements[startPos++];
int length = (byte) (firstLengthByte & 0x7F);
if (length != firstLengthByte)
{
int numLengthBytes = length;
if (numLengthBytes > 4)
{
int msgID = MSGID_ASN1_ELEMENT_SET_INVALID_NUM_LENGTH_BYTES;
String message = getMessage(msgID, numLengthBytes);
throw new ASN1Exception(msgID, message);
}
if ((startPos + numLengthBytes) > encodedElements.length)
{
int msgID = MSGID_ASN1_ELEMENT_SET_TRUNCATED_LENGTH;
String message = getMessage(msgID, numLengthBytes);
throw new ASN1Exception(msgID, message);
}
length = 0x00;
for (int i=0; i < numLengthBytes; i++)
{
length = (length << 8) | (encodedElements[startPos++] & 0xFF);
}
}
// Make sure that there are at least enough bytes to hold the value.
if ((startPos + length) > encodedElements.length)
{
int msgID = MSGID_ASN1_ELEMENT_SET_TRUNCATED_VALUE;
String message = getMessage(msgID, length,
(startPos+length-encodedElements.length));
throw new ASN1Exception(msgID, message);
}
// Create the element and add it to the list.
byte[] value = new byte[length];
System.arraycopy(encodedElements, startPos, value, 0, length);
elements.add(new ASN1Element(type, value));
startPos += length;
}
return elements;
}
/**
* Retrieves the name of the protocol associated with this protocol element.
*
* @return The name of the protocol associated with this protocol element.
*/
public String getProtocolElementName()
{
assert debugEnter(CLASS_NAME, "getProtocolElementName");
return "ASN.1";
}
/**
* Indicates whether the provided object is equal to this ASN.1 element.
*
* @param o The object for which to make the determination.
*
* @return true if the provided object is an ASN.1 element that
* is equal to this element, or false if not. The
* object will be considered equal if it is an ASN.1 element (or a
* subclass) with the same type and encoded value.
*/
public boolean equals(Object o)
{
assert debugEnter(CLASS_NAME, "equals", String.valueOf(o));
if (this == o)
{
return true;
}
if ((o == null) || (! (o instanceof ASN1Element)))
{
return false;
}
ASN1Element e = (ASN1Element) o;
return ((type == e.type) && Arrays.equals(value, e.value));
}
/**
* Indicates whether the provided ASN.1 element has a value that is equal to
* the value of this ASN.1 element.
*
* @param element The ASN.1 element whose value should be compared against
* the value of this element.
*
* @return true if the values of the elements are equal, or
* false if not.
*/
public boolean equalsIgnoreType(ASN1Element element)
{
assert debugEnter(CLASS_NAME, "equalsIgnoreType",
String.valueOf(element));
return Arrays.equals(value, element.value);
}
/**
* Indicates whether the provided byte string has a value that is equal to
* the value of this ASN.1 element.
*
* @param byteString The byte string whose value should be compared against
* the value of this element.
*
* @return true if the values are equal, or false
* if not.
*/
public boolean equalsIgnoreType(ByteString byteString)
{
assert debugEnter(CLASS_NAME, "equalsIgnoreType",
String.valueOf(byteString));
return Arrays.equals(value, byteString.value());
}
/**
* Indicates whether the provided ASN.1 element is equal to this element.
*
* @param e The ASN.1 element for which to make the determination.
*
* @return true ASN.1 element is equal to this element,
* or false if not. The elements will be considered
* equal if they have the same type and encoded value.
*/
public boolean equalsElement(ASN1Element e)
{
assert debugEnter(CLASS_NAME, "equalsElement", String.valueOf(e));
if (this == e)
{
return true;
}
if (e == null)
{
return false;
}
return ((type == e.type) && Arrays.equals(value, e.value));
}
/**
* Retrieves the hash code for this ASN.1 element. It will be constructed
* from the sum of the type and up to the first twenty bytes of the value.
*
* @return The hash code for this ASN.1 element.
*/
public int hashCode()
{
assert debugEnter(CLASS_NAME, "hashCode");
int hashCode = type;
int length = Math.min(20, value.length);
for (int i=0; i < length; i++)
{
hashCode += value[i];
}
return hashCode;
}
/**
* Retrieves a string representation of this ASN.1 element.
*
* @return A string representation of this ASN.1 element.
*/
public String toString()
{
assert debugEnter(CLASS_NAME, "toString");
StringBuilder buffer = new StringBuilder();
toString(buffer);
return buffer.toString();
}
/**
* Appends a string representation of this ASN.1 element to the provided
* buffer.
*
* @param buffer The buffer to which the information should be appended.
*/
public void toString(StringBuilder buffer)
{
assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
buffer.append("ASN1Element(type=");
buffer.append(byteToHex(type));
buffer.append(", length=");
buffer.append(value.length);
buffer.append(")");
}
/**
* Appends a string representation of this protocol element to the provided
* buffer.
*
* @param buffer The buffer into which the string representation should be
* written.
* @param indent The number of spaces that should be used to indent the
* resulting string representation.
*/
public void toString(StringBuilder buffer, int indent)
{
assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder",
String.valueOf(indent));
StringBuilder indentBuf = new StringBuilder(indent);
for (int i=0 ; i < indent; i++)
{
indentBuf.append(' ');
}
buffer.append(indentBuf);
buffer.append("ASN.1 Element");
buffer.append(EOL);
buffer.append(indentBuf);
buffer.append(" BER Type: ");
buffer.append(byteToHex(type));
buffer.append(EOL);
buffer.append(indentBuf);
buffer.append(" Value (");
buffer.append(value.length);
buffer.append(" bytes)");
buffer.append(EOL);
byteArrayToHexPlusAscii(buffer, value, indent+2);
}
}