package com.fasterxml.jackson.databind.deser.std; import java.io.IOException; import java.util.Arrays; import java.util.UUID; import com.fasterxml.jackson.core.Base64Variants; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.exc.InvalidFormatException; public class UUIDDeserializer extends FromStringDeserializer<UUID> { private static final long serialVersionUID = 1L; final static int[] HEX_DIGITS = new int[127]; static { Arrays.fill(HEX_DIGITS, -1); for (int i = 0; i < 10; ++i) { HEX_DIGITS['0' + i] = i; } for (int i = 0; i < 6; ++i) { HEX_DIGITS['a' + i] = 10 + i; HEX_DIGITS['A' + i] = 10 + i; } } public UUIDDeserializer() { super(UUID.class); } @Override protected UUID _deserialize(String id, DeserializationContext ctxt) throws IOException { // Adapted from java-uuid-generator (https://github.com/cowtowncoder/java-uuid-generator) // which is 5x faster than UUID.fromString(value), as oper "ManualReadPerfWithUUID" if (id.length() != 36) { /* 14-Sep-2013, tatu: One trick we do allow, Base64-encoding, since we know * length it must have... */ if (id.length() == 24) { byte[] stuff = Base64Variants.getDefaultVariant().decode(id); return _fromBytes(stuff, ctxt); } return _badFormat(id, ctxt); } // verify hyphens first: if ((id.charAt(8) != '-') || (id.charAt(13) != '-') || (id.charAt(18) != '-') || (id.charAt(23) != '-')) { _badFormat(id, ctxt); } long l1 = intFromChars(id, 0, ctxt); l1 <<= 32; long l2 = ((long) shortFromChars(id, 9, ctxt)) << 16; l2 |= shortFromChars(id, 14, ctxt); long hi = l1 + l2; int i1 = (shortFromChars(id, 19, ctxt) << 16) | shortFromChars(id, 24, ctxt); l1 = i1; l1 <<= 32; l2 = intFromChars(id, 28, ctxt); l2 = (l2 << 32) >>> 32; // sign removal, Java-style. Ugh. long lo = l1 | l2; return new UUID(hi, lo); } @Override protected UUID _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws IOException { if (ob instanceof byte[]) { return _fromBytes((byte[]) ob, ctxt); } super._deserializeEmbedded(ob, ctxt); return null; // never gets here } private UUID _badFormat(String uuidStr, DeserializationContext ctxt) throws IOException { return (UUID) ctxt.handleWeirdStringValue(handledType(), uuidStr, "UUID has to be represented by standard 36-char representation"); } int intFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException { return (byteFromChars(str, index, ctxt) << 24) + (byteFromChars(str, index+2, ctxt) << 16) + (byteFromChars(str, index+4, ctxt) << 8) + byteFromChars(str, index+6, ctxt); } int shortFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException { return (byteFromChars(str, index, ctxt) << 8) + byteFromChars(str, index+2, ctxt); } int byteFromChars(String str, int index, DeserializationContext ctxt) throws JsonMappingException { final char c1 = str.charAt(index); final char c2 = str.charAt(index+1); if (c1 <= 127 && c2 <= 127) { int hex = (HEX_DIGITS[c1] << 4) | HEX_DIGITS[c2]; if (hex >= 0) { return hex; } } if (c1 > 127 || HEX_DIGITS[c1] < 0) { return _badChar(str, index, ctxt, c1); } return _badChar(str, index+1, ctxt, c2); } int _badChar(String uuidStr, int index, DeserializationContext ctxt, char c) throws JsonMappingException { // 15-May-2016, tatu: Ideally should not throw, but call `handleWeirdStringValue`... // however, control flow is gnarly here, so for now just throw throw ctxt.weirdStringException(uuidStr, handledType(), String.format( "Non-hex character '%c' (value 0x%s), not valid for UUID String", c, Integer.toHexString(c))); } private UUID _fromBytes(byte[] bytes, DeserializationContext ctxt) throws JsonMappingException { if (bytes.length != 16) { throw InvalidFormatException.from(ctxt.getParser(), "Can only construct UUIDs from byte[16]; got "+bytes.length+" bytes", bytes, handledType()); } return new UUID(_long(bytes, 0), _long(bytes, 8)); } private static long _long(byte[] b, int offset) { long l1 = ((long) _int(b, offset)) << 32; long l2 = _int(b, offset+4); // faster to just do it than check if it has sign l2 = (l2 << 32) >>> 32; // to get rid of sign return l1 | l2; } private static int _int(byte[] b, int offset) { return (b[offset] << 24) | ((b[offset+1] & 0xFF) << 16) | ((b[offset+2] & 0xFF) << 8) | (b[offset+3] & 0xFF); } }