Skip to content

Commit c93b6bf

Browse files
committed
Fix handling of primitive arrays in annotation metadata. Fixes micronaut-projects#1087
1 parent da143f0 commit c93b6bf

File tree

8 files changed

+144
-17
lines changed

8 files changed

+144
-17
lines changed

core/src/main/java/io/micronaut/core/convert/DefaultConversionService.java

+22
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,28 @@ public <S, T> DefaultConversionService addConverter(Class<S> sourceType, Class<T
167167
@SuppressWarnings({"OptionalIsPresent", "unchecked"})
168168
protected void registerDefaultConverters() {
169169

170+
// wrapper to primitive array converters
171+
addConverter(Double[].class, double[].class, (object, targetType, context) -> {
172+
double[] doubles = new double[object.length];
173+
for (int i = 0; i < object.length; i++) {
174+
Double aDouble = object[i];
175+
if (aDouble != null) {
176+
doubles[i] = aDouble;
177+
}
178+
}
179+
return Optional.of(doubles);
180+
});
181+
addConverter(Integer[].class, int[].class, (object, targetType, context) -> {
182+
int[] integers = new int[object.length];
183+
for (int i = 0; i < object.length; i++) {
184+
Integer o = object[i];
185+
if (o != null) {
186+
integers[i] = o;
187+
}
188+
}
189+
return Optional.of(integers);
190+
});
191+
170192
// Object -> List
171193
addConverter(Object.class, List.class, (object, targetType, context) -> {
172194
Optional<Argument<?>> firstTypeVariable = context.getFirstTypeVariable();

inject-groovy/src/main/groovy/io/micronaut/ast/groovy/InjectTransform.groovy

+1-3
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,7 @@ class InjectTransform implements ASTTransformation, CompilationUnitAware {
242242

243243
} catch (Throwable e) {
244244
AstMessageUtils.error(source, beanClassNode, "Error generating bean definition class for dependency injection of class [${beanTypeName}]: $e.message")
245-
if (e.message == null) {
246-
e.printStackTrace(System.err)
247-
}
245+
e.printStackTrace(System.err)
248246
}
249247
}
250248
if (!beanDefinitionWriters.isEmpty()) {

inject-groovy/src/test/groovy/io/micronaut/AbstractBeanDefinitionSpec.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ abstract class AbstractBeanDefinitionSpec extends Specification {
8383

8484
protected AnnotationMetadata writeAndLoadMetadata(String className, AnnotationMetadata toWrite) {
8585
def stream = new ByteArrayOutputStream()
86-
new AnnotationMetadataWriter(className, toWrite)
86+
new AnnotationMetadataWriter(className, toWrite, true)
8787
.writeTo(stream)
8888
className = className + AnnotationMetadata.CLASS_NAME_SUFFIX
8989
ClassLoader classLoader = new ClassLoader() {

inject-groovy/src/test/groovy/io/micronaut/inject/annotation/AnnotationMetadataWriterSpec.groovy

+40
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,46 @@ import java.lang.annotation.Retention
3434
*/
3535
class AnnotationMetadataWriterSpec extends AbstractBeanDefinitionSpec {
3636

37+
void "test annotation metadata with primitive arrays"() {
38+
given:
39+
AnnotationMetadata toWrite = buildTypeAnnotationMetadata('test.Test','''\
40+
package test;
41+
42+
import java.lang.annotation.Documented;
43+
import java.lang.annotation.ElementType;
44+
import java.lang.annotation.Retention;
45+
import java.lang.annotation.Target;
46+
47+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
48+
49+
import io.micronaut.inject.annotation.*;
50+
51+
@MyAnn(doubleArray=[1.1d])
52+
class Test {
53+
}
54+
55+
56+
@Documented
57+
@Retention(RUNTIME)
58+
@Target([ElementType.TYPE])
59+
@interface MyAnn {
60+
double[] doubleArray() default [];
61+
int[] intArray() default [];
62+
short[] shortArray() default [];
63+
boolean[] booleanArray() default [];
64+
}
65+
66+
''')
67+
when:
68+
def className = "test"
69+
AnnotationMetadata metadata = writeAndLoadMetadata(className, toWrite)
70+
71+
then:
72+
metadata != null
73+
metadata.getValue("test.MyAnn","doubleArray", Double[].class).get() == [1.1d] as Double[]
74+
metadata.getValue("test.MyAnn","doubleArray", double[].class).get() == [1.1d] as double[]
75+
}
76+
3777
void "test read annotation with annotation value"() {
3878
given:
3979
AnnotationMetadata toWrite = buildTypeAnnotationMetadata("test.Test",'''\

inject-java/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dependencies {
4444
testCompile dependencyModuleVersion("groovy", "groovy-json")
4545
testCompile files(org.gradle.internal.jvm.Jvm.current().toolsJar)
4646
testRuntime 'org.glassfish:javax.el:3.0.1-b08'
47+
testCompile dependencyVersion("micrometer")
4748
}
4849

4950

inject-java/src/test/groovy/io/micronaut/inject/AbstractTypeElementSpec.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ abstract class AbstractTypeElementSpec extends Specification {
119119

120120
protected AnnotationMetadata writeAndLoadMetadata(String className, AnnotationMetadata toWrite) {
121121
def stream = new ByteArrayOutputStream()
122-
new AnnotationMetadataWriter(className, toWrite)
122+
new AnnotationMetadataWriter(className, toWrite, true)
123123
.writeTo(stream)
124124
className = className + AnnotationMetadata.CLASS_NAME_SUFFIX
125125
ClassLoader classLoader = new ClassLoader() {

inject-java/src/test/groovy/io/micronaut/inject/annotation/AnnotationMetadataWriterSpec.groovy

+67
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package io.micronaut.inject.annotation
1717

18+
import io.micrometer.core.annotation.Timed
1819
import io.micronaut.aop.Around
1920
import io.micronaut.aop.introduction.StubIntroducer
2021
import io.micronaut.context.annotation.Primary
@@ -38,6 +39,72 @@ import java.lang.annotation.Retention
3839
*/
3940
class AnnotationMetadataWriterSpec extends AbstractTypeElementSpec {
4041

42+
void "test write annotation metadata with primitive arrays"() {
43+
given:
44+
AnnotationMetadata toWrite = new DefaultAnnotationMetadata(
45+
[
46+
"io.micrometer.core.annotation.Timed": [
47+
percentiles: [1.1d] as double[]
48+
]
49+
50+
], null, null, [
51+
"io.micrometer.core.annotation.Timed": [
52+
percentiles: [1.1d] as double[]
53+
]
54+
55+
], null
56+
)
57+
when:
58+
def className = "test"
59+
AnnotationMetadata metadata = writeAndLoadMetadata(className, toWrite)
60+
61+
then:
62+
metadata != null
63+
metadata.getValue(Timed.name, "percentiles", double[].class).get() == [1.1d] as double[]
64+
}
65+
66+
67+
void "test annotation metadata with primitive arrays"() {
68+
given:
69+
AnnotationMetadata toWrite = buildTypeAnnotationMetadata('''\
70+
package test;
71+
72+
import java.lang.annotation.Documented;
73+
import java.lang.annotation.ElementType;
74+
import java.lang.annotation.Retention;
75+
import java.lang.annotation.Target;
76+
77+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
78+
79+
import io.micronaut.inject.annotation.*;
80+
81+
@MyAnn(doubleArray={1.1d})
82+
@io.micrometer.core.annotation.Timed(percentiles={1.1d})
83+
class Test {
84+
}
85+
86+
87+
@Documented
88+
@Retention(RUNTIME)
89+
@Target({ElementType.TYPE})
90+
@interface MyAnn {
91+
double[] doubleArray() default {};
92+
int[] intArray() default {};
93+
short[] shortArray() default {};
94+
boolean[] booleanArray() default {};
95+
}
96+
97+
''')
98+
when:
99+
def className = "test"
100+
AnnotationMetadata metadata = writeAndLoadMetadata(className, toWrite)
101+
102+
then:
103+
metadata != null
104+
metadata.getValue(Timed, "percentiles", double[].class).get() == [1.1d] as double[]
105+
metadata.getValue("test.MyAnn", "doubleArray", double[].class).get() == [1.1d] as double[]
106+
}
107+
41108
void "test annotation metadata inherited stereotypes"() {
42109
given:
43110
AnnotationMetadata toWrite = buildTypeAnnotationMetadata('''\

inject/src/main/java/io/micronaut/inject/annotation/AnnotationMetadataWriter.java

+11-12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.micronaut.core.annotation.AnnotationUtil;
2121
import io.micronaut.core.annotation.AnnotationClassValue;
2222
import io.micronaut.core.annotation.Internal;
23+
import io.micronaut.core.reflect.ClassUtils;
2324
import io.micronaut.core.reflect.ReflectionUtils;
2425
import io.micronaut.core.util.CollectionUtils;
2526
import io.micronaut.inject.writer.AbstractClassFileWriter;
@@ -30,10 +31,8 @@
3031

3132
import java.io.IOException;
3233
import java.io.OutputStream;
33-
import java.util.Collections;
34-
import java.util.HashMap;
35-
import java.util.List;
36-
import java.util.Map;
34+
import java.lang.reflect.Array;
35+
import java.util.*;
3736

3837
/**
3938
* Responsible for writing class files that are instances of {@link AnnotationMetadata}.
@@ -130,8 +129,8 @@ public class AnnotationMetadataWriter extends AbstractClassFileWriter {
130129
/**
131130
* Constructs a new writer for the given class name and metadata.
132131
*
133-
* @param className The class name for which the metadata relates
134-
* @param annotationMetadata The annotation metadata
132+
* @param className The class name for which the metadata relates
133+
* @param annotationMetadata The annotation metadata
135134
* @param writeAnnotationDefaults Whether annotations defaults should be written
136135
*/
137136
public AnnotationMetadataWriter(String className, AnnotationMetadata annotationMetadata, boolean writeAnnotationDefaults) {
@@ -408,13 +407,13 @@ private static void pushValue(Type declaringType, ClassVisitor declaringClassWri
408407
Type t = Type.getType(declaringClass);
409408
methodVisitor.getStatic(t, value.toString(), t);
410409
} else if (value.getClass().isArray()) {
411-
Object[] array = (Object[]) value;
412-
int len = array.length;
413-
pushNewArray(methodVisitor, ((Object[]) value).getClass().getComponentType(), len);
414-
for (int i = 0; i < array.length; i++) {
415-
int index = i;
410+
final Class<?> componentType = ReflectionUtils.getWrapperType(value.getClass().getComponentType());
411+
int len = Array.getLength(value);
412+
pushNewArray(methodVisitor, componentType, len);
413+
for (int i = 0; i < len; i++) {
414+
final Object v = Array.get(value, i);
416415
pushStoreInArray(methodVisitor, i, len, () ->
417-
pushValue(declaringType, declaringClassWriter, methodVisitor, array[index], loadTypeMethods)
416+
pushValue(declaringType, declaringClassWriter, methodVisitor, v, loadTypeMethods)
418417
);
419418
}
420419
} else if (value instanceof List) {

0 commit comments

Comments
 (0)