diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/MessageUtil.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/MessageUtil.java new file mode 100644 index 0000000..ef9b818 --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/MessageUtil.java @@ -0,0 +1,507 @@ +package com.klaro.acquirecore.framework.util; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Static utility class for message encoding/decoding migrated from legacy/util_msg.c + * Uses java.util.Base64 and java.nio.charset for codec operations. + */ +public final class MessageUtil { + + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + public static final Charset EUC_KR_CHARSET = Charset.forName("EUC-KR"); + public static final Charset ISO_8859_1_CHARSET = StandardCharsets.ISO_8859_1; + + private static final Pattern EMAIL_PATTERN = Pattern.compile( + "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$" + ); + private static final Pattern PHONE_PATTERN = Pattern.compile( + "^\\d{2,4}-\\d{3,4}-\\d{4}$|^\\d{10,11}$" + ); + private static final Pattern KOREAN_PHONE_PATTERN = Pattern.compile( + "^01[016789]-\\d{3,4}-\\d{4}$" + ); + + private MessageUtil() { + throw new UnsupportedOperationException("Utility class cannot be instantiated"); + } + + // ==================== Base64 Encoding/Decoding ==================== + + /** + * Encodes a string to Base64. + * + * @param input the string to encode + * @return the Base64 encoded string + */ + public static String encodeBase64(String input) { + Objects.requireNonNull(input, "input must not be null"); + return Base64.getEncoder().encodeToString(input.getBytes(DEFAULT_CHARSET)); + } + + /** + * Encodes a string to Base64 with specified charset. + * + * @param input the string to encode + * @param charset the charset to use + * @return the Base64 encoded string + */ + public static String encodeBase64(String input, Charset charset) { + Objects.requireNonNull(input, "input must not be null"); + Objects.requireNonNull(charset, "charset must not be null"); + return Base64.getEncoder().encodeToString(input.getBytes(charset)); + } + + /** + * Encodes bytes to Base64. + * + * @param bytes the bytes to encode + * @return the Base64 encoded string + */ + public static String encodeBase64(byte[] bytes) { + Objects.requireNonNull(bytes, "bytes must not be null"); + return Base64.getEncoder().encodeToString(bytes); + } + + /** + * Decodes a Base64 string to plain string. + * + * @param encoded the Base64 encoded string + * @return the decoded string + */ + public static String decodeBase64(String encoded) { + Objects.requireNonNull(encoded, "encoded must not be null"); + byte[] decoded = Base64.getDecoder().decode(encoded); + return new String(decoded, DEFAULT_CHARSET); + } + + /** + * Decodes a Base64 string to plain string with specified charset. + * + * @param encoded the Base64 encoded string + * @param charset the charset to use for decoding + * @return the decoded string + */ + public static String decodeBase64(String encoded, Charset charset) { + Objects.requireNonNull(encoded, "encoded must not be null"); + Objects.requireNonNull(charset, "charset must not be null"); + byte[] decoded = Base64.getDecoder().decode(encoded); + return new String(decoded, charset); + } + + /** + * Decodes a Base64 string to bytes. + * + * @param encoded the Base64 encoded string + * @return the decoded bytes + */ + public static byte[] decodeBase64ToBytes(String encoded) { + Objects.requireNonNull(encoded, "encoded must not be null"); + return Base64.getDecoder().decode(encoded); + } + + /** + * Encodes a URL-safe Base64 string. + * + * @param input the string to encode + * @return the URL-safe Base64 encoded string + */ + public static String encodeBase64Url(String input) { + Objects.requireNonNull(input, "input must not be null"); + return Base64.getUrlEncoder().encodeToString(input.getBytes(DEFAULT_CHARSET)); + } + + /** + * Decodes a URL-safe Base64 string. + * + * @param encoded the URL-safe Base64 encoded string + * @return the decoded string + */ + public static String decodeBase64Url(String encoded) { + Objects.requireNonNull(encoded, "encoded must not be null"); + byte[] decoded = Base64.getUrlDecoder().decode(encoded); + return new String(decoded, DEFAULT_CHARSET); + } + + // ==================== Hex Encoding/Decoding ==================== + + /** + * Encodes bytes to hexadecimal string. + * + * @param bytes the bytes to encode + * @return the hexadecimal string + */ + public static String encodeHex(byte[] bytes) { + Objects.requireNonNull(bytes, "bytes must not be null"); + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + /** + * Decodes a hexadecimal string to bytes. + * + * @param hex the hexadecimal string + * @return the decoded bytes + */ + public static byte[] decodeHex(String hex) { + Objects.requireNonNull(hex, "hex must not be null"); + if (hex.length() % 2 != 0) { + throw new IllegalArgumentException("Hex string must have even length"); + } + int len = hex.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + + Character.digit(hex.charAt(i + 1), 16)); + } + return data; + } + + // ==================== URL Encoding/Decoding ==================== + + /** + * URL encodes a string using UTF-8. + * + * @param input the string to encode + * @return the URL encoded string + */ + public static String urlEncode(String input) { + Objects.requireNonNull(input, "input must not be null"); + try { + return java.net.URLEncoder.encode(input, DEFAULT_CHARSET.name()); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("Unsupported encoding", e); + } + } + + /** + * URL decodes a string using UTF-8. + * + * @param encoded the URL encoded string + * @return the decoded string + */ + public static String urlDecode(String encoded) { + Objects.requireNonNull(encoded, "encoded must not be null"); + try { + return java.net.URLDecoder.decode(encoded, DEFAULT_CHARSET.name()); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("Unsupported encoding", e); + } + } + + // ==================== String Manipulation ==================== + + /** + * Masks a string with asterisks, showing only the first and last characters. + * + * @param input the string to mask + * @return the masked string + */ + public static String mask(String input) { + return mask(input, 1, 1); + } + + /** + * Masks a string with asterisks, showing only specified first and last characters. + * + * @param input the string to mask + * @param showFirst number of characters to show at the start + * @param showLast number of characters to show at the end + * @return the masked string + */ + public static String mask(String input, int showFirst, int showLast) { + if (input == null || input.isEmpty()) { + return input; + } + int length = input.length(); + if (length <= showFirst + showLast) { + return input; + } + StringBuilder sb = new StringBuilder(); + sb.append(input, 0, showFirst); + for (int i = showFirst; i < length - showLast; i++) { + sb.append('*'); + } + sb.append(input.substring(length - showLast)); + return sb.toString(); + } + + /** + * Masks an email address. + * + * @param email the email to mask + * @return the masked email + */ + public static String maskEmail(String email) { + if (email == null || !email.contains("@")) { + return email; + } + String[] parts = email.split("@"); + String localPart = parts[0]; + String domain = parts[1]; + if (localPart.length() <= 2) { + return mask(localPart, 1, 0) + "@" + domain; + } + return mask(localPart, 1, 1) + "@" + domain; + } + + /** + * Masks a phone number. + * + * @param phone the phone number to mask + * @return the masked phone number + */ + public static String maskPhone(String phone) { + if (phone == null || phone.isEmpty()) { + return phone; + } + String digitsOnly = phone.replaceAll("[^0-9]", ""); + if (digitsOnly.length() < 7) { + return phone; + } + int length = digitsOnly.length(); + String masked = digitsOnly.substring(0, 3) + "****" + digitsOnly.substring(length - 4); + if (phone.contains("-")) { + return masked.substring(0, 3) + "-" + "****" + "-" + masked.substring(7); + } + return masked; + } + + // ==================== Validation ==================== + + /** + * Validates if a string is a valid email address. + * + * @param email the email to validate + * @return true if valid, false otherwise + */ + public static boolean isValidEmail(String email) { + if (email == null || email.isEmpty()) { + return false; + } + return EMAIL_PATTERN.matcher(email).matches(); + } + + /** + * Validates if a string is a valid phone number. + * + * @param phone the phone number to validate + * @return true if valid, false otherwise + */ + public static boolean isValidPhone(String phone) { + if (phone == null || phone.isEmpty()) { + return false; + } + return PHONE_PATTERN.matcher(phone).matches(); + } + + /** + * Validates if a string is a valid Korean phone number. + * + * @param phone the phone number to validate + * @return true if valid, false otherwise + */ + public static boolean isValidKoreanPhone(String phone) { + if (phone == null || phone.isEmpty()) { + return false; + } + return KOREAN_PHONE_PATTERN.matcher(phone).matches(); + } + + /** + * Checks if a string is null or empty. + * + * @param input the string to check + * @return true if null or empty + */ + public static boolean isEmpty(String input) { + return input == null || input.isEmpty(); + } + + /** + * Checks if a string is null, empty, or whitespace only. + * + * @param input the string to check + * @return true if null, empty, or whitespace only + */ + public static boolean isBlank(String input) { + return input == null || input.trim().isEmpty(); + } + + // ==================== Charset Conversion ==================== + + /** + * Converts a string from one charset to another. + * + * @param input the input string + * @param fromCharset the source charset + * @param toCharset the target charset + * @return the converted string + */ + public static String convertCharset(String input, Charset fromCharset, Charset toCharset) { + Objects.requireNonNull(input, "input must not be null"); + Objects.requireNonNull(fromCharset, "fromCharset must not be null"); + Objects.requireNonNull(toCharset, "toCharset must not be null"); + byte[] bytes = input.getBytes(fromCharset); + return new String(bytes, toCharset); + } + + /** + * Converts EUC-KR encoded bytes to UTF-8 string. + * + * @param eucKrBytes the EUC-KR encoded bytes + * @return the UTF-8 string + */ + public static String eucKrToUtf8(byte[] eucKrBytes) { + Objects.requireNonNull(eucKrBytes, "eucKrBytes must not be null"); + return new String(eucKrBytes, EUC_KR_CHARSET); + } + + /** + * Converts UTF-8 string to EUC-KR encoded bytes. + * + * @param input the UTF-8 string + * @return the EUC-KR encoded bytes + */ + public static byte[] utf8ToEucKr(String input) { + Objects.requireNonNull(input, "input must not be null"); + return input.getBytes(EUC_KR_CHARSET); + } + + // ==================== Truncation and Padding ==================== + + /** + * Truncates a string to the specified length. + * + * @param input the string to truncate + * @param maxLength the maximum length + * @return the truncated string + */ + public static String truncate(String input, int maxLength) { + if (input == null) { + return null; + } + if (input.length() <= maxLength) { + return input; + } + return input.substring(0, maxLength); + } + + /** + * Pads a string on the left with spaces to reach the specified length. + * + * @param input the string to pad + * @param length the target length + * @return the padded string + */ + public static String padLeft(String input, int length) { + if (input == null) { + input = ""; + } + return String.format("%" + length + "s", input); + } + + /** + * Pads a string on the right with spaces to reach the specified length. + * + * @param input the string to pad + * @param length the target length + * @return the padded string + */ + public static String padRight(String input, int length) { + if (input == null) { + input = ""; + } + return String.format("%- " + length + "s", input); + } + + /** + * Pads a string on the left with zeros to reach the specified length. + * + * @param input the string to pad + * @param length the target length + * @return the zero-padded string + */ + public static String padLeftWithZeros(String input, int length) { + if (input == null) { + input = ""; + } + return String.format("%0" + length + "d", Long.parseLong(input)); + } + + /** + * Left pads a numeric string with zeros. + * + * @param value the numeric value + * @param length the target length + * @return the zero-padded string + */ + public static String zeroPad(long value, int length) { + return String.format("%0" + length + "d", value); + } + + // ==================== Byte Array Utilities ==================== + + /** + * Converts a string to bytes using the default charset. + * + * @param input the string to convert + * @return the bytes + */ + public static byte[] toBytes(String input) { + if (input == null) { + return null; + } + return input.getBytes(DEFAULT_CHARSET); + } + + /** + * Converts a string to bytes using the specified charset. + * + * @param input the string to convert + * @param charset the charset to use + * @return the bytes + */ + public static byte[] toBytes(String input, Charset charset) { + if (input == null) { + return null; + } + Objects.requireNonNull(charset, "charset must not be null"); + return input.getBytes(charset); + } + + /** + * Converts bytes to string using the default charset. + * + * @param bytes the bytes to convert + * @return the string + */ + public static String toString(byte[] bytes) { + if (bytes == null) { + return null; + } + return new String(bytes, DEFAULT_CHARSET); + } + + /** + * Converts bytes to string using the specified charset. + * + * @param bytes the bytes to convert + * @param charset the charset to use + * @return the string + */ + public static String toString(byte[] bytes, Charset charset) { + if (bytes == null) { + return null; + } + Objects.requireNonNull(charset, "charset must not be null"); + return new String(bytes, charset); + } +}