Repository navigation
Commit 9c7100d
fix(java): the generated extract mapper did not compile for most scalar subtypes
Reported as a decimal bug. It was not: 9 of 15 scalar subtypes fail as a single
component and 13 of 15 as an array. The reason it looked narrow is that javac
reports only the FIRST incompatible argument of a constructor invocation, so a
fixture carrying several subtypes surfaces one error and hides the rest.
A responding template.prompt types its <Template>Response record through
SpringTypeMapper.javaTypeName — BigDecimal, LocalDate, LocalTime, Instant, UUID,
URI, InetAddress, Float, and Long for currency. The mapper recognised only
Integer/Long/Double/Boolean and fell through to ExtractMap.asString for
everything else, with every scalar array going to asStringList. Every one of
those pairings is `incompatible types`. It shipped because no test payload in
this module carried a decimal, date, time, timestamp, currency, uuid, uri or
inet.
The record stays strictly typed — ADR-0052 makes <Template>Response a strict
record, and a caller who declared a decimal should get a BigDecimal, not a
String. So the coercion happens on the way in, in the generated parser, chosen
by a function that walks the same instanceof chain javaTypeName does. Helpers
are emitted only where used, and every one is never-throws. coerceInet rejects
anything that is not an IP literal rather than resolving it: InetAddress
.getByName does DNS, and a parser reading untrusted model output must not make a
network call.
Two further defects surfaced while proving the values, both upstream of codegen:
MetaObjectExtractor.assemble violated its own documented contract. The javadoc
promises malformed data was classified into the report and assembly cannot
throw, but the per-field write goes through DataConverter, which throws on a
value it cannot convert — so one unconvertible component destroyed the whole
extract. That is the opposite of the lenient tier's purpose: a model answering
"31/12/2026" for one date should cost that date, not the twenty fields beside it
that parsed. The component is now left unset, which is what "lost" already means
elsewhere in that pass.
DataConverter.toDate could not parse an ISO-8601 date. The String arm was a bare
Long.parseLong, so "2026-03-04" — the form normalization.md puts on the wire —
threw NumberFormatException, making every field.date and field.timestamp
unassemblable from its own wire representation. Epoch millis is still tried
first and only for an all-digit string, so every value that parsed before parses
identically; unparseable input still throws, preserving the failure mode for
callers outside the lenient tier.
Gates: GeneratedScalarExtractLockStepTest compiles generated output for every
subtype in FIELD_SUBTYPES in both positions (one payload per subtype, so nothing
is masked). GeneratedScalarExtractRoundTripTest asserts VALUES — compiling
proves nothing about a coercion that returns null for everything or loses
decimal precision through a double — plus the never-throws degradation.
Two things deliberately left alone and documented in the round-trip test:
a decimal ARRAY cannot populate because DataTypes has no DECIMAL_ARRAY
("arrays-of-decimal aren't in the metamodel", matching C#'s SP-A note), so it
compiles and reports the component lost; and DataConverter.toBigDecimal returns
BigDecimal.ZERO for an unparseable string rather than null, so garbage reaches a
money field as 0. The latter is a core converter the OMDB persistence path also
uses, so flipping its contract is its own decision, not a drive-by.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent f332331 commit 9c7100d
6 files changed
Lines changed: 692 additions & 16 deletions
File tree
- server/java
- codegen-spring/src
- main/java/com/metaobjects/generator/spring
- test/java/com/metaobjects/generator/spring
- metadata/src/main/java/com/metaobjects/util
- om/src/main/java/com/metaobjects/object/extract
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
101 | 133 | | |
102 | 134 | | |
103 | 135 | | |
| |||
0 commit comments