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: *

* * * @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); } }