View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.capturing;
6   
7   import static mockit.internal.util.GeneratedClasses.isExternallyGeneratedSubclass;
8   import static mockit.internal.util.GeneratedClasses.isGeneratedClass;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.security.ProtectionDomain;
14  
15  import org.checkerframework.checker.index.qual.NonNegative;
16  
17  final class CapturedType {
18      private static final ProtectionDomain JMOCKIT_DOMAIN = CapturedType.class.getProtectionDomain();
19  
20      @NonNull
21      final Class<?> baseType;
22  
23      CapturedType(@NonNull Class<?> baseType) {
24          this.baseType = baseType;
25      }
26  
27      boolean isToBeCaptured(@NonNull Class<?> aClass) {
28          if (aClass == baseType || aClass.isArray() || !baseType.isAssignableFrom(aClass)
29                  || extendsJMockitBaseType(aClass)) {
30              return false;
31          }
32  
33          return !aClass.isInterface() && !isNotToBeCaptured(aClass.getProtectionDomain(), aClass.getName());
34      }
35  
36      @SuppressWarnings("UnnecessaryFullyQualifiedName")
37      private static boolean extendsJMockitBaseType(@NonNull Class<?> aClass) {
38          return mockit.MockUp.class.isAssignableFrom(aClass) || mockit.Expectations.class.isAssignableFrom(aClass)
39                  || mockit.Verifications.class.isAssignableFrom(aClass)
40                  || mockit.Delegate.class.isAssignableFrom(aClass);
41      }
42  
43      static boolean isNotToBeCaptured(@Nullable ProtectionDomain pd, @NonNull String classNameOrDesc) {
44          return pd == JMOCKIT_DOMAIN || classNameOrDesc.endsWith("Test")
45                  || isNonEligibleInternalJDKClass(classNameOrDesc) || isNonEligibleStandardJavaClass(classNameOrDesc)
46                  || isNonEligibleClassFromIDERuntime(classNameOrDesc)
47                  || isNonEligibleClassFromThirdPartyLibrary(classNameOrDesc) || isGeneratedClass(classNameOrDesc)
48                  || isExternallyGeneratedSubclass(classNameOrDesc);
49      }
50  
51      private static boolean isNonEligibleInternalJDKClass(@NonNull String classNameOrDesc) {
52          return classNameOrDesc.startsWith("jdk/")
53                  || classNameOrDesc.startsWith("sun") && !hasSubPackage(classNameOrDesc, 4, "management")
54                  || classNameOrDesc.startsWith("com") && hasSubPackage(classNameOrDesc, 4, "sun")
55                          && !hasSubPackages(classNameOrDesc, 8, "proxy org");
56      }
57  
58      private static boolean isNonEligibleStandardJavaClass(@NonNull String classNameOrDesc) {
59          return classNameOrDesc.startsWith("java") && !hasSubPackage(classNameOrDesc, 10, "concurrent");
60      }
61  
62      private static boolean isNonEligibleClassFromIDERuntime(@NonNull String classNameOrDesc) {
63          return classNameOrDesc.startsWith("com") && hasSubPackage(classNameOrDesc, 4, "intellij");
64      }
65  
66      private static boolean isNonEligibleClassFromThirdPartyLibrary(@NonNull String classNameOrDesc) {
67          return classNameOrDesc.startsWith("junit") || classNameOrDesc.startsWith("org")
68                  && hasSubPackages(classNameOrDesc, 4, "junit testng hamcrest gradle");
69      }
70  
71      private static boolean hasSubPackage(@NonNull String nameOrDesc, @NonNegative int offset,
72              @NonNull String subPackage) {
73          return nameOrDesc.regionMatches(offset, subPackage, 0, subPackage.length());
74      }
75  
76      private static boolean hasSubPackages(@NonNull String nameOrDesc, @NonNegative int offset,
77              @NonNull String subPackages) {
78          int subPackageStart = 0;
79          int subPackageEnd;
80  
81          do {
82              subPackageEnd = subPackages.indexOf(' ', subPackageStart);
83              int subPackageLength = (subPackageEnd > 0 ? subPackageEnd : subPackages.length()) - subPackageStart;
84  
85              if (nameOrDesc.regionMatches(offset, subPackages, subPackageStart, subPackageLength)) {
86                  return true;
87              }
88  
89              subPackageStart = subPackageEnd + 1;
90          } while (subPackageEnd > 0);
91  
92          return false;
93      }
94  }