diff --git a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/bytecode/Called.java b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/bytecode/Called.java index bbaaa445..a9692896 100644 --- a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/bytecode/Called.java +++ b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/bytecode/Called.java @@ -16,6 +16,7 @@ /** * Indicates that methods annotated with this annotation can be called by ScalaPlugins as a result of bytecode transformation. + * Indicates that interfaces annotated with this annotation are implemented by classes which are generated at runtime. * * @see RuntimeConversions#serialize(Object, ParameterType, ClassLoader) * @see RuntimeConversions#deserialize(Object, ParameterType, ClassLoader) @@ -25,6 +26,7 @@ * @see ParameterType#from(Type) * @see ArrayParameterType#from(ParameterType, boolean) * @see ParameterizedParameterType#from(Class, ParameterType...) + * @see Adapter */ @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE}) @Retention(RetentionPolicy.CLASS) diff --git a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/Adapter.java b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/Adapter.java index a552224b..37b41487 100644 --- a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/Adapter.java +++ b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/Adapter.java @@ -13,6 +13,7 @@ * * @param the type of the wrapped value */ +@Called // there exist implementations of this interface which are generated at runtime through bytecode generation. public interface Adapter extends ConfigurationSerializable { /** diff --git a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/RuntimeConversions.java b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/RuntimeConversions.java index b10ecc14..2c14bb07 100644 --- a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/RuntimeConversions.java +++ b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/RuntimeConversions.java @@ -2,7 +2,6 @@ import org.bukkit.configuration.serialization.ConfigurationSerializable; import xyz.janboerman.scalaloader.bytecode.Called; -import xyz.janboerman.scalaloader.compat.IScalaPluginLoader; import xyz.janboerman.scalaloader.configurationserializable.runtime.types.*; import xyz.janboerman.scalaloader.configurationserializable.transform.ConfigurationSerializableError; import xyz.janboerman.scalaloader.compat.IScalaPluginClassLoader; @@ -11,6 +10,20 @@ import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.Year; +import java.time.YearMonth; +import java.time.ZonedDateTime; +import java.time.chrono.HijrahDate; +import java.time.chrono.JapaneseDate; +import java.time.chrono.MinguoDate; +import java.time.chrono.ThaiBuddhistDate; import java.util.*; import java.util.concurrent.*; import java.util.function.Function; @@ -216,7 +229,38 @@ else if (Tuple.isTuple(live)) { return new xyz.janboerman.scalaloader.configurationserializable.runtime.types.BigDecimal((BigDecimal) live); } else if (live instanceof Enum) { return xyz.janboerman.scalaloader.configurationserializable.runtime.types.Enum.forEnum((Enum) live, pluginClassLoader); + } else if (live instanceof Instant) { + return new DateTime.Instant((Instant) live); + } else if (live instanceof ZonedDateTime) { + return new DateTime.ZonedDateTime((ZonedDateTime) live); + } else if (live instanceof LocalDateTime) { + return new DateTime.LocalDateTime((LocalDateTime) live); + } else if (live instanceof LocalTime) { + return new DateTime.LocalTime((LocalTime) live); + } else if (live instanceof LocalDate) { + return new DateTime.LocalDate((LocalDate) live); + } else if (live instanceof Year) { + return new DateTime.Year((Year) live); + } else if (live instanceof YearMonth) { + return new DateTime.YearMonth((YearMonth) live); + } else if (live instanceof OffsetDateTime) { + return new DateTime.OffsetDateTime((OffsetDateTime) live); + } else if (live instanceof MinguoDate) { + return new DateTime.MinguoDate((MinguoDate) live); + } else if (live instanceof JapaneseDate) { + return new DateTime.JapaneseDate((JapaneseDate) live); + } else if (live instanceof HijrahDate) { + return new DateTime.HijrahDate((HijrahDate) live); + } else if (live instanceof ThaiBuddhistDate) { + return new DateTime.ThaiBuddhistDate((ThaiBuddhistDate) live); + } else if (live instanceof Duration) { + return new DateTime.Duration((Duration) live); + } else if (live instanceof Period) { + return new DateTime.Period((Period) live); + } else if (live instanceof Date) { + return new DateTime.Date((Date) live); } + // TODO adapter for types which implement Serializable ? //if the type is not ConfigurationSerializable, warn the plugin author if (!(live instanceof ConfigurationSerializable) && pluginClassLoader.getPluginLoader().debugSettings().logMissingCodecs()) { diff --git a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/types/DateTime.java b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/types/DateTime.java new file mode 100644 index 00000000..bc62a300 --- /dev/null +++ b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/runtime/types/DateTime.java @@ -0,0 +1,878 @@ +package xyz.janboerman.scalaloader.configurationserializable.runtime.types; + +import java.text.SimpleDateFormat; +import java.time.chrono.HijrahEra; +import java.time.chrono.MinguoEra; +import java.time.chrono.ThaiBuddhistEra; +import java.time.temporal.ChronoField; +import java.util.Map; +import java.util.Objects; + +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.configuration.serialization.SerializableAs; + +import xyz.janboerman.scalaloader.compat.Compat; +import xyz.janboerman.scalaloader.configurationserializable.runtime.Adapter; + +public class DateTime { + + private DateTime() { + } + + public static void registerWithConfigurationSerialization() { + Instant.register(); + ZonedDateTime.register(); + LocalDateTime.register(); + LocalTime.register(); + LocalDate.register(); + Year.register(); + YearMonth.register(); + OffsetDateTime.register(); + OffsetTime.register(); + MinguoDate.register(); + JapaneseDate.register(); + HijrahDate.register(); + ThaiBuddhistDate.register(); + Duration.register(); + Period.register(); + Date.register(); + } + + @SerializableAs("Instant") + public static class Instant implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(Instant.class, "Instant"); + } + + public final java.time.Instant value; + + public Instant(java.time.Instant value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static Instant deserialize(Map map) { + String value = (String) map.get("value"); + return new Instant(value == null ? null : java.time.Instant.parse(value)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof Instant)) return false; + + Instant that = (Instant) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.Instant getValue() { + return value; + } + } + + @SerializableAs("ZonedDateTime") + public static class ZonedDateTime implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(ZonedDateTime.class, "ZonedDateTime"); + } + + public final java.time.ZonedDateTime value; + + public ZonedDateTime(java.time.ZonedDateTime value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static ZonedDateTime deserialize(Map map) { + String value = (String) map.get("value"); + return new ZonedDateTime(value == null ? null : java.time.ZonedDateTime.parse(value)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof ZonedDateTime)) return false; + + ZonedDateTime that = (ZonedDateTime) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.ZonedDateTime getValue() { + return value; + } + } + + @SerializableAs("LocalDateTime") + public static class LocalDateTime implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(LocalDateTime.class, "LocalDateTime"); + } + + public final java.time.LocalDateTime value; + + public LocalDateTime(java.time.LocalDateTime value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static LocalDateTime deserialize(Map map) { + String serialised = (String) map.get("value"); + return new LocalDateTime(serialised == null ? null : java.time.LocalDateTime.parse(serialised)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof LocalDateTime)) return false; + + LocalDateTime that = (LocalDateTime) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.LocalDateTime getValue() { + return value; + } + } + + @SerializableAs("LocalTime") + public static class LocalTime implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(LocalTime.class, "LocalTime"); + } + + public final java.time.LocalTime value; + + public LocalTime(java.time.LocalTime value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static LocalTime deserialize(Map map) { + String value = (String) map.get("value"); + return new LocalTime(value == null ? null : java.time.LocalTime.parse(value)); + } + + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof LocalTime)) return false; + + LocalTime that = (LocalTime) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.LocalTime getValue() { + return value; + } + } + + @SerializableAs("LocalDate") + public static class LocalDate implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(LocalDate.class, "LocalDate"); + } + + public final java.time.LocalDate value; + + public LocalDate(java.time.LocalDate value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static LocalDate deserialize(Map map) { + String value = (String) map.get("value"); + return new LocalDate(value == null ? null : java.time.LocalDate.parse(value)); + } + + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof LocalDate)) return false; + + LocalDate that = (LocalDate) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.LocalDate getValue() { + return value; + } + } + + @SerializableAs("Year") + public static class Year implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(Year.class, "Year"); + } + + public final java.time.Year value; + + public Year(java.time.Year value) { + this.value = value; + } + + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static Year deserialize(Map map) { + Object value = map.get("value"); + return new Year(value == null ? null : java.time.Year.parse(value.toString())); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof Year)) return false; + + Year that = (Year) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.Year getValue() { + return value; + } + } + + @SerializableAs("YearMonth") + public static class YearMonth implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(YearMonth.class, "YearMonth"); + } + + public final java.time.YearMonth value; + + public YearMonth(java.time.YearMonth value) { + this.value = value; + } + + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static YearMonth deserialize(Map map) { + Object value = map.get("value"); + return new YearMonth(value == null ? null : java.time.YearMonth.parse(value.toString())); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof YearMonth)) return false; + + YearMonth that = (YearMonth) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.YearMonth getValue() { + return value; + } + } + + @SerializableAs("OffsetDateTime") + public static class OffsetDateTime implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(OffsetDateTime.class, "OffsetDateTime"); + } + + public final java.time.OffsetDateTime value; + + public OffsetDateTime(java.time.OffsetDateTime value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static OffsetDateTime deserialize(Map map) { + String value = (String) map.get("value"); + return new OffsetDateTime(value == null ? null : java.time.OffsetDateTime.parse(value)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof OffsetDateTime)) return false; + + OffsetDateTime that = (OffsetDateTime) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.OffsetDateTime getValue() { + return value; + } + } + + @SerializableAs("OffsetTime") + public static class OffsetTime implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(OffsetTime.class, "OffsetTime"); + } + + public final java.time.OffsetTime value; + + public OffsetTime(java.time.OffsetTime value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static OffsetTime deserialize(Map map) { + String value = (String) map.get("value"); + return new OffsetTime(value == null ? null : java.time.OffsetTime.parse(value)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof OffsetTime)) return false; + + OffsetTime that = (OffsetTime) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.OffsetTime getValue() { + return value; + } + } + + @SerializableAs("MinguoDate") + public static class MinguoDate implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(MinguoDate.class, "MinguoDate"); + } + + public final java.time.chrono.MinguoDate value; + + public MinguoDate(java.time.chrono.MinguoDate value) { + this.value = value; + } + + @Override + public Map serialize() { + if (value != null) { + return Compat.mapOf( + Compat.mapEntry("era", MinguoEra.of(value.get(ChronoField.ERA)).name()), + Compat.mapEntry("year", value.get(ChronoField.YEAR_OF_ERA)), + Compat.mapEntry("month", value.get(ChronoField.MONTH_OF_YEAR)), + Compat.mapEntry("day", value.get(ChronoField.DAY_OF_MONTH)) + ); + } else { + return Compat.emptyMap(); + } + } + + public static MinguoDate deserialize(Map map) { + String era = (String) map.get("era"); + Integer year = (Integer) map.get("year"); + Integer month = (Integer) map.get("month"); + Integer day = (Integer) map.get("day"); + if (era != null && year != null && month != null && day != null) { + java.time.chrono.MinguoDate value = java.time.chrono.MinguoDate.of(0, 0, 0); + value = value.with(ChronoField.ERA, MinguoEra.valueOf(era).getValue()); + value = value.with(ChronoField.YEAR_OF_ERA, year.longValue()); + value = value.with(ChronoField.MONTH_OF_YEAR, month.longValue()); + value = value.with(ChronoField.DAY_OF_MONTH, day.longValue()); + return new MinguoDate(value); + } else { + return new MinguoDate(null); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof MinguoDate)) return false; + + MinguoDate that = (MinguoDate) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.chrono.MinguoDate getValue() { + return value; + } + } + + @SerializableAs("JapaneseDate") + public static class JapaneseDate implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(JapaneseDate.class, "JapaneseDate"); + } + + public final java.time.chrono.JapaneseDate value; + + public JapaneseDate(java.time.chrono.JapaneseDate value) { + this.value = value; + } + + @Override + public Map serialize() { + if (value != null) { + return Compat.mapOf( + Compat.mapEntry("era", value.get(ChronoField.ERA)), + Compat.mapEntry("year", value.get(ChronoField.YEAR_OF_ERA)), + Compat.mapEntry("month", value.get(ChronoField.MONTH_OF_YEAR)), + Compat.mapEntry("day", value.get(ChronoField.DAY_OF_MONTH)) + ); + } else { + return Compat.emptyMap(); + } + } + + public static JapaneseDate deserialize(Map map) { + Integer era = (Integer) map.get("era"); + Integer year = (Integer) map.get("year"); + Integer month = (Integer) map.get("month"); + Integer day = (Integer) map.get("day"); + if (era != null && year != null && month != null && day != null) { + java.time.chrono.JapaneseDate value = java.time.chrono.JapaneseDate.of(0, 0, 0); + value = value.with(ChronoField.ERA, era.longValue()); + value = value.with(ChronoField.YEAR_OF_ERA, year.longValue()); + value = value.with(ChronoField.MONTH_OF_YEAR, month.longValue()); + value = value.with(ChronoField.DAY_OF_MONTH, day.longValue()); + return new JapaneseDate(value); + } else { + return new JapaneseDate(null); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof JapaneseDate)) return false; + + JapaneseDate that = (JapaneseDate) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.chrono.JapaneseDate getValue() { + return value; + } + } + + @SerializableAs("HijrahDate") + public static class HijrahDate implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(HijrahDate.class, "HijrahDate"); + } + + public final java.time.chrono.HijrahDate value; + + public HijrahDate(java.time.chrono.HijrahDate value) { + this.value = value; + } + + @Override + public Map serialize() { + if (value != null) { + return Compat.mapOf( + Compat.mapEntry("era", HijrahEra.of(value.get(ChronoField.ERA)).name()), + Compat.mapEntry("year", value.get(ChronoField.YEAR_OF_ERA)), + Compat.mapEntry("month", value.get(ChronoField.MONTH_OF_YEAR)), + Compat.mapEntry("day", value.get(ChronoField.DAY_OF_MONTH)) + ); + } else { + return Compat.emptyMap(); + } + } + + public static HijrahDate deserialize(Map map) { + String era = (String) map.get("era"); + Integer year = (Integer) map.get("year"); + Integer month = (Integer) map.get("month"); + Integer day = (Integer) map.get("day"); + if (era != null && year != null && month != null && day != null) { + java.time.chrono.HijrahDate value = java.time.chrono.HijrahDate.of(0, 0, 0); + value = value.with(ChronoField.ERA, HijrahEra.valueOf(era).getValue()); + value = value.with(ChronoField.YEAR_OF_ERA, year.longValue()); + value = value.with(ChronoField.MONTH_OF_YEAR, month.longValue()); + value = value.with(ChronoField.DAY_OF_MONTH, day.longValue()); + return new HijrahDate(value); + } else { + return new HijrahDate(null); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof HijrahDate)) return false; + + HijrahDate that = (HijrahDate) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.chrono.HijrahDate getValue() { + return value; + } + } + + @SerializableAs("ThaiBuddhistDate") + public static class ThaiBuddhistDate implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(ThaiBuddhistDate.class, "ThaiBuddhistDate"); + } + + public final java.time.chrono.ThaiBuddhistDate value; + + public ThaiBuddhistDate(java.time.chrono.ThaiBuddhistDate value) { + this.value = value; + } + + @Override + public Map serialize() { + if (value != null) { + return Compat.mapOf( + Compat.mapEntry("era", ThaiBuddhistEra.of(value.get(ChronoField.ERA)).name()), + Compat.mapEntry("year", value.get(ChronoField.YEAR_OF_ERA)), + Compat.mapEntry("month", value.get(ChronoField.MONTH_OF_YEAR)), + Compat.mapEntry("day", value.get(ChronoField.DAY_OF_MONTH)) + ); + } else { + return Compat.emptyMap(); + } + } + + public static ThaiBuddhistDate deserialize(Map map) { + String era = (String) map.get("era"); + Integer year = (Integer) map.get("year"); + Integer month = (Integer) map.get("month"); + Integer day = (Integer) map.get("day"); + if (era != null && year != null && month != null && day != null) { + java.time.chrono.ThaiBuddhistDate value = java.time.chrono.ThaiBuddhistDate.of(0, 0, 0); + value = value.with(ChronoField.ERA, ThaiBuddhistEra.valueOf(era).getValue()); + value = value.with(ChronoField.YEAR_OF_ERA, year.longValue()); + value = value.with(ChronoField.MONTH_OF_YEAR, month.longValue()); + value = value.with(ChronoField.DAY_OF_MONTH, day.longValue()); + return new ThaiBuddhistDate(value); + } else { + return new ThaiBuddhistDate(null); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof ThaiBuddhistDate)) return false; + + ThaiBuddhistDate that = (ThaiBuddhistDate) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.chrono.ThaiBuddhistDate getValue() { + return value; + } + } + + @SerializableAs("Duration") + public static class Duration implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(Duration.class, "Duration"); + } + + public final java.time.Duration value; + + public Duration(java.time.Duration value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static Duration deserialize(Map map) { + String value = (String) map.get("value"); + return new Duration(value == null ? null : java.time.Duration.parse(value)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof Duration)) return false; + + Duration that = (Duration) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.Duration getValue() { + return value; + } + } + + @SerializableAs("Period") + public static class Period implements Adapter { + private static void register() { + ConfigurationSerialization.registerClass(Period.class, "Period"); + } + + public final java.time.Period value; + + public Period(java.time.Period value) { + this.value = value; + } + + @Override + public Map serialize() { + return Compat.singletonMap("value", value == null ? null : value.toString()); + } + + public static Period deserialize(Map map) { + String value = (String) map.get("value"); + return new Period(value == null ? null : java.time.Period.parse(value)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof Period)) return false; + + Period that = (Period) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.time.Period getValue() { + return value; + } + } + + @SerializableAs("Date") + public static class Date implements Adapter { + private static final String FORMAT = "yyyy-MM-dd HH:mm:ss.SSSZZ"; + private static void register() { + ConfigurationSerialization.registerClass(Date.class, "Date"); + } + + public final java.util.Date value; + + public Date(java.util.Date value) { + this.value = value; + } + + @Override + public Map serialize() { + String serialised = value == null ? null : new SimpleDateFormat(FORMAT).format(value); + return Compat.singletonMap("value", serialised); + } + + public static Date deserialize(Map map) throws Exception { + String serialised = (String) map.get("value"); + return new Date(new SimpleDateFormat(FORMAT).parse(serialised)); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof Date)) return false; + + Date that = (Date) obj; + return Objects.equals(this.value, that.value); + } + + @Override + public String toString() { + return Objects.toString(value); + } + + @Override + public java.util.Date getValue() { + return value; + } + } +} diff --git a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/transform/Conversions.java b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/transform/Conversions.java index 3c961f3c..6b79c33c 100644 --- a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/transform/Conversions.java +++ b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/configurationserializable/transform/Conversions.java @@ -153,7 +153,7 @@ else if (ScalaConversions.isScalaCollection(typeSignature, (ClassLoader) pluginC methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/UUID", "toString", "()Ljava/lang/String;", false); operandStack.replaceTop(STRING_TYPE); break; - //TODO java.util.Date/java.time.Instant maybe? + //TODO java.util.Date/java.time.Instant maybe? We can use the ISO-8601 format. //TODO scala.math.BigInt //TODO scala.math.BigDecimal @@ -178,6 +178,8 @@ else if (ScalaConversions.isScalaCollection(typeSignature, (ClassLoader) pluginC } + // TODO split conversions methods to other (new) classes (ArrayConversions, CollectionConversions, MapConversions, ScalaCollectionsConversions, ScalaMapConversions, more classes for scala-specific types such as tuples, option/either, etc). + private static void arrayToSerializedType(IScalaPluginClassLoader pluginClassLoader, MethodVisitor methodVisitor, TypeSignature arrayTypeSignature, OperandStack operandStack, LocalCounter localCounter, LocalVariableTable localVariableTable) { assert arrayTypeSignature.isArray() : "not an array"; @@ -1819,8 +1821,8 @@ static void serializeCollection(IScalaPluginClassLoader classLoader, MethodVisit //TODO - immutable.WrappedString --- done! //TODO - immutable.Range --- done! //TODO - immutable.NumericRange --- done! (but not yet tested) - //TODO - immutable.ArraySeq - //TODO - mutable.ArraySeq + //TODO - immutable.ArraySeq --- TODO why do we need this? can we serialise it more optimally? is there a different way to construct one? + //TODO - mutable.ArraySeq --- TODO why do we need this? can we serialise it more optimally? is there a different way to construct one? //TODO diff --git a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/util/ScalaLoaderUtils.java b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/util/ScalaLoaderUtils.java index ac9a9da7..55e1598a 100644 --- a/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/util/ScalaLoaderUtils.java +++ b/ScalaLoader-Common/src/main/java/xyz/janboerman/scalaloader/util/ScalaLoaderUtils.java @@ -55,6 +55,7 @@ public static void initConfigura xyz.janboerman.scalaloader.configurationserializable.runtime.types.UUID.registerWithConfigurationSerialization(); xyz.janboerman.scalaloader.configurationserializable.runtime.types.BigInteger.registerWithConfigurationSerialization(); xyz.janboerman.scalaloader.configurationserializable.runtime.types.BigDecimal.registerWithConfigurationSerialization(); + xyz.janboerman.scalaloader.configurationserializable.runtime.types.DateTime.registerWithConfigurationSerialization(); xyz.janboerman.scalaloader.configurationserializable.runtime.types.Option.registerWithConfigurationSerialization(); xyz.janboerman.scalaloader.configurationserializable.runtime.types.Either.registerWithConfigurationSerialization(); }