Skip to content

Commit

Permalink
support SimpleExpression, e.g. boolean ? sea : land #42
Browse files Browse the repository at this point in the history
  • Loading branch information
jflute committed Sep 27, 2024
1 parent be4328e commit 3ccfd56
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public interface ExpressionPlainHook {
// ===================================================================================
// Definition
// ==========
String HATENA = "?";
String COLON = ":";
String DQ = "\"";
String SQ = "'";
String EXISTS_BEGIN = LdiResourceUtil.class.getName() + ".exists('";
Expand All @@ -41,6 +43,7 @@ public interface ExpressionPlainHook {
// e.g. provider.config().getJdbcUrl()
String PROVIDER_CONFIG = "provider.config()";
String PROVIDER_GET = PROVIDER_CONFIG + ".get";
String PROVIDER_IS = PROVIDER_CONFIG + ".is";

// e.g. provider.config().getOrDefault("jdbc.connection.pooling.min.size", null)
String ORDEFAULT_METHOD_NAME = "getOrDefault";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,33 @@ public class SimpleExpressionPlainHook implements ExpressionPlainHook {
// ============
@Override
public Object hookPlainly(String exp, Map<String, ? extends Object> contextMap, LaContainer container, Class<?> resultType) {
final Object hatenaColonResult = resolveHatenaColon(exp, contextMap, container, resultType); // e.g. ? :
if (isReallyResolved(hatenaColonResult)) {
return hatenaColonResult;
}
return doHookPlainly(exp, contextMap, container, resultType);
}

protected Object doHookPlainly(String exp, Map<String, ? extends Object> contextMap, LaContainer container, Class<?> resultType) {
// same filter in JavaScript engine however cannot commonize easily because of OGNL-embedded
// no fix as small cost for now by jflute (2020/09/30)
final String resolvedExp = ExpressionEngine.resolveExpressionVariableSimply(exp, contextMap);
return doHookPlainly(resolvedExp, contextMap, container, resultType);
}

protected Object doHookPlainly(String exp, Map<String, ? extends Object> contextMap, LaContainer container, Class<?> resultType) {
final CastResolved resolved = castResolver.resolveCast(exp, resultType);
final CastResolved resolved = castResolver.resolveCast(resolvedExp, resultType);
final String realExp;
final Class<?> realType;
if (resolved != null) {
realExp = resolved.getFilteredExp();
realType = resolved.getResolvedType();
} else {
realExp = exp.trim();
realExp = resolvedExp.trim();
realType = resultType;
}
return actuallyHookPlainly(realExp, container, realType);
}

protected Object actuallyHookPlainly(String exp, LaContainer container, Class<?> resultType) {
Object resovled = resolveSimpleString(exp, container, resultType); // "sea"
Object resovled = resolveSimpleString(exp, container, resultType); // e.g. "sea"
if (isReallyResolved(resovled)) {
return resovled;
}
Expand All @@ -90,7 +95,7 @@ protected Object actuallyHookPlainly(String exp, LaContainer container, Class<?>
if (isReallyResolved(resovled)) {
return resovled;
}
resovled = resolveSimpleTypeExp(exp, container, resultType); // e.g. @org.docksidestage.Sea@class
resovled = resolveSimpleTypeExp(exp, container, resultType); // e.g. @org.docksidestage.Sea@class or call()
if (isReallyResolved(resovled)) {
return resovled;
}
Expand All @@ -117,6 +122,60 @@ protected boolean isReallyResolved(Object resovled) {
return resovled != null; // includes null return object
}

// ===================================================================================
// Hatena Colon
// ============
// @since 1.0.0
protected Object resolveHatenaColon(String exp, Map<String, ? extends Object> contextMap, LaContainer container, Class<?> resultType) {
// _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
// treated as three expressions by hatena colon
// e.g.
// provider.config().isDevelopmentHere()
// ? new org.lastaflute.jta.core.LaTransaction()
// : @org.lastaflute.jta.helper.timer.LjtTimeoutManager@getInstance()
// _/_/_/_/_/_/_/_/_/_/
if (mayBeSimpleHatenaColon(exp)) {
// e.g. sea ? over : mystic
final String determinationExp = LdiSrl.substringFirstFront(exp, HATENA).trim(); // e.g. sea
final String hatenaRear = LdiSrl.substringFirstRear(exp, HATENA).trim(); // e.g. over : mystic
final String firstSelectionExp = LdiSrl.substringFirstFront(hatenaRear, COLON).trim(); // e.g. over
final String secondSelectionExp = LdiSrl.substringFirstRear(hatenaRear, COLON).trim(); // e.g. mystic
if (isValidHatenaColonElement(determinationExp, firstSelectionExp, secondSelectionExp)) {
try {
final Object determinationResult = doHookPlainly(determinationExp, contextMap, container, resultType);
if (determinationResult instanceof Boolean) { // and not null
final Object selectedResult;
if ((Boolean) determinationResult) {
selectedResult = doHookPlainly(firstSelectionExp, contextMap, container, resultType);
} else {
selectedResult = doHookPlainly(secondSelectionExp, contextMap, container, resultType);
}
return selectedResult; // may be null yet (caller check needed)
}
} catch (RuntimeException continued) { // may be unexpected format? (give up)
logger.debug("Cannot parse it as hatena colon (continued): exp=" + exp, continued);
}
}
}
return null;
}

protected boolean mayBeSimpleHatenaColon(String exp) {
if (exp.contains(HATENA) && exp.contains(COLON)) { // has hatena colon
if (exp.indexOf(HATENA) < exp.indexOf(COLON)) { // correct order (not : ?)
if (LdiSrl.count(exp, HATENA) == 1 && LdiSrl.count(exp, COLON) == 1) { // no nest (simple only)
return true;
}
}
}
return false;
}

protected boolean isValidHatenaColonElement(String determinationExp, String firstSelectionExp, String secondSelectionExp) {
// expcet empty expression e.g. sea ? : mystic
return !determinationExp.isEmpty() && !firstSelectionExp.isEmpty() && !secondSelectionExp.isEmpty();
}

// ===================================================================================
// Basic Handling
// ==============
Expand Down Expand Up @@ -171,14 +230,14 @@ protected Object resolveSimpleNewExp(String exp, LaContainer container, Class<?>
try {
clazz = LdiReflectionUtil.forName(fqcn);
} catch (RuntimeException continued) { // may be framework bug
logger.debug("Failed to find class for the name: exp=" + exp + ", fqcn=" + fqcn, continued);
logger.debug("Failed to find class for the name of expression (continued): exp=" + exp + ", fqcn=" + fqcn, continued);
return null;
}
final Object instance;
try {
instance = LdiReflectionUtil.newInstance(clazz);
} catch (RuntimeException continued) { // may be framework bug
logger.debug("Failed to new instance (of expression): exp=" + exp + ", class=" + clazz, continued);
logger.debug("Failed to new instance of expression (continued): exp=" + exp + ", class=" + clazz, continued);
return null;
}
return instance;
Expand Down Expand Up @@ -355,7 +414,9 @@ protected Object resolveProviderConfig(String exp, LaContainer container, Class<

protected boolean isProviderConfigNoArgMethod(String exp) { // LastaFlute uses
// e.g. provider.config().getJdbcUrl()
return exp.startsWith(PROVIDER_GET) && exp.endsWith(METHOD_MARK) && !exp.contains("\"");
// provider.config().isDevelopmentHere() // @since 1.0.0
final boolean configPrefix = exp.startsWith(PROVIDER_GET) || exp.startsWith(PROVIDER_IS);
return configPrefix && exp.endsWith(METHOD_MARK) && !exp.contains("\"");
}

protected boolean isProviderConfigOrDefaultMethod(String exp) { // LastaFlute uses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,23 @@
import org.lastaflute.di.core.meta.impl.LaContainerImpl;
import org.lastaflute.di.unit.UnitLastaDiTestCase;
import org.lastaflute.jta.core.LaTransaction;
import org.lastaflute.jta.helper.timer.LjtTimeoutManager;

/**
* @author jflute
*/
public class SimpleExpressionPlainHookTest extends UnitLastaDiTestCase {

// ===================================================================================
// new Constructor
// ===============
public void test_hookPlainly_newConstructor_basic() {
// Simple new
// ==========
public void test_hookPlainly_simpleNew_basic() {
// ## Arrange ##
SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();
String exp = "new org.lastaflute.jta.core.LaTransaction()";
Map<String, Object> contextMap = new HashMap<>();
LaContainer container = createProviderContainer(new MyMockProvider(false));
Class<?> resultType = LaTransaction.class; // however unused
LaContainer container = createContainer();
Class<?> resultType = Object.class; // unused

// ## Act ##
Object result = hook.hookPlainly(exp, contextMap, container, resultType);
Expand All @@ -47,6 +48,41 @@ public void test_hookPlainly_newConstructor_basic() {
assertTrue(result instanceof LaTransaction);
}

// ===================================================================================
// Simple Type
// ===========
public void test_hookPlainly_simpleType_class_basic() {
// ## Arrange ##
SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();
String exp = "@org.lastaflute.jta.helper.timer.LjtTimeoutManager@class";
Map<String, Object> contextMap = new HashMap<>();
LaContainer container = createContainer();
Class<?> resultType = Object.class; // unused

// ## Act ##
Object result = hook.hookPlainly(exp, contextMap, container, resultType);

// ## Assert ##
assertNotNull(result);
assertTrue(LjtTimeoutManager.class.equals(result));
}

public void test_hookPlainly_simpleType_method_basic() {
// ## Arrange ##
SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();
String exp = "@org.lastaflute.jta.helper.timer.LjtTimeoutManager@getInstance()";
Map<String, Object> 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 LjtTimeoutManager);
}

// ===================================================================================
// Provider Config
// ===============
Expand Down Expand Up @@ -113,38 +149,65 @@ public void test_hookPlainly_providerConfig_isDetermination() { // since 1.0.0
// ===================================================================================
// Hatena Colon
// ============
public void test_hookPlainly_hatenaColon_integer_false() { // since 1.0.0
public void test_hookPlainly_hatenaColon_false() { // since 1.0.0
// ## Arrange ##
SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();
String exp = "provider.config().isDevelopmentHere() ? new java.lang.Integer(1) : new java.lang.Integer(2)";
String determinationExp = "provider.config().isDevelopmentHere()";
String firstSelectionExp = "new org.lastaflute.jta.core.LaTransaction()";
String secondSelectionExp = "@org.lastaflute.jta.helper.timer.LjtTimeoutManager@getInstance()";
String exp = determinationExp + " ? " + firstSelectionExp + " : " + secondSelectionExp;
Map<String, Object> contextMap = new HashMap<>();
LaContainer container = createProviderContainer(new MyMockProvider(false));
Class<?> resultType = Integer.class;
Class<?> resultType = Object.class; // unused

// ## Act ##
Object result = hook.hookPlainly(exp, contextMap, container, resultType);

// ## Assert ##
log("result: {}", result);
assertNotNull(result);
assertTrue(result instanceof LjtTimeoutManager);
}

public void test_hookPlainly_hatenaColon_true() { // since 1.0.0
// ## Arrange ##
SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();
String determinationExp = "provider.config().isDevelopmentHere()";
String firstSelectionExp = "new org.lastaflute.jta.core.LaTransaction()";
String secondSelectionExp = "@org.lastaflute.jta.helper.timer.LjtTimeoutManager@getInstance()";
String exp = determinationExp + " ? " + firstSelectionExp + " : " + secondSelectionExp;
Map<String, Object> contextMap = new HashMap<>();
LaContainer container = createProviderContainer(new MyMockProvider(true));
Class<?> resultType = Object.class; // unused

// ## Act ##
Object result = hook.hookPlainly(exp, contextMap, container, resultType);

// ## Assert ##
log("result: {}", result);
assertNotNull(result);
assertTrue(result instanceof Integer);
assertEquals(2, (int) result);
assertTrue(result instanceof LaTransaction);
}

public void test_hookPlainly_hatenaColon_integer_true() { // since 1.0.0
public void test_hookPlainly_hatenaColon_variousFormat() { // since 1.0.0
// ## Arrange ##
SimpleExpressionPlainHook hook = new SimpleExpressionPlainHook();
String exp = "provider.config().isDevelopmentHere() ? new java.lang.Integer(1) : new java.lang.Integer(2)";
Map<String, ? extends Object> contextMap = new HashMap<>();
String determinationExp = "provider.config().isDevelopmentHere()";
String firstSelectionExp = "provider.config().getJdbcUrl()";
String secondSelectionExp = "@org.lastaflute.jta.helper.timer.LjtTimeoutManager@class";
String exp = determinationExp + "\n ? " + firstSelectionExp + " \n: \n" + secondSelectionExp;
Map<String, Object> contextMap = new HashMap<>();
LaContainer container = createProviderContainer(new MyMockProvider(true));
Class<?> resultType = Integer.class;
Class<?> resultType = Object.class; // unused

// ## Act ##
Object result = hook.hookPlainly(exp, contextMap, container, resultType);

// ## Assert ##
log("result: {}", result);
assertNotNull(result);
assertTrue(result instanceof Integer);
assertEquals(1, (int) result);
assertTrue(result instanceof String);
assertEquals(MyMockConfig.JDBC_URL, result);
}

// ===================================================================================
Expand Down

0 comments on commit 3ccfd56

Please sign in to comment.