988 lines
46 KiB
Java
988 lines
46 KiB
Java
package com.google.firebase.firestore.util;
|
|
|
|
import android.net.Uri;
|
|
import com.google.firebase.Timestamp;
|
|
import com.google.firebase.firestore.Blob;
|
|
import com.google.firebase.firestore.DocumentId;
|
|
import com.google.firebase.firestore.DocumentReference;
|
|
import com.google.firebase.firestore.Exclude;
|
|
import com.google.firebase.firestore.FieldValue;
|
|
import com.google.firebase.firestore.GeoPoint;
|
|
import com.google.firebase.firestore.IgnoreExtraProperties;
|
|
import com.google.firebase.firestore.PropertyName;
|
|
import com.google.firebase.firestore.ServerTimestamp;
|
|
import com.google.firebase.firestore.ThrowOnExtraProperties;
|
|
import java.lang.reflect.AccessibleObject;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.GenericArrayType;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.lang.reflect.ParameterizedType;
|
|
import java.lang.reflect.Type;
|
|
import java.lang.reflect.TypeVariable;
|
|
import java.lang.reflect.WildcardType;
|
|
import java.net.URI;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentMap;
|
|
|
|
/* loaded from: classes2.dex */
|
|
public class CustomClassMapper {
|
|
private static final ConcurrentMap<Class<?>, BeanMapper<?>> mappers = new ConcurrentHashMap();
|
|
|
|
/* JADX INFO: Access modifiers changed from: private */
|
|
public static void hardAssert(boolean z, String str) {
|
|
if (!z) {
|
|
throw new RuntimeException("Hard assert failed: ".concat(String.valueOf(str)));
|
|
}
|
|
}
|
|
|
|
public static Object convertToPlainJavaTypes(Object obj) {
|
|
return serialize(obj);
|
|
}
|
|
|
|
public static <T> T convertToCustomClass(Object obj, Class<T> cls, DocumentReference documentReference) {
|
|
return (T) deserializeToClass(obj, cls, new DeserializeContext(ErrorPath.EMPTY, documentReference));
|
|
}
|
|
|
|
private static <T> Object serialize(T t) {
|
|
return serialize(t, ErrorPath.EMPTY);
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: private */
|
|
/* JADX WARN: Multi-variable type inference failed */
|
|
public static <T> Object serialize(T t, ErrorPath errorPath) {
|
|
if (errorPath.getLength() > 500) {
|
|
throw serializeError(errorPath, "Exceeded maximum depth of 500, which likely indicates there's an object cycle");
|
|
}
|
|
if (t == 0) {
|
|
return null;
|
|
}
|
|
if (t instanceof Number) {
|
|
if ((t instanceof Long) || (t instanceof Integer) || (t instanceof Double) || (t instanceof Float)) {
|
|
return t;
|
|
}
|
|
throw serializeError(errorPath, String.format("Numbers of type %s are not supported, please use an int, long, float or double", t.getClass().getSimpleName()));
|
|
}
|
|
if ((t instanceof String) || (t instanceof Boolean)) {
|
|
return t;
|
|
}
|
|
if (t instanceof Character) {
|
|
throw serializeError(errorPath, "Characters are not supported, please use Strings");
|
|
}
|
|
if (t instanceof Map) {
|
|
HashMap hashMap = new HashMap();
|
|
for (Map.Entry entry : ((Map) t).entrySet()) {
|
|
Object key = entry.getKey();
|
|
if (key instanceof String) {
|
|
String str = (String) key;
|
|
hashMap.put(str, serialize(entry.getValue(), errorPath.child(str)));
|
|
} else {
|
|
throw serializeError(errorPath, "Maps with non-string keys are not supported");
|
|
}
|
|
}
|
|
return hashMap;
|
|
}
|
|
if (t instanceof Collection) {
|
|
if (t instanceof List) {
|
|
List list = (List) t;
|
|
ArrayList arrayList = new ArrayList(list.size());
|
|
for (int i = 0; i < list.size(); i++) {
|
|
Object obj = list.get(i);
|
|
StringBuilder sb = new StringBuilder("[");
|
|
sb.append(i);
|
|
sb.append("]");
|
|
arrayList.add(serialize(obj, errorPath.child(sb.toString())));
|
|
}
|
|
return arrayList;
|
|
}
|
|
throw serializeError(errorPath, "Serializing Collections is not supported, please use Lists instead");
|
|
}
|
|
if (t.getClass().isArray()) {
|
|
throw serializeError(errorPath, "Serializing Arrays is not supported, please use Lists instead");
|
|
}
|
|
if (t instanceof Enum) {
|
|
String name = ((Enum) t).name();
|
|
try {
|
|
return BeanMapper.propertyName(t.getClass().getField(name));
|
|
} catch (NoSuchFieldException unused) {
|
|
return name;
|
|
}
|
|
}
|
|
if ((t instanceof Date) || (t instanceof Timestamp) || (t instanceof GeoPoint) || (t instanceof Blob) || (t instanceof DocumentReference) || (t instanceof FieldValue)) {
|
|
return t;
|
|
}
|
|
if ((t instanceof Uri) || (t instanceof URI) || (t instanceof URL)) {
|
|
return t.toString();
|
|
}
|
|
return loadOrCreateBeanMapperForClass(t.getClass()).serialize(t, errorPath);
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: private */
|
|
public static <T> T deserializeToType(Object obj, Type type, DeserializeContext deserializeContext) {
|
|
while (obj != null) {
|
|
if (type instanceof ParameterizedType) {
|
|
return (T) deserializeToParameterizedType(obj, (ParameterizedType) type, deserializeContext);
|
|
}
|
|
if (type instanceof Class) {
|
|
return (T) deserializeToClass(obj, (Class) type, deserializeContext);
|
|
}
|
|
if (type instanceof WildcardType) {
|
|
WildcardType wildcardType = (WildcardType) type;
|
|
if (wildcardType.getLowerBounds().length > 0) {
|
|
throw deserializeError(deserializeContext.errorPath, "Generic lower-bounded wildcard types are not supported");
|
|
}
|
|
Type[] upperBounds = wildcardType.getUpperBounds();
|
|
hardAssert(upperBounds.length > 0, "Unexpected type bounds on wildcard ".concat(String.valueOf(type)));
|
|
type = upperBounds[0];
|
|
} else if (type instanceof TypeVariable) {
|
|
Type[] bounds = ((TypeVariable) type).getBounds();
|
|
hardAssert(bounds.length > 0, "Unexpected type bounds on type variable ".concat(String.valueOf(type)));
|
|
type = bounds[0];
|
|
} else {
|
|
if (type instanceof GenericArrayType) {
|
|
throw deserializeError(deserializeContext.errorPath, "Generic Arrays are not supported, please use Lists instead");
|
|
}
|
|
throw deserializeError(deserializeContext.errorPath, "Unknown type encountered: ".concat(String.valueOf(type)));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/* JADX WARN: Multi-variable type inference failed */
|
|
private static <T> T deserializeToClass(Object obj, Class<T> cls, DeserializeContext deserializeContext) {
|
|
if (obj == 0) {
|
|
return null;
|
|
}
|
|
if (cls.isPrimitive() || Number.class.isAssignableFrom(cls) || Boolean.class.isAssignableFrom(cls) || Character.class.isAssignableFrom(cls)) {
|
|
return (T) deserializeToPrimitive(obj, cls, deserializeContext);
|
|
}
|
|
if (String.class.isAssignableFrom(cls)) {
|
|
return (T) convertString(obj, deserializeContext);
|
|
}
|
|
if (Date.class.isAssignableFrom(cls)) {
|
|
return (T) convertDate(obj, deserializeContext);
|
|
}
|
|
if (Timestamp.class.isAssignableFrom(cls)) {
|
|
return (T) convertTimestamp(obj, deserializeContext);
|
|
}
|
|
if (Blob.class.isAssignableFrom(cls)) {
|
|
return (T) convertBlob(obj, deserializeContext);
|
|
}
|
|
if (GeoPoint.class.isAssignableFrom(cls)) {
|
|
return (T) convertGeoPoint(obj, deserializeContext);
|
|
}
|
|
if (DocumentReference.class.isAssignableFrom(cls)) {
|
|
return (T) convertDocumentReference(obj, deserializeContext);
|
|
}
|
|
if (cls.isArray()) {
|
|
throw deserializeError(deserializeContext.errorPath, "Converting to Arrays is not supported, please use Lists instead");
|
|
}
|
|
if (cls.getTypeParameters().length > 0) {
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Class ");
|
|
sb.append(cls.getName());
|
|
sb.append(" has generic type parameters, please use GenericTypeIndicator instead");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
if (cls.equals(Object.class)) {
|
|
return obj;
|
|
}
|
|
if (cls.isEnum()) {
|
|
return (T) deserializeToEnum(obj, cls, deserializeContext);
|
|
}
|
|
return (T) convertBean(obj, cls, deserializeContext);
|
|
}
|
|
|
|
/* JADX WARN: Type inference failed for: r0v5, types: [java.util.AbstractMap, T, java.util.HashMap] */
|
|
/* JADX WARN: Type inference failed for: r0v8, types: [java.util.List, T, java.util.ArrayList] */
|
|
private static <T> T deserializeToParameterizedType(Object obj, ParameterizedType parameterizedType, DeserializeContext deserializeContext) {
|
|
Class cls = (Class) parameterizedType.getRawType();
|
|
int i = 0;
|
|
if (List.class.isAssignableFrom(cls)) {
|
|
Type type = parameterizedType.getActualTypeArguments()[0];
|
|
if (obj instanceof List) {
|
|
List list = (List) obj;
|
|
?? r0 = (T) new ArrayList(list.size());
|
|
while (i < list.size()) {
|
|
Object obj2 = list.get(i);
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("[");
|
|
sb.append(i);
|
|
sb.append("]");
|
|
r0.add(deserializeToType(obj2, type, deserializeContext.newInstanceWithErrorPath(errorPath.child(sb.toString()))));
|
|
i++;
|
|
}
|
|
return r0;
|
|
}
|
|
ErrorPath errorPath2 = deserializeContext.errorPath;
|
|
StringBuilder sb2 = new StringBuilder("Expected a List, but got a ");
|
|
sb2.append(obj.getClass());
|
|
throw deserializeError(errorPath2, sb2.toString());
|
|
}
|
|
if (Map.class.isAssignableFrom(cls)) {
|
|
Type type2 = parameterizedType.getActualTypeArguments()[0];
|
|
Type type3 = parameterizedType.getActualTypeArguments()[1];
|
|
if (!type2.equals(String.class)) {
|
|
throw deserializeError(deserializeContext.errorPath, "Only Maps with string keys are supported, but found Map with key type ".concat(String.valueOf(type2)));
|
|
}
|
|
Map<String, Object> expectMap = expectMap(obj, deserializeContext);
|
|
?? r02 = (T) new HashMap();
|
|
for (Map.Entry<String, Object> entry : expectMap.entrySet()) {
|
|
r02.put(entry.getKey(), deserializeToType(entry.getValue(), type3, deserializeContext.newInstanceWithErrorPath(deserializeContext.errorPath.child(entry.getKey()))));
|
|
}
|
|
return r02;
|
|
}
|
|
if (Collection.class.isAssignableFrom(cls)) {
|
|
throw deserializeError(deserializeContext.errorPath, "Collections are not supported, please use Lists instead");
|
|
}
|
|
Map<String, Object> expectMap2 = expectMap(obj, deserializeContext);
|
|
BeanMapper loadOrCreateBeanMapperForClass = loadOrCreateBeanMapperForClass(cls);
|
|
HashMap hashMap = new HashMap();
|
|
TypeVariable<Class<T>>[] typeParameters = loadOrCreateBeanMapperForClass.clazz.getTypeParameters();
|
|
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
|
if (actualTypeArguments.length != typeParameters.length) {
|
|
throw new IllegalStateException("Mismatched lengths for type variables and actual types");
|
|
}
|
|
while (i < typeParameters.length) {
|
|
hashMap.put(typeParameters[i], actualTypeArguments[i]);
|
|
i++;
|
|
}
|
|
return (T) loadOrCreateBeanMapperForClass.deserialize(expectMap2, hashMap, deserializeContext);
|
|
}
|
|
|
|
private static <T> T deserializeToPrimitive(Object obj, Class<T> cls, DeserializeContext deserializeContext) {
|
|
if (Integer.class.isAssignableFrom(cls) || Integer.TYPE.isAssignableFrom(cls)) {
|
|
return (T) convertInteger(obj, deserializeContext);
|
|
}
|
|
if (Boolean.class.isAssignableFrom(cls) || Boolean.TYPE.isAssignableFrom(cls)) {
|
|
return (T) convertBoolean(obj, deserializeContext);
|
|
}
|
|
if (Double.class.isAssignableFrom(cls) || Double.TYPE.isAssignableFrom(cls)) {
|
|
return (T) convertDouble(obj, deserializeContext);
|
|
}
|
|
if (Long.class.isAssignableFrom(cls) || Long.TYPE.isAssignableFrom(cls)) {
|
|
return (T) convertLong(obj, deserializeContext);
|
|
}
|
|
if (Float.class.isAssignableFrom(cls) || Float.TYPE.isAssignableFrom(cls)) {
|
|
return (T) Float.valueOf(convertDouble(obj, deserializeContext).floatValue());
|
|
}
|
|
throw deserializeError(deserializeContext.errorPath, String.format("Deserializing values to %s is not supported", cls.getSimpleName()));
|
|
}
|
|
|
|
private static <T> T deserializeToEnum(Object obj, Class<T> cls, DeserializeContext deserializeContext) {
|
|
if (obj instanceof String) {
|
|
String str = (String) obj;
|
|
for (Field field : cls.getFields()) {
|
|
if (field.isEnumConstant() && str.equals(BeanMapper.propertyName(field))) {
|
|
str = field.getName();
|
|
break;
|
|
}
|
|
}
|
|
try {
|
|
return (T) Enum.valueOf(cls, str);
|
|
} catch (IllegalArgumentException unused) {
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Could not find enum value of ");
|
|
sb.append(cls.getName());
|
|
sb.append(" for value \"");
|
|
sb.append(str);
|
|
sb.append("\"");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
}
|
|
ErrorPath errorPath2 = deserializeContext.errorPath;
|
|
StringBuilder sb2 = new StringBuilder("Expected a String while deserializing to enum ");
|
|
sb2.append(cls);
|
|
sb2.append(" but got a ");
|
|
sb2.append(obj.getClass());
|
|
throw deserializeError(errorPath2, sb2.toString());
|
|
}
|
|
|
|
private static <T> BeanMapper<T> loadOrCreateBeanMapperForClass(Class<T> cls) {
|
|
ConcurrentMap<Class<?>, BeanMapper<?>> concurrentMap = mappers;
|
|
BeanMapper<T> beanMapper = (BeanMapper) concurrentMap.get(cls);
|
|
if (beanMapper != null) {
|
|
return beanMapper;
|
|
}
|
|
BeanMapper<T> beanMapper2 = new BeanMapper<>(cls);
|
|
concurrentMap.put(cls, beanMapper2);
|
|
return beanMapper2;
|
|
}
|
|
|
|
private static Map<String, Object> expectMap(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Map) {
|
|
return (Map) obj;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Expected a Map while deserializing, but got a ");
|
|
sb.append(obj.getClass());
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static Integer convertInteger(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Integer) {
|
|
return (Integer) obj;
|
|
}
|
|
if ((obj instanceof Long) || (obj instanceof Double)) {
|
|
Number number = (Number) obj;
|
|
double doubleValue = number.doubleValue();
|
|
if (doubleValue >= -2.147483648E9d && doubleValue <= 2.147483647E9d) {
|
|
return Integer.valueOf(number.intValue());
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Numeric value out of 32-bit integer range: ");
|
|
sb.append(doubleValue);
|
|
sb.append(". Did you mean to use a long or double instead of an int?");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
ErrorPath errorPath2 = deserializeContext.errorPath;
|
|
StringBuilder sb2 = new StringBuilder("Failed to convert a value of type ");
|
|
sb2.append(obj.getClass().getName());
|
|
sb2.append(" to int");
|
|
throw deserializeError(errorPath2, sb2.toString());
|
|
}
|
|
|
|
private static Long convertLong(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Integer) {
|
|
return Long.valueOf(((Integer) obj).longValue());
|
|
}
|
|
if (obj instanceof Long) {
|
|
return (Long) obj;
|
|
}
|
|
if (obj instanceof Double) {
|
|
Double d = (Double) obj;
|
|
if (d.doubleValue() >= -9.223372036854776E18d && d.doubleValue() <= 9.223372036854776E18d) {
|
|
return Long.valueOf(d.longValue());
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Numeric value out of 64-bit long range: ");
|
|
sb.append(d);
|
|
sb.append(". Did you mean to use a double instead of a long?");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
ErrorPath errorPath2 = deserializeContext.errorPath;
|
|
StringBuilder sb2 = new StringBuilder("Failed to convert a value of type ");
|
|
sb2.append(obj.getClass().getName());
|
|
sb2.append(" to long");
|
|
throw deserializeError(errorPath2, sb2.toString());
|
|
}
|
|
|
|
private static Double convertDouble(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Integer) {
|
|
return Double.valueOf(((Integer) obj).doubleValue());
|
|
}
|
|
if (obj instanceof Long) {
|
|
Long l = (Long) obj;
|
|
Double valueOf = Double.valueOf(l.doubleValue());
|
|
if (valueOf.longValue() == l.longValue()) {
|
|
return valueOf;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Loss of precision while converting number to double: ");
|
|
sb.append(obj);
|
|
sb.append(". Did you mean to use a 64-bit long instead?");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
if (obj instanceof Double) {
|
|
return (Double) obj;
|
|
}
|
|
ErrorPath errorPath2 = deserializeContext.errorPath;
|
|
StringBuilder sb2 = new StringBuilder("Failed to convert a value of type ");
|
|
sb2.append(obj.getClass().getName());
|
|
sb2.append(" to double");
|
|
throw deserializeError(errorPath2, sb2.toString());
|
|
}
|
|
|
|
private static Boolean convertBoolean(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Boolean) {
|
|
return (Boolean) obj;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to boolean");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static String convertString(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof String) {
|
|
return (String) obj;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to String");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static Date convertDate(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Date) {
|
|
return (Date) obj;
|
|
}
|
|
if (obj instanceof Timestamp) {
|
|
return ((Timestamp) obj).toDate();
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to Date");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static Timestamp convertTimestamp(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Timestamp) {
|
|
return (Timestamp) obj;
|
|
}
|
|
if (obj instanceof Date) {
|
|
return new Timestamp((Date) obj);
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to Timestamp");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static Blob convertBlob(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof Blob) {
|
|
return (Blob) obj;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to Blob");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static GeoPoint convertGeoPoint(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof GeoPoint) {
|
|
return (GeoPoint) obj;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to GeoPoint");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static DocumentReference convertDocumentReference(Object obj, DeserializeContext deserializeContext) {
|
|
if (obj instanceof DocumentReference) {
|
|
return (DocumentReference) obj;
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Failed to convert value of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to DocumentReference");
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static <T> T convertBean(Object obj, Class<T> cls, DeserializeContext deserializeContext) {
|
|
BeanMapper loadOrCreateBeanMapperForClass = loadOrCreateBeanMapperForClass(cls);
|
|
if (obj instanceof Map) {
|
|
return (T) loadOrCreateBeanMapperForClass.deserialize(expectMap(obj, deserializeContext), deserializeContext);
|
|
}
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Can't convert object of type ");
|
|
sb.append(obj.getClass().getName());
|
|
sb.append(" to type ");
|
|
sb.append(cls.getName());
|
|
throw deserializeError(errorPath, sb.toString());
|
|
}
|
|
|
|
private static IllegalArgumentException serializeError(ErrorPath errorPath, String str) {
|
|
String concat = "Could not serialize object. ".concat(String.valueOf(str));
|
|
if (errorPath.getLength() > 0) {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(concat);
|
|
sb.append(" (found in field '");
|
|
sb.append(errorPath.toString());
|
|
sb.append("')");
|
|
concat = sb.toString();
|
|
}
|
|
return new IllegalArgumentException(concat);
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: private */
|
|
public static RuntimeException deserializeError(ErrorPath errorPath, String str) {
|
|
String concat = "Could not deserialize object. ".concat(String.valueOf(str));
|
|
if (errorPath.getLength() > 0) {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(concat);
|
|
sb.append(" (found in field '");
|
|
sb.append(errorPath.toString());
|
|
sb.append("')");
|
|
concat = sb.toString();
|
|
}
|
|
return new RuntimeException(concat);
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: package-private */
|
|
/* loaded from: classes2.dex */
|
|
public static class BeanMapper<T> {
|
|
private final Class<T> clazz;
|
|
private final Constructor<T> constructor;
|
|
private final boolean throwOnUnknownProperties;
|
|
private final boolean warnOnUnknownProperties;
|
|
private final Map<String, String> properties = new HashMap();
|
|
private final Map<String, Method> setters = new HashMap();
|
|
private final Map<String, Method> getters = new HashMap();
|
|
private final Map<String, Field> fields = new HashMap();
|
|
private final HashSet<String> serverTimestamps = new HashSet<>();
|
|
private final HashSet<String> documentIdPropertyNames = new HashSet<>();
|
|
|
|
BeanMapper(Class<T> cls) {
|
|
Constructor<T> constructor;
|
|
this.clazz = cls;
|
|
this.throwOnUnknownProperties = cls.isAnnotationPresent(ThrowOnExtraProperties.class);
|
|
this.warnOnUnknownProperties = !cls.isAnnotationPresent(IgnoreExtraProperties.class);
|
|
try {
|
|
constructor = cls.getDeclaredConstructor(new Class[0]);
|
|
constructor.setAccessible(true);
|
|
} catch (NoSuchMethodException unused) {
|
|
constructor = null;
|
|
}
|
|
this.constructor = constructor;
|
|
for (Method method : cls.getMethods()) {
|
|
if (shouldIncludeGetter(method)) {
|
|
String propertyName = propertyName(method);
|
|
addProperty(propertyName);
|
|
method.setAccessible(true);
|
|
if (this.getters.containsKey(propertyName)) {
|
|
StringBuilder sb = new StringBuilder("Found conflicting getters for name ");
|
|
sb.append(method.getName());
|
|
sb.append(" on class ");
|
|
sb.append(cls.getName());
|
|
throw new RuntimeException(sb.toString());
|
|
}
|
|
this.getters.put(propertyName, method);
|
|
applyGetterAnnotations(method);
|
|
}
|
|
}
|
|
for (Field field : cls.getFields()) {
|
|
if (shouldIncludeField(field)) {
|
|
addProperty(propertyName(field));
|
|
applyFieldAnnotations(field);
|
|
}
|
|
}
|
|
Class<T> cls2 = cls;
|
|
do {
|
|
for (Method method2 : cls2.getDeclaredMethods()) {
|
|
if (shouldIncludeSetter(method2)) {
|
|
String propertyName2 = propertyName(method2);
|
|
String str = this.properties.get(propertyName2.toLowerCase(Locale.US));
|
|
if (str == null) {
|
|
continue;
|
|
} else {
|
|
if (!str.equals(propertyName2)) {
|
|
StringBuilder sb2 = new StringBuilder("Found setter on ");
|
|
sb2.append(cls2.getName());
|
|
sb2.append(" with invalid case-sensitive name: ");
|
|
sb2.append(method2.getName());
|
|
throw new RuntimeException(sb2.toString());
|
|
}
|
|
Method method3 = this.setters.get(propertyName2);
|
|
if (method3 == null) {
|
|
method2.setAccessible(true);
|
|
this.setters.put(propertyName2, method2);
|
|
applySetterAnnotations(method2);
|
|
} else if (!isSetterOverride(method2, method3)) {
|
|
if (cls2 == cls) {
|
|
StringBuilder sb3 = new StringBuilder("Class ");
|
|
sb3.append(cls.getName());
|
|
sb3.append(" has multiple setter overloads with name ");
|
|
sb3.append(method2.getName());
|
|
throw new RuntimeException(sb3.toString());
|
|
}
|
|
StringBuilder sb4 = new StringBuilder("Found conflicting setters with name: ");
|
|
sb4.append(method2.getName());
|
|
sb4.append(" (conflicts with ");
|
|
sb4.append(method3.getName());
|
|
sb4.append(" defined on ");
|
|
sb4.append(method3.getDeclaringClass().getName());
|
|
sb4.append(")");
|
|
throw new RuntimeException(sb4.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (Field field2 : cls2.getDeclaredFields()) {
|
|
String propertyName3 = propertyName(field2);
|
|
if (this.properties.containsKey(propertyName3.toLowerCase(Locale.US)) && !this.fields.containsKey(propertyName3)) {
|
|
field2.setAccessible(true);
|
|
this.fields.put(propertyName3, field2);
|
|
applyFieldAnnotations(field2);
|
|
}
|
|
}
|
|
cls2 = cls2.getSuperclass();
|
|
if (cls2 == null) {
|
|
break;
|
|
}
|
|
} while (!cls2.equals(Object.class));
|
|
if (this.properties.isEmpty()) {
|
|
StringBuilder sb5 = new StringBuilder("No properties to serialize found on class ");
|
|
sb5.append(cls.getName());
|
|
throw new RuntimeException(sb5.toString());
|
|
}
|
|
Iterator<String> it = this.documentIdPropertyNames.iterator();
|
|
while (it.hasNext()) {
|
|
String next = it.next();
|
|
if (!this.setters.containsKey(next) && !this.fields.containsKey(next)) {
|
|
StringBuilder sb6 = new StringBuilder("@DocumentId is annotated on property ");
|
|
sb6.append(next);
|
|
sb6.append(" of class ");
|
|
sb6.append(cls.getName());
|
|
sb6.append(" but no field or public setter was found");
|
|
throw new RuntimeException(sb6.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addProperty(String str) {
|
|
String put = this.properties.put(str.toLowerCase(Locale.US), str);
|
|
if (put == null || str.equals(put)) {
|
|
return;
|
|
}
|
|
StringBuilder sb = new StringBuilder("Found two getters or fields with conflicting case sensitivity for property: ");
|
|
sb.append(str.toLowerCase(Locale.US));
|
|
throw new RuntimeException(sb.toString());
|
|
}
|
|
|
|
T deserialize(Map<String, Object> map, DeserializeContext deserializeContext) {
|
|
return deserialize(map, Collections.emptyMap(), deserializeContext);
|
|
}
|
|
|
|
T deserialize(Map<String, Object> map, Map<TypeVariable<Class<T>>, Type> map2, DeserializeContext deserializeContext) {
|
|
Constructor<T> constructor = this.constructor;
|
|
if (constructor == null) {
|
|
ErrorPath errorPath = deserializeContext.errorPath;
|
|
StringBuilder sb = new StringBuilder("Class ");
|
|
sb.append(this.clazz.getName());
|
|
sb.append(" does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped");
|
|
throw CustomClassMapper.deserializeError(errorPath, sb.toString());
|
|
}
|
|
T t = (T) ApiUtil.newInstance(constructor);
|
|
HashSet<String> hashSet = new HashSet<>();
|
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
String key = entry.getKey();
|
|
ErrorPath child = deserializeContext.errorPath.child(key);
|
|
if (this.setters.containsKey(key)) {
|
|
Method method = this.setters.get(key);
|
|
Type[] genericParameterTypes = method.getGenericParameterTypes();
|
|
if (genericParameterTypes.length != 1) {
|
|
throw CustomClassMapper.deserializeError(child, "Setter does not have exactly one parameter");
|
|
}
|
|
ApiUtil.invoke(method, t, CustomClassMapper.deserializeToType(entry.getValue(), resolveType(genericParameterTypes[0], map2), deserializeContext.newInstanceWithErrorPath(child)));
|
|
hashSet.add(key);
|
|
} else if (this.fields.containsKey(key)) {
|
|
Field field = this.fields.get(key);
|
|
try {
|
|
field.set(t, CustomClassMapper.deserializeToType(entry.getValue(), resolveType(field.getGenericType(), map2), deserializeContext.newInstanceWithErrorPath(child)));
|
|
hashSet.add(key);
|
|
} catch (IllegalAccessException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
} else {
|
|
StringBuilder sb2 = new StringBuilder("No setter/field for ");
|
|
sb2.append(key);
|
|
sb2.append(" found on class ");
|
|
sb2.append(this.clazz.getName());
|
|
String obj = sb2.toString();
|
|
if (this.properties.containsKey(key.toLowerCase(Locale.US))) {
|
|
StringBuilder sb3 = new StringBuilder();
|
|
sb3.append(obj);
|
|
sb3.append(" (fields/setters are case sensitive!)");
|
|
obj = sb3.toString();
|
|
}
|
|
if (this.throwOnUnknownProperties) {
|
|
throw new RuntimeException(obj);
|
|
}
|
|
if (this.warnOnUnknownProperties) {
|
|
Logger.warn("CustomClassMapper", "%s", obj);
|
|
}
|
|
}
|
|
}
|
|
populateDocumentIdProperties(map2, deserializeContext, t, hashSet);
|
|
return t;
|
|
}
|
|
|
|
private void populateDocumentIdProperties(Map<TypeVariable<Class<T>>, Type> map, DeserializeContext deserializeContext, T t, HashSet<String> hashSet) {
|
|
Iterator<String> it = this.documentIdPropertyNames.iterator();
|
|
while (it.hasNext()) {
|
|
String next = it.next();
|
|
if (hashSet.contains(next)) {
|
|
StringBuilder sb = new StringBuilder("'");
|
|
sb.append(next);
|
|
sb.append("' was found from document ");
|
|
sb.append(deserializeContext.documentRef.getPath());
|
|
sb.append(", cannot apply @DocumentId on this property for class ");
|
|
sb.append(this.clazz.getName());
|
|
throw new RuntimeException(sb.toString());
|
|
}
|
|
ErrorPath child = deserializeContext.errorPath.child(next);
|
|
if (this.setters.containsKey(next)) {
|
|
Method method = this.setters.get(next);
|
|
Type[] genericParameterTypes = method.getGenericParameterTypes();
|
|
if (genericParameterTypes.length != 1) {
|
|
throw CustomClassMapper.deserializeError(child, "Setter does not have exactly one parameter");
|
|
}
|
|
if (resolveType(genericParameterTypes[0], map) == String.class) {
|
|
ApiUtil.invoke(method, t, deserializeContext.documentRef.getId());
|
|
} else {
|
|
ApiUtil.invoke(method, t, deserializeContext.documentRef);
|
|
}
|
|
} else {
|
|
Field field = this.fields.get(next);
|
|
try {
|
|
if (field.getType() == String.class) {
|
|
field.set(t, deserializeContext.documentRef.getId());
|
|
} else {
|
|
field.set(t, deserializeContext.documentRef);
|
|
}
|
|
} catch (IllegalAccessException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private Type resolveType(Type type, Map<TypeVariable<Class<T>>, Type> map) {
|
|
if (!(type instanceof TypeVariable)) {
|
|
return type;
|
|
}
|
|
Type type2 = map.get(type);
|
|
if (type2 != null) {
|
|
return type2;
|
|
}
|
|
throw new IllegalStateException("Could not resolve type ".concat(String.valueOf(type)));
|
|
}
|
|
|
|
Map<String, Object> serialize(T t, ErrorPath errorPath) {
|
|
Object obj;
|
|
Object serialize;
|
|
if (!this.clazz.isAssignableFrom(t.getClass())) {
|
|
StringBuilder sb = new StringBuilder("Can't serialize object of class ");
|
|
sb.append(t.getClass());
|
|
sb.append(" with BeanMapper for class ");
|
|
sb.append(this.clazz);
|
|
throw new IllegalArgumentException(sb.toString());
|
|
}
|
|
HashMap hashMap = new HashMap();
|
|
for (String str : this.properties.values()) {
|
|
if (!this.documentIdPropertyNames.contains(str)) {
|
|
if (this.getters.containsKey(str)) {
|
|
obj = ApiUtil.invoke(this.getters.get(str), t, new Object[0]);
|
|
} else {
|
|
Field field = this.fields.get(str);
|
|
if (field == null) {
|
|
throw new IllegalStateException("Bean property without field or getter: ".concat(String.valueOf(str)));
|
|
}
|
|
try {
|
|
obj = field.get(t);
|
|
} catch (IllegalAccessException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
if (!this.serverTimestamps.contains(str) || obj != null) {
|
|
serialize = CustomClassMapper.serialize(obj, errorPath.child(str));
|
|
} else {
|
|
serialize = FieldValue.serverTimestamp();
|
|
}
|
|
hashMap.put(str, serialize);
|
|
}
|
|
}
|
|
return hashMap;
|
|
}
|
|
|
|
private void applyFieldAnnotations(Field field) {
|
|
if (field.isAnnotationPresent(ServerTimestamp.class)) {
|
|
Class<?> type = field.getType();
|
|
if (type != Date.class && type != Timestamp.class) {
|
|
StringBuilder sb = new StringBuilder("Field ");
|
|
sb.append(field.getName());
|
|
sb.append(" is annotated with @ServerTimestamp but is ");
|
|
sb.append(type);
|
|
sb.append(" instead of Date or Timestamp.");
|
|
throw new IllegalArgumentException(sb.toString());
|
|
}
|
|
this.serverTimestamps.add(propertyName(field));
|
|
}
|
|
if (field.isAnnotationPresent(DocumentId.class)) {
|
|
ensureValidDocumentIdType("Field", "is", field.getType());
|
|
this.documentIdPropertyNames.add(propertyName(field));
|
|
}
|
|
}
|
|
|
|
private void applyGetterAnnotations(Method method) {
|
|
if (method.isAnnotationPresent(ServerTimestamp.class)) {
|
|
Class<?> returnType = method.getReturnType();
|
|
if (returnType != Date.class && returnType != Timestamp.class) {
|
|
StringBuilder sb = new StringBuilder("Method ");
|
|
sb.append(method.getName());
|
|
sb.append(" is annotated with @ServerTimestamp but returns ");
|
|
sb.append(returnType);
|
|
sb.append(" instead of Date or Timestamp.");
|
|
throw new IllegalArgumentException(sb.toString());
|
|
}
|
|
this.serverTimestamps.add(propertyName(method));
|
|
}
|
|
if (method.isAnnotationPresent(DocumentId.class)) {
|
|
ensureValidDocumentIdType("Method", "returns", method.getReturnType());
|
|
this.documentIdPropertyNames.add(propertyName(method));
|
|
}
|
|
}
|
|
|
|
private void applySetterAnnotations(Method method) {
|
|
if (method.isAnnotationPresent(ServerTimestamp.class)) {
|
|
StringBuilder sb = new StringBuilder("Method ");
|
|
sb.append(method.getName());
|
|
sb.append(" is annotated with @ServerTimestamp but should not be. @ServerTimestamp can only be applied to fields and getters, not setters.");
|
|
throw new IllegalArgumentException(sb.toString());
|
|
}
|
|
if (method.isAnnotationPresent(DocumentId.class)) {
|
|
ensureValidDocumentIdType("Method", "accepts", method.getParameterTypes()[0]);
|
|
this.documentIdPropertyNames.add(propertyName(method));
|
|
}
|
|
}
|
|
|
|
private void ensureValidDocumentIdType(String str, String str2, Type type) {
|
|
if (type == String.class || type == DocumentReference.class) {
|
|
return;
|
|
}
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(str);
|
|
sb.append(" is annotated with @DocumentId but ");
|
|
sb.append(str2);
|
|
sb.append(" ");
|
|
sb.append(type);
|
|
sb.append(" instead of String or DocumentReference.");
|
|
throw new IllegalArgumentException(sb.toString());
|
|
}
|
|
|
|
private static boolean shouldIncludeGetter(Method method) {
|
|
return ((!method.getName().startsWith("get") && !method.getName().startsWith("is")) || method.getDeclaringClass().equals(Object.class) || !Modifier.isPublic(method.getModifiers()) || Modifier.isStatic(method.getModifiers()) || method.getReturnType().equals(Void.TYPE) || method.getParameterTypes().length != 0 || method.isAnnotationPresent(Exclude.class)) ? false : true;
|
|
}
|
|
|
|
private static boolean shouldIncludeSetter(Method method) {
|
|
return method.getName().startsWith("set") && !method.getDeclaringClass().equals(Object.class) && !Modifier.isStatic(method.getModifiers()) && method.getReturnType().equals(Void.TYPE) && method.getParameterTypes().length == 1 && !method.isAnnotationPresent(Exclude.class);
|
|
}
|
|
|
|
private static boolean shouldIncludeField(Field field) {
|
|
return (field.getDeclaringClass().equals(Object.class) || !Modifier.isPublic(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) || field.isAnnotationPresent(Exclude.class)) ? false : true;
|
|
}
|
|
|
|
private static boolean isSetterOverride(Method method, Method method2) {
|
|
CustomClassMapper.hardAssert(method.getDeclaringClass().isAssignableFrom(method2.getDeclaringClass()), "Expected override from a base class");
|
|
CustomClassMapper.hardAssert(method.getReturnType().equals(Void.TYPE), "Expected void return type");
|
|
CustomClassMapper.hardAssert(method2.getReturnType().equals(Void.TYPE), "Expected void return type");
|
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
|
Class<?>[] parameterTypes2 = method2.getParameterTypes();
|
|
CustomClassMapper.hardAssert(parameterTypes.length == 1, "Expected exactly one parameter");
|
|
CustomClassMapper.hardAssert(parameterTypes2.length == 1, "Expected exactly one parameter");
|
|
return method.getName().equals(method2.getName()) && parameterTypes[0].equals(parameterTypes2[0]);
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: private */
|
|
public static String propertyName(Field field) {
|
|
String annotatedName = annotatedName(field);
|
|
return annotatedName != null ? annotatedName : field.getName();
|
|
}
|
|
|
|
private static String propertyName(Method method) {
|
|
String annotatedName = annotatedName(method);
|
|
return annotatedName != null ? annotatedName : serializedName(method.getName());
|
|
}
|
|
|
|
private static String annotatedName(AccessibleObject accessibleObject) {
|
|
if (accessibleObject.isAnnotationPresent(PropertyName.class)) {
|
|
return ((PropertyName) accessibleObject.getAnnotation(PropertyName.class)).value();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static String serializedName(String str) {
|
|
String[] strArr = {"get", "set", "is"};
|
|
String str2 = null;
|
|
for (int i = 0; i < 3; i++) {
|
|
String str3 = strArr[i];
|
|
if (str.startsWith(str3)) {
|
|
str2 = str3;
|
|
}
|
|
}
|
|
if (str2 == null) {
|
|
throw new IllegalArgumentException("Unknown Bean prefix for method: ".concat(String.valueOf(str)));
|
|
}
|
|
char[] charArray = str.substring(str2.length()).toCharArray();
|
|
for (int i2 = 0; i2 < charArray.length && Character.isUpperCase(charArray[i2]); i2++) {
|
|
charArray[i2] = Character.toLowerCase(charArray[i2]);
|
|
}
|
|
return new String(charArray);
|
|
}
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: package-private */
|
|
/* loaded from: classes2.dex */
|
|
public static class ErrorPath {
|
|
static final ErrorPath EMPTY = new ErrorPath(null, null, 0);
|
|
private final int length;
|
|
private final String name;
|
|
private final ErrorPath parent;
|
|
|
|
ErrorPath(ErrorPath errorPath, String str, int i) {
|
|
this.parent = errorPath;
|
|
this.name = str;
|
|
this.length = i;
|
|
}
|
|
|
|
ErrorPath child(String str) {
|
|
return new ErrorPath(this, str, this.length + 1);
|
|
}
|
|
|
|
public String toString() {
|
|
int i = this.length;
|
|
if (i == 0) {
|
|
return "";
|
|
}
|
|
if (i == 1) {
|
|
return this.name;
|
|
}
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(this.parent.toString());
|
|
sb.append(".");
|
|
sb.append(this.name);
|
|
return sb.toString();
|
|
}
|
|
|
|
int getLength() {
|
|
return this.length;
|
|
}
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: package-private */
|
|
/* loaded from: classes2.dex */
|
|
public static class DeserializeContext {
|
|
final DocumentReference documentRef;
|
|
final ErrorPath errorPath;
|
|
|
|
DeserializeContext(ErrorPath errorPath, DocumentReference documentReference) {
|
|
this.errorPath = errorPath;
|
|
this.documentRef = documentReference;
|
|
}
|
|
|
|
DeserializeContext newInstanceWithErrorPath(ErrorPath errorPath) {
|
|
return new DeserializeContext(errorPath, this.documentRef);
|
|
}
|
|
}
|
|
}
|