Skip to content

Commit

Permalink
Add methods to access enum constants
Browse files Browse the repository at this point in the history
This commit also adds missing javadoc to the `ClassInfo.unsorted*`
methods, describing limitations of these methods.
  • Loading branch information
Ladicek committed Sep 12, 2022
1 parent 925480b commit e0dff46
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 1 deletion.
88 changes: 88 additions & 0 deletions core/src/main/java/org/jboss/jandex/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,18 @@ public final List<MethodInfo> methods() {
return new MethodInfoGenerator(this, methods, EMPTY_POSITIONS);
}

/**
* Returns a list of all methods declared in this class, in the declaration order.
* See {@link #methods()} for more information.
* <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 method positions. At most 256
* methods may be present; if there's more, order is undefined. This also assumes that the bytecode
* order corresponds to declaration order, which is not guaranteed, but practically always holds.
*
* @return a list of methods
* @since 2.4
*/
public final List<MethodInfo> unsortedMethods() {
return new MethodInfoGenerator(this, methods, methodPositions);
}
Expand Down Expand Up @@ -786,6 +798,18 @@ public final List<FieldInfo> fields() {
return new FieldInfoGenerator(this, fields, EMPTY_POSITIONS);
}

/**
* Returns a list of all fields declared in this class, in the declaration order.
* See {@link #fields()} for more information.
* <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. At most 256
* fields may be present; if there's more, order is undefined. This also assumes that the bytecode
* order corresponds to declaration order, which is not guaranteed, but practically always holds.
*
* @return a list of fields
* @since 2.4
*/
public final List<FieldInfo> unsortedFields() {
return new FieldInfoGenerator(this, fields, fieldPositions);
}
Expand Down Expand Up @@ -823,6 +847,19 @@ public final List<RecordComponentInfo> recordComponents() {
return new RecordComponentInfoGenerator(this, recordComponents, EMPTY_POSITIONS);
}

/**
* Returns a list of all record components declared in this class, in the declaration order.
* See {@link #recordComponents()} for more information.
* <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 record component positions.
* At most 256 record components may be present; if there's more, order is undefined. This also
* assumes that the bytecode order corresponds to declaration order, which is not guaranteed,
* but practically always holds.
*
* @return a list of record components
* @since 2.4
*/
public final List<RecordComponentInfo> unsortedRecordComponents() {
return new RecordComponentInfoGenerator(this, recordComponents, recordComponentPositions);
}
Expand All @@ -835,6 +872,57 @@ 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. At most 256
* fields may be present in the class; if there's more, order is undefined. 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
22 changes: 21 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,26 @@ 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. At most 256
* fields may be present in the class; if there's more, outcome is undefined. 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
117 changes: 117 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,117 @@
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) {
ClassInfo simpleEnum = index.getClassByName(SimpleEnum.class);
assertTrue(simpleEnum.isEnum());
List<FieldInfo> simpleEnumConstants = simpleEnum.enumConstants();
assertEquals(4, simpleEnumConstants.size());
assertEquals("FOO", simpleEnumConstants.get(0).name());
assertEquals(0, simpleEnumConstants.get(0).enumConstantOrdinal());
assertEquals("BAZ", simpleEnumConstants.get(1).name());
assertEquals(1, simpleEnumConstants.get(1).enumConstantOrdinal());
assertEquals("QUUX", simpleEnumConstants.get(2).name());
assertEquals(2, simpleEnumConstants.get(2).enumConstantOrdinal());
assertEquals("BAR", simpleEnumConstants.get(3).name());
assertEquals(3, simpleEnumConstants.get(3).enumConstantOrdinal());
for (FieldInfo enumConstant : simpleEnumConstants) {
assertTrue(enumConstant.isEnumConstant());
}

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

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

ClassInfo emptyEnum = index.getClassByName(EmptyEnum.class);
assertTrue(emptyEnum.isEnum());
assertEquals(0, emptyEnum.enumConstants().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 e0dff46

Please # to comment.