blob: 636893afaee44c72e5c8f2b6ac578f77b8c0c190 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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.equinox.internal.p2.artifact.repository.simple;
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, "Clone not supported");
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$
}
}