/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.locks.*;
import org.redkale.annotation.ConstructorParameters;
import org.redkale.annotation.*;
import org.redkale.convert.ext.StringSimpledCoder;
import org.redkale.util.*;
/**
* 自定义对象的序列化操作类
*
*
详情见: https://redkale.org
*
* @author zhangjx
* @param Writer输出的子类
* @param 序列化的数据类型
*/
@SuppressWarnings("unchecked")
public class ObjectEncoder implements Encodeable {
protected final Type type;
protected final Class typeClass;
protected EnMember[] members;
@Nullable
protected ConvertFactory factory;
protected volatile boolean inited = false;
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
protected ObjectEncoder(Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.typeClass = (Class) pt.getRawType();
} else if (type instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) type;
Type[] ts = tv.getBounds();
if (ts.length == 1 && ts[0] instanceof Class) {
this.typeClass = (Class) ts[0];
} else {
throw new ConvertException("[" + type + "] is no a class or ParameterizedType");
}
} else {
this.typeClass = (Class) type;
}
this.members = new EnMember[0];
}
public void init(final ConvertFactory factory) {
this.factory = factory;
try {
if (type == Object.class) {
return;
}
// if (!(type instanceof Class)) throw new ConvertException("[" + type + "] is no a class");
final Class clazz = this.typeClass;
final Set list = new LinkedHashSet();
final boolean reversible = factory.isReversible();
Creator creator = null;
try {
creator = factory.loadCreator(this.typeClass);
} catch (RuntimeException e) {
if (reversible && !Modifier.isAbstract(this.typeClass.getModifiers())) {
throw e;
}
}
final String[] cps = creator == null ? null : ObjectEncoder.findConstructorProperties(creator);
try {
ConvertColumnEntry ref;
ConvertFactory colFactory;
RedkaleClassLoader.putReflectionPublicFields(clazz.getName());
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (factory.isConvertDisabled(field)) {
continue;
}
ref = factory.findRef(clazz, field);
if (ref != null && ref.ignore()) {
continue;
}
ConvertSmallString small = field.getAnnotation(ConvertSmallString.class);
colFactory = factory.columnFactory(
field.getGenericType(), field.getAnnotationsByType(ConvertCoder.class), true);
Encodeable fieldCoder;
if (small != null && field.getType() == String.class) {
fieldCoder = StringSimpledCoder.StandardStringSimpledCoder.instance;
} else {
fieldCoder = colFactory.findFieldCoder(clazz, field.getName());
}
if (fieldCoder == null) {
Type t = TypeToken.createClassType(
TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = colFactory.loadEncoder(t);
}
EnMember member = new EnMember(
createAttribute(colFactory, type, clazz, field, null, null),
fieldCoder,
field,
null,
ref == null ? null : ref.fieldFunc());
if (ref != null) {
member.index = ref.getIndex();
}
list.add(member);
}
RedkaleClassLoader.putReflectionPublicMethods(clazz.getName());
for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
if (Modifier.isAbstract(method.getModifiers())) {
continue;
}
if (method.isSynthetic()) {
continue;
}
if (method.getName().equals("getClass")) {
continue;
}
if (method.getReturnType() == void.class) {
continue;
}
if (method.getParameterCount() != 0) {
continue;
}
if (!(method.getName().startsWith("is") && method.getName().length() > 2)
&& !(method.getName().startsWith("get")
&& method.getName().length() > 3)
&& !Utility.isRecordGetter(clazz, method)) {
continue;
}
if (factory.isConvertDisabled(method)) {
continue;
}
String convertName = ConvertFactory.readGetSetFieldName(method);
if (reversible && (cps == null || !contains(cps, convertName))) {
boolean is = method.getName().startsWith("is");
try {
clazz.getMethod(
method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType());
} catch (Exception e) {
continue;
}
}
ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) {
continue;
}
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
if (small == null) {
try {
Field f = clazz.getDeclaredField(convertName);
if (f != null) {
small = f.getAnnotation(ConvertSmallString.class);
}
} catch (Exception e) {
// do nothing
}
}
Field maybeField = ConvertFactory.readGetSetField(method);
colFactory = factory.columnFactory(
method.getGenericReturnType(), method.getAnnotationsByType(ConvertCoder.class), true);
if (maybeField != null && colFactory == factory) {
colFactory = factory.columnFactory(
maybeField.getGenericType(), maybeField.getAnnotationsByType(ConvertCoder.class), true);
}
Encodeable fieldCoder;
if (small != null && method.getReturnType() == String.class) {
fieldCoder = StringSimpledCoder.StandardStringSimpledCoder.instance;
} else {
String fieldName = ConvertFactory.readGetSetFieldName(method);
fieldCoder = colFactory.findFieldCoder(clazz, fieldName);
}
if (fieldCoder == null) {
Type t = TypeToken.createClassType(
TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
fieldCoder = colFactory.loadEncoder(t);
}
EnMember member = new EnMember(
createAttribute(colFactory, type, clazz, null, method, null),
fieldCoder,
maybeField,
method,
ref == null ? null : ref.fieldFunc());
if (Utility.contains(list, m -> m.attribute.field().equals(member.attribute.field()))) {
continue;
}
if (ref != null) {
member.index = ref.getIndex();
}
list.add(member);
}
List sorts = new ArrayList<>(list);
if (cps != null) {
Set dissorts = new LinkedHashSet<>(list);
for (final String constructorField :
cps) { // reversible模式下需要确保DeMember与EnMember的个数和顺序保持一致,不然postition会不一致导致反序列化对应的字段顺序不同
if (Utility.contains(dissorts, m -> m.attribute.field().equals(constructorField))) {
continue;
}
// 不存在setter方法
try {
Field f = clazz.getDeclaredField(constructorField);
ConvertColumnEntry ref2 = factory.findRef(clazz, f);
// Type t = TypeToken.createClassType(f.getGenericType(), this.type);
try {
EnMember member = new EnMember(
createAttribute(factory, type, clazz, f, null, null),
null,
f,
null,
ref2 == null ? null : ref2.fieldFunc());
if (ref2 != null) {
member.index = ref2.getIndex();
}
dissorts.add(member); // 虚构
} catch (RuntimeException e) {
// do nothing
}
} catch (NoSuchFieldException nsfe) { // 不存在field, 可能存在getter方法
char[] fs = constructorField.toCharArray();
fs[0] = Character.toUpperCase(fs[0]);
String mn = new String(fs);
Method getter;
try {
getter = clazz.getMethod("get" + mn);
} catch (NoSuchMethodException ex) {
getter = clazz.getMethod("is" + mn);
}
ConvertColumnEntry ref2 = factory.findRef(clazz, getter);
// Type t =
// TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0],
// this.type), this.type);
try {
EnMember member = new EnMember(
createAttribute(factory, type, clazz, null, getter, null),
null,
null,
null,
ref2 == null ? null : ref2.fieldFunc());
if (ref2 != null) {
member.index = ref2.getIndex();
}
dissorts.add(member); // 虚构
} catch (RuntimeException e) {
// do nothing
}
}
}
if (dissorts.size() != list.size()) {
sorts = new ArrayList<>(dissorts);
}
}
Collections.sort(sorts, (a, b) -> a.compareTo(factory.isFieldSort(), b));
Set pos = new HashSet<>();
for (EnMember member : sorts) {
if (member.index > 0) {
pos.add(member.index);
}
}
int pidx = 0;
for (EnMember member : sorts) {
if (member.index > 0) {
member.position = member.index;
} else {
while (pos.contains(++pidx)) {
// do nothing
}
member.position = pidx;
}
initForEachEnMember(factory, member);
}
EnMember[] enMembers = list.toArray(new EnMember[list.size()]);
Arrays.sort(enMembers, (a, b) -> a.compareTo(factory.isFieldSort(), b));
this.initFieldMember(enMembers);
afterInitEnMember(factory);
} catch (Exception ex) {
throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex);
}
} finally {
inited = true;
lock.lock();
try {
condition.signalAll();
} finally {
lock.unlock();
}
}
}
protected void initFieldMember(EnMember[] enMembers) {
this.members = enMembers;
}
protected void checkInited() {
if (!this.inited) {
lock.lock();
try {
condition.await();
} catch (Exception e) {
// do nothing
} finally {
lock.unlock();
}
}
}
@Override
public void convertTo(W out, T value) {
this.checkInited();
if (value == null) {
out.writeObjectNull(null);
return;
}
if (value.getClass() != this.typeClass && !this.type.equals(out.specificObjectType())) {
final Class clz = value.getClass();
if (out.needWriteClassName()) {
out.writeClassName(factory.getEntityAlias(clz));
}
factory.loadEncoder(clz).convertTo(out, value);
return;
}
W objout = objectWriter(out, value);
objout.writeObjectB(value);
int maxPosition = 0;
for (EnMember member : members) {
maxPosition = member.getPosition();
objout.writeObjectField(member, value);
}
if (objout.objExtFunc != null) {
ConvertField[] extFields = objout.objExtFunc.apply(value);
if (extFields != null) {
Encodeable anyEncoder = factory.getAnyEncoder();
for (ConvertField en : extFields) {
if (en != null) {
maxPosition++;
objout.writeObjectField(
en.getName(),
en.getType(),
Math.max(en.getPosition(), maxPosition),
anyEncoder,
en.getValue());
}
}
}
}
objout.writeObjectE(value);
offerWriter(out, objout);
}
// ---------------------------------- 可定制方法 ----------------------------------
protected void initForEachEnMember(ConvertFactory factory, EnMember member) {
// do nothing
}
protected void afterInitEnMember(ConvertFactory factory) {
// do nothing
}
protected W objectWriter(W out, T value) {
return out;
}
protected void offerWriter(W parent, W out) {
// do nothing
}
// ---------------------------------------------------------------------------------
protected void setTag(EnMember member, int tag) {
member.tag = tag;
}
protected void setIndex(EnMember member, int index) {
member.index = index;
}
protected void setPosition(EnMember member, int position) {
member.position = position;
}
@Override
public Type getType() {
return this.type;
}
public Class getTypeClass() {
return this.typeClass;
}
public EnMember[] getMembers() {
return members;
}
@Override
public String toString() {
return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
}
static boolean contains(String[] values, String value) {
for (String str : values) {
if (str.equals(value)) {
return true;
}
}
return false;
}
static String[] findConstructorProperties(Creator creator) {
if (creator == null) {
return null;
}
try {
Method method = creator.getClass().getMethod("create", Object[].class);
ConstructorParameters cps = method.getAnnotation(ConstructorParameters.class);
String[] vals = null;
if (cps != null) {
vals = cps.value();
} else {
org.redkale.util.ConstructorParameters cps2 =
method.getAnnotation(org.redkale.util.ConstructorParameters.class);
if (cps2 != null) {
vals = cps2.value();
}
}
return vals;
} catch (Exception e) {
return null;
}
}
static Attribute createAttribute(
final ConvertFactory factory,
final Type realType,
Class clazz,
final Field field,
final Method getter,
final Method setter) {
String fieldalias;
if (field != null) { // public field
ConvertColumnEntry ref = factory.findRef(clazz, field);
fieldalias = ref == null || ref.name().isEmpty() ? field.getName() : ref.name();
} else if (getter != null) {
ConvertColumnEntry ref = factory.findRef(clazz, getter);
String mfieldname = ConvertFactory.readGetSetFieldName(getter);
if (ref == null) {
try {
ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname));
} catch (Exception e) {
// do nothing
}
}
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
} else { // setter != null
ConvertColumnEntry ref = factory.findRef(clazz, setter);
String mfieldname = ConvertFactory.readGetSetFieldName(setter);
if (ref == null) {
try {
ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname));
} catch (Exception e) {
// do nothing
}
}
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
}
Type subclass = realType;
if ((realType instanceof Class) && field != null && getter == null && setter == null) {
// 修复父类含public field,subclass不传父类会导致java.lang.NoSuchFieldError的bug
subclass = field.getDeclaringClass();
}
return Attribute.create(subclass, clazz, fieldalias, null, field, getter, setter, null);
}
}