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