Skip to content

Commit

Permalink
IPROTO-146 Adapters for java.time classes
Browse files Browse the repository at this point in the history
  • Loading branch information
tristantarrant committed Jun 25, 2024
1 parent c430dfe commit c2978ba
Show file tree
Hide file tree
Showing 22 changed files with 682 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* @since 1.0
*/
public interface BaseMarshaller<T> {
String[] EMPTY = new String[0];

/**
* Returns the Java type handled by this marshaller. This must not change over multiple invocations.
Expand All @@ -24,4 +25,11 @@ public interface BaseMarshaller<T> {
* @return the full name of the message or enum type, defined in a proto file.
*/
String getTypeName();

/**
* Returns any subclass names
*/
default String[] getSubClassNames() {
return EMPTY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* An annotation-based generated proto schema file. This is just a more specific flavour of
* {@link SerializationContextInitializer} that also exposes the generated Protobuf schema, which consists of the file
* name and the file contents. Users will never implement this interface directly. Implementations are always generated
* by the annotation processor based on the {@link org.infinispan.protostream.annotations.AutoProtoSchemaBuilder}
* by the annotation processor based on the {@link org.infinispan.protostream.annotations.ProtoSchema}
* annotation, identically as for {@link SerializationContextInitializer}.
*
* @author [email protected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,10 @@
* The actual class being marshalled.
*/
Class<?> value();

/**
* Any sub-classes of the main class which should also be handled by this adaptor. This is useful, for example,
* when the actual class is determined by a factory method, such as {@link java.util.List#of}
*/
String[] subClassNames() default {};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.infinispan.protostream.annotations.impl;

import org.infinispan.protostream.annotations.ProtoAdapter;
import org.infinispan.protostream.annotations.ProtoSyntax;
import org.infinispan.protostream.annotations.ProtoTypeId;
import org.infinispan.protostream.annotations.impl.types.XClass;
Expand Down Expand Up @@ -89,6 +90,11 @@ public String getJavaClassName() {
return canonicalName != null ? canonicalName : javaClass.getName();
}

public String[] getSubClassNames() {
ProtoAdapter adapter = getAnnotatedClass().getAnnotation(ProtoAdapter.class);
return adapter != null ? adapter.subClassNames() : null;
}

/**
* At this level we pretend the Java class and the annotated class are one and the same, but subclasses
* may decide otherwise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ public class ResolutionContext {

private final Map<Integer, GenericDescriptor> typeIds = new HashMap<>();

private Map<String, GenericDescriptor> allGlobalTypes;
private final Map<String, GenericDescriptor> allGlobalTypes;

private Map<String, GenericDescriptor> globalTypes = new HashMap<>();
private final Map<String, GenericDescriptor> globalTypes = new HashMap<>();

private Map<String, EnumValueDescriptor> allEnumValueDescriptors;
private final Map<String, EnumValueDescriptor> allEnumValueDescriptors;

private Map<String, EnumValueDescriptor> enumValueDescriptors = new HashMap<>();
private final Map<String, EnumValueDescriptor> enumValueDescriptors = new HashMap<>();

public ResolutionContext(FileDescriptorSource.ProgressCallback progressCallback,
Map<String, FileDescriptor> fileDescriptorMap,
Expand Down Expand Up @@ -100,8 +100,7 @@ void addGenericDescriptor(GenericDescriptor genericDescriptor) {

globalTypes.put(genericDescriptor.getFullName(), genericDescriptor);

if (genericDescriptor instanceof EnumDescriptor) {
EnumDescriptor enumDescriptor = (EnumDescriptor) genericDescriptor;
if (genericDescriptor instanceof EnumDescriptor enumDescriptor) {
for (EnumValueDescriptor ev : enumDescriptor.getValues()) {
enumValueDescriptors.put(ev.getScopedName(), ev);
}
Expand All @@ -127,8 +126,7 @@ private void checkUniqueName(GenericDescriptor genericDescriptor) {
+ " clashes with enum value " + existingEnumValueDescriptor.getFullName());
}

if (genericDescriptor instanceof EnumDescriptor) {
EnumDescriptor enumDescriptor = (EnumDescriptor) genericDescriptor;
if (genericDescriptor instanceof EnumDescriptor enumDescriptor) {
for (EnumValueDescriptor ev : enumDescriptor.getValues()) {
// check if this enum value constant conflicts with another enum value constant
existingEnumValueDescriptor = lookup(enumValueDescriptors, allEnumValueDescriptors, ev.getScopedName());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.infinispan.protostream.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -32,6 +32,7 @@
* @since 1.0
*/
public final class SerializationContextImpl implements SerializationContext {
static final Class<?>[] EMPTY_CLASSES = new Class[0];

private static final Log log = Log.LogFactory.getLog(SerializationContextImpl.class);

Expand Down Expand Up @@ -80,7 +81,7 @@ public Configuration getConfiguration() {
public Map<String, FileDescriptor> getFileDescriptors() {
long stamp = descriptorLock.readLock();
try {
return Collections.unmodifiableMap(new HashMap<>(fileDescriptors));
return Map.copyOf(fileDescriptors);
} finally {
descriptorLock.unlockRead(stamp);
}
Expand Down Expand Up @@ -248,6 +249,14 @@ public void registerMarshaller(BaseMarshaller<?> marshaller) {
throw new IllegalArgumentException("The given marshaller attempts to override an existing marshaller registered indirectly via an InstanceMarshallerProvider. Please unregister it first.");
}

final Class<?>[] subClasses;
String[] subClassNames = marshaller.getSubClassNames();
if (subClassNames.length > 0) {
subClasses = Arrays.stream(subClassNames).map(SerializationContextImpl::classForName).toArray(Class[]::new);
} else {
subClasses = EMPTY_CLASSES;
}

if (existingByName != null) {
Registration anotherByClass = marshallersByClass.get(existingByName.marshallerDelegate.getMarshaller().getJavaClass());
if (anotherByClass == null) {
Expand All @@ -261,18 +270,33 @@ public void registerMarshaller(BaseMarshaller<?> marshaller) {
}
}
marshallersByClass.remove(existingByName.marshallerDelegate.getMarshaller().getJavaClass());
for (Class<?> subClass : subClasses) {
marshallersByClass.remove(subClass);
}
}
if (existingByClass != null) {
marshallersByName.remove(existingByClass.marshallerDelegate.getMarshaller().getTypeName());
}

Registration registration = new Registration(makeMarshallerDelegate(marshaller));
marshallersByClass.put(marshaller.getJavaClass(), registration);
marshallersByName.put(marshaller.getTypeName(), registration);
for (Class<?> subClass : subClasses) {
marshallersByClass.put(subClass, registration);
}
} finally {
manifestLock.unlockWrite(stamp);
}
}

static Class<?> classForName(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

private <T> BaseMarshallerDelegate<T> makeMarshallerDelegate(BaseMarshaller<T> marshaller) {
if (marshaller.getJavaClass().isEnum() && !(marshaller instanceof EnumMarshaller)) {
throw new IllegalArgumentException("Invalid marshaller (the produced class is a Java Enum, but the marshaller is not an EnumMarshaller) : " + marshaller.getClass().getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;

import javax.lang.model.element.Element;

Expand Down Expand Up @@ -201,6 +203,15 @@ private void generateMessageMarshaller(ProtoMessageTypeMetadata pmtm) throws IOE
iw.println("@Override");
iw.printf("public String getTypeName() { return \"%s\"; }\n", makeQualifiedTypeName(pmtm.getFullName()));
iw.println();
String[] subClassNames = pmtm.getSubClassNames();
if (subClassNames != null && subClassNames.length > 0) {
iw.println("@Override");
iw.println("public String[] getSubClassNames() {");
iw.inc().print("return new String[] {");
iw.print(Arrays.stream(subClassNames).collect(Collectors.joining(",", "\"", "\"")));
iw.println("};");
iw.dec().println("}");
}

if (pmtm.isIndexedContainer()) {
if (pmtm.isAdapter()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.infinispan.protostream.types.java;

import org.infinispan.protostream.GeneratedSchema;
import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
import org.infinispan.protostream.annotations.ProtoSchema;
import org.infinispan.protostream.types.java.arrays.BooleanArrayAdapter;
import org.infinispan.protostream.types.java.arrays.BoxedBooleanArrayAdapter;
import org.infinispan.protostream.types.java.arrays.BoxedByteArrayAdapter;
Expand Down Expand Up @@ -29,7 +29,7 @@
* @author [email protected]
* @since 4.4
*/
@AutoProtoSchemaBuilder(
@ProtoSchema(
className = "CommonContainerTypesSchema",
schemaFileName = "common-java-container-types.proto",
schemaFilePath = "/protostream",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
import org.infinispan.protostream.annotations.ProtoSchema;
import org.infinispan.protostream.types.java.math.BigDecimalAdapter;
import org.infinispan.protostream.types.java.math.BigIntegerAdapter;
import org.infinispan.protostream.types.java.time.LocalDateAdapter;
import org.infinispan.protostream.types.java.time.LocalDateTimeAdapter;
import org.infinispan.protostream.types.java.time.LocalTimeAdapter;
import org.infinispan.protostream.types.java.time.MonthAdapter;
import org.infinispan.protostream.types.java.time.MonthDayAdapter;
import org.infinispan.protostream.types.java.time.OffsetTimeAdapter;
import org.infinispan.protostream.types.java.time.PeriodAdapter;
import org.infinispan.protostream.types.java.time.YearAdapter;
import org.infinispan.protostream.types.java.time.ZoneIdAdapter;
import org.infinispan.protostream.types.java.time.ZoneOffsetAdapter;
import org.infinispan.protostream.types.java.time.ZonedDateTimeAdapter;
import org.infinispan.protostream.types.java.util.BitSetAdapter;
import org.infinispan.protostream.types.java.util.UUIDAdapter;

Expand All @@ -22,7 +33,18 @@
UUIDAdapter.class,
BigIntegerAdapter.class,
BigDecimalAdapter.class,
BitSetAdapter.class
BitSetAdapter.class,
LocalDateAdapter.class,
LocalDateTimeAdapter.class,
LocalTimeAdapter.class,
MonthAdapter.class,
MonthDayAdapter.class,
OffsetTimeAdapter.class,
PeriodAdapter.class,
YearAdapter.class,
ZonedDateTimeAdapter.class,
ZoneIdAdapter.class,
ZoneOffsetAdapter.class
}
)
public interface CommonTypes extends GeneratedSchema {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.infinispan.protostream.types.java.time;

import java.time.LocalDate;

import org.infinispan.protostream.annotations.ProtoAdapter;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.descriptors.Type;

@ProtoAdapter(LocalDate.class)
public class LocalDateAdapter {
@ProtoFactory
LocalDate create(Long epochDay) {
return LocalDate.ofEpochDay(epochDay);
}

@ProtoField(number = 1, type = Type.UINT64)
Long getEpochDay(LocalDate localDate) {
return localDate.toEpochDay();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.infinispan.protostream.types.java.time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

import org.infinispan.protostream.annotations.ProtoAdapter;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.descriptors.Type;

@ProtoAdapter(LocalDateTime.class)
public class LocalDateTimeAdapter {
@ProtoFactory
LocalDateTime create(LocalDate localDate, LocalTime localTime) {
return LocalDateTime.of(localDate, localTime);
}

@ProtoField(number = 1, type = Type.MESSAGE)
LocalDate getLocalDate(LocalDateTime localDateTime) {
return localDateTime.toLocalDate();
}

@ProtoField(number = 2, type = Type.MESSAGE)
LocalTime getLocalTime(LocalDateTime localDateTime) {
return localDateTime.toLocalTime() ;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.infinispan.protostream.types.java.time;

import java.time.LocalTime;

import org.infinispan.protostream.annotations.ProtoAdapter;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.descriptors.Type;

@ProtoAdapter(LocalTime.class)
public class LocalTimeAdapter {
@ProtoFactory
LocalTime create(Long nanoOfDay) {
return LocalTime.ofNanoOfDay(nanoOfDay);
}
@ProtoField(number = 1, type = Type.UINT64)
Long getNanoOfDay(LocalTime localTime) {
return localTime.toNanoOfDay();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.infinispan.protostream.types.java.time;

import java.time.Month;

import org.infinispan.protostream.annotations.ProtoAdapter;
import org.infinispan.protostream.annotations.ProtoEnumValue;
import org.infinispan.protostream.annotations.ProtoName;

@ProtoAdapter(Month.class)
@ProtoName("Month")
public enum MonthAdapter {
@ProtoEnumValue
JANUARY,
@ProtoEnumValue(value = 1)
FEBRUARY,
@ProtoEnumValue(value = 2)
MARCH,
@ProtoEnumValue(value = 3)
APRIL,
@ProtoEnumValue(value = 4)
MAY,
@ProtoEnumValue(value = 5)
JUNE,
@ProtoEnumValue(value = 6)
JULY,
@ProtoEnumValue(value = 7)
AUGUST,
@ProtoEnumValue(value = 8)
SEPTEMBER,
@ProtoEnumValue(value = 9)
OCTOBER,
@ProtoEnumValue(value = 10)
NOVEMBER,
@ProtoEnumValue(value = 11)
DECEMBER;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.infinispan.protostream.types.java.time;

import java.time.MonthDay;

import org.infinispan.protostream.annotations.ProtoAdapter;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.descriptors.Type;

@ProtoAdapter(MonthDay.class)
public class MonthDayAdapter {
@ProtoFactory
MonthDay create(Integer month, Integer dayOfMonth) {
return MonthDay.of(month, dayOfMonth);
}
@ProtoField(number = 1, type = Type.INT32)
Integer getMonth(MonthDay monthDay) {
return monthDay.getMonthValue();
}

@ProtoField(number = 2, type = Type.INT32)
Integer getDayOfMonth(MonthDay monthDay) {
return monthDay.getDayOfMonth();
}
}
Loading

0 comments on commit c2978ba

Please sign in to comment.