Files
fflib-apex-extensions/sfdx-source/apex-extensions/main/default/classes/uuid/fflib_Uuid.cls
T
2023-08-01 12:19:18 +01:00

166 lines
4.9 KiB
OpenEdge ABL

/******************************************************************************************
* This file is part of the Apex UUID project, released under the MIT License. *
* See LICENSE file or go to https://github.com/jongpie/ApexUuid for full license details. *
******************************************************************************************/
/**
* Changes made:
* - Renamed class into fflib_Uuid
* - Renamed some parameters to resolve dictionary warnings
* - Added the fflib Apex styling
*/
public without sharing class fflib_Uuid
{
private static final Integer HEX_BASE = HEX_CHARACTERS.length();
private static final String HEX_CHARACTERS = '0123456789abcdef';
private static final String HEX_PREFIX = '0x';
private static final List<String> HEX_CHARACTER_LIST = HEX_CHARACTERS.split('');
private static final Integer UUID_V4_LENGTH = 36;
private static final String UUID_V4_REGEX = '[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';
private static final String ERROR_MESSAGE_INVALID_UUID = '{0} is not a valid UUID value';
public static String formatValue(String unformattedValue)
{
// Remove any non-alphanumeric characters
String nonformattedValue = unformattedValue.replaceAll('[^a-zA-Z0-9]', '');
// If the nonformatted value isn't even the right length to be valid, then throw an exception
// Subtract 4 because the UUID_V4_LENGTH includes 4 '-' characters in the UUID pattern
if (nonformattedValue.length() != (UUID_V4_LENGTH - 4))
{
throw new UuidException(String.format(ERROR_MESSAGE_INVALID_UUID, new List<String> {unformattedValue}));
}
// UUID Pattern: 8-4-4-4-12
String formattedValue =
nonformattedValue.substring(0, 8) +
'-' +
nonformattedValue.substring(8, 12) +
'-' +
nonformattedValue.substring(12, 16) +
'-' +
nonformattedValue.substring(16, 20) +
'-' +
nonformattedValue.substring(20);
formattedValue = formattedValue.toLowerCase();
if (!fflib_Uuid.isValid(formattedValue))
{
throw new UuidException(String.format(ERROR_MESSAGE_INVALID_UUID, new List<String> {nonformattedValue}));
}
return formattedValue;
}
public static Boolean isValid(String uuidValue)
{
if (String.isBlank(uuidValue) || uuidValue.length() != UUID_V4_LENGTH)
{
return false;
}
Pattern uuidPattern = Pattern.compile(UUID_V4_REGEX.toLowerCase());
Matcher uuidMatcher = uuidPattern.matcher(uuidValue.toLowerCase());
return uuidMatcher.matches();
}
public static fflib_Uuid valueOf(String uuidValue)
{
return new fflib_Uuid(uuidValue);
}
private final String value;
public fflib_Uuid() {
this.value = this.generateValue();
}
private fflib_Uuid(String uuidValue) {
this.value = fflib_Uuid.formatValue(uuidValue);
}
public String getValue()
{
return this.value;
}
private String generateValue()
{
String hexValue = EncodingUtil.convertToHex(Crypto.generateAesKey(128));
// Version Calculation: (i & 0x0f) | 0x40
// Version Format: Always begins with 4
String versionShiftedHexBits = this.getShiftedHexBits(
hexValue.substring(14, 16),
this.convertHexToInteger('0x0f'),
this.convertHexToInteger('0x40')
);
// Variant Calculation: (i & 0x3f) | 0x80
// Variant Format: Always begins with 8, 9, A or B
String variantShiftedHexBits = this.getShiftedHexBits(
hexValue.substring(18, 20),
this.convertHexToInteger('0x3f'),
this.convertHexToInteger('0x80')
);
String uuidValue =
hexValue.substring(0, 8) + // time-low
hexValue.substring(8, 12) + // time-mid
versionShiftedHexBits +
hexValue.substring(14, 16) + // time-high-and-version
variantShiftedHexBits +
hexValue.substring(18, 20) + // clock-seq-and-reserved + clock-seq-low
hexValue.substring(20); // node
return fflib_Uuid.formatValue(uuidValue);
}
private String getShiftedHexBits(
String hexSubstring,
Integer lowerThreshold,
Integer upperThreshold
)
{
Integer shiftedIntegerBits =
(this.convertHexToInteger(hexSubstring) & lowerThreshold) |
upperThreshold;
return this.convertIntegerToHex(shiftedIntegerBits);
}
private Integer convertHexToInteger(String hexValue)
{
hexValue = hexValue.toLowerCase();
if (hexValue.startsWith(HEX_PREFIX))
{
hexValue = hexValue.substringAfter(HEX_PREFIX);
}
Integer integerValue = 0;
for (String hexCharacter : hexValue.split(''))
{
Integer hexCharacterIndex = HEX_CHARACTERS.indexOf(hexCharacter);
integerValue = HEX_BASE * integerValue + hexCharacterIndex;
}
return integerValue;
}
private String convertIntegerToHex(Integer integerValue)
{
String hexValue = '';
while (integerValue > 0)
{
Integer hexCharacterIndex = Math.mod(integerValue, HEX_BASE);
hexValue = HEX_CHARACTER_LIST[hexCharacterIndex] + hexValue;
integerValue = integerValue / HEX_BASE;
}
return hexValue;
}
private class UuidException extends Exception {}
}