Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPROTO-311 Simple enums #246

Merged
merged 1 commit into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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