package org.simantics.datatypes.literal;
import java.util.UUID;
import org.simantics.databoard.Bindings;
import org.simantics.databoard.binding.Binding;
import org.simantics.databoard.util.Bean;
public class GUID extends Bean {
public static final Binding BINDING = Bindings.getBindingUnchecked(GUID.class);
public long mostSignificant;
public long leastSignificant;
public GUID(long mostSignificant, long leastSignificant) {
super(BINDING);
this.mostSignificant = mostSignificant;
this.leastSignificant = leastSignificant;
}
public static GUID invalid() {
return new GUID(0, 0);
}
public static GUID invalid2() {
return new GUID(0, 1);
}
public boolean isInvalid() {
return mostSignificant == 0 && leastSignificant == 0;
}
public static GUID random() {
UUID random = UUID.randomUUID();
return new GUID(random.getMostSignificantBits(), random.getLeastSignificantBits());
}
public String indexString() {
StringBuilder b = new StringBuilder();
b.append(Long.toHexString(mostSignificant));
b.append("_");
b.append(Long.toHexString(leastSignificant));
return b.toString();
}
public static GUID parseIndexString(String indexString) {
String[] parts = indexString.split("_");
if(parts.length != 2) throw new IllegalArgumentException();
Long mostSignificant = parseUnsignedLong(parts[0].toUpperCase(), 16);
Long leastSignificant = parseUnsignedLong(parts[1], 16);
return new GUID(mostSignificant, leastSignificant);
}
/**
* Parses the string argument as an unsigned {@code long} in the
* radix specified by the second argument. An unsigned integer
* maps the values usually associated with negative numbers to
* positive numbers larger than {@code MAX_VALUE}.
*
* The characters in the string must all be digits of the
* specified radix (as determined by whether {@link
* java.lang.Character#digit(char, int)} returns a nonnegative
* value), except that the first character may be an ASCII plus
* sign {@code '+'} ({@code '\u005Cu002B'}). The resulting
* integer value is returned.
*
*
An exception of type {@code NumberFormatException} is
* thrown if any of the following situations occurs:
*
* - The first argument is {@code null} or is a string of
* length zero.
*
*
- The radix is either smaller than
* {@link java.lang.Character#MIN_RADIX} or
* larger than {@link java.lang.Character#MAX_RADIX}.
*
*
- Any character of the string is not a digit of the specified
* radix, except that the first character may be a plus sign
* {@code '+'} ({@code '\u005Cu002B'}) provided that the
* string is longer than length 1.
*
*
- The value represented by the string is larger than the
* largest unsigned {@code long}, 264-1.
*
*
*
*
* @param s the {@code String} containing the unsigned integer
* representation to be parsed
* @param radix the radix to be used while parsing {@code s}.
* @return the unsigned {@code long} represented by the string
* argument in the specified radix.
* @throws NumberFormatException if the {@code String}
* does not contain a parsable {@code long}.
* @since 1.8
*/
public static long parseUnsignedLong(String s, int radix)
throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("null");
}
int len = s.length();
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar == '-') {
throw new
NumberFormatException(String.format("Illegal leading minus sign " +
"on unsigned string %s.", s));
} else {
if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
(radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
return Long.parseLong(s, radix);
}
// No need for range checks on len due to testing above.
long first = Long.parseLong(s.substring(0, len - 1), radix);
int second = Character.digit(s.charAt(len - 1), radix);
if (second < 0) {
throw new NumberFormatException("Bad digit at end of " + s);
}
long result = first * radix + second;
if (compareUnsigned(result, first) < 0) {
/*
* The maximum unsigned value, (2^64)-1, takes at
* most one more digit to represent than the
* maximum signed value, (2^63)-1. Therefore,
* parsing (len - 1) digits will be appropriately
* in-range of the signed parsing. In other
* words, if parsing (len -1) digits overflows
* signed parsing, parsing len digits will
* certainly overflow unsigned parsing.
*
* The compareUnsigned check above catches
* situations where an unsigned overflow occurs
* incorporating the contribution of the final
* digit.
*/
throw new NumberFormatException(String.format("String value %s exceeds " +
"range of unsigned long.", s));
}
return result;
}
} else {
throw new NumberFormatException("For input string: \"" + s + "\"");
}
}
/**
* Compares two {@code long} values numerically treating the values
* as unsigned.
*
* @param x the first {@code long} to compare
* @param y the second {@code long} to compare
* @return the value {@code 0} if {@code x == y}; a value less
* than {@code 0} if {@code x < y} as unsigned values; and
* a value greater than {@code 0} if {@code x > y} as
* unsigned values
* @since 1.8
*/
public static int compareUnsigned(long x, long y) {
return Long.compare(x + Long.MIN_VALUE, y + Long.MIN_VALUE);
}
}