[
https://issues.apache.org/jira/browse/CAMEL-22894?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18082371#comment-18082371
]
Adriano Machado commented on CAMEL-22894:
-----------------------------------------
h1. Simple Language Function Evaluation Order
_Claude Code on behalf of Adriano Machado_
Based on the if-else chain in
{{SimpleFunctionExpression.doCreateSimpleExpression()}} starting at line 109 in
{{core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java}}.
h2. 1. Direct Literals (Highest Priority)
Lines 760-812. Exact string matches, no prefix parsing.
||Function||Description||
|{{body}}, {{in.body}}|Message body|
|{{bodyType}}|Body type name|
|{{prettyBody}}|Pretty-printed body|
|{{toJsonBody}}|Body as JSON|
|{{toPrettyJsonBody}}|Body as pretty JSON|
|{{bodyOneLine}}|Body on single line|
|{{originalBody}}|Original message body|
|{{id}}|Message ID|
|{{messageTimestamp}}|Message timestamp|
|{{exchangeId}}|Exchange ID|
|{{exchange}}|Exchange object|
|{{logExchange}}|Exchange for logging|
|{{exception}}|Exception object|
|{{exception.message}}|Exception message|
|{{exception.stacktrace}}|Exception stack trace|
|{{threadId}}|Current thread ID|
|{{threadName}}|Current thread name|
|{{hostname}}|Machine hostname|
|{{camelId}}|CamelContext name|
|{{routeId}}|Current route ID|
|{{fromRouteId}}|From route ID|
|{{routeGroup}}|Route group|
|{{stepId}}|Step ID|
|{{null}}|Null literal|
h2. 2. messageAs(type) - Line 435
With optional OGNL after the type.
h2. 3. Body and Header Functions - Lines 460-573
In order:
* {{bodyAs(type)}} (+ optional OGNL)
* {{mandatoryBodyAs(type)}} (+ optional OGNL)
* {{body}} + OGNL / {{in.body}} + OGNL (e.g., {{body.name}})
* {{headerAs(key, type)}}
* {{headers}} / {{in.headers}} (exact)
* {{headers.size()}} / {{headers.length()}}
* {{header.name}} / {{in.header.name}} / {{headers.name}} / {{in.headers.name}}
h2. 4. Variable Functions - Lines 575-633
* {{variableAs(key, type)}}
* {{variables}} (exact)
* {{variables.size()}} / {{variables.length()}}
* {{variable.name}}
h2. 5. function(name) / function(name, expression) - Lines 635-665
h2. 6. Custom Language Functions - Lines 667-758
* {{jq(expression)}} (+ optional input source)
* {{simpleJsonpath(expression)}} (+ optional input source)
* {{jsonpath(expression)}} (+ optional input source)
* {{xpath(expression)}} (+ optional input source)
h2. 7. camelContext + OGNL - Lines 145-152
h2. 8. exception + OGNL - Lines 155-162
h2. 9. exchangeProperty - Lines 165-189
Supports {{.name}}, {{[name]}}, {{:name}}, {{?name}} with optional OGNL.
h2. 10. System/Environment Properties - Lines 192-208
* {{sys.name}} (JVM system property)
* {{sysenv.name}} / {{sysenv:name}} (env var)
* {{env.name}} / {{env:name}} (env var)
h2. 11. exchange + OGNL - Lines 211-218
h2. 12. Wrapper and Prefix Functions - Lines 220-290
* {{pretty(expression)}}
* {{toJson(expression)}} / {{toPrettyJson(expression)}}
* {{file:}} prefix ({{file:name}}, {{file:ext}}, {{file:path}},
{{file:onlyname}}, {{file:parent}}, {{file:absolute}}, {{file:modified}},
{{file:size}}, etc.)
* {{date:}} prefix ({{date:now}}, {{date:millis}}, {{date:exchangeCreated}},
{{date:header.xxx}}, {{date:variable.xxx}}, {{date:exchangeProperty.xxx}},
{{date:file}})
* {{date-with-timezone:command:timezone:pattern}}
h2. 13. bean: - Lines 293-345
{{bean:name}}, {{bean:name.method}}, {{bean:name::staticMethod}},
{{bean:type:ClassName}} with optional {{?method=}} / {{?scope=}}.
h2. 14. Property Functions - Lines 348-375
* {{propertiesExist:key}} / {{propertiesExist:!key}}
* {{properties:key}} / {{properties:key:defaultValue}}
h2. 15. ref:beanName - Lines 378-381
h2. 16. type:className / type:className.FIELD - Lines 384-390
h2. 17. Built-in Dispatcher Functions (SimpleFunctionDispatcher)
* {{random(max)}} / {{random(min, max)}}
* {{skip(number)}}
* {{collate(group)}}
* {{join(separator, prefix, expression)}}
h2. 18. Miscellaneous Functions - Lines 850-1738
In order:
* {{setHeader(name, exp)}} / {{setHeader(name, type, exp)}}
* {{setVariable(name, exp)}} / {{setVariable(name, type, exp)}}
* {{replace(from, to, exp)}}
* {{substring(num)}} / {{substring(num, num)}} / {{substring(num, num, exp)}}
* {{substringBefore(exp)}} / {{substringBefore(exp, exp)}}
* {{substringAfter(exp)}} / {{substringAfter(exp, exp)}}
* {{substringBetween(after, before)}} / {{substringBetween(exp, after, before)}}
* {{contains(exp, text)}} / {{contains(text)}}
* {{range(min, max)}} / {{range(max)}}
* {{distinct(val...)}}
* {{reverse(val...)}}
* {{shuffle(val...)}}
* {{split(separator)}} / {{split(exp, separator)}}
* {{sort(reverse)}} / {{sort(exp, reverse)}}
* {{forEach(exp, exp)}}
* {{filter(exp, exp)}}
* {{listAdd(source, exp)}} / {{listAdd(exp)}}
* {{listRemove(source, exp)}} / {{listRemove(exp)}}
* {{mapAdd(key, exp)}} / {{mapAdd(exp, key, exp)}}
* {{mapRemove(key)}} / {{mapRemove(exp, key)}}
* {{isEmpty(exp)}} / {{isEmpty()}}
* {{isAlpha(exp)}} / {{isAlpha()}}
* {{isAlphaNumeric(exp)}} / {{isAlphaNumeric()}}
* {{isNumeric(exp)}} / {{isNumeric()}}
* {{not(exp)}} / {{not()}}
* {{kindOfType(exp)}} / {{kindOfType()}}
* {{quote(exp)}} / {{quote()}}
* {{safeQuote(exp)}} / {{safeQuote()}}
* {{unquote(exp)}} / {{unquote()}}
* {{trim(exp)}} / {{trim()}}
* {{val(exp)}}
* {{capitalize(exp)}} / {{capitalize()}}
* {{pad(exp, len)}} / {{pad(exp, len, separator)}} / {{pad(len)}}
* {{concat(exp, exp)}} / {{concat(exp, exp, separator)}} / {{concat(exp)}}
* {{throwException(msg)}} / {{throwException(type, msg)}}
* {{assert(exp, msg)}}
* {{convertTo(type)}} / {{convertTo(exp, type)}} with optional OGNL
* {{uppercase(exp)}} / {{uppercase()}}
* {{lowercase(exp)}} / {{lowercase()}}
* {{length(exp)}} / {{length()}}
* {{size(exp)}} / {{size()}}
* {{normalizeWhitespace(exp)}} / {{normalizeWhitespace()}}
* {{messageHistory()}} / {{messageHistory(boolean)}}
* {{uuid()}} / {{uuid(type)}}
* {{hash(value)}} / {{hash(value, algorithm)}}
* {{empty(type)}} / {{newEmpty(type)}}
* {{iif(predicate, trueExp, falseExp)}}
* {{list(val...)}}
* {{map(key1, value1, ...)}}
* {{load(name)}}
h2. 19. Math Functions - Lines 1740-1815
* {{abs(exp)}} / {{abs()}}
* {{floor(exp)}} / {{floor()}}
* {{ceil(exp)}} / {{ceil()}}
* {{sum(val...)}}
* {{max(val...)}}
* {{min(val...)}}
* {{average(val...)}}
h2. 20. External Component Functions (SimpleFunctionDispatcher)
Require optional JARs on classpath:
* *camel-attachments*: {{attachments}}, {{clearAttachments}},
{{setAttachment(key, exp)}}, {{attachment(key)}}
* *camel-base64*: {{base64Encode(exp)}}, {{base64Decode(exp)}}
* *camel-jsoup*: {{htmlClean(exp)}}, {{htmlParse(exp)}}, {{htmlDecode(exp)}}
h2. 21. Plugin Registry Custom Functions - Lines 414-426
Functions registered via {{SimpleFunctionRegistry}} (lowest priority).
h2. Key Takeaway
Exact literal matches ({{body}}, {{exchange}}, {{null}}, etc.) always win. When
two patterns could overlap (e.g., {{body}} vs {{bodyAs}}), the exact literal is
resolved first in step 1, while the parameterized form is checked later in step
3. The OGNL-based variants (e.g., {{exchange.in.body}}) are checked after the
direct literals but before prefix-based functions like {{file:}} and {{date:}}.
> Refactor Simple/CSimple language implementation for better maintainability
> --------------------------------------------------------------------------
>
> Key: CAMEL-22894
> URL: https://issues.apache.org/jira/browse/CAMEL-22894
> Project: Camel
> Issue Type: Task
> Components: camel-core
> Reporter: Guillaume Nodet
> Assignee: Adriano Machado
> Priority: Major
> Fix For: 4.21.0
>
>
> h2. Background
> The Simple and CSimple language implementations have grown organically over
> time and now present significant maintainability challenges. This issue is to
> discuss potential refactoring approaches with the community.
> h2. Current State Analysis
> || File || Lines || Concern ||
> | SimpleFunctionExpression.java | 3,713 | Dual responsibility: runtime + code
> generation |
> | CSimpleHelper.java | 1,199 | Static helper methods for compiled expressions
> |
> | SimpleFunctionStart.java | 707 | Function block handling |
> | SimpleTest.java | 3,595 | Simple language tests |
> | OriginalSimpleTest.java | 3,344 | Near-duplicate CSimple tests |
> | *Total* | *~12,500* | |
> h2. Key Issues
> h3. 1. Massive Class with Dual Responsibility
> {{SimpleFunctionExpression.java}} (3,713 lines) contains both:
> - {{createExpression()}} path for runtime Simple language
> - {{createCode()}} path for compile-time CSimple language
> Each function (body, header, variable, date, bean, file, math, etc.) is
> implemented *twice* with similar parsing logic but different output
> generation.
> h3. 2. Test Duplication
> {{SimpleTest.java}} and {{OriginalSimpleTest.java}} are essentially copies
> (~7,000 lines combined) with "simple" vs "csimple" language. When adding new
> features (e.g., ternary operator in CAMEL-22873), tests must be manually
> duplicated.
> h3. 3. Parallel Implementation Pattern
> Every AST node supporting csimple must implement both {{createExpression()}}
> and {{createCode()}}. Adding the ternary operator required ~140 lines of
> nearly duplicate code.
> h3. 4. String-Based Code Generation
> Code generation uses error-prone string concatenation:
> {code:java}
> return "Object value = " + exp + ";\n return convertTo(exchange, " +
> type + ", value)";
> {code}
> h2. Proposed Refactoring Approach
> h3. 1. Declarative Function Registry
> Replace if-else chains with declarative function definitions:
> {code:java}
> @SimpleFunction(name = "headerAs", syntax = "headerAs(key, type)")
> public class HeaderAsFunction implements FunctionDescriptor {
> Expression interpret(FunctionContext ctx) { ... }
> CodeFragment compile(FunctionContext ctx) { ... }
> }
> {code}
> h3. 2. Visitor Pattern for AST Processing
> Separate parsing from output generation:
> - Parse once → produce AST
> - InterpreterVisitor → produces Expression (Simple)
> - CodeGenVisitor → produces Java code (CSimple)
> h3. 3. Unified Test Framework
> Use JUnit 5 parameterized tests:
> {code:java}
> @ParameterizedTest
> @ValueSource(strings = {"simple", "csimple"})
> void testTernaryOperator(String languageName) {
> // Single test runs on both backends
> }
> {code}
> h3. 4. Type-Safe Code Generation
> Replace string concatenation with builders that can validate generated code.
> h2. Expected Benefits
> || Aspect || Current || After Refactoring ||
> | Add new function | Edit 2 places in 3700-line file | Create single class |
> | Add new operator | Edit multiple AST classes + tests | One AST node +
> visitor methods |
> | Test maintenance | Duplicate ~3500 lines | Single parameterized suite |
> | Estimated LOC | ~12,500 | ~6,000-7,000 |
> h2. Migration Approach
> This could be done incrementally:
> 1. Create unified test framework (low risk, immediate benefit)
> 2. Extract function registry, migrate functions one at a time
> 3. Introduce visitor pattern for new features
> 4. Migrate existing AST nodes to visitor pattern
> 5. Clean up legacy code
> h2. Questions for Discussion
> 1. Is there appetite for this level of refactoring?
> 2. Should we prioritize any specific aspect (e.g., tests first)?
> 3. Are there backward compatibility concerns to consider?
> 4. Should this be a GSoC or similar project given the scope?
> Related: CAMEL-22873 (ternary operator) exposed the maintenance burden when
> adding new features.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)