ide-template: an authored default is read the same way in every generated language (#7293) - #7336
Merged
Merged
Conversation
…ated language (#7293) One authored `defaultValue:` is written into several generated languages - the repository's Java literal and the item dialog's JavaScript seed - and `AuthoredDefaults` exists to state that every one of them has to read the authored text the same way, or the value the column holds and the value the form offers stop agreeing. They did not. `JsLiterals` unquotes first, so the SQL-quoted shape a working DB DEFAULT needs reads as the bare one; `JavaLiterals` unquoted in the `String` arm only. So `defaultValue: "'20'"` on an integer column seeded 20 in the dialog and emitted `entity.Quantity = Integer.valueOf("'20'")` in the repository - a NumberFormatException on every create that relied on the default, in the create path every writer passes through. - Every arm of `JavaLiterals.defaultValueExpression` reads the authored text through `AuthoredDefaults.unquote`, once, up front. - The boolean reader does the unquoting itself, in `AuthoredDefaults`, rather than in the Java half: `'true'` now reads as `true` in BOTH languages, where unquoting it in one would have reintroduced the very disagreement being fixed. - Each numeric arm validates the text by running the factory the generated expression calls (`Integer::valueOf`, `BigDecimal::new`, ...), so an unparsable default is refused while the author is generating, naming the property, its value and its type - the `resolveMultiselect` precedent in the same class. This replaces the old "fails that one create" rationale, which understated the cost: the literal is assigned in the repository's create path, so `Integer.valueOf("8.0")` failed every create of that entity, not one field. Every default in the repository's own fixtures and ITs parses, so nothing that generates today stops generating. Tests: the `'20'` case beside the `JsLiterals` one and the refusal case in `JavaLiteralsTest`, the quoted-boolean parity case in `JsLiteralsTest`, and the invariant end to end in `ModelParameterProcessorTest` - both literals asserted together, since agreeing is the whole point. Verified: `ide-template` unit suite (147), `ModelGenerationIT`, `IntentEngineIT` (80), `IntentEmissionCoverageIT` - which compiles the generated client Java - `formatter:validate` with the cache wiped, and the `-P release` javadoc build. Fixes #7293 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| case "Short" -> "Short.valueOf(\"" + escape(defaultValue) + "\")"; | ||
| case "java.math.BigDecimal" -> numericExpression("new java.math.BigDecimal", value, javaClass, property, | ||
| java.math.BigDecimal::new); | ||
| case "Double" -> numericExpression("Double.valueOf", value, javaClass, property, Double::valueOf); |
| case "java.math.BigDecimal" -> numericExpression("new java.math.BigDecimal", value, javaClass, property, | ||
| java.math.BigDecimal::new); | ||
| case "Double" -> numericExpression("Double.valueOf", value, javaClass, property, Double::valueOf); | ||
| case "Float" -> numericExpression("Float.valueOf", value, javaClass, property, Float::valueOf); |
| java.math.BigDecimal::new); | ||
| case "Double" -> numericExpression("Double.valueOf", value, javaClass, property, Double::valueOf); | ||
| case "Float" -> numericExpression("Float.valueOf", value, javaClass, property, Float::valueOf); | ||
| case "Long" -> numericExpression("Long.valueOf", value, javaClass, property, Long::valueOf); |
| case "Double" -> numericExpression("Double.valueOf", value, javaClass, property, Double::valueOf); | ||
| case "Float" -> numericExpression("Float.valueOf", value, javaClass, property, Float::valueOf); | ||
| case "Long" -> numericExpression("Long.valueOf", value, javaClass, property, Long::valueOf); | ||
| case "Integer" -> numericExpression("Integer.valueOf", value, javaClass, property, Integer::valueOf); |
| case "Float" -> numericExpression("Float.valueOf", value, javaClass, property, Float::valueOf); | ||
| case "Long" -> numericExpression("Long.valueOf", value, javaClass, property, Long::valueOf); | ||
| case "Integer" -> numericExpression("Integer.valueOf", value, javaClass, property, Integer::valueOf); | ||
| case "Short" -> numericExpression("Short.valueOf", value, javaClass, property, Short::valueOf); |
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.
Cause
One authored
defaultValue:is written into several generated languages - the repository's Java literal (JavaLiterals) and the item dialog's JavaScript seed (JsLiterals) - andAuthoredDefaultsexists to state the invariant that every one of them has to read the authored text the same way, or the value the column holds and the value the form offers stop agreeing.They did not.
JsLiterals.defaultValueExpressionunquotes first, so the SQL-quoted shape a working DB DEFAULT needs ('20') reads as the bare one;JavaLiterals.defaultValueExpressionunquoted in theStringarm only. SodefaultValue: "'20'"on an integer column seeded20in the item dialog and emittedin the repository (
Repository.java.template:152) - aNumberFormatExceptionon every create that relied on the default, in the create path every writer passes through.Change
Every arm of
JavaLiterals.defaultValueExpressionreads the authored text throughAuthoredDefaults.unquote, once, up front.The boolean reader does the unquoting itself, in
AuthoredDefaults.readsAsTrue, rather than in the Java half:'true'now reads astruein BOTH languages, where unquoting it on one side only would have reintroduced the very disagreement being fixed.Each numeric arm validates the text by running the factory the generated expression calls (
Integer::valueOf,BigDecimal::new, …), so an unparsable numeric default is refused while the author is generating, with a message naming the property, its value and its type - the precedentresolveMultiselectalready set in the same class.That replaces the previous "fails that one create instead of failing the whole generated build" rationale, which understated the cost: the literal is assigned in the repository's create path, so
Integer.valueOf("8.0")failed every create of that entity, not one field - and the.schemacarries the same text as the column's DB DEFAULT, which PostgreSQL rejects at DDL time anyway. Every default in the repository's own fixtures and ITs parses, so nothing that generates today stops generating.Verification
ide-templateunit suite: 147/147, including the new cases - the'20'case beside theJsLiteralsone and the refusal case inJavaLiteralsTest, the quoted-boolean parity case inJsLiteralsTest, and the invariant end to end inModelParameterProcessorTest, where both literals are asserted together since agreeing is the whole point.ModelGenerationITgreen (the generation regression harness).IntentEngineIT80/80 andIntentEmissionCoverageITgreen - the latter compiles the generated client Java, so the emitted literals really compile.mvn formatter:validategreen with the formatter cache wiped, and-P releasejavadoc green on the module.Fixes #7293
🤖 Generated with Claude Code