/*******************************************************************************
* 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;
/**
* Defines the SNMP object identifier class for naming variables. An object
* identifier is a sequence of numbers that correspond to branches in the
* Management Information Base (MIB). Each vendor is free to define their own
* branch of the tree. The SnmpObjectId class provides an interface for naming
* those tree instances.
*
* @author <a href="mailto:weave@oculan.com">Brian Weaver </a>
* @author <A HREF="mailto:naz@personalgenie.com">Nazario Irizarry, Jr. </A>
*
*/
public class SnmpObjectId extends Object implements SnmpSyntax, Cloneable, Serializable {
/**
* Deifnes the version of the serialization format.
*
*/
static final long serialVersionUID = 2633631219460364065L;
/**
* The array of object identifiers minimum length for a valid object id is 2
* (.0.0)
*/
private int[] m_data;
/**
* Converts a textual object identifier to an array of integer values.
*
* @param idstr
* An object identifier string
*
* @return Returns an array of integers converted from the string. If an
* error occurs then a null is returned.
*/
private static int[] convert(String idstr) {
//
// ids is the counter
// idArray is the array of characters
//
int numIds = 0;
char[] idArray = idstr.toCharArray();
//
// if the length is equal to zero then
// the number of ids is equal to zero :)
//
if (idArray.length == 0) {
int[] tmp = new int[2];
tmp[0] = tmp[1] = 0;
return tmp;
}
//
// if the object string does not start
// with a dot then we need to increment
// the id count
//
if (idArray[0] != '.')
numIds++;
//
// count the number of objects
//
int x = 0;
while (x < idArray.length) {
if (idArray[x++] == '.')
++numIds;
}
//
// check for bad strings
//
if (numIds == 0) {
int[] tmp = new int[2];
tmp[0] = tmp[1] = 0;
return tmp;
}
//
// get an array to store objects into
//
int[] objects = new int[numIds];
int objectsNdx = 0; // reset the ids counter
int idArrayNdx = 0; // set the objects ndx counter
//
// if the string begins with a dot(.) then
// increment the ndx
//
if (idArray[0] == '.')
++idArrayNdx;
//
// create an object id variable and
// set it equal to zero
//
int oid = 0;
while (idArrayNdx < idArray.length) {
//
// if there is a dot(.) then
// store the id
//
if (idArray[idArrayNdx] == '.') {
objects[objectsNdx++] = oid;
oid = 0;
} else {
//
// multiply the object id by 10
//
oid *= 10;
switch (idArray[idArrayNdx]) {
case '1':
oid += 1;
break;
case '2':
oid += 2;
break;
case '3':
oid += 3;
break;
case '4':
oid += 4;
break;
case '5':
oid += 5;
break;
case '6':
oid += 6;
break;
case '7':
oid += 7;
break;
case '8':
oid += 8;
break;
case '9':
oid += 9;
break;
}
}
++idArrayNdx;
}
//
// save the last object id
//
objects[objectsNdx++] = oid;
return objects;
}
/**
* Defines the SNMP SMI type for this particular object.
*/
public static final byte ASNTYPE = SnmpSMI.SMI_OBJECTID;
/**
* Creates a default empty object identifier.
*
*/
public SnmpObjectId() {
m_data = new int[2];
m_data[0] = m_data[1] = 0;
}
/**
* Creates an object identifier from the passed array of identifiers. If the
* passed argument is null then a default object id (.0.0) is created for
* the instance.
*
* @param data
* The array of object identifiers
*
*/
public SnmpObjectId(int[] data) {
this();
if (data != null) {
m_data = new int[data.length];
System.arraycopy(data, 0, m_data, 0, data.length);
}
}
/**
* Creates a duplicate object. The passed object identifier is copied into
* the newly created object.
*
* @param second
* The object to copy
*
*/
public SnmpObjectId(SnmpObjectId second) {
this(second.m_data);
}
/**
* Creates an object identifier from the pased dotted decimal object
* identifier string. The string is converted to the internal
* representation. If the conversion fails then a default (.0.0) object
* identifier is assigned to the object.
*
* @param strOid
* The dotted decimal object identifier string
*
*/
public SnmpObjectId(String strOid) {
m_data = convert(strOid);
if (m_data == null) {
m_data = new int[2];
m_data[0] = m_data[1] = 0;
}
}
/**
* Gets the number of object identifiers in the object.
*
* @return Returns the number of object identifiers
*
*/
public int getLength() {
return m_data.length;
}
/**
* Returns the value of the last object identifier component value
*/
public int getLastIdentifier() {
return m_data[m_data.length-1];
}
/**
* Gets the array of object identifiers from the object. The instance is
* returned as a reference. The caller should not make any modifications to
* the returned list.
*
* @return Returns the list of identifiers
*/
public int[] getIdentifiers() {
return m_data;
}
/**
* Sets the object to the passed object identifier
*
* @param data
* The new object identifier
*
*/
public void setIdentifiers(int[] data) {
if (data != null) {
m_data = new int[data.length];
System.arraycopy(data, 0, m_data, 0, data.length);
} else {
m_data = new int[2];
m_data[0] = m_data[1] = 0;
}
}
/**
* Sets the object to the passed dotted decimal object identifier string.
*
* @param strOid
* The dotted decimal object identifier.
*
*/
public void setIdentifiers(String strOid) {
m_data = null;
if (strOid != null) {
m_data = convert(strOid);
}
if (m_data == null) {
m_data = new int[2];
m_data[0] = m_data[1] = 0;
}
}
/**
* Appends the specified identifiers to the current object.
*
* @param ids
* The array of identifiers to append
*
*/
public void append(int[] ids) {
if (ids != null && ids.length != 0) {
int[] tmp = new int[m_data.length + ids.length];
System.arraycopy(m_data, 0, tmp, 0, m_data.length);
System.arraycopy(ids, 0, tmp, m_data.length, ids.length);
m_data = tmp;
}
}
/**
* Converts the passed string to an object identifier and appends them to
* the current object.
*
* @param strOids
* The dotted decimal identifiers to append
*
*/
public void append(String strOids) {
int[] tmp = convert(strOids);
append(tmp);
}
/**
* Appends the passed SnmpObjectId object to self.
*
* @param second
* The object to append to self
*
*/
public void append(SnmpObjectId second) {
append(second.m_data);
}
/**
* Prepends the passed set of identifiers to the front of the object.
*
* @param ids
* The list of identifiers
*
*/
public void prepend(int[] ids) {
if (ids != null && ids.length != 0) {
int[] tmp = new int[m_data.length + ids.length];
System.arraycopy(ids, 0, tmp, 0, ids.length);
System.arraycopy(m_data, 0, tmp, ids.length, m_data.length);
m_data = tmp;
}
}
/**
* Converts the passed string to an object identifier and prepends them to
* the current object.
*
* @param strOids
* The dotted decimal identifiers to prepend
*
*/
public void prepend(String strOids) {
int[] tmp = convert(strOids);
prepend(tmp);
}
/**
* Prepends the passed SnmpObjectId object to self.
*
* @param second
* The object to prepend to self
*
*/
public void prepend(SnmpObjectId second) {
prepend(second.m_data);
}
/**
* Lexigraphically compares the object identifer to the array of
* identifiers. If the object is lexigraphically less than ids a negative
* number is returned. A positive number is returned if self is greater than
* the passed identifers and a zero is returned if they are equal. The
* length of the identifiers do not have to be equal.
*
* @param ids
* The array if identifier to compare
*
* @return Returns zero if the ids are equal. Less than zero if the object
* is less than 'ids' and greater than zero if the object is greater
* than 'ids'.
*
*/
public int compare(int[] ids) {
//
// compare A(self) to
// B(ids) irrelivant of length
//
int a, aNdx = 0;
int b, bNdx = 0;
int rc = 0;
//
// loop so long as either array index
// has not reached the end of it's array
//
// This solves the problem of one object id
// being longer than another
//
while (aNdx < m_data.length || bNdx < ids.length) {
//
// get the id from self. If the end of
// self has been reached then it will
// be equal to zero. If not then the
// id will be offset by 1
//
a = 0;
if (aNdx < m_data.length)
a = m_data[aNdx++] + 1;
//
// Just like 'a', only for the array ids.
//
b = 0;
if (bNdx < ids.length)
b = ids[bNdx++] + 1;
//
// compare
//
rc = a - b;
if (rc != 0)
break;
}
return rc;
}
/**
* Lexigraphically compares the object identifer to the array of
* identifiers. If the object is lexigraphically less than ids a negative
* number is returned. A positive number is returned if self is greater than
* the passed identifers and a zero is returned if they are equal. The
* length of the identifiers do not have to be equal.
*
* @param ids
* The array if identifier to compare.
* @param dist
* The maximum number of ids to compare.
*
* @return Returns zero if the ids are equal. Less than zero if the object
* is less than 'ids' and greater than zero if the object is greater
* than 'ids'.
*
*/
public int compare(int[] ids, int dist) {
//
// compare A(self) to
// B(ids) irrelivant of length
//
int a, aNdx = 0;
int b, bNdx = 0;
int rc = 0;
//
// loop so long as either array index
// has not reached the end of it's array
//
// This solves the problem of one object id
// being longer than another
//
while ((aNdx < m_data.length || bNdx < ids.length) && --dist >= 0) {
//
// get the id from self. If the end of
// self has been reached then it will
// be equal to zero. If not then the
// id will be offset by 1
//
a = 0;
if (aNdx < m_data.length)
a = m_data[aNdx++] + 1;
//
// Just like 'a', only for the array ids.
//
b = 0;
if (bNdx < ids.length)
b = ids[bNdx++] + 1;
//
// compare
//
rc = a - b;
if (rc != 0)
break;
}
return rc;
}
/**
* Lexigraphically compares the object identifer to the passed object
* identifer. If the object is lexigraphically less than 'cmp' a negative
* number is returned. A positive number is returned if self is greater than
* the passed identifer and a zero is returned if they are equal. The length
* of the identifiers do not have to be equal.
*
* @param cmp
* The object identifier to compare
*
* @return Returns zero if the ids are equal. Less than zero if the object
* is less than 'cmp' and greater than zero if the object is greater
* than 'cmp'.
*
*/
public int compare(SnmpObjectId cmp) {
return compare(cmp.m_data);
}
/**
* <P>
* Compares the passed object identifier against self to determine if self
* is the root of the passed object. If the passed object is in the same
* root tree as self then a true value is returned. Otherwise a false value
* is returned from the object.
* </P>
*
* @param leaf
* The object to be tested
*
* @return True if leaf is in the tree.
*/
public boolean isRootOf(SnmpObjectId leaf) {
return (compare(leaf.m_data, m_data.length) == 0);
}
/**
* Test for equality. Returns true if 'o' is an instance of an SnmpObjectId
* and is equal to self.
*
* @param o
* The object to be tested for equality.
*
* @return True if the object is an SnmpObjectId and is equal to self. False
* otherwise.
*
*/
public boolean equals(Object o) {
if (o instanceof SnmpObjectId) {
return (compare(((SnmpObjectId) o).m_data) == 0);
} else if (o instanceof String) {
return (compare(convert((String) o)) == 0);
} else if (o instanceof int[]) {
return (compare((int[]) o) == 0);
}
return false;
}
/**
* Converts the object identifier to a dotted decimal string representation.
*
* @return Returns the dotted decimal object id string.
*
*/
public String toString() {
//
// assume two digit ids, plus one dot(.) per id.
//
StringBuffer buf = new StringBuffer();
buf.ensureCapacity(m_data.length * 3);
for (int x = 0; x < m_data.length; x++) {
buf.append('.');
if (m_data[x] >= 0) {
buf.append(m_data[x]); // Same as String.valueOf(int)
} else {
long oid = (long) m_data[x] & 0xffffffffL;
buf.append(oid); // Same as String.valueOf(long)
}
}
return buf.toString();
}
/**
* Returns a computed hash code value for the object identifier. If the
* value of the object identifier is changed through any method the hash
* value for the identifer will change. Care must be exercised by developers
* to ensure that ObjectIds do not change when inserted into managers that
* track object by their hash value.
*
* @return The hash code for the object.
*
*
* @since 1.8
*/
public int hashCode() {
int hash = 0;
for (int i = 0; i < m_data.length; i++) {
hash = (hash * 31) + m_data[i];
}
return hash;
}
/**
* Used to get the ASN.1 type for this particular object.
*/
public byte typeId() {
return ASNTYPE;
}
/**
* Encodes the ASN.1 object identifier 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 {
return encoder.buildObjectId(buf, offset, typeId(), m_data);
}
/**
* Decodes the ASN.1 object identifer 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.parseObjectId(buf, offset);
if (((Byte) rVals[1]).byteValue() != typeId())
throw new AsnDecodingException("Invalid ASN.1 type");
m_data = (int[]) rVals[2];
return ((Integer) rVals[0]).intValue();
}
/**
* Serves the same purpose as the method clone().
*
* @return A new copy of self.
*
*/
public SnmpSyntax duplicate() {
return new SnmpObjectId(this);
}
//
// override clone() interface to support
// Cloneable interface
//
/**
* Implements the cloneable interface.
*
* @return Returns a new SnmpObjectId copy of self.
*
*/
public Object clone() {
return new SnmpObjectId(this);
}
}