Skip to content

Commit

Permalink
Add methods to access enum constants
Browse files Browse the repository at this point in the history
  • Loading branch information
Ladicek committed Sep 9, 2022
1 parent 925480b commit 463c20f
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 1 deletion.
50 changes: 50 additions & 0 deletions core/src/main/java/org/jboss/jandex/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,56 @@ final byte[] recordComponentPositionArray() {
return recordComponentPositions;
}

/**
* Returns a list of enum constants declared by this enum class, represented as {@link FieldInfo}.
* Enum constants are returned in declaration order.
* <p>
* If this class is not an enum, returns an empty list.
* <p>
* Note that for the result to actually be in declaration order, the index must be produced
* by at least Jandex 2.4. Previous Jandex versions do not store field positions. This also
* assumes that the bytecode order corresponds to declaration order, which is not guaranteed,
* but practically always holds.
*
* @return immutable list of enum constants, never {@code null}
* @since 3.0.1
*/
public final List<FieldInfo> enumConstants() {
if (!isEnum()) {
return Collections.emptyList();
}

List<FieldInfo> result = new ArrayList<>();
byte[] positions = fieldPositions;
for (int i = 0; i < fields.length; i++) {
FieldInternal field = (positions.length > 0) ? fields[positions[i] & 0xFF] : fields[i];
if (field.isEnumConstant()) {
result.add(new FieldInfo(this, field));
}
}
return Collections.unmodifiableList(result);
}

final int enumConstantOrdinal(FieldInternal enumConstant) {
if (!isEnum()) {
return -1;
}

int counter = 0;
byte[] positions = fieldPositions;
for (int i = 0; i < fields.length; i++) {
FieldInternal field = (positions.length > 0) ? fields[positions[i] & 0xFF] : fields[i];
if (field.isEnumConstant()) {
if (field.equals(enumConstant)) {
return counter;
} else {
counter++;
}
}
}
return -1;
}

/**
* Returns a list of names for all interfaces this class implements. This list may be empty, but never null.
* <p>
Expand Down
21 changes: 20 additions & 1 deletion core/src/main/java/org/jboss/jandex/FieldInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ public final List<AnnotationInstance> declaredAnnotations() {
* @see java.lang.reflect.Field#isEnumConstant()
*/
public final boolean isEnumConstant() {
return (flags() & Modifiers.ENUM) != 0;
return internal.isEnumConstant();
}

/**
Expand All @@ -363,6 +363,25 @@ public final boolean isSynthetic() {
return Modifiers.isSynthetic(internal.flags());
}

/**
* Returns an ordinal of this enum constant, that is, the zero-based position in the enum declaration.
* This is currently very inefficient (requires traversing fields of the declaring class),
* but may be improved in the future.
* <p>
* If this field is not an enum constant, returns -1.
* <p>
* Note that for the result to actually be the ordinal value, the index must be produced
* by at least Jandex 2.4. Previous Jandex versions do not store field positions. This also
* assumes that the bytecode order corresponds to declaration order, which is not guaranteed,
* but practically always holds.
*
* @return ordinal of this enum constant, or -1 if this field is not an enum constant
* @since 3.0.1
*/
public int enumConstantOrdinal() {
return clazz.enumConstantOrdinal(internal);
}

/**
* Returns a string representation describing this field. It is similar although not necessarily equivalent
* to a Java source code expression representing this field.
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/jboss/jandex/FieldInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ final short flags() {
return flags;
}

final boolean isEnumConstant() {
return (flags & Modifiers.ENUM) != 0;
}

@Override
public String toString() {
return type.toString(true) + " " + name();
Expand Down
113 changes: 113 additions & 0 deletions core/src/test/java/org/jboss/jandex/test/EnumConstantsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.jboss.jandex.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.util.List;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.Index;
import org.jboss.jandex.test.util.IndexingUtil;
import org.junit.jupiter.api.Test;

public class EnumConstantsTest {
enum SimpleEnum {
FOO,
BAZ,
QUUX,
BAR,
}

enum ComplexEnum {
ONE(1),
TWO(2),
THREE(3),
FOUR(4),
;

private final int value;

ComplexEnum(int value) {
this.value = value;
}
}

enum EnumSingleton {
INSTANCE
}

enum EmptyEnum {
}

static class NotAnEnum {
static final NotAnEnum INSTANCE = new NotAnEnum(0);

final int value;

NotAnEnum(int value) {
this.value = value;
}
}

@Test
public void test() throws IOException {
Index index = Index.of(SimpleEnum.class, ComplexEnum.class, EnumSingleton.class, EmptyEnum.class, NotAnEnum.class);
doTest(index);
doTest(IndexingUtil.roundtrip(index));
}

private void doTest(Index index) {
assertTrue(index.getClassByName(SimpleEnum.class).isEnum());
List<FieldInfo> simpleEnum = index.getClassByName(SimpleEnum.class).enumConstants();
assertEquals(4, simpleEnum.size());
assertEquals("FOO", simpleEnum.get(0).name());
assertEquals(0, simpleEnum.get(0).enumConstantOrdinal());
assertEquals("BAZ", simpleEnum.get(1).name());
assertEquals(1, simpleEnum.get(1).enumConstantOrdinal());
assertEquals("QUUX", simpleEnum.get(2).name());
assertEquals(2, simpleEnum.get(2).enumConstantOrdinal());
assertEquals("BAR", simpleEnum.get(3).name());
assertEquals(3, simpleEnum.get(3).enumConstantOrdinal());
for (FieldInfo enumConstant : simpleEnum) {
assertTrue(enumConstant.isEnumConstant());
}

assertTrue(index.getClassByName(ComplexEnum.class).isEnum());
List<FieldInfo> complexEnum = index.getClassByName(ComplexEnum.class).enumConstants();
assertEquals(4, complexEnum.size());
assertEquals("ONE", complexEnum.get(0).name());
assertEquals(0, complexEnum.get(0).enumConstantOrdinal());
assertEquals("TWO", complexEnum.get(1).name());
assertEquals(1, complexEnum.get(1).enumConstantOrdinal());
assertEquals("THREE", complexEnum.get(2).name());
assertEquals(2, complexEnum.get(2).enumConstantOrdinal());
assertEquals("FOUR", complexEnum.get(3).name());
assertEquals(3, complexEnum.get(3).enumConstantOrdinal());
for (FieldInfo enumConstant : complexEnum) {
assertTrue(enumConstant.isEnumConstant());
}
assertEquals(-1, index.getClassByName(ComplexEnum.class).field("value").enumConstantOrdinal());

assertTrue(index.getClassByName(EnumSingleton.class).isEnum());
List<FieldInfo> enumSingleton = index.getClassByName(EnumSingleton.class).enumConstants();
assertEquals(1, enumSingleton.size());
assertEquals("INSTANCE", enumSingleton.get(0).name());
assertEquals(0, enumSingleton.get(0).enumConstantOrdinal());
for (FieldInfo enumConstant : enumSingleton) {
assertTrue(enumConstant.isEnumConstant());
}

assertTrue(index.getClassByName(EmptyEnum.class).isEnum());
List<FieldInfo> emptyEnum = index.getClassByName(EmptyEnum.class).enumConstants();
assertEquals(0, emptyEnum.size());

ClassInfo notAnEnum = index.getClassByName(NotAnEnum.class);
assertFalse(notAnEnum.isEnum());
assertTrue(notAnEnum.enumConstants().isEmpty());
assertEquals(-1, notAnEnum.field("INSTANCE").enumConstantOrdinal());
assertEquals(-1, notAnEnum.field("value").enumConstantOrdinal());
}
}

0 comments on commit 463c20f

Please # to comment.