Skip to content

Commit

Permalink
Merge pull request #199 from Ladicek/fix-array-type-name
Browse files Browse the repository at this point in the history
fix ArrayType.name() and add test
  • Loading branch information
Ladicek authored May 13, 2022
2 parents 1121d96 + bfaf00a commit 18212ea
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 23 deletions.
28 changes: 20 additions & 8 deletions core/src/main/java/org/jboss/jandex/ArrayType.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@ public static ArrayType create(Type component, int dimensions) {
}

/**
* Returns the component type of the array. As an example, <code>String[]</code>
* has a component type of <code>String</code>
* Returns the component type of the array. For example, {@code String[]} has
* a component type of {@code String}.
* <p>
* It is possible for an {@code ArrayType} to have another {@code ArrayType}
* as its component type. This happens when an array has some of its dimensions
* annotated (e.g. {@code String[] @Ann []}). In such case, having multiple nested
* {@code ArrayType}s is necessary to faithfully represent the annotations.
*
* @return the component type
*/
Expand All @@ -64,17 +69,24 @@ public Type component() {
@Override
public DotName name() {
StringBuilder builder = new StringBuilder();
int dimensions = this.dimensions;
while (dimensions-- > 0) {
builder.append('[');

Type type = this;
while (type.kind() == Kind.ARRAY) {
int dimensions = type.asArrayType().dimensions;
while (dimensions-- > 0) {
builder.append('[');
}
type = type.asArrayType().component;
}
if (component instanceof PrimitiveType) {
builder.append(((PrimitiveType) component).toCode());

// here, `type` is an element type of the array, i.e., never array
if (type.kind() == Kind.PRIMITIVE) {
builder.append(type.asPrimitiveType().toCode());
} else {
// This relies on name() representing the erased type name
// For historical 1.x reasons, we follow the Java reflection format
// instead of the Java descriptor format.
builder.append('L').append(component.name().toString()).append(';');
builder.append('L').append(type.name().toString()).append(';');
}

return DotName.createSimple(builder.toString());
Expand Down
11 changes: 5 additions & 6 deletions core/src/main/java/org/jboss/jandex/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,13 @@ public static Type create(DotName name, Kind kind) {

/**
* Returns the raw name of this type. Primitives and void are returned as the
* Java reserved word (void, boolean, byte, short, char, int, long, float,
* double). Arrays are returned using the Java reflection array syntax
* (e.g. "[[[Ljava.lang.String;") Classes are returned as a normal <code>DotName</code>.
*
* Java keyword (void, boolean, byte, short, int, long, float, double, char).
* Arrays are returned using the Java reflection array syntax (e.g.
* {@code [[[Ljava.lang.String;}) Classes are returned as a normal {@code DotName}.
* <p>
* Generic values are returned as the underlying raw value. For example,
* a wildcard such as <code>? extends Number</code>, has a raw type of
* <code>Number</code>
* a wildcard such as {@code ? extends Number} has a raw type of
* {@code Number}.
*
* @return the name of this type
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
package org.jboss.jandex;

/**
* Represents a type variable that could not be resolved during indexing. This type will only occur
* as a result of a bug, or a non-compliant Java class file. It is provided in order to prevent
* failure.
* Represents a type variable that could not be resolved during indexing. This type may occur
* when indexing an incomplete classpath, or as a result of a bug, or a non-compliant Java class file.
* It is provided in order to prevent failure.
*
* @author Jason T. Greene
*/
Expand Down
16 changes: 10 additions & 6 deletions core/src/main/java/org/jboss/jandex/WildcardType.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,24 @@ public static WildcardType create(Type bound, boolean isExtends) {
}

/**
* Returns the extends (upper) bound of this wildcard. If this wildcard declares a super (lower)
* bound, this method will return <code>java.lang.Object</code>
* Returns the upper bound of this wildcard (e.g. {@code SomeType} for {@code ? extends SomeType}).
* <p>
* Returns {@code java.lang.Object} if this wildcard declares a lower bound
* ({@code ? super SomeType}).
*
* @return the extends bound, or Object if this wildcard has a super bound
* @return the upper bound, or {@code Object} if this wildcard has a lower bound
*/
public Type extendsBound() {
return isExtends ? bound : OBJECT;
}

/**
* Returns the super (lower) bound of this wildcard. If this wildcard declares an extends (upper)
* bound, this method will return nnull
* Returns the lower bound of this wildcard (e.g. {@code SomeType} for {@code ? super SomeType}).
* <p>
* Returns {@code null} if this wildcard declares an upper bound
* ({@code ? extends SomeType}).
*
* @return the super bound, or null if this wildcard has a extends bound
* @return the lower bound, or {@code null} if this wildcard has an uper bound
*/
public Type superBound() {
return isExtends ? null : bound;
Expand Down
101 changes: 101 additions & 0 deletions core/src/test/java/org/jboss/jandex/test/TypeNameTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.jboss.jandex.test;

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

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

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.Index;
import org.jboss.jandex.Type;
import org.jboss.jandex.TypeVariable;
import org.jboss.jandex.UnresolvedTypeVariable;
import org.junit.jupiter.api.Test;

public class TypeNameTest {
interface TestMethods<X> {
void nothing();

int primitive();

String clazz();

List<String> parameterized();

String[][] array();

// annotations are present to make sure the `ArrayType` has multiple levels of nesting
@MyAnnotation("1")
String[] @MyAnnotation("2") [][] @MyAnnotation("3") [] annotatedArray();

X typeParameter();

<Y extends Number> Y typeParameterWithSingleBound();

<Y extends Number & Comparable<Y>> Y typeParameterWithMultipleBounds();

<Y extends Comparable<Y>> Y typeParameterWithSingleParameterizedBound();

<Y extends Comparable<Y> & Serializable> Y typeParameterWithMultipleBoundsFirstParameterized();

<Y extends Serializable & Comparable<Y>> Y typeParameterWithMultipleBoundsSecondParameterized();

List<?> unboundedWildcard();

List<? extends Number> wildcardWithUpperBound();

List<? super String> wildcardWithLowerBound();

List<? extends X> wildcardWithUnboundedTypeParameterAsUpperBound();

<Y extends Number> List<? extends Y> wildcardWithBoundedTypeParameterAsUpperBound();

<Y extends Number> List<? super Y> wildcardWithBoundedTypeParameterAsLowerBound();
}

static class NestedClass<T> {
class InnerClass<U extends T> {
}
}

@Test
public void test() throws IOException {
// intentionally _not_ indexing `NestedClass`, so that the type parameter bound of `InnerClass` is unresolved
Index index = Index.of(TestMethods.class, NestedClass.InnerClass.class);

ClassInfo clazz = index.getClassByName(TestMethods.class);
assertEquals("void", typeName(clazz, "nothing"));
assertEquals("int", typeName(clazz, "primitive"));
assertEquals("java.lang.String", typeName(clazz, "clazz"));
assertEquals("java.util.List", typeName(clazz, "parameterized"));
assertEquals("[[Ljava.lang.String;", typeName(clazz, "array"));
assertEquals("[[[[Ljava.lang.String;", typeName(clazz, "annotatedArray"));
assertEquals("java.lang.Object", typeName(clazz, "typeParameter"));
assertEquals("java.lang.Number", typeName(clazz, "typeParameterWithSingleBound"));
assertEquals("java.lang.Number", typeName(clazz, "typeParameterWithMultipleBounds"));
assertEquals("java.lang.Comparable", typeName(clazz, "typeParameterWithSingleParameterizedBound"));
assertEquals("java.lang.Comparable", typeName(clazz, "typeParameterWithMultipleBoundsFirstParameterized"));
assertEquals("java.io.Serializable", typeName(clazz, "typeParameterWithMultipleBoundsSecondParameterized"));
assertEquals("java.lang.Object", firstTypeArgumentName(clazz, "unboundedWildcard"));
assertEquals("java.lang.Number", firstTypeArgumentName(clazz, "wildcardWithUpperBound"));
assertEquals("java.lang.Object", firstTypeArgumentName(clazz, "wildcardWithLowerBound"));
assertEquals("java.lang.Object", firstTypeArgumentName(clazz, "wildcardWithUnboundedTypeParameterAsUpperBound"));
assertEquals("java.lang.Number", firstTypeArgumentName(clazz, "wildcardWithBoundedTypeParameterAsUpperBound"));
assertEquals("java.lang.Object", firstTypeArgumentName(clazz, "wildcardWithBoundedTypeParameterAsLowerBound"));

TypeVariable u = index.getClassByName(NestedClass.InnerClass.class).typeParameters().get(0);
assertEquals("java.lang.Object", u.name().toString());
assertEquals(Type.Kind.UNRESOLVED_TYPE_VARIABLE, u.bounds().get(0).kind());
UnresolvedTypeVariable t = u.bounds().get(0).asUnresolvedTypeVariable();
assertEquals("java.lang.Object", t.name().toString());
}

private String typeName(ClassInfo clazz, String method) {
return clazz.firstMethod(method).returnType().name().toString();
}

private String firstTypeArgumentName(ClassInfo clazz, String method) {
return clazz.firstMethod(method).returnType().asParameterizedType().arguments().get(0).name().toString();
}
}

0 comments on commit 18212ea

Please sign in to comment.