Skip to content

Improvement/reflection and performance fixes#658

Merged
fborriello merged 12 commits into
masterfrom
improvement/reflection-and-performance-fixes
Apr 1, 2026
Merged

Improvement/reflection and performance fixes#658
fborriello merged 12 commits into
masterfrom
improvement/reflection-and-performance-fixes

Conversation

@fborriello

@fborriello fborriello commented Apr 1, 2026

Copy link
Copy Markdown
Member

Description

bull-bean-transformer/pom.xml

  • Set slf4j-api scope to compile to enable @slf4j in production code (parent manages it as test).

Populator.java

  • Introduced SHARED_REFLECTION_UTILS and SHARED_CLASS_UTILS static singletons. Both utilities are stateless (all cache state lives in static fields), so sharing a single instance across all Populator instances avoids redundant allocations on every PopulatorFactory.getPopulator() call.

TransformerImpl.java

  • ThreadLocal stack: replaced ThreadLocal rootSourceObj with ThreadLocal<Deque> rootSourceStack using ArrayDeque, with explicit push/pop in finally blocks. Preserves isRoot = stack.isEmpty() semantics for nested transformations.
  • EffectiveSource record: replaced private inner class with a Java record for conciseness.
  • Dead variable removed: eliminated dead K k = null in getConstructorValuesFromFields, replaced with (K) null cast inline.
  • Debug logging: added @slf4j + log.debug(...) in getSourceFieldValue when a field is silently not found, rather than failing silently.
  • Null-safe caching sentinels: fixed ineffective null caching in getDestFieldName (using ABSENT_FIELD_NAME = "\0"), getSourceFieldType (using AbsentFieldType sentinel class), and getPrimitiveTypeTransformer (using a Boolean.TRUE marker stored under a secondary key cacheKey + ".nullConversion"). Previously, null results caused a cache miss on every call.
  • getConstructorArgsValues refactor: replaced side-effectful IntStream.forEach + pre-allocated array with mapToObj().toArray(), making it safe to parallelize in the future.
  • Test improvements

    • AbstractBeanTransformerTest: replaced three individual reset calls with underTest.reset() + underTest.resetFieldsTransformer() with explanatory comments.
    • Added targeted tests to reach 100% JaCoCo coverage:
    • testResetFieldsTransformationSkipWorksProperly — covers resetFieldsTransformationSkip()
    • testGetSourceFieldTypeReturnsCachedNullForAbsentField — covers the ABSENT_SOURCE_FIELD_TYPE sentinel cache-hit branch
    • testGetDestFieldNameReturnsCachedNullForParameterWithNoAnnotation — covers the ABSENT_FIELD_NAME sentinel cache-hit branch
    • testAutomaticPrimitiveTypeTransformationCachesNullConversionForSameTypeFields — covers the null-marker cache-hit branch in getPrimitiveTypeTransformer
    • testCanBeInjectedByConstructorParamsLambdaBranchesWorkCorrectly — covers the areParameterNamesAvailable=false branches of the || expression inside the canBeInjectedByConstructorParams lambda

    How Has This Been Tested?

    • mvn verify passes on bull-bean-transformer — all JaCoCo checks met (methods=1.0, branches, lines, instructions)
    • Full mvn verify on the snapshot passes with BUILD SUCCESS
    • All 114 tests pass (0 failures, 0 errors)

    Checklist:

    • The branch follows the best practices naming convention:
      • Use grouping tokens (words) at the beginning of your branch names.
        • feature: Feature I'm adding or expanding
        • bug: Bugfix or experiment
        • wip: Work in progress; stuff I know won't be finished soon
    • My code follows the style guidelines of this project
    • I have performed a self-review of my own code
    • I have commented on my code, particularly in hard-to-understand areas
    • I have made corresponding changes to the documentation
    • My changes generate no new warnings
    • Any dependent changes have been merged and published in downstream modules
    • My changes have no bad impacts on performances
    • My changes have been tested through Unit Tests and the overall coverage has not decreased. Check the coverall report for your branch: here
    • Any implemented change has been added in the CHANGELOG.md file
    • Any implemented change has been added in the CHANGELOG-JDK11.md file
    • My changes have been applied on a separate branch too with compatibility with Java 11. Follow this instructions for help.
    • The maven version has been increased.

#2 - ThreadLocal<Object> → ThreadLocal<Deque<Object>> for rootSourceStack.
     Explicit push/pop in finally blocks replaces the old set/remove pattern.
     Semantics are unchanged (isRoot = stack.isEmpty()); the Deque structure
     documents the call-stack nature and prepares for future re-entrancy fixes.
     Comment explains the current limitation for FieldTransformer callbacks.

#3 - EffectiveSource private inner class replaced with a Java record.
     Eliminates boilerplate constructor/field declarations; immutability is
     enforced by the language.

#4 - Removed confusing dead variable `K k = null` in getConstructorValuesFromFields.
     null is passed directly, making intent obvious.

#5 - Added @slf4j (SLF4J promoted from test to compile scope) and a DEBUG log
     in getSourceFieldValue when a field is missing but a transformer is defined.
     This makes previously silent null propagation into transformers visible.

#7 - AbstractBeanTransformerTest.beforeMethod() consolidated: replaced three
     individual reset calls with underTest.reset() + underTest.resetFieldsTransformer().
     reset() atomically replaces all settings; resetFieldsTransformer() additionally
     clears the shared static CacheManager entries that reset() does not touch.

#8 - Populator: ReflectionUtils and ClassUtils are stateless (all state in static
     caches. Two private static final singletons back the instance fields, so
     PopulatorFactory no longer allocates fresh utility instances per field
     transformation.

#9 - Fixed ineffective null caching in three methods:
     * getDestFieldName: ABSENT_FIELD_NAME (0) sentinel avoids a cache miss
       on every call when a constructor parameter has no compiled name and no
       @ConstructorArg annotation.
     * getSourceFieldType: AbsentFieldType sentinel class avoids a cache miss
       when a source field is absent and default-value mode is active.
     * getPrimitiveTypeTransformer: NO_OP_FIELD_TRANSFORMER_SENTINEL (identity
       FieldTransformer) avoids a cache miss when no type conversion is needed.

#11 - getConstructorArgsValues: replaced side-effectful IntStream.forEach +
      pre-allocated array with IntStream.mapToObj().toArray(). The stream
      now owns index ordering, making it safe to enable .parallel() in the
      future without introducing data races.
EOF
)
- Replace NO_OP_FIELD_TRANSFORMER_SENTINEL (FieldTransformer with an
  unreachable lambda body) with a two-key cache approach in
  getPrimitiveTypeTransformer: a Boolean.TRUE marker stored under
  cacheKey + ".nullConversion" represents a cached null result,
  eliminating the uncoverable lambda$static$0 method.
- Add testResetFieldsTransformationSkipWorksProperly to cover the
  previously uncalled resetFieldsTransformationSkip() method.
- Add testGetSourceFieldTypeReturnsCachedNullForAbsentField to cover
  the ABSENT_SOURCE_FIELD_TYPE sentinel cache-hit branch.
- Add testGetDestFieldNameReturnsCachedNullForParameterWithNoAnnotation
  to cover the ABSENT_FIELD_NAME sentinel cache-hit branch.
- Add testAutomaticPrimitiveTypeTransformationCachesNullConversionForSameTypeFields
  to cover the null-marker cache-hit branch in getPrimitiveTypeTransformer.
- Add testCanBeInjectedByConstructorParamsLambdaBranchesWorkCorrectly
  to cover the areNamesAvailable=false branches of the || expression
  inside canBeInjectedByConstructorParams lambda.

All JaCoCo coverage checks now pass (methods=1.0, branches, lines, etc.).
Raise all JaCoCo coverage ratios to 1.0 (instructions, branches,
complexity, lines, methods, classes) and add the tests required to
achieve full branch coverage across bull-common and bull-bean-transformer.

bull-common / ClassUtilsTest:
- Cover isSynthetic() true branch via lambda (hidden/synthetic class)
- Cover isPrimitiveTypeArray false branch (non-array class)
- Cover getDefaultTypeValue false branch (non-primitive type)
- Cover isSpecialType enum branch via ClassType enum
- Cover getPrivateFields null-superclass and static-field branches
- Cover getConcreteClass non-interface field branch
- Cover hasFinalFields null-superclass branch
- Cover getInstance happy-path branch
- Add proxy import → lambda approach for synthetic class

bull-common / ReflectionUtilsTest:
- Cover isGetter/isSetter edge cases (void return, non-void return, params)
- Cover getGenericFieldType non-parameterised field → null branch
- Cover getMapGenericType wildcard and complex-map branches
- Cover getGenericClassType empty type-arguments branch (via reflection)
- Cover getArgumentTypeClass List-instance branch and non-WildcardType path
- Cover getFieldValue "class" fallback to getter method

bull-common / MutableToFooAdvFields:
- Add setFluent() (non-void setter) and getNone() (void getter-like method)
  to enable edge-case branch coverage in isSetter/isGetter

bull-bean-transformer / MutableObjectTransformationTest:
- Add testSkipTransformationForFieldWithNoArgsDoesNothing (empty varargs)
- Replace null-marker cache test to use a source missing a primitive field
  with setDefaultValueForMissingField=true, so the null-marker is actually
  stored and retrieved on the second transform call

bull-bean-transformer / BeanTransformerTest:
- Add testResolveEffectiveSourceWithMappingAndEmptyStackFallsThrough:
  covers mapped!=null && stack.isEmpty() false branch via reflection
- Add testVoidTransformWithPrePopulatedStackDoesNotPushAgain:
  covers isRoot=false branches in void transform(T,K,String) via
  pre-populating the ThreadLocal stack with reflection

bull-bean-transformer / PopulatorFactoryTest:
- Add testGetPopulatorReturnsOptionalPopulatorWhenOnlyDestIsOptional:
  covers source!=Optional, dest==Optional branch

bull-bean-transformer / ArrayPopulatorTest:
- Add Object[] test cases for isPrimitiveTypeArray=false+isPrimitiveOrSpecialType=true
  and for lambda TRUE branch (primitive element in Object array)

bull-bean-transformer / CollectionPopulatorTest (new):
- Add direct unit test for CollectionPopulator covering toSet() branch
  when fieldType is Set
Add testAutomaticPrimitiveTypeTransformerCacheHitOnSecondTransform to
cover the fromCache.isPresent() == true branch (lines 696-697) by
transforming fromFooPrimitiveTypes twice in the same test, so the
second call hits the cached FieldTransformer.
…e coverage

Add ImmutableToFooWithStaticField sample class (non-static final field +
static non-final field) and two new DataProvider entries in ClassUtilsTest:

- dataGetPrivateFinalFieldsTesting: add CLASS_WITH_STATIC_FIELDS (expected=0)
  → IS_FINAL_AND_NOT_STATIC_FIELD evaluates isFinal=true/isStatic=true → false
- dataGetClassTypeTesting: add ImmutableToFooWithStaticField (expected=IMMUTABLE)
  → hasNotFinalFields scans the static non-final field, evaluating
    !isFinal=true/!isStatic=false → false (second && operand now exercised)

These two missing branches were the only gap preventing 100% in the
jacoco:report-aggregate output (Branches 490/492, Complexity 784/786).
@fborriello fborriello requested a review from a team as a code owner April 1, 2026 12:44
@github-actions

github-actions Bot commented Apr 1, 2026

Copy link
Copy Markdown

Test Coverage Report

Coverage Baseline Build Diff
Lines 100.00% 1225/1225 100.00% 1236/1236 +0.00% 💙
Branches 94.81% 457/482 100.00% 492/492 +5.19% 💚
Instructions 99.79% 6070/6083 100.00% 6125/6125 +0.21% 💚
Complexity 96.93% 757/781 100.00% 786/786 +3.07% 💚
Methods 100.00% 540/540 100.00% 540/540 +0.00% 💙
Classes 100.00% 50/50 100.00% 50/50 +0.00% 💙
Normalised Score 98.59% 100.00% +1.41% 💚

Replace the two-column (% | count) layout with a single combined cell
showing '100.00% (492/492)' for both Baseline and Build, and drop the
now-redundant empty header columns.
@fborriello fborriello merged commit a9dc5bf into master Apr 1, 2026
10 checks passed
@fborriello fborriello deleted the improvement/reflection-and-performance-fixes branch April 1, 2026 13:51
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.

1 participant