Skip to content

Commit

Permalink
IPROTO-311 Simple enums
Browse files Browse the repository at this point in the history
  • Loading branch information
tristantarrant authored and karesti committed Mar 12, 2024
1 parent 89d1b0d commit 73963bf
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@
import java.lang.annotation.Target;

/**
* Defines a Protocol Buffers message without having to annotate all fields with {@link ProtoField}.
* Use this annotation to quickly generate messages from records or classes with public fields.
* Fields must be public and they will be assigned incremental numbers based on the declaration order.
* It is possible to override the automated defaults for a field by using the {@link ProtoField} annotation.
* Defines a Protocol Buffers message or enum without having to annotate all fields with {@link ProtoField} or {@link ProtoEnumValue}.
* <p>
* Use this annotation on records or classes with public fields to quickly generate protocol buffers messages.
* Fields must be public and they will be assigned incremental numbers based on the declaration order.
* It is possible to override the automated defaults for a field by using the {@link ProtoField} annotation.
* </p>
* <p>
* Use this annotation on Java enums to quickly generate protocol buffer enums.
* The enums will use the natural ordinal number of the values.
* It is possible to override the automated defaults for an enum value by using the {@link ProtoEnumValue} annotation.
* </p>
*
* @since 5.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.SortedMap;
import java.util.TreeMap;

import org.infinispan.protostream.annotations.Proto;
import org.infinispan.protostream.annotations.ProtoEnumValue;
import org.infinispan.protostream.annotations.ProtoName;
import org.infinispan.protostream.annotations.ProtoSchemaBuilderException;
Expand Down Expand Up @@ -73,20 +74,21 @@ public boolean isAdapter() {

@Override
public void scanMemberAnnotations() {
final boolean implicit = annotatedEnumClass.getAnnotation(Proto.class) != null;
if (membersByNumber == null) {
membersByNumber = new TreeMap<>();
membersByName = new HashMap<>();
for (XEnumConstant ec : annotatedEnumClass.getEnumConstants()) {
ProtoEnumValue annotation = ec.getAnnotation(ProtoEnumValue.class);
if (annotation == null) {
throw new ProtoSchemaBuilderException("Enum constants must have the @ProtoEnumValue annotation: " + getAnnotatedClassName() + '.' + ec.getName());
if (annotation == null && !implicit) {
throw Log.LOG.explicitEnumValueAnnotations(ec.getName(), annotatedEnumClass.getName());
}
int number = getNumber(annotation, ec);
int number = annotation == null ? ec.getOrdinal() : getNumber(annotation, ec);
if (membersByNumber.containsKey(number)) {
throw new ProtoSchemaBuilderException("Found duplicate definition of Protobuf enum tag " + number + " on enum constant: " + getAnnotatedClassName() + '.' + ec.getName()
+ " clashes with " + membersByNumber.get(number).getJavaEnumName());
}
String name = annotation.name();
String name = annotation == null ? ec.getName() : annotation.name();
if (name.isEmpty()) {
name = ec.getName();
}
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/org/infinispan/protostream/impl/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ default MalformedProtobufException messageTruncated() {
@Message(value = "@ProtoFactory annotated method has wrong return type: %s", id = 28)
ProtoSchemaBuilderException wrongFactoryReturnType(String s);

@Message(value = "Value `%s` on enum `%s` must be annotated with @ProtoEnumValue", id = 29)
ProtoSchemaBuilderException explicitEnumValueAnnotations(String value, String name);

class LogFactory {
public static Log getLog(Class<?> clazz) {
return Logger.getMessageLogger(Log.class, clazz.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void discoverClasses(RoundEnvironment roundEnv) throws AnnotationProcessingExcep
}

for (Element e : roundEnv.getElementsAnnotatedWith(Proto.class)) {
visitProtoMessage(e);
visitProto(e);
}

for (Element e : roundEnv.getElementsAnnotatedWith(ProtoAdapter.class)) {
Expand Down Expand Up @@ -187,7 +187,7 @@ private void visitTypeElement(TypeElement e) {
visitProtoName(e);
}
if (e.getAnnotation(Proto.class) != null) {
visitProtoMessage(e);
visitProto(e);
}

for (Element member : e.getEnclosedElements()) {
Expand Down Expand Up @@ -238,29 +238,27 @@ private void visitProtoAdapter(Element e) {
}

private void visitProtoTypeId(Element e) {
if (e.getKind() != ElementKind.CLASS && e.getKind() != ElementKind.INTERFACE && e.getKind() != ElementKind.ENUM) {
throw new AnnotationProcessingException(e, "@ProtoTypeId can only be applied to classes, interfaces and enums.");
switch (e.getKind()) {
case CLASS, INTERFACE, RECORD, ENUM -> collectClasses((TypeElement) e);
default ->
throw new AnnotationProcessingException(e, "@ProtoTypeId can only be applied to classes, records, interfaces and enums.");
}
collectClasses((TypeElement) e);
}

private void visitProtoName(Element e) {
if (e.getKind() != ElementKind.CLASS && e.getKind() != ElementKind.INTERFACE && e.getKind() != ElementKind.ENUM) {
throw new AnnotationProcessingException(e, "@ProtoName can only be applied to classes, interfaces and enums.");
}
collectClasses((TypeElement) e);
}

private void visitProtoMessage(Element e) {
if (e.getKind() != ElementKind.CLASS && e.getKind() != ElementKind.INTERFACE && e.getKind() != ElementKind.RECORD) {
throw new AnnotationProcessingException(e, "@ProtoMessage can only be applied to classes and interfaces.");
switch (e.getKind()) {
case CLASS, INTERFACE, RECORD, ENUM -> collectClasses((TypeElement) e);
default ->
throw new AnnotationProcessingException(e, "@ProtoName can only be applied to classes, interfaces, records and enums.");
}
collectClasses((TypeElement) e);
}

private void visitProtoEnum(Element e) {
if (e.getKind() != ElementKind.CLASS && e.getKind() != ElementKind.INTERFACE) {
throw new AnnotationProcessingException(e, "@ProtoEnum can only be applied to enums.");
private void visitProto(Element e) {
switch (e.getKind()) {
case CLASS, INTERFACE, RECORD, ENUM -> collectClasses((TypeElement) e);
default ->
throw new AnnotationProcessingException(e, "@Proto can only be applied to classes, interfaces, records and enums.");
}
collectClasses((TypeElement) e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.infinispan.protostream.annotations.impl.AbstractMarshallerCodeGenerator;
import org.infinispan.protostream.annotations.impl.BaseProtoSchemaGenerator;
import org.infinispan.protostream.annotations.impl.ImportedProtoTypeMetadata;
import org.infinispan.protostream.annotations.impl.ProtoEnumTypeMetadata;
import org.infinispan.protostream.annotations.impl.ProtoTypeMetadata;
import org.infinispan.protostream.annotations.impl.processor.dependency.CompileTimeDependency;
import org.infinispan.protostream.annotations.impl.processor.types.MirrorTypeFactory;
Expand Down Expand Up @@ -52,11 +51,6 @@ protected AbstractMarshallerCodeGenerator makeMarshallerCodeGenerator() {
return marshallerSourceCodeGenerator;
}

@Override
protected ProtoTypeMetadata makeEnumTypeMetadata(XClass javaType) {
return new ProtoEnumTypeMetadata(javaType, getTargetClass(javaType));
}

@Override
protected ProtoTypeMetadata makeMessageTypeMetadata(XClass javaType) {
XClass targetClass = getTargetClass(javaType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1384,16 +1384,25 @@ public void testGenericMessage() throws Exception {
assertEquals("asdfg", ((GenericMessage.OtherMessage) genericMessage.field4.getValue()).field1);
}

@Proto
enum BareEnum {
ZERO,
ONE,
TWO
}

@Proto
static final class BareMessage {
public int anInt;
public String aString;
public List<String> things;
public Map<String, String> moreThings;
public BareEnum anEnum;
}

@ProtoSchema(schemaFileName = "bare_message.proto",
includeClasses = {
BareEnum.class,
BareMessage.class,
}, syntax = ProtoSyntax.PROTO3
)
Expand Down Expand Up @@ -1430,6 +1439,10 @@ public void testBareMessage() {
assertEquals(4, map.getNumber());
assertEquals(Type.STRING, map.getKeyType());
assertEquals(Type.STRING, map.getType());
field = message.getFields().get(4);
assertEquals("anEnum", field.getName());
assertEquals(5, field.getNumber());
assertEquals(Type.ENUM, field.getType());
}

//todo warnings logged to log4j during generation do not end up in compiler's message log
Expand Down

0 comments on commit 73963bf

Please sign in to comment.