github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/java/CustomInstantDeserializer.mustache (about)

     1  package {{invokerPackage}};
     2  
     3  import com.fasterxml.jackson.core.JsonParser;
     4  import com.fasterxml.jackson.core.JsonTokenId;
     5  import com.fasterxml.jackson.databind.DeserializationContext;
     6  import com.fasterxml.jackson.databind.DeserializationFeature;
     7  import com.fasterxml.jackson.databind.JsonDeserializer;
     8  import com.fasterxml.jackson.datatype.threetenbp.DecimalUtils;
     9  import com.fasterxml.jackson.datatype.threetenbp.deser.ThreeTenDateTimeDeserializerBase;
    10  import com.fasterxml.jackson.datatype.threetenbp.function.BiFunction;
    11  import com.fasterxml.jackson.datatype.threetenbp.function.Function;
    12  import org.threeten.bp.DateTimeException;
    13  import org.threeten.bp.DateTimeUtils;
    14  import org.threeten.bp.Instant;
    15  import org.threeten.bp.OffsetDateTime;
    16  import org.threeten.bp.ZoneId;
    17  import org.threeten.bp.ZonedDateTime;
    18  import org.threeten.bp.format.DateTimeFormatter;
    19  import org.threeten.bp.temporal.Temporal;
    20  import org.threeten.bp.temporal.TemporalAccessor;
    21  
    22  import java.io.IOException;
    23  import java.math.BigDecimal;
    24  
    25  /**
    26   * Deserializer for ThreeTen temporal {@link Instant}s, {@link OffsetDateTime}, and {@link ZonedDateTime}s.
    27   * Adapted from the jackson threetenbp InstantDeserializer to add support for deserializing rfc822 format.
    28   *
    29   * @author Nick Williams
    30   */
    31  public class CustomInstantDeserializer<T extends Temporal>
    32      extends ThreeTenDateTimeDeserializerBase<T> {
    33    private static final long serialVersionUID = 1L;
    34  
    35    public static final CustomInstantDeserializer<Instant> INSTANT = new CustomInstantDeserializer<Instant>(
    36        Instant.class, DateTimeFormatter.ISO_INSTANT,
    37        new Function<TemporalAccessor, Instant>() {
    38          @Override
    39          public Instant apply(TemporalAccessor temporalAccessor) {
    40            return Instant.from(temporalAccessor);
    41          }
    42        },
    43        new Function<FromIntegerArguments, Instant>() {
    44          @Override
    45          public Instant apply(FromIntegerArguments a) {
    46            return Instant.ofEpochMilli(a.value);
    47          }
    48        },
    49        new Function<FromDecimalArguments, Instant>() {
    50          @Override
    51          public Instant apply(FromDecimalArguments a) {
    52            return Instant.ofEpochSecond(a.integer, a.fraction);
    53          }
    54        },
    55        null
    56    );
    57  
    58    public static final CustomInstantDeserializer<OffsetDateTime> OFFSET_DATE_TIME = new CustomInstantDeserializer<OffsetDateTime>(
    59        OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME,
    60        new Function<TemporalAccessor, OffsetDateTime>() {
    61          @Override
    62          public OffsetDateTime apply(TemporalAccessor temporalAccessor) {
    63            return OffsetDateTime.from(temporalAccessor);
    64          }
    65        },
    66        new Function<FromIntegerArguments, OffsetDateTime>() {
    67          @Override
    68          public OffsetDateTime apply(FromIntegerArguments a) {
    69            return OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId);
    70          }
    71        },
    72        new Function<FromDecimalArguments, OffsetDateTime>() {
    73          @Override
    74          public OffsetDateTime apply(FromDecimalArguments a) {
    75            return OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId);
    76          }
    77        },
    78        new BiFunction<OffsetDateTime, ZoneId, OffsetDateTime>() {
    79          @Override
    80          public OffsetDateTime apply(OffsetDateTime d, ZoneId z) {
    81            return d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime()));
    82          }
    83        }
    84    );
    85  
    86    public static final CustomInstantDeserializer<ZonedDateTime> ZONED_DATE_TIME = new CustomInstantDeserializer<ZonedDateTime>(
    87        ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME,
    88        new Function<TemporalAccessor, ZonedDateTime>() {
    89          @Override
    90          public ZonedDateTime apply(TemporalAccessor temporalAccessor) {
    91            return ZonedDateTime.from(temporalAccessor);
    92          }
    93        },
    94        new Function<FromIntegerArguments, ZonedDateTime>() {
    95          @Override
    96          public ZonedDateTime apply(FromIntegerArguments a) {
    97            return ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId);
    98          }
    99        },
   100        new Function<FromDecimalArguments, ZonedDateTime>() {
   101          @Override
   102          public ZonedDateTime apply(FromDecimalArguments a) {
   103            return ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId);
   104          }
   105        },
   106        new BiFunction<ZonedDateTime, ZoneId, ZonedDateTime>() {
   107          @Override
   108          public ZonedDateTime apply(ZonedDateTime zonedDateTime, ZoneId zoneId) {
   109            return zonedDateTime.withZoneSameInstant(zoneId);
   110          }
   111        }
   112    );
   113  
   114    protected final Function<FromIntegerArguments, T> fromMilliseconds;
   115  
   116    protected final Function<FromDecimalArguments, T> fromNanoseconds;
   117  
   118    protected final Function<TemporalAccessor, T> parsedToValue;
   119  
   120    protected final BiFunction<T, ZoneId, T> adjust;
   121  
   122    protected CustomInstantDeserializer(Class<T> supportedType,
   123                      DateTimeFormatter parser,
   124                      Function<TemporalAccessor, T> parsedToValue,
   125                      Function<FromIntegerArguments, T> fromMilliseconds,
   126                      Function<FromDecimalArguments, T> fromNanoseconds,
   127                      BiFunction<T, ZoneId, T> adjust) {
   128      super(supportedType, parser);
   129      this.parsedToValue = parsedToValue;
   130      this.fromMilliseconds = fromMilliseconds;
   131      this.fromNanoseconds = fromNanoseconds;
   132      this.adjust = adjust == null ? new BiFunction<T, ZoneId, T>() {
   133        @Override
   134        public T apply(T t, ZoneId zoneId) {
   135          return t;
   136        }
   137      } : adjust;
   138    }
   139  
   140    @SuppressWarnings("unchecked")
   141    protected CustomInstantDeserializer(CustomInstantDeserializer<T> base, DateTimeFormatter f) {
   142      super((Class<T>) base.handledType(), f);
   143      parsedToValue = base.parsedToValue;
   144      fromMilliseconds = base.fromMilliseconds;
   145      fromNanoseconds = base.fromNanoseconds;
   146      adjust = base.adjust;
   147    }
   148  
   149    @Override
   150    protected JsonDeserializer<T> withDateFormat(DateTimeFormatter dtf) {
   151      if (dtf == _formatter) {
   152        return this;
   153      }
   154      return new CustomInstantDeserializer<T>(this, dtf);
   155    }
   156  
   157    @Override
   158    public T deserialize(JsonParser parser, DeserializationContext context) throws IOException {
   159      //NOTE: Timestamps contain no timezone info, and are always in configured TZ. Only
   160      //string values have to be adjusted to the configured TZ.
   161      switch (parser.getCurrentTokenId()) {
   162        case JsonTokenId.ID_NUMBER_FLOAT: {
   163          BigDecimal value = parser.getDecimalValue();
   164          long seconds = value.longValue();
   165          int nanoseconds = DecimalUtils.extractNanosecondDecimal(value, seconds);
   166          return fromNanoseconds.apply(new FromDecimalArguments(
   167              seconds, nanoseconds, getZone(context)));
   168        }
   169  
   170        case JsonTokenId.ID_NUMBER_INT: {
   171          long timestamp = parser.getLongValue();
   172          if (context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) {
   173            return this.fromNanoseconds.apply(new FromDecimalArguments(
   174                timestamp, 0, this.getZone(context)
   175            ));
   176          }
   177          return this.fromMilliseconds.apply(new FromIntegerArguments(
   178              timestamp, this.getZone(context)
   179          ));
   180        }
   181  
   182        case JsonTokenId.ID_STRING: {
   183          String string = parser.getText().trim();
   184          if (string.length() == 0) {
   185            return null;
   186          }
   187          if (string.endsWith("+0000")) {
   188            string = string.substring(0, string.length() - 5) + "Z";
   189          }
   190          T value;
   191          try {
   192            TemporalAccessor acc = _formatter.parse(string);
   193            value = parsedToValue.apply(acc);
   194            if (context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)) {
   195              return adjust.apply(value, this.getZone(context));
   196            }
   197          } catch (DateTimeException e) {
   198            throw _peelDTE(e);
   199          }
   200          return value;
   201        }
   202      }
   203      throw context.mappingException("Expected type float, integer, or string.");
   204    }
   205  
   206    private ZoneId getZone(DeserializationContext context) {
   207      // Instants are always in UTC, so don't waste compute cycles
   208      return (_valueClass == Instant.class) ? null : DateTimeUtils.toZoneId(context.getTimeZone());
   209    }
   210  
   211    private static class FromIntegerArguments {
   212      public final long value;
   213      public final ZoneId zoneId;
   214  
   215      private FromIntegerArguments(long value, ZoneId zoneId) {
   216        this.value = value;
   217        this.zoneId = zoneId;
   218      }
   219    }
   220  
   221    private static class FromDecimalArguments {
   222      public final long integer;
   223      public final int fraction;
   224      public final ZoneId zoneId;
   225  
   226      private FromDecimalArguments(long integer, int fraction, ZoneId zoneId) {
   227        this.integer = integer;
   228        this.fraction = fraction;
   229        this.zoneId = zoneId;
   230      }
   231    }
   232  }