View Javadoc
1   /*
2    * JavaBean Tester (https://github.com/hazendaz/javabean-tester)
3    *
4    * Copyright 2012-2024 Hazendaz.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of The Apache Software License,
8    * Version 2.0 which accompanies this distribution, and is available at
9    * http://www.apache.org/licenses/LICENSE-2.0.txt
10   *
11   * Contributors:
12   *     CodeBox (Rob Dawson).
13   *     Hazendaz (Jeremy Landis).
14   */
15  package com.codebox.bean;
16  
17  import java.lang.reflect.Modifier;
18  
19  import net.bytebuddy.ByteBuddy;
20  import net.bytebuddy.NamingStrategy;
21  import net.bytebuddy.description.NamedElement;
22  import net.bytebuddy.description.modifier.Visibility;
23  import net.bytebuddy.description.type.TypeDescription;
24  import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
25  import net.bytebuddy.implementation.EqualsMethod;
26  import net.bytebuddy.implementation.HashCodeMethod;
27  import net.bytebuddy.implementation.MethodDelegation;
28  import net.bytebuddy.implementation.ToStringMethod;
29  import net.bytebuddy.matcher.ElementMatchers;
30  
31  /**
32   * This helper class can be used to unit test the get/set/equals/canEqual/toString/hashCode methods of JavaBean-style
33   * Value Objects.
34   */
35  public enum JavaBeanTester {
36  
37      // Private Usage
38      ;
39  
40      /**
41       * Configure JavaBeanTester using Fluent API.
42       *
43       * @param <T>
44       *            the generic type
45       * @param clazz
46       *            the clazz
47       *
48       * @return A builder implementing the fluent API to configure JavaBeanTester
49       */
50      public static <T> JavaBeanTesterBuilder<T, ?> builder(final Class<T> clazz) {
51          // If class is final, use Object.class for comparison needs
52          if (Modifier.isFinal(clazz.getModifiers())) {
53              return new JavaBeanTesterBuilder<>(clazz, Object.class);
54          }
55  
56          // Build extension from class using byte buddy
57          Class<? extends T> loaded = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
58              @Override
59              protected String name(TypeDescription superClass) {
60                  return clazz.getPackageName() + ".ByteBuddyExt" + superClass.getSimpleName();
61              }
62          }).subclass(clazz).method(ElementMatchers.isEquals()).intercept(EqualsMethod.requiringSuperClassEquality())
63                  .method(ElementMatchers.isHashCode()).intercept(HashCodeMethod.usingSuperClassOffset())
64                  .method(ElementMatchers.isToString()).intercept(ToStringMethod.prefixedBySimpleClassName())
65                  .method(ElementMatchers.named("canEqual")).intercept(MethodDelegation.to(CanEqualInterceptor.class))
66                  .defineField("javabeanExtension", String.class, Visibility.PACKAGE_PRIVATE).make()
67                  .load(clazz.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER).getLoaded();
68  
69          // Builder with proper extension class
70          return builder(clazz, loaded);
71      }
72  
73      /**
74       * Configure JavaBeanTester using Fluent API.
75       *
76       * @param <T>
77       *            the generic type
78       * @param <E>
79       *            the element type
80       * @param clazz
81       *            the clazz
82       * @param extension
83       *            the extension
84       *
85       * @return A builder implementing the fluent API to configure JavaBeanTester
86       */
87      public static <T, E> JavaBeanTesterBuilder<T, E> builder(final Class<T> clazz, final Class<E> extension) {
88          return new JavaBeanTesterBuilder<>(clazz, extension);
89      }
90  
91      /**
92       * The Class CanEqualInterceptor.
93       */
94      public static class CanEqualInterceptor {
95  
96          /**
97           * Can Equal Interceptor.
98           *
99           * @param object
100          *            The object to check can equals
101          * @return boolean of can equals
102          */
103         public static boolean canEqual(final Object object) {
104             return object instanceof NamedElement.WithRuntimeName;
105         }
106 
107     }
108 }