1 package com.google.gson.internal; 2 3 import java.lang.reflect.AccessibleObject; 4 import java.lang.reflect.Method; 5 import java.util.List; 6 7 import com.google.gson.ReflectionAccessFilter; 8 import com.google.gson.ReflectionAccessFilter.FilterResult; 9 10 /** 11 * Internal helper class for {@link ReflectionAccessFilter}. 12 */ 13 public class ReflectionAccessFilterHelper { ReflectionAccessFilterHelper()14 private ReflectionAccessFilterHelper() { } 15 16 // Platform type detection is based on Moshi's Util.isPlatformType(Class) 17 // See https://github.com/square/moshi/blob/3c108919ee1cce88a433ffda04eeeddc0341eae7/moshi/src/main/java/com/squareup/moshi/internal/Util.java#L141 18 isJavaType(Class<?> c)19 public static boolean isJavaType(Class<?> c) { 20 return isJavaType(c.getName()); 21 } 22 isJavaType(String className)23 private static boolean isJavaType(String className) { 24 return className.startsWith("java.") || className.startsWith("javax."); 25 } 26 isAndroidType(Class<?> c)27 public static boolean isAndroidType(Class<?> c) { 28 return isAndroidType(c.getName()); 29 } 30 isAndroidType(String className)31 private static boolean isAndroidType(String className) { 32 return className.startsWith("android.") 33 || className.startsWith("androidx.") 34 || isJavaType(className); 35 } 36 isAnyPlatformType(Class<?> c)37 public static boolean isAnyPlatformType(Class<?> c) { 38 String className = c.getName(); 39 return isAndroidType(className) // Covers Android and Java 40 || className.startsWith("kotlin.") 41 || className.startsWith("kotlinx.") 42 || className.startsWith("scala."); 43 } 44 45 /** 46 * Gets the result of applying all filters until the first one returns a result 47 * other than {@link FilterResult#INDECISIVE}, or {@link FilterResult#ALLOW} if 48 * the list of filters is empty or all returned {@code INDECISIVE}. 49 */ getFilterResult(List<ReflectionAccessFilter> reflectionFilters, Class<?> c)50 public static FilterResult getFilterResult(List<ReflectionAccessFilter> reflectionFilters, Class<?> c) { 51 for (ReflectionAccessFilter filter : reflectionFilters) { 52 FilterResult result = filter.check(c); 53 if (result != FilterResult.INDECISIVE) { 54 return result; 55 } 56 } 57 return FilterResult.ALLOW; 58 } 59 60 /** 61 * See {@link AccessibleObject#canAccess(Object)} (Java >= 9) 62 */ canAccess(AccessibleObject accessibleObject, Object object)63 public static boolean canAccess(AccessibleObject accessibleObject, Object object) { 64 return AccessChecker.INSTANCE.canAccess(accessibleObject, object); 65 } 66 67 private static abstract class AccessChecker { 68 public static final AccessChecker INSTANCE; 69 static { 70 AccessChecker accessChecker = null; 71 // TODO: Ideally should use Multi-Release JAR for this version specific code 72 if (JavaVersion.isJava9OrLater()) { 73 try { 74 final Method canAccessMethod = AccessibleObject.class.getDeclaredMethod("canAccess", Object.class); 75 accessChecker = new AccessChecker() { 76 @Override public boolean canAccess(AccessibleObject accessibleObject, Object object) { 77 try { 78 return (Boolean) canAccessMethod.invoke(accessibleObject, object); 79 } catch (Exception e) { 80 throw new RuntimeException("Failed invoking canAccess", e); 81 } 82 } 83 }; 84 } catch (NoSuchMethodException ignored) { 85 } 86 } 87 88 if (accessChecker == null) { 89 accessChecker = new AccessChecker() { 90 @Override public boolean canAccess(AccessibleObject accessibleObject, Object object) { 91 // Cannot determine whether object can be accessed, so assume it can be accessed 92 return true; 93 } 94 }; 95 } 96 INSTANCE = accessChecker; 97 } 98 canAccess(AccessibleObject accessibleObject, Object object)99 public abstract boolean canAccess(AccessibleObject accessibleObject, Object object); 100 } 101 } 102