Skip to content

Fix initializr localization bundle path (NPE in MyAppName.init)#4852

Merged
shai-almog merged 5 commits intomasterfrom
fix-initializr-l10n-bundles
May 2, 2026
Merged

Fix initializr localization bundle path (NPE in MyAppName.init)#4852
shai-almog merged 5 commits intomasterfrom
fix-initializr-l10n-bundles

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • Fixes the NullPointerException in Resources.getL10N when running an initializr project generated with the Include localization bundles option. The bundles were written to common/src/main/resources/ but the CN1 maven plugin's CSS compiler scans common/src/main/l10n/ (or i18n/), so they were never baked into theme.res. Moving them to src/main/l10n/ makes the data actually present in the resource file at runtime.
  • Hardens the framework's Resources.getL10N / listL10NLocales / l10NLocaleSet to return null instead of NPE-ing when the bundle id is missing -- defensive across all projects, not just initializr ones.
  • Strengthens initializr CI so the regression can't ship again: the matrix test asserts bundles land under l10n/ (and explicitly not under resources/), and the integration build test opens theme.res after mvn compile and verifies the messages L10N data is present for default + Hebrew locales.
  • While here, the simulator's CSS watcher / component tree inspector now prefer the version-pinned designer jar from m2 (resolved via the running codenameone-core jar's path) over ~/.codenameone/designer_1.jar, with a clear warning on the legacy fallback. The build-time CSS goal already pinned via getDesignerJar(); this only changes the simulator runtime path.
  • CompileCSSMojo forks the CSS compiler with INFO log level instead of DEBUG so subprocess stack traces are visible without -X. Addresses the diagnosability gap behind issue Unable to launch simulator because of Java.lang.StringIndexOutOfBoundsException in latest as of reporting (7.0.236) #4850 (does not fix that bug -- the underlying CSS compiler exception is still in there, but next report will arrive with a usable stack trace).
  • The four initializr pom templates (barebones, kotlin, grub, tweet) replace skipexisting=\"true\" with usetimestamp=\"true\" on the UpdateCodenameOne.jar download so the bootstrapper refreshes when newer instead of pinning forever.

Files changed

  • CodenameOne/src/com/codename1/ui/util/Resources.java -- null-safe getL10N / listL10NLocales / l10NLocaleSet.
  • Ports/JavaSE/src/.../util/MavenUtils.java -- new findDesignerJarInM2() helper.
  • Ports/JavaSE/src/.../CSSWatcher.java, ComponentTreeInspector.java -- prefer m2 designer over home-dir fallback.
  • maven/codenameone-maven-plugin/.../CompileCSSMojo.java -- INFO log level for CSS subprocess.
  • scripts/initializr/.../GeneratorModel.java -- bundles to src/main/l10n/; bootstrap null/locale fallback.
  • scripts/initializr/.../{barebones,kotlin,grub,tweet}-pom.xml -- usetimestamp on updater download.
  • scripts/initializr/.../GeneratorModelMatrixTest.java -- assert l10n/ location, reject resources/.
  • scripts/initializr/.../GeneratorModelIntegrationBuildTest.java -- post-compile assertion that theme.res actually contains the messages bundle.
  • tests/core/src/com/codename1/ui/util/ResourcesL10NTest.java (new) -- framework regression test for null-safe lookups.

Test plan

  • cd scripts/initializr && ./mvnw -pl common test -- matrix + integration tests pass; integration build verifies theme.res content.
  • cd maven && mvn -Plocal-dev-javase -pl javase -am compile -- JavaSE port compiles.
  • cd maven && mvn -Plocal-dev-javase -pl codenameone-maven-plugin -am compile -- plugin compiles.
  • Manually generate a barebones project with Include localization bundles enabled, mvn cn1:run -- simulator starts without the previously-reported NPE; on a non-Hebrew/Arabic device the default messages.properties fallback is applied.
  • Verify a project run that previously fell back to ~/.codenameone/designer_1.jar now logs the m2 path (or the warning if no m2 designer is available).

🤖 Generated with Claude Code

The initializr's "include localization bundles" option generated bundles
under common/src/main/resources/messages*.properties, but the CN1 maven
plugin's CSS compiler scans common/src/main/l10n (or i18n) for bundles to
bake into theme.res. The result: Resources.getGlobalResources().getL10N(
"messages", lang) hit a missing resource id at simulator startup and
threw NPE in MyAppName.init -- the project couldn't run.

- GeneratorModel.addLocalizationEntries: write to src/main/l10n so the
  bundles actually end up inside theme.res.
- Bootstrap (Java + Kotlin) injected into the starter class is now
  null-safe and falls back to the default locale when the device
  language has no specific bundle.
- Resources.getL10N / listL10NLocales / l10NLocaleSet now return null
  instead of NPE-ing when the bundle id is absent. Defensive change at
  the framework level so any project shipping mismatched bundles
  degrades gracefully.
- New tests/core/.../ResourcesL10NTest covers the framework null-safety.
- GeneratorModelMatrixTest now asserts bundles land under l10n and are
  NOT under src/main/resources (catches the regression at unit-test
  time).
- GeneratorModelIntegrationBuildTest now opens common/target/classes/
  theme.res after mvn compile and verifies "messages" L10N data is
  present for both the default ("") and Hebrew ("he") locales -- the
  end-to-end signal the previous tests missed.

While here, harden the simulator's CSS compiler invocation against
stale ~/.codenameone/designer_1.jar:

- New MavenUtils.findDesignerJarInM2 derives the running CN1 version
  from the codenameone-core jar's m2 path and resolves the matching
  codenameone-designer-<version>-jar-with-dependencies.jar. Any plugin
  invocation has already pulled this into m2 as a plugin dependency.
- CSSWatcher and ComponentTreeInspector now prefer codename1.designer.jar
  -> m2 designer -> ~/.codenameone fallback (with a clear warning when
  the legacy fallback is hit). The build-time CSS goal already used
  getDesignerJar() so this only affects the simulator runtime / live
  CSS reload paths.
- CompileCSSMojo now invokes the forked CSS compiler with INFO log
  level instead of DEBUG so subprocess stack traces are visible without
  re-running with -X. This won't fix issue #4850 but makes the next
  similar report actionable.
- The four initializr pom templates replace skipexisting="true" with
  usetimestamp="true" on the UpdateCodenameOne.jar download so future
  installs refresh the updater jar instead of pinning forever to the
  first copy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 2, 2026

Compared 7 screenshots: 7 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

The previous patch raised the CSS subprocess log level by passing it
explicitly at the call site (createJava(LEVEL_INFO)), which bypasses
CompileCSSMojoTest's TestCompileCSSMojo.createJava() override -- the
test substitutes a RecordingJava there to capture the command line
without forking. The override was no longer hit, so the test fell
through to a real fork against a stub designer.jar and four tests
errored out with "Invalid or corrupt jarfile".

Move the INFO log level into a createJava() override on CompileCSSMojo
itself. The call site stays at createJava(), so the test override
continues to win, and production still gets INFO so subprocess stack
traces remain visible without -X.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 2, 2026

Cloudflare Preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 2, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 2, 2026

Compared 86 screenshots: 86 matched.

Native Android coverage

  • 📊 Line coverage: 9.87% (5367/54363 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.75% (26316/339620), branch 3.59% (1169/32596), complexity 4.55% (1419/31207), method 7.97% (1162/14574), class 13.02% (254/1951)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.87% (5367/54363 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.75% (26316/339620), branch 3.59% (1169/32596), complexity 4.55% (1419/31207), method 7.97% (1162/14574), class 13.02% (254/1951)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 901.000 ms
Base64 CN1 encode 124.000 ms
Base64 encode ratio (CN1/native) 0.138x (86.2% faster)
Base64 native decode 794.000 ms
Base64 CN1 decode 318.000 ms
Base64 decode ratio (CN1/native) 0.401x (59.9% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 2, 2026

iOS screenshot updates

Compared 86 screenshots: 85 matched, 1 updated.

  • landscape — updated screenshot. Screenshot differs (2556x1179 px, bit depth 8).

    landscape
    Preview info: Preview provided by instrumentation.
    Full-resolution PNG saved as landscape.png in workflow artifacts.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 180 seconds

Build and Run Timing

Metric Duration
Simulator Boot 58000 ms
Simulator Boot (Run) 1000 ms
App Install 13000 ms
App Launch 3000 ms
Test Execution 270000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1040.000 ms
Base64 CN1 encode 1343.000 ms
Base64 encode ratio (CN1/native) 1.291x (29.1% slower)
Base64 native decode 757.000 ms
Base64 CN1 decode 962.000 ms
Base64 decode ratio (CN1/native) 1.271x (27.1% slower)
Base64 SIMD encode 396.000 ms
Base64 encode ratio (SIMD/native) 0.381x (61.9% faster)
Base64 encode ratio (SIMD/CN1) 0.295x (70.5% faster)
Base64 SIMD decode 435.000 ms
Base64 decode ratio (SIMD/native) 0.575x (42.5% faster)
Base64 decode ratio (SIMD/CN1) 0.452x (54.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 80.000 ms
Image createMask (SIMD on) 14.000 ms
Image createMask ratio (SIMD on/off) 0.175x (82.5% faster)
Image applyMask (SIMD off) 127.000 ms
Image applyMask (SIMD on) 74.000 ms
Image applyMask ratio (SIMD on/off) 0.583x (41.7% faster)
Image modifyAlpha (SIMD off) 141.000 ms
Image modifyAlpha (SIMD on) 72.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.511x (48.9% faster)
Image modifyAlpha removeColor (SIMD off) 147.000 ms
Image modifyAlpha removeColor (SIMD on) 64.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.435x (56.5% faster)
Image PNG encode (SIMD off) 987.000 ms
Image PNG encode (SIMD on) 824.000 ms
Image PNG encode ratio (SIMD on/off) 0.835x (16.5% faster)
Image JPEG encode 592.000 ms

shai-almog and others added 3 commits May 2, 2026 20:02
The user shared the missing stack trace from issue #4850:

  at java.base/java.lang.String.substring(String.java:2899)
  at com.codename1.ui.plaf.UIManager.parseTextFieldInputMode(UIManager.java:2434)
  at com.codename1.ui.plaf.UIManager.setBundle(UIManager.java:2419)
  at com.codename1.impl.javase.JavaSEPort.enableAutoLocalizationBundle(...)
  at com.codename1.impl.javase.JavaSEPort.init(JavaSEPort.java:5598)
  at com.codename1.impl.CodenameOneImplementation.initImpl(...)
  at com.codename1.ui.Display.init(Display.java:351)
  at com.codename1.designer.css.CN1CSSCLI.main(CN1CSSCLI.java:713)

This is not a path-related bug -- every initializr-generated project hits
it at css-goal time. Three pieces interact:

1. JavaSEPort.findLocalizationDirectory auto-creates src/main/l10n if it
   is missing, and enableAutoLocalizationBundle installs an
   AutoLocalizationBundle for it.
2. AutoLocalizationBundle.get echoes any missing key back as its own
   value -- the simulator's "wormhole" so devs can spot untranslated
   strings.
3. UIManager.setBundle queries "@im" on every bundle install. With the
   echo behavior, "@im" -> "@im", which is then tokenized to ["@im"],
   "@im-@im" is queried (which echoes "@im-@im"), and
   parseTextFieldInputMode crashes on substring(0, indexOf('=')) because
   that token has no '=' (range [0, -1) of length 7).

The CSS compiler subprocess inherits all of this because CN1CSSCLI.main
calls Display.init -> JavaSEPort.init -> enableAutoLocalizationBundle.

Fixes:

- AutoLocalizationBundle.get returns null for keys starting with '@'.
  Meta-keys (@rtl, @im, @im-<name>) are configuration entries that
  callers distinguish from "missing" by checking for null. Echoing the
  key back is semantically wrong AND broke setBundle. Real meta-key
  values that exist in the underlying file (e.g. @rtl=true in a Hebrew
  bundle) are still returned -- only fabrication is suppressed.
- UIManager.parseTextFieldInputMode skips tokens without '=' and skips
  entries whose key isn't a valid integer. Defensive belt-and-suspenders
  so any bundle with malformed input-mode entries degrades gracefully
  instead of failing the whole bundle install.
- New UIManagerSetBundleTest exercises setBundle against an echo-bundle
  (matches pre-fix AutoLocalizationBundle behavior) and against directly
  malformed @im/@im-x entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… fix

Per review: silently skipping malformed `@im` tokens hides legitimate
bugs in user-supplied bundles. Real malformed input should fail loudly,
not be swallowed.

The actual root cause -- AutoLocalizationBundle fabricating values for
@-prefixed meta-keys -- stays fixed. That's the surgical change: the
auto-localize wormhole was returning fake values for keys that callers
explicitly use null/non-null to gate features (@im, @rtl, @im-<name>),
which is semantically wrong and broke setBundle.

Moves the regression coverage from the no-op stub in core to the
existing AutoLocalizationBundleTest in the JavaSE port (where the bundle
class actually lives), asserting:
- @-prefixed meta-keys are NOT auto-fabricated
- @-prefixed meta-keys that exist in the underlying file ARE returned

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…wormhole

Workaround for the AutoLocalizationBundle @im fabrication crash in
shipped Codename One <= 7.0.236. The proper fix lives in JavaSEPort
(don't fabricate values for `@`-prefixed meta-keys) and is on this
branch, but it requires a new framework release. Until then, every
initializr-generated project crashes at css-goal time inside the CSS
compiler subprocess (CN1CSSCLI -> Display.init -> JavaSEPort.init ->
enableAutoLocalizationBundle -> UIManager.setBundle ->
parseTextFieldInputMode on substring(0, -1) for "@im-@im").

Ship `common/src/main/l10n/Bundle.properties` with a single `@im=`
entry on every generated project. Two reasons it works:

1. JavaSEPort.findDefaultLocalizationBundleFile prefers Bundle.properties
   over any other file in src/main/l10n, so the AutoLocalizationBundle
   loads our stub as its base.
2. With `@im=""` already in the bundle's underlying Hashtable,
   AutoLocalizationBundle.get("@im") returns "" instead of fabricating
   "@im". setBundle sees length 0 and skips the input-mode block, so
   parseTextFieldInputMode is never called.

The stub is unconditional (added to every project, with or without
localization bundles enabled) because the bug fires regardless --
enableAutoLocalizationBundle auto-creates src/main/l10n in the CSS
compiler subprocess even on projects that didn't request localization.

The matrix test asserts the stub is present on every generated project
combination so this workaround can't silently regress.

Once the AutoLocalizationBundle fix lands in a release and the
initializr is bumped past it, this stub can be removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog merged commit 25dbe3e into master May 2, 2026
22 checks passed
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