package org.doomdark.uuid;
import java.io.Serializable;
/* JUG Java Uuid Generator
*
* Copyright (c) 2002 Tatu Saloranta, tatu.saloranta@iki.fi
*
* You can redistribute this work and/or modify it under the terms of
* LGPL (Lesser General Public License), as published by
* Free Software Foundation (http://www.fsf.org). No warranty is
* implied. See LICENSE for details about licensing.
*/
/**
* EthernetAddress encapsulates the 6-byte Mac address defined in
* IEEE 802.1 standard.
*/
public class EthernetAddress
implements Serializable, Cloneable, Comparable
{
private final static String kHexChars = "0123456789abcdefABCDEF";
private final byte[] mAddress = new byte[6];
/* *** Creation methods *** */
/**
* String constructor; given a 'standard' ethernet MAC address string
* (like '00:C0:F0:3D:5B:7C'), constructs an EthernetAddress instance.
*
* Note that string is case-insensitive, and also that leading zeroes
* may be omitted. Thus '00:C0:F0:3D:5B:7C' and '0:c0:f0:3d:5b:7c' are
* equivalent, and a 'null' address could be passed as ':::::' as well
* as '00:00:00:00:00:00' (or any other intermediate combination).
*
* @param addrStr String representation of the ethernet address
*/
public EthernetAddress(String addrStr)
throws NumberFormatException
{
byte[] addr = mAddress;
int len = addrStr.length();
/* Ugh. Although the most logical format would be the 17-char one
* (12 hex digits separated by colons), apparently leading zeroes
* can be omitted. Thus... Can't just check string length. :-/
*/
for (int i = 0, j = 0; j < 6; ++j) {
if (i >= len) {
// Is valid if this would have been the last byte:
if (j == 5) {
addr[5] = (byte) 0;
break;
}
throw new NumberFormatException("Incomplete ethernet address (missing one or more digits");
}
char c = addrStr.charAt(i);
++i;
int value;
// The whole number may be omitted (if it was zero):
if (c == ':') {
value = 0;
} else {
// No, got at least one digit?
if (c >= '0' && c <= '9') {
value = (c - '0');
} else if (c >= 'a' && c <= 'f') {
value = (c - 'a' + 10);
} else if (c >= 'A' && c <= 'F') {
value = (c - 'A' + 10);
} else {
throw new NumberFormatException("Non-hex character '"+c+"'");
}
// How about another one?
if (i < len) {
c = addrStr.charAt(i);
++i;
if (c != ':') {
value = (value << 4);
if (c >= '0' && c <= '9') {
value |= (c - '0');
} else if (c >= 'a' && c <= 'f') {
value |= (c - 'a' + 10);
} else if (c >= 'A' && c <= 'F') {
value |= (c - 'A' + 10);
} else {
throw new NumberFormatException("Non-hex character '"+c+"'");
}
}
}
}
addr[j] = (byte) value;
if (c != ':') {
if (i < len) {
if (addrStr.charAt(i) != ':') {
throw new NumberFormatException("Expected ':', got ('"
+ addrStr.charAt(i)
+"')");
}
++i;
} else if (j < 5) {
throw new NumberFormatException("Incomplete ethernet address (missing one or more digits");
}
}
}
}
/**
* Binary constructor that constructs an instance given the 6 byte
* (48-bit) address. Useful if an address is saved in binary format
* (for saving space for example).
*/
public EthernetAddress(byte [] addr)
throws NumberFormatException
{
if (addr.length != 6) {
throw new NumberFormatException("Ethernet address has to consist of 6 bytes");
}
for (int i = 0; i < 6; ++i) {
mAddress[i] = addr[i];
}
}
/**
* Another binary constructor; constructs an instance from the given
* long argument; the lowest 6 bytes contain the address.
*
* @param addr long that contains the MAC address in 6 least significant
* bytes.
*/
public EthernetAddress(long addr)
{
for (int i = 0; i < 6; ++i) {
mAddress[5 - i] = (byte) addr;
addr >>>= 8;
}
}
/**
* Package internal constructor for creating an 'empty' ethernet address
*/
EthernetAddress()
{
byte z = (byte) 0;
for (int i = 0; i < 6; ++i) {
mAddress[i] = z;
}
}
/**
* Default cloning behaviour (bitwise copy) is just fine...
*/
public Object clone()
{
try {
return super.clone();
} catch (CloneNotSupportedException e) {
// shouldn't happen
return null;
}
}
/* *** Comparison methods *** */
public boolean equals(Object o)
{
if (!(o instanceof EthernetAddress)) {
return false;
}
byte[] otherAddress = ((EthernetAddress) o).mAddress;
byte[] thisAddress = mAddress;
for (int i = 0; i < 6; ++i) {
if (otherAddress[i] != thisAddress[i]) {
return false;
}
}
return true;
}
/**
* Method that compares this EthernetAddress to one passed in as
* argument. Comparison is done simply by comparing individual
* address bytes in the order.
*
* @return -1 if this EthernetAddress should be sorted before the
* one passed in, 1 if after and 0 if they are equal.
*/
public int compareTo(Object o)
{
byte[] thatA = ((EthernetAddress) o).mAddress;
byte[] thisA = mAddress;
for (int i = 0; i < 6; ++i) {
int cmp = (((int) thisA[i]) & 0xFF)
- (((int) thatA[i]) & 0xFF);
if (cmp != 0) {
return cmp;
}
}
return 0;
}
/* *** Type conversion *** */
/**
* Returns the canonical string representation of this ethernet address.
* Canonical means that all characters are lower-case and string length
* is always 17 characters (ie. leading zeroes are not omitted).
*
* @return Canonical string representation of this ethernet address.
*/
public String toString()
{
/* Let's not cache the output here (unlike with UUID), assuming
* this won't be called as often:
*/
StringBuffer b = new StringBuffer(17);
byte[] addr = mAddress;
for (int i = 0; i < 6; ++i) {
if (i > 0) {
b.append(":");
}
int hex = addr[i] & 0xFF;
b.append(kHexChars.charAt(hex >> 4));
b.append(kHexChars.charAt(hex & 0x0f));
}
return b.toString();
}
/**
* Returns 6 byte byte array that contains the binary representation
* of this ethernet address; byte 0 is the most significant byte
* (and so forth)
*
* @return 6 byte byte array that contains the binary representation
*/
public byte[] asByteArray()
{
byte[] result = new byte[6];
toByteArray(result);
return result;
}
/**
* Synonym to 'asByteArray()'
*
* @return 6 byte byte array that contains the binary representation
*/
public byte[] toByteArray() { return asByteArray(); }
public void toByteArray(byte[] array) { toByteArray(array, 0); }
public void toByteArray(byte[] array, int pos)
{
for (int i = 0; i < 6; ++i) {
array[pos+i] = mAddress[i];
}
}
public long toLong()
{
/* Damn Java's having signed bytes sucks... they are NEVER what
* anyone needs; and sign extension work-arounds are slow.
*/
byte[] addr = mAddress;
int hi = (((int) addr[0]) & 0xFF) << 8
| (((int) addr[1]) & 0xFF);
int lo = ((int) addr[2]) & 0xFF;
for (int i = 3; i < 6; ++i) {
lo = (lo << 8) | (((int) addr[i]) & 0xFF);
}
return ((long) hi) << 32 | (((long) lo) & 0xFFFFFFFFL);
}
/**
* Constructs a new EthernetAddress given the byte array that contains
* binary representation of the address.
*
* Note that calling this method returns the same result as would
* using the matching constructor.
*
* @param addr Binary representation of the ethernet address
*
* @throws NumberFormatException if addr is invalid (less or more than
* 6 bytes in array)
*/
public static EthernetAddress valueOf(byte[] addr)
throws NumberFormatException
{
return new EthernetAddress(addr);
}
/**
* Constructs a new EthernetAddress given the byte array that contains
* binary representation of the address.
*
* Note that calling this method returns the same result as would
* using the matching constructor.
*
* @param addr Binary representation of the ethernet address
*
* @throws NumberFormatException if addr is invalid (less or more than
* 6 ints in array)
*/
public static EthernetAddress valueOf(int[] addr)
throws NumberFormatException
{
byte[] bAddr = new byte[addr.length];
for (int i = 0; i < addr.length; ++i) {
bAddr[i] = (byte) addr[i];
}
return new EthernetAddress(bAddr);
}
/**
* Constructs a new EthernetAddress given a string representation of
* the ethernet address.
*
* Note that calling this method returns the same result as would
* using the matching constructor.
*
* @param addrStr String representation of the ethernet address
*
* @throws NumberFormatException if addr representation is invalid
*/
public static EthernetAddress valueOf(String addrStr)
throws NumberFormatException
{
return new EthernetAddress(addrStr);
}
/**
* Constructs a new EthernetAddress given the long int value (64-bit)
* representation of the ethernet address (of which 48 LSB contain
* the definition)
*
* Note that calling this method returns the same result as would
* using the matching constructor.
*
* @param addr Long int representation of the ethernet address
*/
public static EthernetAddress valueOf(long addr)
{
return new EthernetAddress(addr);
}
public static void main(String[] args)
{
System.out.println("EthernetAddress.main, test:");
System.out.println("---------------------------");
int len;
long rnd = 0;
if (args == null || args.length == 0) {
System.out.println("[no address passed, using a random address]");
len = 1;
rnd = System.currentTimeMillis()
^ (long) (Math.random() * (double) 0x100000000L);
args = new String[] { new EthernetAddress(rnd).toString() };
}
for (int i = 0; i < args.length; ++i) {
String s = args[i];
System.out.println("Address '"+s+"':");
try {
EthernetAddress a = EthernetAddress.valueOf(s);
System.err.println(" Ok, comes out as '"+a.toString()+"'");
// EthernetAddress <-> long
System.err.print(" Converting to long, result = ");
long l = a.toLong();
System.err.println(""+Long.toHexString(l));
System.err.print(" Creating address from long, are equal: ");
EthernetAddress b = EthernetAddress.valueOf(l);
if (b.equals(a)) {
System.err.println("yes (OK)");
} else {
System.err.println("no (FAIL)");
break;
}
// EthernetAddress <-> byte[]
System.err.println(" Converting to byte array.");
byte[] ba = a.asByteArray();
System.err.print(" Creating address from byte[], are equal: ");
b = EthernetAddress.valueOf(ba);
if (b.equals(a)) {
System.err.println("yes (OK)");
} else {
System.err.println("no (FAIL)");
break;
}
} catch (NumberFormatException e) {
System.out.println(" Fail: "+e.toString());
}
}
System.out.println("---------------------------");
System.out.println("Done.");
}
}