Skip to content

CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes#23719

Open
ammachado wants to merge 2 commits into
apache:mainfrom
ammachado:CAMEL-22894
Open

CAMEL-22894: Split SimpleExpressionBuilder into domain-aligned builder classes#23719
ammachado wants to merge 2 commits into
apache:mainfrom
ammachado:CAMEL-22894

Conversation

@ammachado
Copy link
Copy Markdown
Contributor

@ammachado ammachado commented Jun 3, 2026

Description

SimpleExpressionBuilder had grown into a ~3500-line monolith housing expression factories for every domain in the simple language. This PR splits it into six focused, domain-aligned builder classes:

New class Responsibility
CollectionExpressionBuilder List, array, and map expressions
DateExpressionBuilder Date/time and exchange creation-time expressions
FileExpressionBuilder File-related expressions (size, name, path, …)
MathExpressionBuilder Arithmetic and random-number expressions
MiscExpressionBuilder Exchange, header, property, variable, and other misc expressions
OgnlExpressionBuilder OGNL navigation chain support
StringExpressionBuilder String manipulation expressions (join, skip, collate, …)

SimpleExpressionBuilder itself is removed; each function factory (*FunctionFactory) now imports from the appropriate domain class.

A subsequent commit fixes several pre-existing bugs that were surfaced and reviewed after the split:

  • shuffleExpression.toString() returned "reverse(...)" (copy-paste error) — fixed to "shuffle(...)"
  • mapExpression.toString() printed compiled Expression[] object refs instead of source pairs — fixed
  • collateExpression(String, String) overwrote the exp field inside evaluate(), double-wrapping the iterator on reuse and creating a data race under concurrent route execution — fixed using a local variable
  • throwExceptionExpression catch block caught the RuntimeException it had just thrown and double-wrapped it, breaking typed onException handlers — restructured so RuntimeExceptions propagate unwrapped
  • skipExpression(String, String) unboxed a potentially-null Integer without a null check — now throws IllegalArgumentException with a clear message
  • padExpression threw NullPointerException when the string or width expression resolved to null — now returns null for a null string and throws IllegalArgumentException for a null width
  • floor() and ceil() cast Math.floor/Math.ceil to int, silently overflowing for values outside Integer range — fixed to return long
  • ${range(max)} in the csimple code path used min=0 while the simple interpreter used min=1 — unified to min=1 to match documented behaviour

Tests added to each fix to document the pre-existing bug and verify the correction.

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it).

JIRA: https://issues.apache.org/jira/browse/CAMEL-22894

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.

Claude Code on behalf of Adriano Machado

ammachado added 2 commits June 2, 2026 22:04
…r classes

Replace the monolithic SimpleExpressionBuilder (3,523 lines, 100+ static
methods) with seven focused builder classes:

- CollectionExpressionBuilder: list, map, filter, sort, set-header, etc.
- DateExpressionBuilder:       date/time expressions with offset support
- FileExpressionBuilder:       file name, path, size, extension, etc.
- MathExpressionBuilder:       abs, floor, ceil, sum, min, max, average
- MiscExpressionBuilder:       uuid, random, type, hash, convert, etc.
- OgnlExpressionBuilder:       OGNL body/header/variable/exchange navigation
- StringExpressionBuilder:     pad, trim, substring, replace, contains, etc.

KeyedOgnlExpressionAdapter (inner class) moves to OgnlExpressionBuilder.
OFFSET_PATTERN static field moves to DateExpressionBuilder.

All 22 function factories and ast/SimpleFunctionExpression updated to import
their respective domain builder. AttachmentExpressionBuilder (camel-attachments)
updated to use OgnlExpressionBuilder.KeyedOgnlExpressionAdapter.

Pure mechanical refactor: no behavioral change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
…r split

- shuffleExpression.toString() returned "reverse(...)" due to a copy-paste
  error; now returns "shuffle(...)"
- mapExpression.toString() printed compiled Expression[] object refs instead
  of the source String[] pairs; now prints the source pairs
- collateExpression(String,String) overwrote the 'exp' field inside
  evaluate(), wrapping an already-grouped iterator on every call and causing
  a data race under concurrent route execution; fixed by using a local
  variable for the grouped expression
- throwExceptionExpression's catch(Exception) block caught the RuntimeException
  it had just thrown and double-wrapped it, breaking typed onException handlers;
  restructured so RuntimeExceptions propagate unwrapped
- skipExpression(String,String) unboxed a potentially-null Integer without a
  null check; now throws IllegalArgumentException with a clear message
- padExpression returned NPE when the string or width expression was null;
  now returns null for a null string and throws IllegalArgumentException for
  a null width
- floor() and ceil() cast Math.floor/ceil to int, silently overflowing for
  values outside Integer range; now returns long
- range(max) in the csimple code path used min=0 while the simple interpreter
  used min=1; unified to min=1 to match documented behaviour

Tests added to document each pre-existing bug and verify the fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
@ammachado ammachado marked this pull request as ready for review June 3, 2026 02:07
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

🧪 CI tested the following changed modules:

  • components/camel-attachments
  • core/camel-core-languages
  • core/camel-core

ℹ️ Dependent modules were not tested because the total number of affected modules exceeded the threshold (50). Use the test-dependents label to force testing all dependents.

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • core/camel-core: 2 test(s) disabled on GitHub Actions
Build reactor — dependencies compiled but only changed modules were tested (3 modules)
  • Camel :: Attachments
  • Camel :: Core
  • Camel :: Core Languages

⚙️ View full build and test results

Copy link
Copy Markdown
Contributor

@davsclaus davsclaus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the final step of the CAMEL-22894 refactoring series. 34 files changed, splitting the 3523-line SimpleExpressionBuilder monolith into 7 domain-aligned builder classes, plus 8 pre-existing bug fixes.

Structural split verified:

  • All 18 function factory files updated to import from the correct new builder class
  • camel-attachments (only external reference) updated
  • SimpleFunctionExpression file references updated
  • No remaining references to the deleted SimpleExpressionBuilder (verified with grep)
  • Two logical commits properly separated: structural split, then bug fixes

Bug fixes verified against old code:

  • throwException double-wrapping: old catch(Exception) caught the just-thrown RuntimeException and re-wrapped it, breaking typed onException handlers
  • collate field overwrite: exp = groupIteratorExpression(exp, ...) mutated state, causing double-wrapping on reuse and a data race under concurrent execution
  • shuffle.toString() copy-paste error: returned "reverse(...)" instead of "shuffle(...)"
  • map.toString(): printed compiled Expression[] object refs instead of source pairs
  • floor/ceil overflow: (int) Math.floor(...) silently overflowed for values > Integer.MAX_VALUE — fixed to (long)
  • skip/pad null safety: NPE on null unboxing — now throws IllegalArgumentException with clear message
  • range(max) csimple/simple inconsistency: csimple used min=0, simple used min=1 — unified to min=1

Each bug fix has a dedicated test documenting the pre-existing bug and verifying the correction.

Non-blocking suggestions:

  • Consider an upgrade guide note mentioning the SimpleExpressionBuilder removal and new class names — the class is internal (camel-core-languages, not camel-api) but advanced users building custom Simple functions might reference it directly
  • The floor/ceil return type change from int to long and range(N) starting at 1 instead of 0 in csimple are behavioral changes worth noting in the upgrade guide

This review does not replace specialized AI review tools (CodeRabbit, Sourcery) or static analyzers (SonarCloud).

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Claus Ibsen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants