Skip to content

Conversation

aidanow-uni
Copy link

Thanks for your contribution to Apache Commons! Your help is appreciated!

Before you push a pull request, review this list:

  • Read the contribution guidelines for this project.
  • Read the ASF Generative Tooling Guidance if you use Artificial Intelligence (AI).
  • Run a successful build using the default Maven goal with mvn; that's mvn on the command line by itself.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible, but it is a best-practice.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body. Note that a maintainer may squash commits during the merge process.

Description of Issue
The PR addresses the JIRA issue titled 'Short-circuit operation of firstNonBlank and firstNonEmpty', a minor feature request originally proposed by Zhengkai Wang.

The existing methods firstNonBlank and firstNonEmpty in StringUtils.java evaluate all input strings immediately. This can lead to extra computational overhead when some arguments are expensive to compute, such as those involving remote API calls or database queries.

The feature request aims to introduce supplier-based variants of these methods, which support lazy evaluation and short-circuit behaviour.

Implementation
To resolve this, two new methods were added to StringUtils:

  1. firstNonBlankSupplier(Supplier... suppliers)
    • Iterates through suppliers in order
    • Returns immediately once a non-blank supplier is found
  2. firstNonEmptySupplier(Supplier... suppliers)
    • Similar to above, but checks for non-empty rather than non-blank strings.

Introducing new methods instead of modifying existing ones ensures backward compatibility with existing firstNonBlank and firstNonEmpty implementations. It avoids method overloading ambiguity and also creates a clear separation between eager and lazy variants.

Validation and Testing

  1. Basic functionality tests
    • Implemented in StringUtilsEmptyBlankTest.
    • Methods testFirstNonBlankSupplier() and testFirstNonEmptySupplier() validate:
      • Correct return of the first non-blank/non-empty string.
      • Handling of null inputs, empty arrays, and suppliers that return blank or empty values.
  2. Lazy evaluation tests
    • testFirstNonBlankSupplierLazyEvaluation() and testFirstNonEmptySupplierLazyEvaluation() verify short-circuit behavior using AtomicBoolean flags.
    • Confirms that once a valid result is found, subsequent suppliers are not invoked, ensuring performance efficiency.
  3. Safety guarantees
    • All methods include null checks to prevent NullPointerException.
    • Edge cases covered in original tests are preserved.

Introduced a new method:
- StringUtils.firstNonBlankSupplier(Supplier<String>... suppliers)

This variant of firstNonBlank accepts suppliers evaluated lazily in order until a non-blank value is found. It enables short-circuit behavior to avoid unnecessary or expensive computations.
Introduced a new method:
- StringUtils.firstNonEmptySupplier(Supplier<String>... suppliers)

This variant of firstNonEmpty accepts suppliers evaluated lazily in order until a non-empty value is found. It enables short-circuit behavior to avoid unnecessary or expensive computations.
Fixed a minor typo on method name:
- StringUtils.firstNonEmptySuppler -> StringUtils.firstNonEmptySupplier
Added unit tests in StringUtilsEmptyBlankTest to verify:
  - StringUtils.firstNonBlankSupplier()
  - StringUtils.firstNonEmptySupplier()

For each method, test:
  - Correct return values
  - null handling
  - Lazy evaluation / short-circuit behaviour
Fixed Checkstyle issues:
 - Local variables are declared 'final'
 - Reordered imports (Static imports go first)
 - LeftCurly '{' fixes
@garydgregory
Copy link
Member

@garydgregory
Copy link
Member

I don't see the need for such a utility. Can this even be reused in Lang? What's a use-case in another code base, like any other Commons component?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants