diff --git a/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java b/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java index cf3c5aceea..ddee5dd49f 100644 --- a/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java +++ b/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java @@ -18,8 +18,10 @@ package org.jclouds.json.internal; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Predicates.in; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.get; +import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Iterables.tryFind; import static org.jclouds.reflect.Reflection2.constructors; @@ -30,6 +32,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Map; import javax.inject.Named; @@ -44,6 +47,7 @@ import com.google.common.base.Supplier; import com.google.common.collect.FluentIterable; import com.google.common.collect.Maps; import com.google.common.reflect.Invokable; +import com.google.common.reflect.Parameter; import com.google.common.reflect.TypeToken; import com.google.gson.FieldNamingStrategy; import com.google.gson.annotations.SerializedName; @@ -178,11 +182,30 @@ public class NamingStrategies { SerializedNames names = target.getAnnotation(SerializedNames.class); if (names != null && target.isStatic()) { // == factory method // Fields and constructor params are written by AutoValue in same order as methods are declared. - // By contract, SerializedNames factory methods must declare its names in the same order, - Field[] fields = f.getDeclaringClass().getDeclaredFields(); - checkState(fields.length == names.value().length, "Incorrect number of names on " + names); - for (int i = 0; i < fields.length; i++) { - if (fields[i].equals(f)) { + // By contract, SerializedNames factory methods must declare its names in the same order. + List declaredFields = Arrays.asList(f.getDeclaringClass().getDeclaredFields()); + // Instrumentation libraries, such as JaCoCo might introduce synthetic fields to the class. + // We should ignore them + Iterable fields = filter(declaredFields, new Predicate() { + @Override + public boolean apply(Field input) { + return !input.isSynthetic(); + } + }); + int numFields = size(fields); + if (numFields != names.value().length) { + String message = "Incorrect number of names on %s. Class [%s]. Annotation config: [%s]. Fields in object; [%s]"; + String types = Joiner.on(",").join(transform(fields, new Function() { + @Override + public String apply(Field input) { + return input.getType().getName(); + } + })); + throw new IllegalStateException(String.format(message, names, f.getDeclaringClass().getName(), + Joiner.on(",").join(names.value()), types)); + } + for (int i = 0; i < numFields; i++) { + if (get(fields, i).equals(f)) { return names.value()[i]; } } @@ -259,7 +282,17 @@ public class NamingStrategies { ? c.getAnnotation(SerializedNames.class).value() : c.getAnnotation(ConstructorProperties.class).value(); - checkArgument(names.length == c.getParameters().size(), "Incorrect count of names on annotation of %s", c); + if (names.length != c.getParameters().size()) { + String message = "Incorrect count of names on annotation of %s. Class: [%s]. Annotation config: [%s]. Parameters; [%s]"; + String types = Joiner.on(",").join(transform(c.getParameters(), new Function() { + @Override + public String apply(Parameter input) { + return input.getClass().getName(); + } + })); + throw new IllegalArgumentException(String.format(message, c, c.getDeclaringClass().getName(), + Joiner.on(",").join(names), types)); + } if (names != null && names.length > index) { name = names[index];