davsclaus commented on code in PR #27585:
URL: https://github.com/apache/camel/pull/27585#discussion_r4224510614
##########
components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveConverter.java:
##########
@@ -153,12 +158,14 @@ private String emitNode(DataWeaveAst node) {
return emitAttributeAccess(aa);
} else if (node instanceof ExistenceCheck ec) {
return emitExistenceCheck(ec);
+ } else if (node instanceof DoubleDollar && shorthandDoubleDollar !=
null) {
+ return shorthandDoubleDollar;
} else if (node instanceof DoubleDollar) {
- // $$ means accumulator only inside reduce. In
map/filter/mapObject/pluck it means index or key.
- // Outside the reduce-shorthand emitter this context is unknown,
so emit a TODO.
+ // $$ means accumulator inside reduce and index inside map. In
filter/mapObject/pluck it means index or key,
+ // which is not supported, so emit a TODO.
Review Comment:
This code no longer exists: the converter was rewritten in the later
commits. `$$` is now supported in every function that passes it: the index in
`map`/`filter`/`groupBy`/`orderBy`/`distinctBy`/`flatMap`, the key in
`mapObject`/`filterObject`/`pluck` (with `$$$` as the index), and the
accumulator in `reduce`. A `$` outside of any of these is a TODO with the
message `$ outside of a lambda`.
_Claude Code on behalf of davsclaus_
##########
components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveConverter.java:
##########
@@ -153,12 +158,14 @@ private String emitNode(DataWeaveAst node) {
return emitAttributeAccess(aa);
} else if (node instanceof ExistenceCheck ec) {
return emitExistenceCheck(ec);
+ } else if (node instanceof DoubleDollar && shorthandDoubleDollar !=
null) {
+ return shorthandDoubleDollar;
} else if (node instanceof DoubleDollar) {
- // $$ means accumulator only inside reduce. In
map/filter/mapObject/pluck it means index or key.
- // Outside the reduce-shorthand emitter this context is unknown,
so emit a TODO.
+ // $$ means accumulator inside reduce and index inside map. In
filter/mapObject/pluck it means index or key,
+ // which is not supported, so emit a TODO.
todoCount++;
return includeComments
- ? "/* TODO: manual conversion needed -- $$ outside reduce
context */" + "\nnull"
+ ? "/* TODO: manual conversion needed -- $$ outside map and
reduce */" + "\nnull"
Review Comment:
Obsolete as well: since the rewrite `$$` is converted in `filter`,
`mapObject` and `pluck` too, so this TODO message is gone. See the previous
thread.
_Claude Code on behalf of davsclaus_
##########
components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/CML.java:
##########
@@ -264,17 +291,188 @@ private Val formatDate(Val value, Val format) {
if (isNull(value)) {
return Val.Null$.MODULE$;
}
- if (!(value instanceof Val.Str valStr)) {
- throw new IllegalArgumentException("Expected String date value,
got: " + value.prettyName());
- }
if (!(format instanceof Val.Str fmtStr)) {
throw new IllegalArgumentException("Expected String format, got: "
+ format.prettyName());
}
- ZonedDateTime dateTime = parseToZonedDateTime(valStr.value());
+ ZonedDateTime dateTime;
+ if (value instanceof Val.Num num) {
+ // epoch milliseconds, as returned by parseDate
+ dateTime = Instant.ofEpochMilli((long)
num.value()).atZone(ZoneId.of("UTC"));
+ } else if (value instanceof Val.Str valStr) {
+ dateTime = parseToZonedDateTime(valStr.value());
+ } else {
+ throw new IllegalArgumentException("Expected String or Number date
value, got: " + value.prettyName());
+ }
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(fmtStr.value());
return new Val.Str(dateTime.format(formatter));
}
+ // A number with a java.text.DecimalFormat pattern, or an ISO-8601 date or
time with a DateTimeFormatter pattern
+ private Val format(Val value, Val format) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ String pattern = string(format, "format");
+ if (value instanceof Val.Num num) {
+ DecimalFormat decimalFormat = new DecimalFormat(pattern,
DecimalFormatSymbols.getInstance(Locale.ROOT));
+ return new
Val.Str(decimalFormat.format(BigDecimal.valueOf(num.value())));
+ }
+ if (value instanceof Val.Str str) {
+ return new Val.Str(DateTimeFormatter.ofPattern(pattern,
Locale.ROOT).format(parseTemporal(str.value())));
+ }
+ throw new IllegalArgumentException("Cannot format " +
value.prettyName());
+ }
+
+ // A date or time (as an ISO-8601 string) of the given type (Date,
DateTime, LocalDateTime, Time or LocalTime),
+ // from a string in the given format (or ISO-8601 when the format is null)
or a number of seconds since the epoch
+ private Val parseDateTime(Val value, Val format, Val type) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ String kind = string(type, "type");
+ TemporalAccessor parsed;
+ if (value instanceof Val.Num num) {
+ parsed = Instant.ofEpochSecond((long)
num.value()).atZone(ZoneOffset.UTC);
+ } else if (value instanceof Val.Str str && isNull(format)) {
+ parsed = parseTemporal(str.value());
+ } else if (value instanceof Val.Str str) {
+ parsed = DateTimeFormatter.ofPattern(string(format, "format"),
Locale.ROOT).parse(str.value());
+ } else {
+ throw new IllegalArgumentException("Cannot convert " +
value.prettyName() + " to " + kind);
+ }
+ return new Val.Str(switch (kind) {
+ case "Date" -> LocalDate.from(parsed).toString();
+ case "DateTime" ->
DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(toZonedDateTime(parsed));
+ case "LocalDateTime" ->
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(toLocalDateTime(parsed));
+ case "Time" ->
DateTimeFormatter.ISO_OFFSET_TIME.format(toOffsetTime(parsed));
+ case "LocalTime" ->
DateTimeFormatter.ISO_LOCAL_TIME.format(LocalTime.from(parsed));
+ default -> throw new IllegalArgumentException("Unknown date type:
" + kind);
+ });
+ }
+
+ // A date or time (as an ISO-8601 string) plus an ISO-8601 period or
duration such as P1D, PT1H or -P1M
+ private Val dateAdd(Val value, Val amount) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ String text = string(value, "value");
+ TemporalAccessor temporal = parseTemporal(text);
+ String period = string(amount, "amount");
+ boolean negative = period.startsWith("-");
+ String unsigned = negative ? period.substring(1) : period;
+ TemporalAmount temporalAmount = unsigned.contains("T") ?
Duration.parse(unsigned) : Period.parse(unsigned);
+ Temporal result = negative
+ ? ((Temporal) temporal).minus(temporalAmount) : ((Temporal)
temporal).plus(temporalAmount);
+ return new Val.Str(formatIso(result));
+ }
+
+ // A part of a date or time (as an ISO-8601 string): year, month, day,
hour, minutes, seconds, ...
+ private Val datePart(Val value, Val part) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ TemporalAccessor temporal = parseTemporal(string(value, "value"));
+ String name = string(part, "part");
+ if ("timezone".equals(name)) {
+ return temporal instanceof ZonedDateTime zdt ? new
Val.Str(zdt.getZone().getId()) : Val.Null$.MODULE$;
+ }
+ ChronoField field = switch (name) {
+ case "year" -> ChronoField.YEAR;
+ case "month" -> ChronoField.MONTH_OF_YEAR;
+ case "day" -> ChronoField.DAY_OF_MONTH;
+ case "hour" -> ChronoField.HOUR_OF_DAY;
+ case "minutes" -> ChronoField.MINUTE_OF_HOUR;
+ case "seconds" -> ChronoField.SECOND_OF_MINUTE;
+ case "milliseconds" -> ChronoField.MILLI_OF_SECOND;
+ case "nanoseconds" -> ChronoField.NANO_OF_SECOND;
+ case "dayOfWeek" -> ChronoField.DAY_OF_WEEK;
+ case "dayOfYear" -> ChronoField.DAY_OF_YEAR;
+ case "offsetSeconds" -> ChronoField.OFFSET_SECONDS;
+ case "quarter" -> null;
+ default -> throw new IllegalArgumentException("Unknown date part:
" + name);
+ };
+ if (field == null) {
+ return new Val.Num(temporal.get(IsoFields.QUARTER_OF_YEAR));
+ }
Review Comment:
Good catch, fixed in adbb8c3ba9: `datePart(time, 'quarter')` is now `null`,
as for any other part a date or time does not have, with a test in
`CmlFunctionsTest`.
_Claude Code on behalf of davsclaus_
##########
components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/CML.java:
##########
@@ -264,17 +291,188 @@ private Val formatDate(Val value, Val format) {
if (isNull(value)) {
return Val.Null$.MODULE$;
}
- if (!(value instanceof Val.Str valStr)) {
- throw new IllegalArgumentException("Expected String date value,
got: " + value.prettyName());
- }
if (!(format instanceof Val.Str fmtStr)) {
throw new IllegalArgumentException("Expected String format, got: "
+ format.prettyName());
}
- ZonedDateTime dateTime = parseToZonedDateTime(valStr.value());
+ ZonedDateTime dateTime;
+ if (value instanceof Val.Num num) {
+ // epoch milliseconds, as returned by parseDate
+ dateTime = Instant.ofEpochMilli((long)
num.value()).atZone(ZoneId.of("UTC"));
+ } else if (value instanceof Val.Str valStr) {
+ dateTime = parseToZonedDateTime(valStr.value());
+ } else {
+ throw new IllegalArgumentException("Expected String or Number date
value, got: " + value.prettyName());
+ }
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(fmtStr.value());
return new Val.Str(dateTime.format(formatter));
}
+ // A number with a java.text.DecimalFormat pattern, or an ISO-8601 date or
time with a DateTimeFormatter pattern
+ private Val format(Val value, Val format) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ String pattern = string(format, "format");
+ if (value instanceof Val.Num num) {
+ DecimalFormat decimalFormat = new DecimalFormat(pattern,
DecimalFormatSymbols.getInstance(Locale.ROOT));
+ return new
Val.Str(decimalFormat.format(BigDecimal.valueOf(num.value())));
+ }
+ if (value instanceof Val.Str str) {
+ return new Val.Str(DateTimeFormatter.ofPattern(pattern,
Locale.ROOT).format(parseTemporal(str.value())));
+ }
+ throw new IllegalArgumentException("Cannot format " +
value.prettyName());
+ }
+
+ // A date or time (as an ISO-8601 string) of the given type (Date,
DateTime, LocalDateTime, Time or LocalTime),
+ // from a string in the given format (or ISO-8601 when the format is null)
or a number of seconds since the epoch
+ private Val parseDateTime(Val value, Val format, Val type) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ String kind = string(type, "type");
+ TemporalAccessor parsed;
+ if (value instanceof Val.Num num) {
+ parsed = Instant.ofEpochSecond((long)
num.value()).atZone(ZoneOffset.UTC);
+ } else if (value instanceof Val.Str str && isNull(format)) {
+ parsed = parseTemporal(str.value());
+ } else if (value instanceof Val.Str str) {
+ parsed = DateTimeFormatter.ofPattern(string(format, "format"),
Locale.ROOT).parse(str.value());
+ } else {
+ throw new IllegalArgumentException("Cannot convert " +
value.prettyName() + " to " + kind);
+ }
+ return new Val.Str(switch (kind) {
+ case "Date" -> LocalDate.from(parsed).toString();
+ case "DateTime" ->
DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(toZonedDateTime(parsed));
+ case "LocalDateTime" ->
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(toLocalDateTime(parsed));
+ case "Time" ->
DateTimeFormatter.ISO_OFFSET_TIME.format(toOffsetTime(parsed));
+ case "LocalTime" ->
DateTimeFormatter.ISO_LOCAL_TIME.format(LocalTime.from(parsed));
+ default -> throw new IllegalArgumentException("Unknown date type:
" + kind);
+ });
+ }
+
+ // A date or time (as an ISO-8601 string) plus an ISO-8601 period or
duration such as P1D, PT1H or -P1M
+ private Val dateAdd(Val value, Val amount) {
+ if (isNull(value)) {
+ return Val.Null$.MODULE$;
+ }
+ String text = string(value, "value");
+ TemporalAccessor temporal = parseTemporal(text);
+ String period = string(amount, "amount");
+ boolean negative = period.startsWith("-");
+ String unsigned = negative ? period.substring(1) : period;
+ TemporalAmount temporalAmount = unsigned.contains("T") ?
Duration.parse(unsigned) : Period.parse(unsigned);
Review Comment:
The double negation is actually correct (`-P-1D` is minus minus one day, so
one day later), but the manual sign handling wasn't needed: `Duration.parse`
and `Period.parse` accept a sign for the whole amount and for its parts.
`cml.dateAdd` now passes the amount to them as is. Fixed in adbb8c3ba9, with
tests for `-P1D`, `P-1D`, `-P-1D` and `-PT2H`.
_Claude Code on behalf of davsclaus_
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]