From 229156d51ddbddac13f1e7d4324d804d88cdb41b Mon Sep 17 00:00:00 2001 From: curry772 Date: Mon, 27 Jul 2026 09:56:24 +0900 Subject: [PATCH] =?UTF-8?q?=ED=94=84=EB=9F=AC=ED=8D=BC=ED=8B=B0=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=20=EC=9D=B4=EC=9A=A9=20=ED=95=A8=EC=88=98=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../function/generic/GetMapping.java | 107 ++++++++++++ .../transformer/GetMappingFunctionTest.java | 158 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 src/main/java/com/eactive/eai/transformer/function/generic/GetMapping.java create mode 100644 src/test/java/com/eactive/eai/transformer/GetMappingFunctionTest.java diff --git a/src/main/java/com/eactive/eai/transformer/function/generic/GetMapping.java b/src/main/java/com/eactive/eai/transformer/function/generic/GetMapping.java new file mode 100644 index 0000000..ad046dc --- /dev/null +++ b/src/main/java/com/eactive/eai/transformer/function/generic/GetMapping.java @@ -0,0 +1,107 @@ +package com.eactive.eai.transformer.function.generic; + +import java.util.Stack; + +import org.nfunk.jep.ParseException; +import org.nfunk.jep.function.PostfixMathCommand; + +import com.eactive.eai.common.property.PropManager; + +/** + * JEP 커스텀 함수 - PropManager 프러퍼티 그룹 기반 값 매핑 + * + *

고정된 변환 룰이 아닌, PropManager의 프러퍼티 그룹(예: 공통코드 매핑 테이블)을 + * 동적으로 조회하여 입력값을 매핑된 값으로 변환한다. + * 매핑 정보는 {@code 1000 -> AAAAA}, {@code 1001 -> BBBBB} 처럼 그룹의 key/value로 관리된다. + * + *

사용법: + *

+ * 파라미터: + * + * + *

예: 그룹 "COMMON_CODE"에 {@code 1000=AAAAA}가 있을 때 + * {@code getmapping("COMMON_CODE", "1000")} → {@code "AAAAA"} + */ +public class GetMapping extends PostfixMathCommand { + public GetMapping() { + numberOfParameters = -1; + } + + /** + * 스택에서 (group, value) 또는 (group, value, default)를 꺼내 매핑값을 조회한다. + * + * @param inStack JEP 후위 연산 스택 + * @throws ParseException 파라미터 개수가 2 또는 3이 아니거나 group이 문자열이 아닌 경우 + */ + @SuppressWarnings("unchecked") + public void run(Stack inStack) throws ParseException { + checkStack(inStack); + + int n = curNumberOfParameters; + if (n != 2 && n != 3) { + throw new ParseException( + "getmapping() requires 2 or 3 parameters: getmapping(group, value [, default])"); + } + + boolean hasDefault = (n == 3); + Object defaultValue = hasDefault ? inStack.pop() : null; + Object value = inStack.pop(); + Object groupObj = inStack.pop(); + + if (!(groupObj instanceof String)) { + throw new ParseException("getmapping(): first parameter (group name) must be a string"); + } + + String group = (String) groupObj; + String key = toKey(value); + + String mapped = (key == null) ? null : lookup(group, key); + + if (mapped != null) { + inStack.push(mapped); + } else if (hasDefault) { + inStack.push(defaultValue); + } else { + inStack.push(value); // 매핑 없음 → 원본값 그대로 반환 + } + } + + /** + * PropManager 프러퍼티 그룹에서 key에 해당하는 값을 조회한다. + * 매핑이 없거나 그룹이 존재하지 않으면 null을 반환한다. + * + *

단위 테스트에서는 이 메서드를 오버라이드하여 Spring 컨텍스트 없이 매핑을 주입할 수 있다. + */ + protected String lookup(String group, String key) { + return PropManager.getInstance().getProperty(group, key); + } + + /** + * 매핑 조회에 사용할 문자열 key로 변환한다. + * 정수형 숫자(JEP는 숫자를 Double로 처리)는 {@code "1000.0"}이 아닌 {@code "1000"}으로 변환한다. + */ + private String toKey(Object value) { + if (value == null) { + return null; + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof Number) { + double d = ((Number) value).doubleValue(); + if (!Double.isInfinite(d) && !Double.isNaN(d) && d == Math.rint(d)) { + return Long.toString((long) d); + } + } + return value.toString(); + } +} diff --git a/src/test/java/com/eactive/eai/transformer/GetMappingFunctionTest.java b/src/test/java/com/eactive/eai/transformer/GetMappingFunctionTest.java new file mode 100644 index 0000000..2e58f0c --- /dev/null +++ b/src/test/java/com/eactive/eai/transformer/GetMappingFunctionTest.java @@ -0,0 +1,158 @@ +package com.eactive.eai.transformer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.nfunk.jep.JEP; + +import com.eactive.eai.transformer.function.generic.GetMapping; + +@DisplayName("GetMapping 프러퍼티 그룹 매핑 함수 테스트") +class GetMappingFunctionTest { + + private JEP jep; + + /** + * PropManager(Spring 컨텍스트) 없이 테스트하기 위해 lookup()을 로컬 맵으로 대체한 스텁. + * groups: 그룹명 → (key → value) 매핑 테이블 + */ + static class StubGetMapping extends GetMapping { + private final Map> groups; + + StubGetMapping(Map> groups) { + this.groups = groups; + } + + @Override + protected String lookup(String group, String key) { + Map g = groups.get(group); + if (g == null) { + return null; + } + return g.get(key); + } + } + + @BeforeEach + void setUp() { + // 프러퍼티 그룹 "COMMON_CODE": 1000->AAAAA, 1001->BBBBB + Map commonCode = new HashMap<>(); + commonCode.put("1000", "AAAAA"); + commonCode.put("1001", "BBBBB"); + + // 한글 그룹명/값도 지원하는지 확인용 + Map bankCode = new HashMap<>(); + bankCode.put("081", "하나은행"); + bankCode.put("088", "신한은행"); + + Map> groups = new HashMap<>(); + groups.put("COMMON_CODE", commonCode); + groups.put("BANK_CODE", bankCode); + + jep = new JEP(); + jep.addStandardFunctions(); + jep.addStandardConstants(); + jep.setAllowUndeclared(true); + jep.addFunction("getmapping", new StubGetMapping(groups)); + } + + private Object eval(String expr) { + jep.parseExpression(expr); + assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo()); + Object result = jep.getValueAsObject(); + assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo()); + return result; + } + + @Test + @DisplayName("2-인자 매핑 성공 — 1000 → AAAAA") + void testMappingFound() { + assertEquals("AAAAA", eval("getmapping(\"COMMON_CODE\", \"1000\")")); + assertEquals("BBBBB", eval("getmapping(\"COMMON_CODE\", \"1001\")")); + } + + @Test + @DisplayName("2-인자 매핑 없음 — 원본값 그대로 반환(passthrough)") + void testMappingNotFoundPassthrough() { + assertEquals("9999", eval("getmapping(\"COMMON_CODE\", \"9999\")")); + } + + @Test + @DisplayName("2-인자 그룹 자체가 없음 — 원본값 그대로 반환") + void testGroupNotFoundPassthrough() { + assertEquals("1000", eval("getmapping(\"NO_SUCH_GROUP\", \"1000\")")); + } + + @Test + @DisplayName("3-인자 매핑 성공 — default 무시하고 매핑값 반환") + void testMappingFoundWithDefault() { + assertEquals("AAAAA", eval("getmapping(\"COMMON_CODE\", \"1000\", \"UNKNOWN\")")); + } + + @Test + @DisplayName("3-인자 매핑 없음 — default 반환") + void testMappingNotFoundWithDefault() { + assertEquals("UNKNOWN", eval("getmapping(\"COMMON_CODE\", \"9999\", \"UNKNOWN\")")); + } + + @Test + @DisplayName("3-인자 그룹 자체가 없음 — default 반환") + void testGroupNotFoundWithDefault() { + assertEquals("UNKNOWN", eval("getmapping(\"NO_SUCH_GROUP\", \"1000\", \"UNKNOWN\")")); + } + + @Test + @DisplayName("숫자 리터럴 값 — 1000.0이 아닌 \"1000\"으로 조회") + void testNumericValueLookup() { + // JEP는 1000 리터럴을 Double로 처리하므로 toKey에서 "1000"으로 변환되어야 매핑됨 + assertEquals("AAAAA", eval("getmapping(\"COMMON_CODE\", 1000)")); + } + + @Test + @DisplayName("숫자 리터럴 값 매핑 없음 — 원본 숫자값 그대로 반환") + void testNumericValueNotFoundPassthrough() { + Object result = eval("getmapping(\"COMMON_CODE\", 9999)"); + assertEquals(9999.0, ((Number) result).doubleValue()); + } + + @Test + @DisplayName("변수 바인딩 값 매핑 — 필드값 변환") + void testVariableValueMapping() { + jep.addVariable("code", "1001"); + assertEquals("BBBBB", eval("getmapping(\"COMMON_CODE\", code)")); + } + + @Test + @DisplayName("한글 그룹/값 매핑 — 081 → 하나은행") + void testKoreanGroupAndValue() { + assertEquals("하나은행", eval("getmapping(\"BANK_CODE\", \"081\")")); + assertEquals("신한은행", eval("getmapping(\"BANK_CODE\", \"088\")")); + } + + @Test + @DisplayName("첫 번째 파라미터(group)가 문자열이 아니면 오류") + void testNonStringGroupCausesError() { + jep.parseExpression("getmapping(1000, \"1000\")"); + if (!jep.hasError()) { + jep.getValueAsObject(); + assertTrue(jep.hasError(), "숫자 group 파라미터는 오류가 발생해야 합니다"); + } + } + + @Test + @DisplayName("파라미터 개수가 1개면 오류") + void testTooFewParamsCausesError() { + jep.parseExpression("getmapping(\"COMMON_CODE\")"); + if (!jep.hasError()) { + jep.getValueAsObject(); + assertTrue(jep.hasError(), "파라미터 1개는 오류가 발생해야 합니다"); + } + } +}