View Javadoc
1   /*
2    * JavaBean Tester (https://github.com/hazendaz/javabean-tester)
3    *
4    * Copyright 2012-2025 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 com.codebox.enums.LoadData;
18  import com.codebox.enums.LoadType;
19  import com.codebox.instance.ClassInstance;
20  import com.codebox.instance.ConstructorInstance;
21  
22  import java.lang.reflect.Array;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.Field;
25  import java.math.BigDecimal;
26  import java.time.Instant;
27  import java.time.LocalDate;
28  import java.time.LocalDateTime;
29  import java.time.LocalTime;
30  import java.time.OffsetDateTime;
31  import java.time.ZoneId;
32  import java.time.ZoneOffset;
33  import java.time.ZonedDateTime;
34  import java.util.ArrayList;
35  import java.util.Arrays;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  import java.util.TreeSet;
41  import java.util.UUID;
42  import java.util.concurrent.ConcurrentHashMap;
43  import java.util.concurrent.ConcurrentMap;
44  
45  import lombok.Data;
46  
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  /**
51   * Value Builder Builds values from given type.
52   */
53  @Data
54  public class ValueBuilder {
55  
56      /** The Constant Logger. */
57      private static final Logger logger = LoggerFactory.getLogger(ValueBuilder.class);
58  
59      /** The load data. */
60      private LoadData loadData;
61  
62      /**
63       * Builds the value.
64       *
65       * @param <T>
66       *            the generic type
67       * @param clazz
68       *            the clazz
69       * @param loadType
70       *            the load type
71       *
72       * @return the object
73       */
74      public <T> Object buildValue(final Class<T> clazz, final LoadType loadType) {
75          // Next check for a no-arg constructor
76          final Constructor<?>[] ctrs = clazz.getConstructors();
77          for (final Constructor<?> ctr : ctrs) {
78              if (ctr.getParameterTypes().length == 0 && clazz != String.class) {
79                  if (this.loadData == LoadData.ON && !containsSelf(clazz)) {
80                      // Load Underlying Data to deeply test when it doesn't contain self
81                      final JavaBeanTesterWorker<T, Object> beanTesterWorker = new JavaBeanTesterWorker<>(clazz);
82                      beanTesterWorker.setLoadData(this.loadData);
83                      beanTesterWorker.getterSetterTests(new ClassInstance<T>().newInstance(clazz));
84                      return null;
85                  }
86                  // The class has a no-arg constructor, so just call it (e.g. String, Date, etc.)
87                  return ConstructorInstance.newInstance(ctr);
88              }
89          }
90  
91          // Specific rules for common classes
92          Object returnObject = null;
93          switch (loadType) {
94              case ALTERNATE_DATA:
95                  returnObject = ValueBuilder.setAlternateValues(clazz);
96                  break;
97              case NULL_DATA:
98                  returnObject = ValueBuilder.setNullValues(clazz);
99                  break;
100             case STANDARD_DATA:
101             default:
102                 returnObject = ValueBuilder.setStandardValues(clazz);
103                 break;
104         }
105 
106         if (returnObject != null || loadType == LoadType.NULL_DATA) {
107             return returnObject;
108         }
109 
110         if (clazz.isAssignableFrom(BigDecimal.class)) {
111             return BigDecimal.ONE;
112         }
113 
114         if (clazz.isAssignableFrom(UUID.class)) {
115             return UUID.fromString("00000000-0000-0000-0000-000123456789");
116         }
117 
118         if (clazz.isAssignableFrom(Instant.class)) {
119             return Instant.ofEpochSecond(1L);
120         }
121 
122         if (clazz.isAssignableFrom(List.class)) {
123             return new ArrayList<>();
124         }
125 
126         if (clazz.isAssignableFrom(Map.class)) {
127             return new HashMap<>();
128         }
129 
130         if (clazz.isAssignableFrom(ConcurrentMap.class)) {
131             return new ConcurrentHashMap<>();
132         }
133 
134         if (clazz.isAssignableFrom(Set.class)) {
135             return new TreeSet<>();
136         }
137 
138         if (clazz.isAssignableFrom(LocalDate.class)) {
139             return LocalDate.now();
140         }
141 
142         if (clazz.isAssignableFrom(LocalDateTime.class)) {
143             return LocalDateTime.of(2000, 10, 1, 0, 0);
144         }
145 
146         if (clazz.isAssignableFrom(LocalTime.class)) {
147             return LocalTime.of(0, 0);
148         }
149 
150         if (clazz.isAssignableFrom(OffsetDateTime.class)) {
151             return OffsetDateTime.of(2000, 10, 1, 0, 0, 0, 0, ZoneOffset.MIN);
152         }
153 
154         if (clazz.isAssignableFrom(ZonedDateTime.class)) {
155             return ZonedDateTime.of(LocalDateTime.of(2020, 11, 16, 10, 26, 00, 01), ZoneId.of("UTC"));
156         }
157 
158         if (clazz == Logger.class) {
159             return LoggerFactory.getLogger(clazz);
160         }
161 
162         if (clazz.isEnum()) {
163             return clazz.getEnumConstants()[0];
164         }
165 
166         // XXX Add additional rules here
167 
168         // XXX Don't fail this...until alternative solution is determined
169         ValueBuilder.logger.warn(
170                 "Unable to build value for class '{}', please raise ticket with JavaBeanTester for desired support.",
171                 clazz.getName());
172         return null;
173     }
174 
175     /**
176      * Set using alternate test values.
177      *
178      * @param <T>
179      *            the type parameter associated with the class under test.
180      * @param clazz
181      *            the class under test.
182      *
183      * @return Object the Object to use for test.
184      */
185     private static <T> Object setAlternateValues(final Class<T> clazz) {
186         return ValueBuilder.setValues(clazz, "ALT_VALUE", 1, Boolean.FALSE, Integer.valueOf(2), Long.valueOf(2),
187                 Double.valueOf(2.0), Float.valueOf(2.0F), Character.valueOf('N'), Byte.valueOf((byte) 2));
188     }
189 
190     /**
191      * Set using null test values.
192      *
193      * @param <T>
194      *            the type parameter associated with the class under test.
195      * @param clazz
196      *            the class under test.
197      *
198      * @return Object the Object to use for test.
199      */
200     private static <T> Object setNullValues(final Class<T> clazz) {
201         return ValueBuilder.setValues(clazz, null, 0, null, null, null, null, null, null, null);
202     }
203 
204     /**
205      * Set using standard test values.
206      *
207      * @param <T>
208      *            the type parameter associated with the class under test.
209      * @param clazz
210      *            the class under test.
211      *
212      * @return Object the Object to use for test.
213      */
214     private static <T> Object setStandardValues(final Class<T> clazz) {
215         return ValueBuilder.setValues(clazz, "TEST_VALUE", 1, Boolean.TRUE, Integer.valueOf(1), Long.valueOf(1),
216                 Double.valueOf(1.0), Float.valueOf(1.0F), Character.valueOf('Y'), Byte.valueOf((byte) 1));
217     }
218 
219     /**
220      * Set Values for object.
221      *
222      * @param <T>
223      *            the type parameter associated with the class under test.
224      * @param clazz
225      *            the class instance under test.
226      * @param string
227      *            value of string object.
228      * @param arrayLength
229      *            amount of array objects to create.
230      * @param booleanValue
231      *            value of boolean object.
232      * @param integerValue
233      *            value of integer object.
234      * @param longValue
235      *            value of long object.
236      * @param doubleValue
237      *            value of double object.
238      * @param floatValue
239      *            value of float object.
240      * @param characterValue
241      *            value of character object.
242      * @param byteValue
243      *            value of character object.
244      *
245      * @return Object value determined by input class. If not found, returns null.
246      */
247     private static <T> Object setValues(final Class<T> clazz, final String string, final int arrayLength,
248             final Boolean booleanValue, final Integer integerValue, final Long longValue, final Double doubleValue,
249             final Float floatValue, final Character characterValue, final Byte byteValue) {
250         if (clazz == String.class) {
251             return string;
252         }
253         if (clazz.isArray()) {
254             return Array.newInstance(clazz.getComponentType(), arrayLength);
255         }
256         if (clazz == boolean.class || clazz == Boolean.class) {
257             return ValueBuilder.initializeBoolean(clazz, booleanValue);
258         }
259         if (clazz == int.class || clazz == Integer.class) {
260             return ValueBuilder.initializeInteger(clazz, integerValue);
261         }
262         if (clazz == long.class || clazz == Long.class) {
263             return ValueBuilder.initializeLong(clazz, longValue);
264         }
265         if (clazz == double.class || clazz == Double.class) {
266             return ValueBuilder.initializeDouble(clazz, doubleValue);
267         }
268         if (clazz == float.class || clazz == Float.class) {
269             return ValueBuilder.initializeFloat(clazz, floatValue);
270         }
271         if (clazz == char.class || clazz == Character.class) {
272             return ValueBuilder.initializeCharacter(clazz, characterValue);
273         }
274         if (clazz == byte.class || clazz == Byte.class) {
275             return ValueBuilder.initializeByte(clazz, byteValue);
276         }
277         return null;
278     }
279 
280     /**
281      * Initialize boolean.
282      *
283      * @param <T>
284      *            the generic type
285      * @param clazz
286      *            the clazz
287      * @param booleanValue
288      *            the boolean value
289      *
290      * @return the object
291      */
292     private static <T> Object initializeBoolean(final Class<T> clazz, final Boolean booleanValue) {
293         if (clazz == boolean.class && booleanValue == null) {
294             return Boolean.FALSE;
295         }
296         return booleanValue;
297     }
298 
299     /**
300      * Initialize integer.
301      *
302      * @param <T>
303      *            the generic type
304      * @param clazz
305      *            the clazz
306      * @param integerValue
307      *            the integer value
308      *
309      * @return the object
310      */
311     private static <T> Object initializeInteger(final Class<T> clazz, final Integer integerValue) {
312         if (clazz == int.class && integerValue == null) {
313             return Integer.valueOf(-1);
314         }
315         return integerValue;
316     }
317 
318     /**
319      * Initialize long.
320      *
321      * @param <T>
322      *            the generic type
323      * @param clazz
324      *            the clazz
325      * @param longValue
326      *            the long value
327      *
328      * @return the object
329      */
330     private static <T> Object initializeLong(final Class<T> clazz, final Long longValue) {
331         if (clazz == long.class && longValue == null) {
332             return Long.valueOf(-1);
333         }
334         return longValue;
335     }
336 
337     /**
338      * Initialize double.
339      *
340      * @param <T>
341      *            the generic type
342      * @param clazz
343      *            the clazz
344      * @param doubleValue
345      *            the double value
346      *
347      * @return the object
348      */
349     private static <T> Object initializeDouble(final Class<T> clazz, final Double doubleValue) {
350         if (clazz == double.class && doubleValue == null) {
351             return Double.valueOf(-1.0);
352         }
353         return doubleValue;
354     }
355 
356     /**
357      * Initialize float.
358      *
359      * @param <T>
360      *            the generic type
361      * @param clazz
362      *            the clazz
363      * @param floatValue
364      *            the float value
365      *
366      * @return the object
367      */
368     private static <T> Object initializeFloat(final Class<T> clazz, final Float floatValue) {
369         if (clazz == float.class && floatValue == null) {
370             return Float.valueOf(-1.0F);
371         }
372         return floatValue;
373     }
374 
375     /**
376      * Initialize character.
377      *
378      * @param <T>
379      *            the generic type
380      * @param clazz
381      *            the clazz
382      * @param characterValue
383      *            the character value
384      *
385      * @return the object
386      */
387     private static <T> Object initializeCharacter(final Class<T> clazz, final Character characterValue) {
388         if (clazz == char.class && characterValue == null) {
389             return Character.valueOf('\u0000');
390         }
391         return characterValue;
392     }
393 
394     /**
395      * Initialize byte.
396      *
397      * @param <T>
398      *            the generic type
399      * @param clazz
400      *            the clazz
401      * @param byteValue
402      *            the byte value
403      *
404      * @return the object
405      */
406     private static <T> Object initializeByte(final Class<T> clazz, final Byte byteValue) {
407         if (clazz == byte.class && byteValue == null) {
408             return Byte.valueOf((byte) -1);
409         }
410         return byteValue;
411     }
412 
413     /**
414      * Contains self.
415      *
416      * @param <T>
417      *            the generic type
418      * @param clazz
419      *            the clazz
420      * @return true, if successful
421      */
422     private <T> boolean containsSelf(final Class<T> clazz) {
423         final List<Field> fields = Arrays.asList(clazz.getDeclaredFields());
424         for (Field field : fields) {
425             if (field.getType().equals(clazz)) {
426                 return true;
427             }
428         }
429         return false;
430     }
431 
432 }