/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.protocols.snmp;
import java.io.Serializable;
import org.opennms.protocols.snmp.asn1.AsnDecodingException;
import org.opennms.protocols.snmp.asn1.AsnEncoder;
import org.opennms.protocols.snmp.asn1.AsnEncodingException;
/**
* Implements the ASN1.UNIVERSAL Octet String datatype. The string is a sequence
* of 8-bit octet data. The format of the 8-bit characters are defined by the
* application.
*
* @author <a href="mailto:weave@oculan.com>Brian Weaver </a>
*/
public class SnmpOctetString extends Object implements SnmpSyntax, Cloneable, Serializable {
/**
* Required to allow evolution of serialization format.
*/
static final long serialVersionUID = 1848780739976444105L; // generated by
// serialver
// tool
/**
* The actual octet string data (UTF-8)
*
*/
private byte[] m_data;
/**
* The ASN.1 value for the OCTET STRING type.
*
*/
public static final byte ASNTYPE = SnmpSMI.SMI_STRING;
/**
* This can be used by a derived class to <em>force</em> the data
* contained by the octet string. The data is not duplicated, only the
* reference to the array is stored. No validation of data is performed at
* all.
*
* @param data
* The new data buffer.
*/
protected void assumeString(byte[] data) {
m_data = data;
}
/**
* The default class constructor. Constructs an Octet String with a length
* of zero and no data.
*
*/
public SnmpOctetString() {
m_data = null;
}
/**
* Constructs an octet string with the inital value equal to data. The data
* is actually copied so changes to the data reference do not affect the
* Octet string object.
*
* @param data
* The data to be copied to self
*
*/
public SnmpOctetString(byte[] data) {
this();
if (data != null) {
m_data = new byte[data.length];
System.arraycopy(data, 0, m_data, 0, data.length);
}
}
/**
* Class copy constructor. Constructs and octet string object that is a
* duplicate of the object second.
*
* @param second
* The object to copy into self
*
*/
public SnmpOctetString(SnmpOctetString second) {
this(second.m_data);
}
/**
* Returns a reference to the internal object string. Changes to this byte
* array WILL affect the octet string object. These changes should not be
* made lightly.
*
* @return A reference to the internal byte array.
*/
public byte[] getString() {
return m_data;
}
/**
* Sets the internal string array so that it is identical to the passed
* array. The array is actually copied so that changes to data after the
* construction of the object are not reflected in the SnmpOctetString
* Object.
*
* @param data
* The new octet string data.
*
*/
public void setString(byte[] data) {
m_data = null;
if (data != null) {
m_data = new byte[data.length];
System.arraycopy(data, 0, m_data, 0, data.length);
}
}
/**
* Sets the internal octet string equal to the converted stirng via the
* method getBytes(). This may cause some data corruption since the
* conversion is platform specific.
*
* @param data
* The new octet string data.
*
* @see java.lang.String#getBytes()
*/
public void setString(String data) {
m_data = null;
if (data != null) {
m_data = data.getBytes();
}
}
/**
* Returns the internal length of the octet string. This method is favored
* over recovereing the length from the internal array. The method
* compensates for a null set of data and returns zero if the internal array
* is null.
*
* @return The length of the octet string.
*
*/
public int getLength() {
int len = 0;
if (m_data != null)
len = m_data.length;
return len;
}
/**
* Returns the ASN.1 type identifier for the Octet String.
*
* @return The ASN.1 identifier.
*
*/
public byte typeId() {
return ASNTYPE;
}
/**
* Encodes the ASN.1 octet string using the passed encoder and stores the
* results in the passed buffer. An exception is thrown if an error occurs
* with the encoding of the information.
*
* @param buf
* The buffer to write the encoded information.
* @param offset
* The offset to start writing information
* @param encoder
* The encoder object.
*
* @return The offset of the byte immediantly after the last encoded byte.
*
* @exception AsnEncodingException
* Thrown if the encoder finds an error in the buffer.
*/
public int encodeASN(byte[] buf, int offset, AsnEncoder encoder) throws AsnEncodingException {
if (m_data == null)
throw new AsnEncodingException("No data in octet string");
return encoder.buildString(buf, offset, typeId(), m_data);
}
/**
* Decodes the ASN.1 octet string from the passed buffer. If an error occurs
* during the decoding sequence then an AsnDecodingException is thrown by
* the method. The value is decoded using the AsnEncoder passed to the
* object.
*
* @param buf
* The encode buffer
* @param offset
* The offset byte to begin decoding
* @param encoder
* The decoder object.
*
* @return The index of the byte immediantly after the last decoded byte of
* information.
*
* @exception AsnDecodingException
* Thrown by the encoder if an error occurs trying to decode
* the data buffer.
*/
public int decodeASN(byte[] buf, int offset, AsnEncoder encoder) throws AsnDecodingException {
Object[] rVals = encoder.parseString(buf, offset);
if (((Byte) rVals[1]).byteValue() != typeId())
throw new AsnDecodingException("Invalid ASN.1 type");
m_data = (byte[]) rVals[2];
return ((Integer) rVals[0]).intValue();
}
/**
* Creates a duplicate copy of the object and returns it to the caller.
*
* @return A newly constructed copy of self
*
*/
public SnmpSyntax duplicate() {
return new SnmpOctetString(this);
}
/**
* Creates a duplicate copy of the object and returns it to the caller.
*
* @return A newly constructed copy of self
*
*/
public Object clone() {
return new SnmpOctetString(this);
}
/**
* Returns a string representation of the object. If the object contains
* non-printable characters then the contents are printed in hexidecimal.
*
*/
public String toString() {
//
// check for non-printable characters. If they
// exist then print the string out as hexidecimal
//
boolean asHex = false;
for (int i = 0; i < m_data.length; i++) {
byte b = m_data[i];
if ((b < 32 && b != 10 && b != 13) || b == 127) {
asHex = true;
break;
}
}
String rs = null;
if (asHex) {
//
// format the string for hex
//
StringBuffer b = new StringBuffer();
// b.append("SNMP Octet String [length = " + m_data.length + ", fmt
// = HEX] = [");
for (int i = 0; i < m_data.length; ++i) {
int x = (int) m_data[i] & 0xff;
if (x < 16)
b.append('0');
b.append(Integer.toString(x, 16).toUpperCase());
if (i < m_data.length - 1)
b.append(' ');
}
// b.append(']');
rs = b.toString();
} else {
//
// raw output
//
// rs = "SNMP Octet String [length = " + m_data.length + ", fmt =
// RAW] = [" + new String(m_data) + "]";
rs = new String(m_data);
}
return rs;
}
/**
* This method takes an SnmpOctetString and replaces any unprintable
* characters with ASCII period ('.') and returns the resulting character
* string. Special case in which the supplied SnmpOctetString consists of a
* single ASCII Null byte is also handled. In this special case an empty
* string is returned.
*
* NOTE: A character is considered unprintable if its decimal value falls
* outside of the range: 32 - 126.
*
* @param octetString
* SnmpOctetString from which to generate the String
*
* @return a Java String object created from the octet string's byte array.
*/
public static String toDisplayString(SnmpOctetString octetString) {
// TODO: Move this to a more common place
// Valid SnmpOctetString object
if (octetString == null) {
return null;
}
byte bytes[] = octetString.getString();
// Sanity check
if (bytes == null || bytes.length == 0) {
return null;
}
// Check for special case where byte array contains a single
// ASCII null character
if (bytes.length == 1 && bytes[0] == 0) {
return null;
}
// Replace all unprintable chars (chars outside of
// decimal range 32 - 126 inclusive) with an
// ASCII period char (decimal 46).
//
byte newBytes[] = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
newBytes[i] = Character.isISOControl((char)bytes[i]) ? (byte)'.' : bytes[i];
}
// Create string, trim any white-space and return
String result = new String(newBytes);
return result.trim();
}
// TODO: Move this to common base class
public static String toHexString(SnmpOctetString ostr) {
if (ostr == null) return null;
StringBuffer sbuf = new StringBuffer();
if (ostr != null && ostr.getLength() > 0) {
byte[] bytes = ostr.getString();
for (int i = 0; i < bytes.length; i++) {
sbuf.append(Integer.toHexString(((int) bytes[i] >> 4) & 0xf));
sbuf.append(Integer.toHexString((int) bytes[i] & 0xf));
}
}
String physAddr = sbuf.toString().trim();
return physAddr;
}
public boolean equals(Object obj) {
if (obj instanceof SnmpOctetString) {
SnmpOctetString str = (SnmpOctetString)obj;
if (m_data == null) return (str.m_data == null);
if (m_data.length != str.m_data.length) return false;
for(int i = 0; i < m_data.length; i++)
if (m_data[i] != str.m_data[i])
return false;
return true;
}
return false;
}
public int hashCode() {
return 0;
}
}