From 9597e8f9f3d39f29515397072a57253263a62d7e Mon Sep 17 00:00:00 2001 From: jflute Date: Fri, 27 Sep 2024 12:09:01 +0900 Subject: [PATCH] support SimpleExpression, e.g. "new org.docksidestage.Sea()" #43 --- .../expression/dwarf/ExpressionPlainHook.java | 11 ++++ .../dwarf/SimpleExpressionPlainHook.java | 37 +++++++++++-- .../dwarf/SimpleExpressionPlainHookTest.java | 54 +++++++++++++++++++ 3 files changed, 97 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/lastaflute/di/core/expression/dwarf/ExpressionPlainHook.java b/src/main/java/org/lastaflute/di/core/expression/dwarf/ExpressionPlainHook.java index c24ab2c..c1f658a 100644 --- a/src/main/java/org/lastaflute/di/core/expression/dwarf/ExpressionPlainHook.java +++ b/src/main/java/org/lastaflute/di/core/expression/dwarf/ExpressionPlainHook.java @@ -32,13 +32,18 @@ public interface ExpressionPlainHook { String COLON = ":"; String DQ = "\""; String SQ = "'"; + String EXISTS_BEGIN = LdiResourceUtil.class.getName() + ".exists('"; String EXISTS_END = "')"; + String TYPE_BEGIN = "@"; // compatible with OGNL e.g. @org.dbflute.Entity@class, and for minor domain String TYPE_END = "@"; // me too String TYPE_END_CLASS = TYPE_END + "class"; // me too + String NEW_PREFIX = "new "; String METHOD_MARK = "()"; + String DQ_NEW_PREFIX = DQ + "new "; + String DQ_METHOD_SUFFIX = "()" + DQ; // e.g. provider.config().getJdbcUrl() String PROVIDER_CONFIG = "provider.config()"; @@ -53,8 +58,14 @@ public interface ExpressionPlainHook { Object NULL_RETURN = new Object(); + // =================================================================================== + // Hook + // ====== Object hookPlainly(String exp, Map contextMap, LaContainer container, Class resultType); + // =================================================================================== + // for Caller + // ========== static Object resolveHookedReturn(Object hooked) { return NULL_RETURN.equals(hooked) ? null : hooked; } diff --git a/src/main/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHook.java b/src/main/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHook.java index c5d5d68..05be2f1 100644 --- a/src/main/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHook.java +++ b/src/main/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHook.java @@ -181,6 +181,9 @@ protected boolean isValidHatenaColonElement(String determinationExp, String firs // ============== protected Object resolveSimpleString(String exp, LaContainer container, Class resultType) { if (exp.startsWith(DQ) && exp.endsWith(DQ) && exp.length() > DQ.length()) { + if (mayBeDoubleQuotedStatement(exp)) { // except e.g. "new ...Sea()" @since 1.0.0 + return null; // treated as statement (not simple string) + } final String unquoted = exp.substring(DQ.length(), exp.length() - DQ.length()); if (!unquoted.contains(DQ)) { // simple string e.g. "sea" return unquoted; @@ -189,6 +192,12 @@ protected Object resolveSimpleString(String exp, LaContainer container, Class return null; } + protected boolean mayBeDoubleQuotedStatement(String exp) { + // not use resultType determination because of compatible for expected rare cases + // will be incrementaly added if new pattern is found + return isDoubleQuotedNewExp(exp); + } + protected Object resolveSimpleNumber(String exp, LaContainer container, Class resultType) { if (LdiStringUtil.isNumber(exp)) { if (exp.length() > 9) { @@ -223,7 +232,7 @@ protected Object resolveSimpleEqualEqual(String exp, LaContainer container, Clas // Simple New // ========== protected Object resolveSimpleNewExp(String exp, LaContainer container, Class resultType) { // @since 1.0.0 - if (exp.startsWith(NEW_PREFIX) && exp.endsWith(METHOD_MARK)) { + if (canBeSimpleNewExp(exp)) { final String rear = LdiSrl.substringFirstRear(exp, NEW_PREFIX); // e.g. org.docksidestage.Sea() final String fqcn = LdiSrl.substringLastFront(rear, METHOD_MARK); // e.g. org.docksidestage.Sea final Class clazz; @@ -245,25 +254,43 @@ protected Object resolveSimpleNewExp(String exp, LaContainer container, Class return null; } + protected boolean canBeSimpleNewExp(String exp) { + if (isPlainNewExp(exp)) { // no quoted + return true; + } + if (mayBeDoubleQuotedStatement(exp)) { // e.g. "new ...Sea()" + return true; // Nashorn treats it as statement + } + return false; + } + + protected boolean isPlainNewExp(String exp) { + return exp.startsWith(NEW_PREFIX) && exp.endsWith(METHOD_MARK); + } + + protected boolean isDoubleQuotedNewExp(String exp) { + return exp.startsWith(DQ_NEW_PREFIX) && exp.endsWith(DQ_METHOD_SUFFIX); + } + // =================================================================================== // Simple Type Expression // ====================== protected Object resolveSimpleTypeExp(String exp, LaContainer container, Class resultType) { - if (exp.startsWith(TYPE_BEGIN) && exp.endsWith(TYPE_END_CLASS)) { // @org.docksidestage.Sea@class + if (exp.startsWith(TYPE_BEGIN) && exp.endsWith(TYPE_END_CLASS)) { // e.g. @org.docksidestage.Sea@class // mainly for OGNL compatibility final String className = exp.substring(TYPE_BEGIN.length(), exp.lastIndexOf(TYPE_END_CLASS)); return LdiClassUtil.forName(className); } - if (exp.startsWith(TYPE_BEGIN) && exp.contains(TYPE_END)) { // @org.docksidestage.Sea@call() + if (exp.startsWith(TYPE_BEGIN) && exp.contains(TYPE_END)) { // e.g. @org.docksidestage.Sea@call() or @FIELD // minor domain, e.g. jp, cannot be parsed by Nashon so original logic here final String className = exp.substring(TYPE_BEGIN.length(), exp.lastIndexOf(TYPE_END)); final String rear = exp.substring(exp.lastIndexOf(TYPE_END) + TYPE_END.length()); final Class clazz = LdiClassUtil.forName(className); - if (rear.endsWith(METHOD_MARK)) { + if (rear.endsWith(METHOD_MARK)) { // e.g. @org.docksidestage.Sea@call() final String methodName = rear.substring(0, rear.lastIndexOf(METHOD_MARK)); final Method method = LdiReflectionUtil.getMethod(clazz, methodName, (Class[]) null); return LdiReflectionUtil.invoke(method, null, (Object[]) null); - } else { + } else { // e.g. @org.docksidestage.Sea@FIELD final Field field = LdiReflectionUtil.getField(clazz, rear); return LdiReflectionUtil.getValue(field, null); } diff --git a/src/test/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHookTest.java b/src/test/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHookTest.java index 9c34819..7ec90f9 100644 --- a/src/test/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHookTest.java +++ b/src/test/java/org/lastaflute/di/core/expression/dwarf/SimpleExpressionPlainHookTest.java @@ -19,6 +19,7 @@ import java.util.Map; import org.lastaflute.di.core.LaContainer; +import org.lastaflute.di.core.LastaDiProperties; import org.lastaflute.di.core.meta.impl.LaContainerImpl; import org.lastaflute.di.unit.UnitLastaDiTestCase; import org.lastaflute.jta.core.LaTransaction; @@ -29,6 +30,26 @@ */ public class SimpleExpressionPlainHookTest extends UnitLastaDiTestCase { + // =================================================================================== + // Basic Handling + // ============== + public void test_hookPlainly_simpleString_basic() { + // ## Arrange ## + SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook(); + String exp = "\"sea\""; + Map contextMap = new HashMap<>(); + LaContainer container = createContainer(); + Class resultType = Object.class; // unused + + // ## Act ## + Object result = hook.hookPlainly(exp, contextMap, container, resultType); + + // ## Assert ## + assertNotNull(result); + assertTrue(result instanceof String); + assertEquals("sea", result); + } + // =================================================================================== // Simple new // ========== @@ -48,6 +69,22 @@ public void test_hookPlainly_simpleNew_basic() { assertTrue(result instanceof LaTransaction); } + public void test_hookPlainly_simpleNew_doubleQuoted() { + // ## Arrange ## + SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook(); + String exp = "\"new org.lastaflute.jta.core.LaTransaction()\""; + Map contextMap = new HashMap<>(); + LaContainer container = createContainer(); + Class resultType = Object.class; // unused + + // ## Act ## + Object result = hook.hookPlainly(exp, contextMap, container, resultType); + + // ## Assert ## + assertNotNull(result); + assertTrue(result instanceof LaTransaction); + } + // =================================================================================== // Simple Type // =========== @@ -67,6 +104,23 @@ public void test_hookPlainly_simpleType_class_basic() { assertTrue(LjtTimeoutManager.class.equals(result)); } + public void test_hookPlainly_simpleType_field_basic() { + // ## Arrange ## + SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook(); + String exp = "@org.lastaflute.di.core.LastaDiProperties@LASTA_DI_PROPERTIES"; + Map contextMap = new HashMap<>(); + LaContainer container = createContainer(); + Class resultType = Object.class; // unused + + // ## Act ## + Object result = hook.hookPlainly(exp, contextMap, container, resultType); + + // ## Assert ## + assertNotNull(result); + assertTrue(result instanceof String); + assertEquals(LastaDiProperties.LASTA_DI_PROPERTIES, result); + } + public void test_hookPlainly_simpleType_method_basic() { // ## Arrange ## SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();