/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.utils;
import java.io.*;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.GregorianCalendar;
import java.util.Random;
import org.eclipse.core.runtime.Assert;
public class UniversalUniqueIdentifier implements java.io.Serializable {
/**
* All serializable objects should have a stable serialVersionUID
*/
private static final long serialVersionUID= 1L;
/* INSTANCE FIELDS =============================================== */
private byte[] fBits= new byte[BYTES_SIZE];
/* NON-FINAL PRIVATE STATIC FIELDS =============================== */
private static BigInteger fgPreviousClockValue;
private static int fgClockAdjustment= 0;
private static int fgClockSequence= -1;
private static byte[] nodeAddress;
static {
nodeAddress= computeNodeAddress();
}
/* PRIVATE STATIC FINAL FIELDS =================================== */
private static Random fgRandomNumberGenerator= new Random();
/* PUBLIC STATIC FINAL FIELDS ==================================== */
public static final int BYTES_SIZE= 16;
public static final byte[] UNDEFINED_UUID_BYTES= new byte[16];
public static final int MAX_CLOCK_SEQUENCE= 0x4000;
public static final int MAX_CLOCK_ADJUSTMENT= 0x7FFF;
public static final int TIME_FIELD_START= 0;
public static final int TIME_FIELD_STOP= 6;
public static final int TIME_HIGH_AND_VERSION= 7;
public static final int CLOCK_SEQUENCE_HIGH_AND_RESERVED= 8;
public static final int CLOCK_SEQUENCE_LOW= 9;
public static final int NODE_ADDRESS_START= 10;
public static final int NODE_ADDRESS_BYTE_SIZE= 6;
public static final int BYTE_MASK= 0xFF;
public static final int HIGH_NIBBLE_MASK= 0xF0;
public static final int LOW_NIBBLE_MASK= 0x0F;
public static final int SHIFT_NIBBLE= 4;
public static final int ShiftByte= 8;
/**
* UniversalUniqueIdentifier default constructor returns a new instance that has been
* initialized to a unique value.
*/
public UniversalUniqueIdentifier() {
this.setVersion(1);
this.setVariant(1);
this.setTimeValues();
this.setNode(getNodeAddress());
}
/**
* Constructor that accepts the bytes to use for the instance. The format of the
* byte array is compatible with the <code>toBytes()</code> method.
*
* <p>
* The constructor returns the undefined uuid if the byte array is invalid.
*
* @see #toBytes()
* @see #BYTES_SIZE
*/
public UniversalUniqueIdentifier(byte[] byteValue) {
fBits= new byte[BYTES_SIZE];
if (byteValue.length >= BYTES_SIZE)
System.arraycopy(byteValue, 0, fBits, 0, BYTES_SIZE);
}
private void appendByteString(StringBuffer buffer, byte value) {
String hexString;
if (value < 0)
hexString= Integer.toHexString(256 + value);
else
hexString= Integer.toHexString(value);
if (hexString.length() == 1)
buffer.append("0"); //$NON-NLS-1$
buffer.append(hexString);
}
private static BigInteger clockValueNow() {
GregorianCalendar now= new GregorianCalendar();
BigInteger nowMillis= BigInteger.valueOf(now.getTime().getTime());
BigInteger baseMillis= BigInteger.valueOf(now.getGregorianChange().getTime());
return (nowMillis.subtract(baseMillis).multiply(BigInteger.valueOf(10000L)));
}
/**
* Simply increases the visibility of <code>Object</code>'s clone. Otherwise, no new behaviour.
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
Assert.isTrue(false, Messages.utils_clone);
return null;
}
}
public static int compareTime(byte[] fBits1, byte[] fBits2) {
for (int i= TIME_FIELD_STOP; i >= 0; i--)
if (fBits1[i] != fBits2[i])
return (0xFF & fBits1[i]) - (0xFF & fBits2[i]);
return 0;
}
/**
* Answers the node address attempting to mask the IP address of this machine.
*
* @return byte[] the node address
*/
private static byte[] computeNodeAddress() {
byte[] address= new byte[NODE_ADDRESS_BYTE_SIZE];
// Seed the secure randomizer with some oft-varying inputs
int thread= Thread.currentThread().hashCode();
long time= System.currentTimeMillis();
int objectId= System.identityHashCode(new String());
ByteArrayOutputStream byteOut= new ByteArrayOutputStream();
DataOutputStream out= new DataOutputStream(byteOut);
byte[] ipAddress= getIPAddress();
try {
if (ipAddress != null)
out.write(ipAddress);
out.write(thread);
out.writeLong(time);
out.write(objectId);
out.close();
} catch (IOException exc) {
//ignore the failure, we're just trying to come up with a random seed
}
byte[] rand= byteOut.toByteArray();
SecureRandom randomizer= new SecureRandom(rand);
randomizer.nextBytes(address);
// set the MSB of the first octet to 1 to distinguish from IEEE node addresses
address[0]= (byte)(address[0] | (byte)0x80);
return address;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof UniversalUniqueIdentifier))
return false;
byte[] other= ((UniversalUniqueIdentifier)obj).fBits;
if (fBits == other)
return true;
if (fBits.length != other.length)
return false;
for (int i= 0; i < fBits.length; i++) {
if (fBits[i] != other[i])
return false;
}
return true;
}
/**
* Answers the IP address of the local machine using the Java API class <code>InetAddress</code>
* .
*
* @return byte[] the network address in network order
* @see java.net.InetAddress#getLocalHost()
* @see java.net.InetAddress#getAddress()
*/
protected static byte[] getIPAddress() {
try {
return InetAddress.getLocalHost().getAddress();
} catch (UnknownHostException e) {
//valid for this to be thrown be a machine with no IP connection
//It is VERY important NOT to throw this exception
return null;
}
}
private static byte[] getNodeAddress() {
return nodeAddress;
}
public int hashCode() {
return fBits[0] + fBits[3] + fBits[7] + fBits[11] + fBits[15];
}
private static int nextClockSequence() {
if (fgClockSequence == -1)
fgClockSequence= (int)(fgRandomNumberGenerator.nextDouble() * MAX_CLOCK_SEQUENCE);
fgClockSequence= (fgClockSequence + 1) % MAX_CLOCK_SEQUENCE;
return fgClockSequence;
}
private static BigInteger nextTimestamp() {
BigInteger timestamp= clockValueNow();
int timestampComparison;
timestampComparison= timestamp.compareTo(fgPreviousClockValue);
if (timestampComparison == 0) {
if (fgClockAdjustment == MAX_CLOCK_ADJUSTMENT) {
while (timestamp.compareTo(fgPreviousClockValue) == 0)
timestamp= clockValueNow();
timestamp= nextTimestamp();
} else
fgClockAdjustment++;
} else {
fgClockAdjustment= 0;
if (timestampComparison < 0)
nextClockSequence();
}
return timestamp;
}
private void setClockSequence(int clockSeq) {
int clockSeqHigh= (clockSeq >>> ShiftByte) & LOW_NIBBLE_MASK;
int reserved= fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & HIGH_NIBBLE_MASK;
fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED]= (byte)(reserved | clockSeqHigh);
fBits[CLOCK_SEQUENCE_LOW]= (byte)(clockSeq & BYTE_MASK);
}
protected void setNode(byte[] bytes) {
for (int index= 0; index < NODE_ADDRESS_BYTE_SIZE; index++)
fBits[index + NODE_ADDRESS_START]= bytes[index];
}
private void setTimestamp(BigInteger timestamp) {
BigInteger value= timestamp;
BigInteger bigByte= BigInteger.valueOf(256L);
BigInteger[] results;
int version;
int timeHigh;
for (int index= TIME_FIELD_START; index < TIME_FIELD_STOP; index++) {
results= value.divideAndRemainder(bigByte);
value= results[0];
fBits[index]= (byte)results[1].intValue();
}
version= fBits[TIME_HIGH_AND_VERSION] & HIGH_NIBBLE_MASK;
timeHigh= value.intValue() & LOW_NIBBLE_MASK;
fBits[TIME_HIGH_AND_VERSION]= (byte)(timeHigh | version);
}
protected synchronized void setTimeValues() {
this.setTimestamp(timestamp());
this.setClockSequence(fgClockSequence);
}
protected int setVariant(int variantIdentifier) {
int clockSeqHigh= fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & LOW_NIBBLE_MASK;
int variant= variantIdentifier & LOW_NIBBLE_MASK;
fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED]= (byte)((variant << SHIFT_NIBBLE) | clockSeqHigh);
return (variant);
}
protected void setVersion(int versionIdentifier) {
int timeHigh= fBits[TIME_HIGH_AND_VERSION] & LOW_NIBBLE_MASK;
int version= versionIdentifier & LOW_NIBBLE_MASK;
fBits[TIME_HIGH_AND_VERSION]= (byte)(timeHigh | (version << SHIFT_NIBBLE));
}
private static BigInteger timestamp() {
BigInteger timestamp;
if (fgPreviousClockValue == null) {
fgClockAdjustment= 0;
nextClockSequence();
timestamp= clockValueNow();
} else
timestamp= nextTimestamp();
fgPreviousClockValue= timestamp;
return fgClockAdjustment == 0 ? timestamp : timestamp.add(BigInteger.valueOf(fgClockAdjustment));
}
/**
* This representation is compatible with the (byte[]) constructor.
*
* @see #UniversalUniqueIdentifier(byte[])
*/
public byte[] toBytes() {
byte[] result= new byte[fBits.length];
System.arraycopy(fBits, 0, result, 0, fBits.length);
return result;
}
public String toString() {
StringBuffer buffer= new StringBuffer();
for (int i= 0; i < fBits.length; i++)
appendByteString(buffer, fBits[i]);
return buffer.toString();
}
public String toStringAsBytes() {
String result= "{"; //$NON-NLS-1$
for (int i= 0; i < fBits.length; i++) {
result+= fBits[i];
if (i < fBits.length + 1)
result+= ","; //$NON-NLS-1$
}
return result + "}"; //$NON-NLS-1$
}
}