jclouds TypeCoercions for domain objects
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/1b23cb6b Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/1b23cb6b Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/1b23cb6b Branch: refs/heads/master Commit: 1b23cb6b6e7471258835b1f0e4820a78f9d5251f Parents: 57ded18 Author: Aled Sage <[email protected]> Authored: Mon May 22 14:09:29 2017 +0100 Committer: Aled Sage <[email protected]> Committed: Tue May 23 14:45:33 2017 +0100 ---------------------------------------------------------------------- .../location/jclouds/JcloudsLocation.java | 4 +- .../jclouds/JcloudsLocationResolver.java | 6 +- .../location/jclouds/JcloudsTypeCoercions.java | 168 +++++++++++ .../JcloudsTypeCoercionsWithBuilderTest.java | 250 +++++++++++++++++ .../JcloudsTypeCoercionsWithCreateTest.java | 278 +++++++++++++++++++ 5 files changed, 704 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java index de71585..074ec5f 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java @@ -210,7 +210,9 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im @SetFromFlag // so it's persisted private final Map<MachineLocation,String> vmInstanceIds = Maps.newLinkedHashMap(); - static { Networking.init(); } + static { + Networking.init(); + } public JcloudsLocation() { super(); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java index aba0a3e..2c2f3ef 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java @@ -50,7 +50,11 @@ public class JcloudsLocationResolver implements LocationResolver { // from http://docs.amazonwebservices.com/general/latest/gr/rande.html as of Apr 2012. // it is suggested not to maintain this list here, instead to require aws-ec2 explicitly named. "eu-west-1","us-east-1","us-west-1","us-west-2","ap-southeast-1","ap-northeast-1","sa-east-1"); - + + static { + JcloudsTypeCoercions.init(); + } + private ManagementContext managementContext; @Override http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java new file mode 100644 index 0000000..56534ed --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercions.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.brooklyn.util.core.flags.MethodCoercions; +import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.javalang.coerce.TryCoercer; +import org.jclouds.json.SerializedNames; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.collect.Sets.SetView; +import com.google.common.reflect.TypeToken; + +public class JcloudsTypeCoercions { + + private JcloudsTypeCoercions() {} + + private static final AtomicBoolean initialized = new AtomicBoolean(false); + + public static void init() { + synchronized (initialized) { + if (initialized.compareAndSet(false, true)) { + TypeCoercions.registerAdapter(new CoercionFromAutoValueBuilder()); + TypeCoercions.registerAdapter(new CoercionFromAutoValueCreate()); + } + } + } + + static class CoercionFromAutoValueBuilder implements TryCoercer { + @Override + public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetTypeToken) { + Class<? super T> targetType = targetTypeToken.getRawType(); + + // If we don't have a map of named params, we can't map it to builder methods + if (!(input instanceof Map)) { + return null; + } + + Optional<Method> builderMethod = findBuilderMethod(targetType); + if (!builderMethod.isPresent()) return null; + + Maybe<?> builder = MethodCoercions.tryFindAndInvokeMultiParameterMethod(targetType, ImmutableList.of(builderMethod.get()), ImmutableList.of()); + if (builder.isAbsent()) { + return (Maybe<T>) (Maybe) builder; + } + + Optional<Method> buildMethod = findBuildMethod(builder.get()); + if (!buildMethod.isPresent()) { + return Maybe.absent("Builder for "+targetType.getCanonicalName()+" has no build() method"); + } + + for (Map.Entry<?, ?> entry : ((Map<?,?>)input).entrySet()) { + String key = (String) entry.getKey(); + Object val = entry.getValue(); + + Maybe<?> invoked = MethodCoercions.tryFindAndInvokeBestMatchingMethod(builder.get(), key, val); + if (invoked.isAbsent()) { + RuntimeException cause = Maybe.Absent.getException(invoked); + return Maybe.absent("Builder for "+targetType.getCanonicalName()+" failed to call method for "+key, cause); + } + } + + Maybe<?> result = MethodCoercions.tryFindAndInvokeMultiParameterMethod(builder.get(), ImmutableList.of(buildMethod.get()), ImmutableList.of()); + if (result.isAbsent()) { + RuntimeException cause = Maybe.Absent.getException(result); + return Maybe.absent("Builder for "+targetType.getCanonicalName()+" failed to call build method", cause); + } else { + return (Maybe<T>) (Maybe) result; + } + } + + private Optional<Method> findBuilderMethod(Class<?> clazz) { + for (Method m: clazz.getMethods()) { + if (((m.getModifiers()&Modifier.STATIC) == Modifier.STATIC) && m.getName().equals("builder") + && m.getParameterTypes().length == 0) { + return Optional.of(m); + } + } + return Optional.empty(); + } + + private Optional<Method> findBuildMethod(Object instance) { + for (Method m: instance.getClass().getMethods()) { + if (((m.getModifiers()&Modifier.STATIC) != Modifier.STATIC) && m.getName().equals("build") + && m.getParameterTypes().length == 0) { + return Optional.of(m); + } + } + return Optional.empty(); + } + } + + static class CoercionFromAutoValueCreate implements TryCoercer { + @Override + public <T> Maybe<T> tryCoerce(Object input, TypeToken<T> targetTypeToken) { + Class<? super T> targetType = targetTypeToken.getRawType(); + Maybe<?> result = null; + Maybe<?> firstError = null; + + // If we don't have a map of named params, we can't map it to "@SerializedNames"? + if (!(input instanceof Map)) { + return null; + } + + Optional<Method> method = findCreateMethod(targetType); + if (!method.isPresent()) return null; + + // Do we have a map of args, and need to look at the "@SerializedNames" annotation? + if (method.get().isAnnotationPresent(SerializedNames.class)) { + String[] argNames = method.get().getAnnotation(SerializedNames.class).value(); + + SetView<?> extraArgs = Sets.difference(((Map<?,?>)input).keySet(), ImmutableSet.copyOf(argNames)); + if (!extraArgs.isEmpty()) { + return Maybe.absent("create() for "+targetType.getCanonicalName()+" does not accept extra args "+extraArgs); + } + + List<Object> orderedArgs = Lists.newArrayList(); + for (String argName : argNames) { + orderedArgs.add(((Map<?,?>)input).get(argName)); + } + result = MethodCoercions.tryFindAndInvokeMultiParameterMethod(targetType, ImmutableList.of(method.get()), orderedArgs); + if (result != null && result.isPresent()) return Maybe.of((T)result.get()); + if (result != null && firstError == null) firstError = result; + } + + //not found + if (firstError != null) return (Maybe<T>) (Maybe) firstError; + return null; + } + + private Optional<Method> findCreateMethod(Class<?> clazz) { + for (Method m: clazz.getMethods()) { + if (((m.getModifiers()&Modifier.STATIC) == Modifier.STATIC) && m.getName().equals("create") + && clazz.isAssignableFrom(m.getReturnType())) { + return Optional.of(m); + } + } + return Optional.empty(); + } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java new file mode 100644 index 0000000..f863102 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithBuilderTest.java @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import static org.apache.brooklyn.util.core.flags.TypeCoercions.coerce; +import static org.testng.Assert.assertEquals; + +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException; +import org.testng.annotations.Test; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; + +public class JcloudsTypeCoercionsWithBuilderTest { + + static { + JcloudsTypeCoercions.init(); + } + + @Test + public void testCallsBuilder() { + assertEquals( + coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2"), MyClazz.class), + MyClazz.builder().arg1("val1").arg2("val2").build()); + assertEquals( + coerce(ImmutableMap.of("arg2", "val2", "arg1", "val1"), MyClazz.class), + MyClazz.builder().arg1("val1").arg2("val2").build()); + assertEquals( + coerce(ImmutableMap.of("arg1", "val1"), MyClazz.class), + MyClazz.builder().arg1("val1").build()); + assertEquals( + coerce(ImmutableMap.of("arg2", "val2"), MyClazz.class), + MyClazz.builder().arg2("val2").build()); + } + + @Test + public void testFailsIfExtraArgs() { + try { + coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2", "arg3", "val3"), MyClazz.class); + Asserts.shouldHaveFailedPreviously(); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "Builder for", "MyClazz", "failed to call method for arg3"); + } + } + + @Test + public void testFailsIfNoBuildMethod() { + try { + coerce(ImmutableMap.of("arg1", "val1"), MyClazzWithNoBuildMethod.class); + Asserts.shouldHaveFailedPreviously(); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "Builder for", "MyClazzWithNoBuildMethod", "has no build() method"); + } + } + + @Test + public void testFailsIfNoNoargBuildMethod() { + try { + coerce(ImmutableMap.of("arg1", "val1"), MyClazzWithNoNoargBuildMethod.class); + Asserts.shouldHaveFailedPreviously(); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "Builder for", "MyClazzWithNoNoargBuildMethod", "has no build() method"); + } + } + + @Test + public void testFailsIfNoNoargBuilderMethod() { + try { + coerce(ImmutableMap.of("arg1", "val1"), MyClazzWithNoNoargBuilderMethod.class); + Asserts.shouldHaveFailedPreviously(); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "MyClazzWithNoNoargBuilderMethod", "no adapter known"); + } + } + + @Test + public void testCompositeOfCreatedObjs() { + assertEquals( + coerce(ImmutableMap.of("val",ImmutableMap.of("arg1", "val1", "arg2", "val2")), MyCompositeClazz.class), + MyCompositeClazz.builder().val((MyClazz.builder().arg1("val1").arg2("val2").build())).build()); + } + + public static class MyClazz { + private final String arg1; + private final String arg2; + + public static class Builder { + private String arg1; + private String arg2; + + public Builder arg1(String val) { + this.arg1 = val; + return this; + } + public Builder arg2(String val) { + this.arg2 = val; + return this; + } + public MyClazz build() { + return new MyClazz(arg1, arg2); + } + } + + public static Builder builder() { + return new Builder(); + } + + private MyClazz(String arg1, String arg2) { + this.arg1 = arg1; + this.arg2 = arg2; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) return false; + MyClazz o = (MyClazz) obj; + return Objects.equal(arg1, o.arg1) && Objects.equal(arg2, o.arg2); + } + + @Override + public int hashCode() { + return Objects.hashCode(arg1, arg2); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("arg1", arg1).add("arg2", arg2).toString(); + } + } + + public static class MyCompositeClazz { + private final MyClazz val; + + public static class Builder { + private MyClazz val; + + public Builder val(MyClazz val) { + this.val = val; + return this; + } + public MyCompositeClazz build() { + return new MyCompositeClazz(val); + } + } + + public static Builder builder() { + return new Builder(); + } + + private MyCompositeClazz(MyClazz val) { + this.val = val; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) return false; + MyCompositeClazz o = (MyCompositeClazz) obj; + return Objects.equal(val, o.val); + } + + @Override + public int hashCode() { + return Objects.hashCode(val); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("val", val).toString(); + } + } + + public static class MyClazzWithNoNoargBuilderMethod { + @SuppressWarnings("unused") + private final String arg1; + + public static class Builder { + private String arg1; + + public Builder arg1(String val) { + this.arg1 = val; + return this; + } + public MyClazzWithNoNoargBuilderMethod build() { + return new MyClazzWithNoNoargBuilderMethod(arg1); + } + } + + public static Builder builder(String extraArg) { + return new Builder(); + } + + private MyClazzWithNoNoargBuilderMethod(String arg1) { + this.arg1 = arg1; + } + } + + public static class MyClazzWithNoBuildMethod { + public static class Builder { + public Builder arg1(String val) { + return this; + } + } + + public static Builder builder() { + return new Builder(); + } + + private MyClazzWithNoBuildMethod(String arg1) { + } + } + + public static class MyClazzWithNoNoargBuildMethod { + public static class Builder { + private String arg1; + + public Builder arg1(String val) { + this.arg1 = val; + return this; + } + public MyClazzWithNoNoargBuildMethod build(String extraArg) { + return new MyClazzWithNoNoargBuildMethod(arg1); + } + } + + public static Builder builder() { + return new Builder(); + } + + private MyClazzWithNoNoargBuildMethod(String arg1) { + } + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b23cb6b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java new file mode 100644 index 0000000..8adab07 --- /dev/null +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsTypeCoercionsWithCreateTest.java @@ -0,0 +1,278 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import static org.apache.brooklyn.util.core.flags.TypeCoercions.coerce; +import static org.testng.Assert.assertEquals; + +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException; +import org.jclouds.json.SerializedNames; +import org.testng.annotations.Test; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class JcloudsTypeCoercionsWithCreateTest { + + static { + JcloudsTypeCoercions.init(); + } + + @Test + public void testCallsCreate() { + assertEquals( + coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2"), MyClazz.class), + MyClazz.create("val1", "val2")); + assertEquals( + coerce(ImmutableMap.of("arg2", "val2", "arg1", "val1"), MyClazz.class), + MyClazz.create("val1", "val2")); + assertEquals( + coerce(ImmutableMap.of("arg1", "val1"), MyClazz.class), + MyClazz.create("val1", null)); + assertEquals( + coerce(ImmutableMap.of("arg2", "val2"), MyClazz.class), + MyClazz.create(null, "val2")); + } + + @Test + public void testFailsIfExtraArgs() { + try { + coerce(ImmutableMap.of("arg1", "val1", "arg2", "val2", "arg3", "val3"), MyClazz.class); + Asserts.shouldHaveFailedPreviously(); + } catch (ClassCoercionException e) { + Asserts.expectedFailureContains(e, "create()", "MyClazz", "does not accept extra args [arg3]"); + } + } + + @Test + public void testCallsCreateWithPrimitives() { + assertEquals( + coerce(ImmutableMap.builder().put("boolArg", true).put("byteArg", (byte)1).put("shortArg", (short)2) + .put("intArg", (int)3).put("longArg", (long)4).put("floatArg", (float)5.0) + .put("doubleArg", (double)6.0).build(), MyClazzWithPrimitives.class), + MyClazzWithPrimitives.create(true, (byte)1, (short)2, (int)3, (long)4, (float)5.0, (double)6.0)); + assertEquals( + coerce(ImmutableMap.builder().put("boolArg", "true").put("byteArg", "1").put("shortArg", "2") + .put("intArg", "3").put("longArg", "4").put("floatArg", "5.0") + .put("doubleArg", "6.0").build(), MyClazzWithPrimitives.class), + MyClazzWithPrimitives.create(true, (byte)1, (short)2, (int)3, (long)4, (float)5.0, (double)6.0)); + } + @Test + public void testListOfCreatedObjs() { + assertEquals( + coerce(ImmutableMap.of("vals", ImmutableList.of()), ListOfMyClazz.class), + ListOfMyClazz.create(ImmutableList.of())); + assertEquals( + coerce(ImmutableMap.of("vals", ImmutableList.of(ImmutableMap.of("arg1", "val1", "arg2", "val2"))), ListOfMyClazz.class), + ListOfMyClazz.create(ImmutableList.of(MyClazz.create("val1", "val2")))); + } + + @Test + public void testCompositeOfCreatedObjs() { + assertEquals( + coerce(ImmutableMap.of("val",ImmutableMap.of("arg1", "val1", "arg2", "val2")), MyCompositeClazz.class), + MyCompositeClazz.create(MyClazz.create("val1", "val2"))); + } + + @Test + public void testMapOfCreatedObjs() { + assertEquals( + coerce(ImmutableMap.of("vals", ImmutableMap.of()), MapOfMyClazz.class), + MapOfMyClazz.create(ImmutableMap.of())); + assertEquals( + coerce(ImmutableMap.of("vals", ImmutableMap.of("key1", ImmutableMap.of("arg1", "val1", "arg2", "val2"))), MapOfMyClazz.class), + MapOfMyClazz.create(ImmutableMap.of("key1", MyClazz.create("val1", "val2")))); + } + + public static class MyClazz { + private final String arg1; + private final String arg2; + + @SerializedNames({"arg1", "arg2"}) + public static MyClazz create(String arg1, String arg2) { + return new MyClazz(arg1, arg2); + } + + private MyClazz(String arg1, String arg2) { + this.arg1 = arg1; + this.arg2 = arg2; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MyClazz)) return false; + MyClazz o = (MyClazz) obj; + return Objects.equal(arg1, o.arg1) && Objects.equal(arg2, o.arg2); + } + + @Override + public int hashCode() { + return Objects.hashCode(arg1, arg2); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("arg1", arg1).add("arg2", arg2).toString(); + } + } + + public static class MyCompositeClazz { + private final MyClazz val; + + @SerializedNames({"val"}) + public static MyCompositeClazz create(MyClazz val) { + return new MyCompositeClazz(val); + } + + private MyCompositeClazz(MyClazz val) { + this.val = val; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MyCompositeClazz)) return false; + MyCompositeClazz o = (MyCompositeClazz) obj; + return Objects.equal(val, o.val); + } + + @Override + public int hashCode() { + return Objects.hashCode(val); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("val", val).toString(); + } + } + + public static class MyClazzWithPrimitives { + private final boolean boolArg; + private final byte byteArg; + private final short shortArg; + private final int intArg; + private final long longArg; + private final float floatArg; + private final double doubleArg; + + @SerializedNames({"boolArg", "byteArg", "shortArg", "intArg", "longArg", "floatArg", "doubleArg"}) + public static MyClazzWithPrimitives create(boolean boolArg, byte byteArg, short shortArg, int intArg, long longArg, float floatArg, double doubleArg) { + return new MyClazzWithPrimitives(boolArg, byteArg, shortArg, intArg, longArg, floatArg, doubleArg); + } + + private MyClazzWithPrimitives(boolean boolArg, byte byteArg, short shortArg, int intArg, long longArg, float floatArg, double doubleArg) { + this.boolArg = boolArg; + this.byteArg = byteArg; + this.shortArg = shortArg; + this.intArg = intArg; + this.longArg = longArg; + this.floatArg = floatArg; + this.doubleArg = doubleArg; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MyClazzWithPrimitives)) return false; + MyClazzWithPrimitives o = (MyClazzWithPrimitives) obj; + return Objects.equal(boolArg, o.boolArg) && Objects.equal(byteArg, o.byteArg) + && Objects.equal(shortArg, o.shortArg) && Objects.equal(intArg, o.intArg) + && Objects.equal(longArg, o.longArg) && Objects.equal(floatArg, o.floatArg) + && Objects.equal(doubleArg, o.doubleArg); + } + + @Override + public int hashCode() { + return Objects.hashCode(boolArg, byteArg, shortArg, intArg, longArg, floatArg, doubleArg); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("boolArg", boolArg).add("byteArg", byteArg) + .add("shortArg", shortArg).add("intArg", intArg).add("longArg", longArg) + .add("floatArg", floatArg).add("doubleArg", doubleArg) + .toString(); + } + } + + public static class ListOfMyClazz { + private final List<MyClazz> vals; + + @SerializedNames({"vals"}) + public static ListOfMyClazz create(List<MyClazz> vals) { + return new ListOfMyClazz(vals); + } + + private ListOfMyClazz(List<MyClazz> vals) { + this.vals = vals; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ListOfMyClazz)) return false; + ListOfMyClazz o = (ListOfMyClazz) obj; + return Objects.equal(vals, o.vals); + } + + @Override + public int hashCode() { + return Objects.hashCode(vals); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("vals", vals).toString(); + } + } + + public static class MapOfMyClazz { + private final Map<String, MyClazz> vals; + + @SerializedNames({"vals"}) + public static MapOfMyClazz create(Map<String, MyClazz> vals) { + return new MapOfMyClazz(vals); + } + + private MapOfMyClazz(Map<String, MyClazz> vals) { + this.vals = vals; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MapOfMyClazz)) return false; + MapOfMyClazz o = (MapOfMyClazz) obj; + return Objects.equal(vals, o.vals); + } + + @Override + public int hashCode() { + return Objects.hashCode(vals); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("vals", vals).toString(); + } + } +}
