• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / ibatis-2 / 730

28 Dec 2025 10:16PM UTC coverage: 65.615% (+0.5%) from 65.146%
730

push

github

web-flow
Update README.md

1602 of 2802 branches covered (57.17%)

5053 of 7701 relevant lines covered (65.61%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.04
/src/main/java/com/ibatis/common/beans/ClassInfo.java
1
/*
2
 * Copyright 2004-2025 the original author or authors.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *    https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.ibatis.common.beans;
17

18
import java.lang.reflect.Constructor;
19
import java.lang.reflect.Field;
20
import java.lang.reflect.InvocationTargetException;
21
import java.lang.reflect.Method;
22
import java.lang.reflect.ReflectPermission;
23
import java.lang.reflect.UndeclaredThrowableException;
24
import java.math.BigDecimal;
25
import java.math.BigInteger;
26
import java.util.ArrayList;
27
import java.util.Collection;
28
import java.util.Date;
29
import java.util.Enumeration;
30
import java.util.HashMap;
31
import java.util.HashSet;
32
import java.util.Hashtable;
33
import java.util.Iterator;
34
import java.util.LinkedList;
35
import java.util.List;
36
import java.util.Locale;
37
import java.util.Map;
38
import java.util.Set;
39
import java.util.TreeMap;
40
import java.util.TreeSet;
41
import java.util.Vector;
42
import java.util.concurrent.ConcurrentHashMap;
43

44
/**
45
 * This class represents a cached set of class definition information that allows for easy mapping between property
46
 * names and getter/setter methods.
47
 */
48
public class ClassInfo {
49

50
  /** The cache enabled. */
51
  private static boolean cacheEnabled = true;
1✔
52

53
  /** The Constant EMPTY_STRING_ARRAY. */
54
  private static final String[] EMPTY_STRING_ARRAY = {};
1✔
55

56
  /** The Constant SIMPLE_TYPE_SET. */
57
  private static final Set SIMPLE_TYPE_SET = new HashSet<>();
1✔
58

59
  /** The Constant CLASS_INFO_MAP. */
60
  private static final Map<Class, ClassInfo> CLASS_INFO_MAP = new ConcurrentHashMap<>();
1✔
61

62
  /** The class name. */
63
  private String className;
64

65
  /** The readable property names. */
66
  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
1✔
67

68
  /** The writeable property names. */
69
  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
1✔
70

71
  /** The set methods. */
72
  private HashMap setMethods = new HashMap<>();
1✔
73

74
  /** The get methods. */
75
  private HashMap getMethods = new HashMap<>();
1✔
76

77
  /** The set types. */
78
  private HashMap setTypes = new HashMap<>();
1✔
79

80
  /** The get types. */
81
  private HashMap getTypes = new HashMap<>();
1✔
82

83
  /** The default constructor. */
84
  private Constructor defaultConstructor;
85

86
  static {
87
    SIMPLE_TYPE_SET.add(String.class);
1✔
88
    SIMPLE_TYPE_SET.add(Byte.class);
1✔
89
    SIMPLE_TYPE_SET.add(Short.class);
1✔
90
    SIMPLE_TYPE_SET.add(Character.class);
1✔
91
    SIMPLE_TYPE_SET.add(Integer.class);
1✔
92
    SIMPLE_TYPE_SET.add(Long.class);
1✔
93
    SIMPLE_TYPE_SET.add(Float.class);
1✔
94
    SIMPLE_TYPE_SET.add(Double.class);
1✔
95
    SIMPLE_TYPE_SET.add(Boolean.class);
1✔
96
    SIMPLE_TYPE_SET.add(Date.class);
1✔
97
    SIMPLE_TYPE_SET.add(Class.class);
1✔
98
    SIMPLE_TYPE_SET.add(BigInteger.class);
1✔
99
    SIMPLE_TYPE_SET.add(BigDecimal.class);
1✔
100

101
    SIMPLE_TYPE_SET.add(Collection.class);
1✔
102
    SIMPLE_TYPE_SET.add(Set.class);
1✔
103
    SIMPLE_TYPE_SET.add(Map.class);
1✔
104
    SIMPLE_TYPE_SET.add(List.class);
1✔
105
    SIMPLE_TYPE_SET.add(HashMap.class);
1✔
106
    SIMPLE_TYPE_SET.add(TreeMap.class);
1✔
107
    SIMPLE_TYPE_SET.add(ArrayList.class);
1✔
108
    SIMPLE_TYPE_SET.add(LinkedList.class);
1✔
109
    SIMPLE_TYPE_SET.add(HashSet.class);
1✔
110
    SIMPLE_TYPE_SET.add(TreeSet.class);
1✔
111
    SIMPLE_TYPE_SET.add(Vector.class);
1✔
112
    SIMPLE_TYPE_SET.add(Hashtable.class);
1✔
113
    SIMPLE_TYPE_SET.add(Enumeration.class);
1✔
114
  }
1✔
115

116
  /**
117
   * Instantiates a new class info.
118
   *
119
   * @param clazz
120
   *          the clazz
121
   */
122
  private ClassInfo(Class clazz) {
1✔
123
    className = clazz.getName();
1✔
124
    addDefaultConstructor(clazz);
1✔
125
    addGetMethods(clazz);
1✔
126
    addSetMethods(clazz);
1✔
127
    addFields(clazz);
1✔
128
    readablePropertyNames = (String[]) getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
1✔
129
    writeablePropertyNames = (String[]) setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
1✔
130
  }
1✔
131

132
  /**
133
   * Adds the default constructor.
134
   *
135
   * @param clazz
136
   *          the clazz
137
   */
138
  private void addDefaultConstructor(Class clazz) {
139
    Constructor[] consts = clazz.getDeclaredConstructors();
1✔
140
    for (Constructor constructor : consts) {
1✔
141
      if (constructor.getParameterTypes().length == 0) {
1✔
142
        if (canAccessPrivateMethods()) {
1!
143
          try {
144
            constructor.setAccessible(true);
1✔
145
          } catch (Exception e) {
×
146
            // Ignored. This is only a final precaution, nothing we can do.
147
          }
1✔
148
        }
149
        if (constructor.isAccessible()) {
1!
150
          this.defaultConstructor = constructor;
1✔
151
        }
152
      }
153
    }
154
  }
1✔
155

156
  /**
157
   * Adds the get methods.
158
   *
159
   * @param cls
160
   *          the cls
161
   */
162
  private void addGetMethods(Class cls) {
163
    Method[] methods = getClassMethods(cls);
1✔
164
    for (Method method : methods) {
1✔
165
      String name = method.getName();
1✔
166
      if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) {
1!
167
        if (method.getParameterTypes().length == 0) {
1✔
168
          name = dropCase(name);
1✔
169
          addGetMethod(name, method);
1✔
170
        }
171
      }
172
    }
173
  }
1✔
174

175
  /**
176
   * Adds the get method.
177
   *
178
   * @param name
179
   *          the name
180
   * @param method
181
   *          the method
182
   */
183
  private void addGetMethod(String name, Method method) {
184
    getMethods.put(name, new MethodInvoker(method));
1✔
185
    getTypes.put(name, method.getReturnType());
1✔
186
  }
1✔
187

188
  /**
189
   * Adds the set methods.
190
   *
191
   * @param cls
192
   *          the cls
193
   */
194
  private void addSetMethods(Class cls) {
195
    Map conflictingSetters = new HashMap<>();
1✔
196
    Method[] methods = getClassMethods(cls);
1✔
197
    for (Method method : methods) {
1✔
198
      String name = method.getName();
1✔
199
      if (name.startsWith("set") && name.length() > 3) {
1✔
200
        if (method.getParameterTypes().length == 1) {
1✔
201
          name = dropCase(name);
1✔
202
          // /------------
203
          addSetterConflict(conflictingSetters, name, method);
1✔
204
          // addSetMethod(name, method);
205
          // /------------
206
        }
207
      }
208
    }
209
    resolveSetterConflicts(conflictingSetters);
1✔
210
  }
1✔
211

212
  /**
213
   * Adds the setter conflict.
214
   *
215
   * @param conflictingSetters
216
   *          the conflicting setters
217
   * @param name
218
   *          the name
219
   * @param method
220
   *          the method
221
   */
222
  private void addSetterConflict(Map conflictingSetters, String name, Method method) {
223
    List list = (List) conflictingSetters.get(name);
1✔
224
    if (list == null) {
1✔
225
      list = new ArrayList<>();
1✔
226
      conflictingSetters.put(name, list);
1✔
227
    }
228
    list.add(method);
1✔
229
  }
1✔
230

231
  /**
232
   * Resolve setter conflicts.
233
   *
234
   * @param conflictingSetters
235
   *          the conflicting setters
236
   */
237
  private void resolveSetterConflicts(Map conflictingSetters) {
238
    for (Iterator propNames = conflictingSetters.keySet().iterator(); propNames.hasNext();) {
1✔
239
      String propName = (String) propNames.next();
1✔
240
      List setters = (List) conflictingSetters.get(propName);
1✔
241
      Method firstMethod = (Method) setters.get(0);
1✔
242
      if (setters.size() == 1) {
1✔
243
        addSetMethod(propName, firstMethod);
1✔
244
      } else {
245
        Class expectedType = (Class) getTypes.get(propName);
1✔
246
        if (expectedType == null) {
1✔
247
          throw new RuntimeException("Illegal overloaded setter method with ambiguous type for property " + propName
1✔
248
              + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans "
1✔
249
              + "specification and can cause unpredicatble results.");
250
        } else {
251
          Iterator methods = setters.iterator();
1✔
252
          Method setter = null;
1✔
253
          while (methods.hasNext()) {
1✔
254
            Method method = (Method) methods.next();
1✔
255
            if (method.getParameterTypes().length == 1 && expectedType.equals(method.getParameterTypes()[0])) {
1!
256
              setter = method;
1✔
257
              break;
1✔
258
            }
259
          }
1✔
260
          if (setter == null) {
1✔
261
            throw new RuntimeException("Illegal overloaded setter method with ambiguous type for property " + propName
1✔
262
                + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans "
1✔
263
                + "specification and can cause unpredicatble results.");
264
          }
265
          addSetMethod(propName, setter);
1✔
266
        }
267
      }
268
    }
1✔
269
  }
1✔
270

271
  /**
272
   * Adds the set method.
273
   *
274
   * @param name
275
   *          the name
276
   * @param method
277
   *          the method
278
   */
279
  private void addSetMethod(String name, Method method) {
280
    setMethods.put(name, new MethodInvoker(method));
1✔
281
    setTypes.put(name, method.getParameterTypes()[0]);
1✔
282
  }
1✔
283

284
  /**
285
   * Adds the fields.
286
   *
287
   * @param clazz
288
   *          the clazz
289
   */
290
  private void addFields(Class clazz) {
291
    Field[] fields = clazz.getDeclaredFields();
1✔
292
    for (Field field : fields) {
1✔
293
      if (canAccessPrivateMethods()) {
1!
294
        try {
295
          field.setAccessible(true);
1✔
296
        } catch (Exception e) {
1✔
297
          // Ignored. This is only a final precaution, nothing we can do.
298
        }
1✔
299
      }
300
      if (field.isAccessible()) {
1✔
301
        if (!setMethods.containsKey(field.getName())) {
1✔
302
          addSetField(field);
1✔
303
        }
304
        if (!getMethods.containsKey(field.getName())) {
1✔
305
          addGetField(field);
1✔
306
        }
307
      }
308
    }
309
    if (clazz.getSuperclass() != null) {
1✔
310
      addFields(clazz.getSuperclass());
1✔
311
    }
312
  }
1✔
313

314
  /**
315
   * Adds the set field.
316
   *
317
   * @param field
318
   *          the field
319
   */
320
  private void addSetField(Field field) {
321
    setMethods.put(field.getName(), new SetFieldInvoker(field));
1✔
322
    setTypes.put(field.getName(), field.getType());
1✔
323
  }
1✔
324

325
  /**
326
   * Adds the get field.
327
   *
328
   * @param field
329
   *          the field
330
   */
331
  private void addGetField(Field field) {
332
    getMethods.put(field.getName(), new GetFieldInvoker(field));
1✔
333
    getTypes.put(field.getName(), field.getType());
1✔
334
  }
1✔
335

336
  /**
337
   * This method returns an array containing all methods declared in this class and any superclass. We use this method,
338
   * instead of the simpler Class.getMethods(), because we want to look for private methods as well.
339
   *
340
   * @param cls
341
   *          The class
342
   *
343
   * @return An array containing all methods in this class
344
   */
345
  private Method[] getClassMethods(Class cls) {
346
    HashMap uniqueMethods = new HashMap<>();
1✔
347
    Class currentClass = cls;
1✔
348
    while (currentClass != null) {
1✔
349
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
1✔
350

351
      // we also need to look for interface methods -
352
      // because the class may be abstract
353
      Class[] interfaces = currentClass.getInterfaces();
1✔
354
      for (Class element : interfaces) {
1✔
355
        addUniqueMethods(uniqueMethods, element.getMethods());
1✔
356
      }
357

358
      currentClass = currentClass.getSuperclass();
1✔
359
    }
1✔
360

361
    Collection methods = uniqueMethods.values();
1✔
362

363
    return (Method[]) methods.toArray(new Method[methods.size()]);
1✔
364
  }
365

366
  /**
367
   * Adds the unique methods.
368
   *
369
   * @param uniqueMethods
370
   *          the unique methods
371
   * @param methods
372
   *          the methods
373
   */
374
  private void addUniqueMethods(HashMap uniqueMethods, Method[] methods) {
375
    for (Method currentMethod : methods) {
1✔
376
      if (!currentMethod.isBridge()) {
1✔
377
        String signature = getSignature(currentMethod);
1✔
378
        // check to see if the method is already known
379
        // if it is known, then an extended class must have
380
        // overridden a method
381
        if (!uniqueMethods.containsKey(signature)) {
1✔
382
          if (canAccessPrivateMethods()) {
1!
383
            try {
384
              currentMethod.setAccessible(true);
1✔
385
            } catch (Exception e) {
1✔
386
              // Ignored. This is only a final precaution, nothing we can do.
387
            }
1✔
388
          }
389

390
          uniqueMethods.put(signature, currentMethod);
1✔
391
        }
392
      }
393
    }
394
  }
1✔
395

396
  /**
397
   * Gets the signature.
398
   *
399
   * @param method
400
   *          the method
401
   *
402
   * @return the signature
403
   */
404
  private String getSignature(Method method) {
405
    StringBuilder sb = new StringBuilder();
1✔
406
    sb.append(method.getName());
1✔
407
    Class[] parameters = method.getParameterTypes();
1✔
408

409
    for (int i = 0; i < parameters.length; i++) {
1✔
410
      if (i == 0) {
1✔
411
        sb.append(':');
1✔
412
      } else {
413
        sb.append(',');
1✔
414
      }
415
      sb.append(parameters[i].getName());
1✔
416
    }
417

418
    return sb.toString();
1✔
419
  }
420

421
  /**
422
   * Drop case.
423
   *
424
   * @param name
425
   *          the name
426
   *
427
   * @return the string
428
   */
429
  private static String dropCase(String name) {
430
    if (name.startsWith("is")) {
1✔
431
      name = name.substring(2);
1✔
432
    } else if (name.startsWith("get") || name.startsWith("set")) {
1!
433
      name = name.substring(3);
1✔
434
    } else {
435
      throw new ProbeException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
×
436
    }
437

438
    if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
1!
439
      name = name.substring(0, 1).toLowerCase(Locale.US) + name.substring(1);
1✔
440
    }
441

442
    return name;
1✔
443
  }
444

445
  /**
446
   * Can access private methods.
447
   *
448
   * @return true, if successful
449
   */
450
  private static boolean canAccessPrivateMethods() {
451
    try {
452
      SecurityManager securityManager = System.getSecurityManager();
1✔
453
      if (null != securityManager) {
1!
454
        securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
×
455
      }
456
    } catch (SecurityException e) {
×
457
      return false;
×
458
    }
1✔
459
    return true;
1✔
460
  }
461

462
  /**
463
   * Gets the name of the class the instance provides information for.
464
   *
465
   * @return The class name
466
   */
467
  public String getClassName() {
468
    return className;
×
469
  }
470

471
  /**
472
   * Instantiate class.
473
   *
474
   * @return the object
475
   */
476
  public Object instantiateClass() {
477
    if (defaultConstructor != null) {
1!
478
      try {
479
        return defaultConstructor.newInstance();
1✔
480
      } catch (Exception e) {
×
481
        throw new RuntimeException("Error instantiating class. Cause: " + e, e);
×
482
      }
483
    } else {
484
      throw new RuntimeException("Error instantiating class.  There is no default constructor for class " + className);
×
485
    }
486
  }
487

488
  /**
489
   * Gets the setter for a property as a Method object.
490
   *
491
   * @param propertyName
492
   *          - the property
493
   *
494
   * @return The Method
495
   */
496
  public Method getSetter(String propertyName) {
497
    Invoker method = (Invoker) setMethods.get(propertyName);
1✔
498
    if (method == null) {
1!
499
      throw new ProbeException(
×
500
          "There is no WRITEABLE property named '" + propertyName + "' in class '" + className + "'");
501
    }
502
    if (!(method instanceof MethodInvoker)) {
1!
503
      throw new ProbeException(
×
504
          "Can't get setter method because '" + propertyName + "' is a field in class '" + className + "'");
505
    }
506
    return ((MethodInvoker) method).getMethod();
1✔
507
  }
508

509
  /**
510
   * Gets the getter for a property as a Method object.
511
   *
512
   * @param propertyName
513
   *          - the property
514
   *
515
   * @return The Method
516
   */
517
  public Method getGetter(String propertyName) {
518
    Invoker method = (Invoker) getMethods.get(propertyName);
1✔
519
    if (method == null) {
1!
520
      throw new ProbeException(
×
521
          "There is no READABLE property named '" + propertyName + "' in class '" + className + "'");
522
    }
523
    if (!(method instanceof MethodInvoker)) {
1!
524
      throw new ProbeException(
×
525
          "Can't get getter method because '" + propertyName + "' is a field in class '" + className + "'");
526
    }
527
    return ((MethodInvoker) method).getMethod();
1✔
528
  }
529

530
  /**
531
   * Gets the sets the invoker.
532
   *
533
   * @param propertyName
534
   *          the property name
535
   *
536
   * @return the sets the invoker
537
   */
538
  public Invoker getSetInvoker(String propertyName) {
539
    Invoker method = (Invoker) setMethods.get(propertyName);
1✔
540
    if (method == null) {
1!
541
      throw new ProbeException(
×
542
          "There is no WRITEABLE property named '" + propertyName + "' in class '" + className + "'");
543
    }
544
    return method;
1✔
545
  }
546

547
  /**
548
   * Gets the gets the invoker.
549
   *
550
   * @param propertyName
551
   *          the property name
552
   *
553
   * @return the gets the invoker
554
   */
555
  public Invoker getGetInvoker(String propertyName) {
556
    Invoker method = (Invoker) getMethods.get(propertyName);
1✔
557
    if (method == null) {
1!
558
      throw new ProbeException(
×
559
          "There is no READABLE property named '" + propertyName + "' in class '" + className + "'");
560
    }
561
    return method;
1✔
562
  }
563

564
  /**
565
   * Gets the type for a property setter.
566
   *
567
   * @param propertyName
568
   *          - the name of the property
569
   *
570
   * @return The Class of the propery setter
571
   */
572
  public Class getSetterType(String propertyName) {
573
    Class clazz = (Class) setTypes.get(propertyName);
1✔
574
    if (clazz == null) {
1✔
575
      throw new ProbeException(
1✔
576
          "There is no WRITEABLE property named '" + propertyName + "' in class '" + className + "'");
577
    }
578
    return clazz;
1✔
579
  }
580

581
  /**
582
   * Gets the type for a property getter.
583
   *
584
   * @param propertyName
585
   *          - the name of the property
586
   *
587
   * @return The Class of the propery getter
588
   */
589
  public Class getGetterType(String propertyName) {
590
    Class clazz = (Class) getTypes.get(propertyName);
1✔
591
    if (clazz == null) {
1!
592
      throw new ProbeException(
×
593
          "There is no READABLE property named '" + propertyName + "' in class '" + className + "'");
594
    }
595
    return clazz;
1✔
596
  }
597

598
  /**
599
   * Gets an array of the readable properties for an object.
600
   *
601
   * @return The array
602
   */
603
  public String[] getReadablePropertyNames() {
604
    return readablePropertyNames;
×
605
  }
606

607
  /**
608
   * Gets an array of the writeable properties for an object.
609
   *
610
   * @return The array
611
   */
612
  public String[] getWriteablePropertyNames() {
613
    return writeablePropertyNames;
1✔
614
  }
615

616
  /**
617
   * Check to see if a class has a writeable property by name.
618
   *
619
   * @param propertyName
620
   *          - the name of the property to check
621
   *
622
   * @return True if the object has a writeable property by the name
623
   */
624
  public boolean hasWritableProperty(String propertyName) {
625
    return setMethods.keySet().contains(propertyName);
1✔
626
  }
627

628
  /**
629
   * Check to see if a class has a readable property by name.
630
   *
631
   * @param propertyName
632
   *          - the name of the property to check
633
   *
634
   * @return True if the object has a readable property by the name
635
   */
636
  public boolean hasReadableProperty(String propertyName) {
637
    return getMethods.keySet().contains(propertyName);
1✔
638
  }
639

640
  /**
641
   * Tells us if the class passed in is a knwon common type.
642
   *
643
   * @param clazz
644
   *          The class to check
645
   *
646
   * @return True if the class is known
647
   */
648
  public static boolean isKnownType(Class clazz) {
649
    if (SIMPLE_TYPE_SET.contains(clazz)) {
1!
650
      return true;
×
651
    } else if (Collection.class.isAssignableFrom(clazz)) {
1!
652
      return true;
×
653
    } else if (Map.class.isAssignableFrom(clazz)) {
1!
654
      return true;
×
655
    } else if (List.class.isAssignableFrom(clazz)) {
1!
656
      return true;
×
657
    } else if (Set.class.isAssignableFrom(clazz)) {
1!
658
      return true;
×
659
    } else if (Iterator.class.isAssignableFrom(clazz)) {
1!
660
      return true;
×
661
    } else {
662
      return false;
1✔
663
    }
664
  }
665

666
  /**
667
   * Gets an instance of ClassInfo for the specified class.
668
   *
669
   * @param clazz
670
   *          The class for which to lookup the method cache.
671
   *
672
   * @return The method cache for the class
673
   */
674
  public static ClassInfo getInstance(Class clazz) {
675
    if (cacheEnabled) {
1!
676
      ClassInfo cached = (ClassInfo) CLASS_INFO_MAP.get(clazz);
1✔
677
      if (cached == null) {
1✔
678
        cached = new ClassInfo(clazz);
1✔
679
        CLASS_INFO_MAP.put(clazz, cached);
1✔
680
      }
681
      return cached;
1✔
682
    } else {
683
      return new ClassInfo(clazz);
×
684
    }
685
  }
686

687
  /**
688
   * Sets the cache enabled.
689
   *
690
   * @param cacheEnabled
691
   *          the new cache enabled
692
   */
693
  public static void setCacheEnabled(boolean cacheEnabled) {
694
    ClassInfo.cacheEnabled = cacheEnabled;
1✔
695
  }
1✔
696

697
  /**
698
   * Examines a Throwable object and gets it's root cause.
699
   *
700
   * @param t
701
   *          - the exception to examine
702
   *
703
   * @return The root cause
704
   */
705
  public static Throwable unwrapThrowable(Throwable t) {
706
    Throwable t2 = t;
1✔
707
    while (true) {
708
      if (t2 instanceof InvocationTargetException) {
1✔
709
        t2 = ((InvocationTargetException) t).getTargetException();
1✔
710
      } else if (t2 instanceof UndeclaredThrowableException) {
1✔
711
        t2 = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
1✔
712
      } else {
713
        return t2;
1✔
714
      }
715
    }
716
  }
717

718
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc