com.jsoniter.spi.ClassDescriptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsoniter Show documentation
Show all versions of jsoniter Show documentation
jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go
package com.jsoniter.spi;
import java.lang.reflect.*;
import java.util.*;
public class ClassDescriptor {
public ClassInfo classInfo;
public Class clazz;
public Map lookup;
public ConstructorDescriptor ctor;
public List fields;
public List setters;
public List getters;
public List bindingTypeWrappers;
public List keyValueTypeWrappers;
public List unwrappers;
public boolean asExtraForUnknownProperties;
public Binding onMissingProperties;
public Binding onExtraProperties;
private ClassDescriptor() {
}
public static ClassDescriptor getDecodingClassDescriptor(ClassInfo classInfo, boolean includingPrivate) {
Class clazz = classInfo.clazz;
Map lookup = collectTypeVariableLookup(clazz);
ClassDescriptor desc = new ClassDescriptor();
desc.classInfo = classInfo;
desc.clazz = clazz;
desc.lookup = lookup;
desc.ctor = getCtor(clazz);
desc.fields = getFields(lookup, classInfo, includingPrivate);
desc.setters = getSetters(lookup, classInfo, includingPrivate);
desc.getters = new ArrayList();
desc.bindingTypeWrappers = new ArrayList();
desc.keyValueTypeWrappers = new ArrayList();
desc.unwrappers = new ArrayList();
for (Extension extension : JsoniterSpi.getExtensions()) {
extension.updateClassDescriptor(desc);
}
for (Binding field : desc.fields) {
if (field.valueType instanceof Class) {
Class valueClazz = (Class) field.valueType;
if (valueClazz.isArray()) {
field.valueCanReuse = false;
continue;
}
}
field.valueCanReuse = field.valueTypeLiteral.nativeType == null;
}
decodingDeduplicate(desc);
if (includingPrivate) {
if (desc.ctor.ctor != null) {
desc.ctor.ctor.setAccessible(true);
}
if (desc.ctor.staticFactory != null) {
desc.ctor.staticFactory.setAccessible(true);
}
for (WrapperDescriptor setter : desc.bindingTypeWrappers) {
setter.method.setAccessible(true);
}
}
for (Binding binding : desc.allDecoderBindings()) {
if (binding.fromNames == null) {
binding.fromNames = new String[]{binding.name};
}
if (binding.field != null && includingPrivate) {
binding.field.setAccessible(true);
}
if (binding.method != null && includingPrivate) {
binding.method.setAccessible(true);
}
if (binding.decoder != null) {
JsoniterSpi.addNewDecoder(binding.decoderCacheKey(), binding.decoder);
}
}
return desc;
}
public static ClassDescriptor getEncodingClassDescriptor(ClassInfo classInfo, boolean includingPrivate) {
Class clazz = classInfo.clazz;
Map lookup = collectTypeVariableLookup(clazz);
ClassDescriptor desc = new ClassDescriptor();
desc.classInfo = classInfo;
desc.clazz = clazz;
desc.lookup = lookup;
desc.fields = getFields(lookup, classInfo, includingPrivate);
desc.getters = getGetters(lookup, classInfo, includingPrivate);
desc.bindingTypeWrappers = new ArrayList();
desc.keyValueTypeWrappers = new ArrayList();
desc.unwrappers = new ArrayList();
for (Extension extension : JsoniterSpi.getExtensions()) {
extension.updateClassDescriptor(desc);
}
encodingDeduplicate(desc);
for (Binding binding : desc.allEncoderBindings()) {
if (binding.toNames == null) {
binding.toNames = new String[]{binding.name};
}
if (binding.field != null && includingPrivate) {
binding.field.setAccessible(true);
}
if (binding.method != null && includingPrivate) {
binding.method.setAccessible(true);
}
if (binding.encoder != null) {
JsoniterSpi.addNewEncoder(binding.encoderCacheKey(), binding.encoder);
}
}
return desc;
}
private static void decodingDeduplicate(ClassDescriptor desc) {
HashMap byName = new HashMap();
for (Binding field : desc.fields) {
for (String fromName : field.fromNames) {
if (byName.containsKey(fromName)) {
throw new JsonException("field decode from same name: " + fromName);
}
byName.put(fromName, field);
}
}
ArrayList iteratingSetters = new ArrayList(desc.setters);
Collections.reverse(iteratingSetters);
for (Binding setter : iteratingSetters) {
for (String fromName : setter.fromNames) {
Binding existing = byName.get(fromName);
if (existing == null) {
byName.put(fromName, setter);
continue;
}
if (desc.fields.remove(existing)) {
continue;
}
if (existing.method != null && existing.method.getName().equals(setter.method.getName())) {
// inherited interface setter
// iterate in reverse order, so that the setter from child class will be kept
desc.setters.remove(existing);
continue;
}
throw new JsonException("setter decode from same name: " + fromName);
}
}
for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) {
for (Binding param : wrapper.parameters) {
for (String fromName : param.fromNames) {
Binding existing = byName.get(fromName);
if (existing == null) {
byName.put(fromName, param);
continue;
}
if (desc.fields.remove(existing)) {
continue;
}
if (desc.setters.remove(existing)) {
continue;
}
throw new JsonException("wrapper parameter decode from same name: " + fromName);
}
}
}
for (Binding param : desc.ctor.parameters) {
for (String fromName : param.fromNames) {
Binding existing = byName.get(fromName);
if (existing == null) {
byName.put(fromName, param);
continue;
}
if (desc.fields.remove(existing)) {
continue;
}
if (desc.setters.remove(existing)) {
continue;
}
throw new JsonException("ctor parameter decode from same name: " + fromName);
}
}
}
private static void encodingDeduplicate(ClassDescriptor desc) {
HashMap byName = new HashMap();
for (Binding field : desc.fields) {
for (String toName : field.toNames) {
if (byName.containsKey(toName)) {
throw new JsonException("field encode to same name: " + toName);
}
byName.put(toName, field);
}
}
for (Binding getter : new ArrayList(desc.getters)) {
for (String toName : getter.toNames) {
Binding existing = byName.get(toName);
if (existing == null) {
byName.put(toName, getter);
continue;
}
if (desc.fields.remove(existing)) {
continue;
}
if (existing.method != null && existing.method.getName().equals(getter.method.getName())) {
// inherited interface getter
desc.getters.remove(getter);
continue;
}
throw new JsonException("field encode to same name: " + toName);
}
}
}
private static ConstructorDescriptor getCtor(Class clazz) {
ConstructorDescriptor cctor = new ConstructorDescriptor();
if (JsoniterSpi.canCreate(clazz)) {
cctor.objectFactory = JsoniterSpi.getObjectFactory(clazz);
return cctor;
}
try {
cctor.ctor = clazz.getDeclaredConstructor();
} catch (Exception e) {
cctor.ctor = null;
}
return cctor;
}
private static List getFields(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList bindings = new ArrayList();
for (Field field : getAllFields(classInfo.clazz, includingPrivate)) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (Modifier.isTransient(field.getModifiers())) {
continue;
}
if (!includingPrivate && !Modifier.isPublic(field.getType().getModifiers())) {
continue;
}
if (includingPrivate) {
field.setAccessible(true);
}
Binding binding = createBindingFromField(lookup, classInfo, field);
bindings.add(binding);
}
return bindings;
}
private static Binding createBindingFromField(Map lookup, ClassInfo classInfo, Field field) {
try {
Binding binding = new Binding(classInfo, lookup, field.getGenericType());
binding.fromNames = new String[]{field.getName()};
binding.toNames = new String[]{field.getName()};
binding.name = field.getName();
binding.annotations = field.getAnnotations();
binding.field = field;
return binding;
} catch (Exception e) {
throw new JsonException("failed to create binding for field: " + field, e);
}
}
private static List getAllFields(Class clazz, boolean includingPrivate) {
List allFields = Arrays.asList(clazz.getFields());
if (includingPrivate) {
allFields = new ArrayList();
Class current = clazz;
while (current != null) {
allFields.addAll(Arrays.asList(current.getDeclaredFields()));
current = current.getSuperclass();
}
}
return allFields;
}
private static List getSetters(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList setters = new ArrayList();
for (Method method : getAllMethods(classInfo.clazz, includingPrivate)) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
String methodName = method.getName();
if (methodName.length() < 4) {
continue;
}
if (!methodName.startsWith("set")) {
continue;
}
Type[] paramTypes = method.getGenericParameterTypes();
if (paramTypes.length != 1) {
continue;
}
if (!includingPrivate && !Modifier.isPublic(method.getParameterTypes()[0].getModifiers())) {
continue;
}
if (includingPrivate) {
method.setAccessible(true);
}
try {
String fromName = translateSetterName(methodName);
Binding binding = new Binding(classInfo, lookup, paramTypes[0]);
binding.fromNames = new String[]{fromName};
binding.name = fromName;
binding.method = method;
binding.annotations = method.getAnnotations();
setters.add(binding);
} catch (Exception e) {
throw new JsonException("failed to create binding from setter: " + method, e);
}
}
return setters;
}
private static List getAllMethods(Class clazz, boolean includingPrivate) {
List allMethods = Arrays.asList(clazz.getMethods());
if (includingPrivate) {
allMethods = new ArrayList();
Class current = clazz;
while (current != null) {
allMethods.addAll(Arrays.asList(current.getDeclaredMethods()));
current = current.getSuperclass();
}
}
return allMethods;
}
private static String translateSetterName(String methodName) {
if (!methodName.startsWith("set")) {
return null;
}
String fromName = methodName.substring("set".length());
char[] fromNameChars = fromName.toCharArray();
fromNameChars[0] = Character.toLowerCase(fromNameChars[0]);
fromName = new String(fromNameChars);
return fromName;
}
private static List getGetters(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList getters = new ArrayList();
for (Method method : getAllMethods(classInfo.clazz, includingPrivate)) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
String methodName = method.getName();
if ("getClass".equals(methodName)) {
continue;
}
if (methodName.length() < 4) {
continue;
}
if (!methodName.startsWith("get")) {
continue;
}
if (method.getGenericParameterTypes().length != 0) {
continue;
}
String toName = methodName.substring("get".length());
char[] fromNameChars = toName.toCharArray();
fromNameChars[0] = Character.toLowerCase(fromNameChars[0]);
toName = new String(fromNameChars);
Binding getter = new Binding(classInfo, lookup, method.getGenericReturnType());
getter.toNames = new String[]{toName};
getter.name = toName;
getter.method = method;
getter.annotations = method.getAnnotations();
getters.add(getter);
}
return getters;
}
private static Map collectTypeVariableLookup(Type type) {
HashMap vars = new HashMap();
if (null == type) {
return vars;
}
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Type[] actualTypeArguments = pType.getActualTypeArguments();
Class clazz = (Class) pType.getRawType();
for (int i = 0; i < clazz.getTypeParameters().length; i++) {
TypeVariable variable = clazz.getTypeParameters()[i];
vars.put(variable.getName() + "@" + clazz.getCanonicalName(), actualTypeArguments[i]);
}
vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass()));
return vars;
}
if (type instanceof Class) {
Class clazz = (Class) type;
vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass()));
return vars;
}
throw new JsonException("unexpected type: " + type);
}
public List allBindings() {
ArrayList bindings = new ArrayList(8);
bindings.addAll(fields);
if (setters != null) {
bindings.addAll(setters);
}
if (getters != null) {
bindings.addAll(getters);
}
if (ctor != null) {
bindings.addAll(ctor.parameters);
}
if (bindingTypeWrappers != null) {
for (WrapperDescriptor setter : bindingTypeWrappers) {
bindings.addAll(setter.parameters);
}
}
return bindings;
}
public List allDecoderBindings() {
ArrayList bindings = new ArrayList(8);
bindings.addAll(fields);
bindings.addAll(setters);
if (ctor != null) {
bindings.addAll(ctor.parameters);
}
for (WrapperDescriptor setter : bindingTypeWrappers) {
bindings.addAll(setter.parameters);
}
return bindings;
}
public List allEncoderBindings() {
ArrayList bindings = new ArrayList(8);
bindings.addAll(fields);
bindings.addAll(getters);
return bindings;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy