WW-5685 Stop dropping the rest of a -conversion.properties file after the first already-mapped key - #1860
Merged
Conversation
…e file DefaultConversionFileProcessor.process skipped keys already present in the converter mapping with a break rather than a continue, so the first already-mapped entry ended the loop and every remaining entry in that -conversion.properties file was silently dropped. No warning, no error; the affected properties just fell back to default conversion. XWorkConverter.buildConverterMapping walks the class, then its interfaces, then its superclass, passing one accumulating mapping into each call, so a key claimed earlier in that walk aborted a later file outright. Annotation derived entries land in the same map and could abort a file the same way. Properties extends Hashtable and entrySet() has no defined order, so which entries survived depended on hash order rather than file order. WW-3871 fixed the same defect in the annotation path (PR #1812, 7.3.0); this is the properties-file path that change did not touch. Two tests, both mutation-checked by reverting continue to break: DefaultConversionFileProcessorTest derives the colliding key from the actual iteration order at run time and pre-maps whichever key comes first, so break registers nothing at all. That makes it discriminating on any JDK in any hash order, rather than depending on where a fixture's colliding key happens to land. XWorkConverterTest covers the realistic trigger through the hierarchy walk, a subclass and superclass file sharing a key. This one does depend on the fixture's iteration order, and the first version of it was vacuous -- the shared key landed last, so break dropped nothing and the test passed against the unfixed code. The key is now named so that it is read first (verified identical on Temurin 17, 21 and 25), and the test asserts that precondition, so a future reordering fails loudly instead of going quiet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes WW-5685
DefaultConversionFileProcessor.processskipped keys already present in the converter mapping with abreakrather than acontinue, so the first already-mapped entry ended the loop and every remaining entry in that-conversion.propertiesfile was silently dropped — no warning, no error, the affected properties simply fell back to default conversion.if (mapping.containsKey(key)) { - break; + continue; }Why it triggers in practice
XWorkConverter.buildConverterMappingwalks the class, then its interfaces, then its superclass, passing one accumulating mapping into eachaddConverterMappingcall. So a key claimed earlier in that walk aborts a later file outright:Foo-conversion.propertiesdeclaresbarFooBase-conversion.propertiesdeclaresbar,baz,quxbaris already mapped →bazandquxare never registeredAnnotation-derived entries land in the same map, so an
@TypeConversionon the class could abort its properties file the same way.PropertiesextendsHashtableandentrySet()has no defined order, so which entries survived depended on hash order rather than file order — which is what makes this hard to diagnose from the symptom.WW-3871 fixed the identical defect in the annotation path (
XWorkConverter, #1812, shipped 7.3.0). This is the properties-file path that change did not touch. The remainingbreakatXWorkConverter:761is unrelated and correct — it exits an interface search after a hit.Testing — and a vacuous test caught by mutation
DefaultConversionFileProcessorTest(new). The ticket suggested a fixture "whose first key is already mapped", butHashtableordering means no fixture can pin down which key is read first. So the test derives the colliding key from the actual iteration order at run time and pre-maps that one:With
break, the loop stops on that first entry and registers nothing at all — discriminating on any JDK, in any hash order, with no reliance onHashtablelayout. A second test asserts every entry registers when nothing is pre-mapped, guarding against a fix that skips too much.XWorkConverterTest.testPropertiesEntriesAfterAKeyCollisionAreStillRegisteredcovers the realistic hierarchy trigger. Worth flagging: the first version of this test was vacuous. Its shared key hashed to the last position, sobreakdropped nothing and it passed against the unfixed code — only the mutation check exposed it. The key is renamed so it is read first (verified identical on Temurin 17.0.14, 21.0.7 and 25.0.1), and the test now asserts that precondition explicitly:so a future reordering fails loudly instead of going quiet.
Both tests were mutation-checked by reverting
continuetobreak; both fail.mvn test -DskipAssembly -pl core— 3193 tests, 0 failures, 0 errors.Compatibility
Behaviour changes as intended: entries previously dropped now register. That matches what WW-3871 shipped for the annotation path in 7.3.0. The
containsKeyprecedence rule itself is untouched — first source to claim a key still wins, so nothing that already worked is overridden. Worth a Version Notes line; no Migration Guide entry needed.🤖 Generated with Claude Code