Skip to content

Commit 6ba3b78

Browse files
authored
Merge pull request #278 from metaobjectsdev/fix/date-array-storage-and-effective-type
fix(metadata): close the DATE-array storage gap left by #275
2 parents ce90dab + 312f1bd commit 6ba3b78

7 files changed

Lines changed: 406 additions & 77 deletions

File tree

server/java/metadata/src/main/java/com/metaobjects/field/MetaField.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,11 @@ public void setDate(Object obj, Date value) {
10891089
}
10901090

10911091
public void setObject(Object obj, Object value) {
1092-
setObjectAttribute(obj, DataConverter.toType(getDataType(), value ));
1092+
// ADR-0039: use the RESOLVING, array-aware getEffectiveDataType() -- the field's SCALAR
1093+
// getDataType() corrupted a List for an isArray field (e.g. comma-joining a STRING array
1094+
// into a single string) before setObjectAttribute's own instanceof check rejected it.
1095+
// getEffectiveDataType() is a strict no-op for every non-array field (#275 carry-forward).
1096+
setObjectAttribute(obj, DataConverter.toType(getEffectiveDataType(), value ));
10931097
}
10941098

10951099
public void setObjectArray(Object obj, List<?> value) {

server/java/metadata/src/main/java/com/metaobjects/io/object/gson/MetaObjectDeserializer.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,14 @@ protected void readFieldValue(MetaObject mo, MetaField mf, Object vo,
126126
// nothing that parsed before stops parsing). String -> tolerant ISO parse
127127
// (TemporalWireFormat). Array: element-wise into a List<Date> via
128128
// setObjectArray, skipping context.deserialize(el, List.class), which yields a
129-
// type-losing List<Double>. setObjectArray only bypasses MetaField's OWN
130-
// DataConverter.toType call; setObjectAttribute still routes through
131-
// AbstractObjectRepresentation.setValue, which unconditionally applies
132-
// DataConverter.toType(effectiveDataType, value) -- and DataConverter's
133-
// DATE_ARRAY case is unimplemented (unsupported()). So today this branch
134-
// throws UnsupportedOperationException for a non-empty date array on the
135-
// default (non-proxy) representation path; it becomes correct once
136-
// DataConverter grows a DATE_ARRAY conversion.
129+
// type-losing List<Double>. setObjectArray routes through
130+
// AbstractObjectRepresentation.setValue, which applies
131+
// DataConverter.toType(effectiveDataType, value) -- backed, since the #275
132+
// carry-forward unit, by DataConverter.toDateArray. This branch genuinely
133+
// round-trips a date array end to end today, INCLUDING a null element (see
134+
// readDateElement's isJsonNull() guard -- required because the write side,
135+
// MetaObjectSerializer, deliberately emits JsonNull.INSTANCE at a null element
136+
// position; see GsonArrayWriteRoundTripTest's Step 3b/A6 coverage).
137137
if (mf.isArrayType() && el.isJsonArray()) {
138138
List<Date> dates = new ArrayList<>();
139139
for (JsonElement item : el.getAsJsonArray()) {
@@ -203,8 +203,15 @@ protected void readFieldValue(MetaObject mo, MetaField mf, Object vo,
203203
}
204204
}
205205

206-
/** Single JSON array element of a DATE-array field: number -> epoch millis, string -> tolerant ISO parse. */
206+
/** Single JSON array element of a DATE-array field: null -> null (JsonNull.getAsString()
207+
* throws UnsupportedOperationException, so this must be checked before isJsonPrimitive --
208+
* JsonNull is not a JsonPrimitive), number -> epoch millis, string -> tolerant ISO parse.
209+
* Mirrors what MetaObjectSerializer.writeField's DATE-array branch emits at a null element
210+
* position (JsonNull.INSTANCE), so a null element round-trips instead of throwing. */
207211
private Date readDateElement(MetaField mf, JsonElement el) {
212+
if (el.isJsonNull()) {
213+
return null;
214+
}
208215
if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isNumber()) {
209216
return new Date(el.getAsLong());
210217
}

server/java/metadata/src/main/java/com/metaobjects/io/object/gson/MetaObjectSerializer.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.metaobjects.loader.MetaDataLoader;
99
import com.metaobjects.object.MetaObject;
1010
import com.metaobjects.object.MetaObjectAware;
11+
import com.metaobjects.util.DataConverter;
1112
import com.google.gson.*;
1213

1314
import static com.metaobjects.io.json.JsonIOUtil.*;
@@ -94,7 +95,13 @@ protected void writeField(MetaObject mo, MetaField mf, Object vo,
9495
} else {
9596
JsonArray arr = new JsonArray();
9697
for (Object o : dates) {
97-
java.util.Date d = (java.util.Date) o;
98+
// C1: route through the shared DataConverter.toDate(Object) rather
99+
// than a hard (Date) cast -- matches the non-array DATE branch below
100+
// (mf.getDate(vo) is itself backed by DataConverter.toDate), and
101+
// accepts the same scalar inputs that converter always has, instead
102+
// of throwing a bare ClassCastException for anything not already a
103+
// java.util.Date.
104+
java.util.Date d = DataConverter.toDate(o);
98105
if (d == null) arr.add(JsonNull.INSTANCE);
99106
else arr.add(TemporalWireFormat.format(mf, d));
100107
}

server/java/metadata/src/main/java/com/metaobjects/util/DataConverter.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ public static Object toType( DataTypes dataType, Object val ) {
4747

4848
case BYTE_ARRAY://return toByteArray( val );
4949
case SHORT_ARRAY: //return toShortArray( val );
50-
case DATE_ARRAY: //toDateArray( val );
5150
return unsupported(dataType,val);
5251

52+
case DATE_ARRAY: return toDateArray( val );
5353
case STRING_ARRAY: return toStringArray( val );
5454
case OBJECT_ARRAY: return toObjectArray( val );
5555

@@ -755,6 +755,33 @@ public static List<Boolean> toBooleanArray(Object val) {
755755
}
756756
}
757757

758+
/**
759+
* Convert value to Date array (List&lt;Date&gt;)
760+
*/
761+
public static List<Date> toDateArray(Object val) {
762+
if (val == null) return null;
763+
764+
if (val instanceof List<?>) {
765+
List<?> list = (List<?>) val;
766+
return list.stream()
767+
.map(DataConverter::toDate)
768+
.collect(java.util.stream.Collectors.toList());
769+
} else if (val instanceof String) {
770+
String s = (String) val;
771+
if (s.trim().isEmpty()) return new java.util.ArrayList<>();
772+
773+
if (s.contains(",")) {
774+
return java.util.Arrays.stream(s.split(","))
775+
.map(item -> toDate(item.trim()))
776+
.collect(java.util.stream.Collectors.toList());
777+
} else {
778+
return java.util.Arrays.asList(toDate(s.trim()));
779+
}
780+
} else {
781+
return java.util.Arrays.asList(toDate(val));
782+
}
783+
}
784+
758785
/**
759786
* Convert value to Double array (List&lt;Double&gt;)
760787
*/

0 commit comments

Comments
 (0)