diff --git a/com/wentch/redkale/convert/AnyEncoder.java b/com/wentch/redkale/convert/AnyEncoder.java new file mode 100644 index 000000000..0a8f253aa --- /dev/null +++ b/com/wentch/redkale/convert/AnyEncoder.java @@ -0,0 +1,40 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.Type; + +/** + * 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。 + * + * @author zhangjx + * @param + */ +public final class AnyEncoder implements Encodeable { + + final Factory factory; + + AnyEncoder(Factory factory) { + this.factory = factory; + } + + @Override + @SuppressWarnings("unchecked") + public void convertTo(final Writer out, final T value) { + if (value == null) { + out.writeNull(); + } else { + out.wirteClassName(factory.getEntity(value.getClass())); + factory.loadEncoder(value.getClass()).convertTo(out, value); + } + } + + @Override + public Type getType() { + return Object.class; + } + +} diff --git a/com/wentch/redkale/convert/ArrayDecoder.java b/com/wentch/redkale/convert/ArrayDecoder.java new file mode 100644 index 000000000..fa41b8b3a --- /dev/null +++ b/com/wentch/redkale/convert/ArrayDecoder.java @@ -0,0 +1,79 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.*; +import java.util.*; + +/** + * 对象数组的序列化,不包含int[]、long[]这样的primitive class数组. + * 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。 + * 支持一定程度的泛型。 + * + * @author zhangjx + * @param + */ +@SuppressWarnings("unchecked") +public final class ArrayDecoder implements Decodeable { + + private final Type type; + + private final Type componentType; + + private final Class componentClass; + + private final Decodeable decoder; + + public ArrayDecoder(final Factory factory, final Type type) { + this.type = type; + if (type instanceof GenericArrayType) { + Type t = ((GenericArrayType) type).getGenericComponentType(); + this.componentType = t instanceof TypeVariable ? Object.class : t; + } else if ((type instanceof Class) && ((Class) type).isArray()) { + this.componentType = ((Class) type).getComponentType(); + } else { + throw new ConvertException("(" + type + ") is not a array type"); + } + if (this.componentType instanceof ParameterizedType) { + this.componentClass = (Class) ((ParameterizedType) this.componentType).getRawType(); + } else { + this.componentClass = (Class) this.componentType; + } + factory.register(type, this); + this.decoder = factory.loadDecoder(this.componentType); + } + + @Override + public T[] convertFrom(Reader in) { + final int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) return null; + final Decodeable localdecoder = this.decoder; + final List result = new ArrayList(); + if (len == Reader.SIGN_NOLENGTH) { + while (in.hasNext()) { + result.add(localdecoder.convertFrom(in)); + } + } else { + for (int i = 0; i < len; i++) { + result.add(localdecoder.convertFrom(in)); + } + } + in.readArrayE(); + T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size()); + return result.toArray(rs); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.decoder + "}"; + } + + @Override + public Type getType() { + return type; + } + +} diff --git a/com/wentch/redkale/convert/ArrayEncoder.java b/com/wentch/redkale/convert/ArrayEncoder.java new file mode 100644 index 000000000..b75fe4be9 --- /dev/null +++ b/com/wentch/redkale/convert/ArrayEncoder.java @@ -0,0 +1,75 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.*; + +/** + * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组. + * 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。 + * 支持一定程度的泛型。 + * + * @author zhangjx + * @param + */ +@SuppressWarnings("unchecked") +public final class ArrayEncoder implements Encodeable { + + private final Type type; + + private final Type componentType; + + private final Encodeable anyEncoder; + + private final Encodeable encoder; + + public ArrayEncoder(final Factory factory, final Type type) { + this.type = type; + if (type instanceof GenericArrayType) { + Type t = ((GenericArrayType) type).getGenericComponentType(); + this.componentType = t instanceof TypeVariable ? Object.class : t; + } else if ((type instanceof Class) && ((Class) type).isArray()) { + this.componentType = ((Class) type).getComponentType(); + } else { + throw new ConvertException("(" + type + ") is not a array type"); + } + factory.register(type, this); + this.encoder = factory.loadEncoder(this.componentType); + this.anyEncoder = factory.getAnyEncoder(); + } + + @Override + public void convertTo(Writer out, T[] value) { + if (value == null) { + out.writeNull(); + return; + } + if (value.length == 0) { + out.writeArrayB(0); + out.writeArrayE(); + return; + } + out.writeArrayB(value.length); + final Type comp = this.componentType; + boolean first = true; + for (Object v : value) { + if (!first) out.writeArrayMark(); + ((v != null && v.getClass() == comp) ? encoder : anyEncoder).convertTo(out, v); + if (first) first = false; + } + out.writeArrayE(); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.encoder + "}"; + } + + @Override + public Type getType() { + return type; + } +} diff --git a/com/wentch/redkale/convert/CollectionDecoder.java b/com/wentch/redkale/convert/CollectionDecoder.java new file mode 100644 index 000000000..df87cfd99 --- /dev/null +++ b/com/wentch/redkale/convert/CollectionDecoder.java @@ -0,0 +1,69 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.util.Creator; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; + +/** + * 对象集合的反序列化. + * 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。 + * 支持一定程度的泛型。 + * + * @author zhangjx + * @param + */ +@SuppressWarnings("unchecked") +public final class CollectionDecoder implements Decodeable> { + + private final Type type; + + private final Type componentType; + + protected Creator> creator; + + private final Decodeable decoder; + + public CollectionDecoder(final Factory factory, final Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.componentType = pt.getActualTypeArguments()[0]; + this.creator = factory.loadCreator((Class) pt.getRawType()); + factory.register(type, this); + this.decoder = factory.loadDecoder(this.componentType); + } else { + throw new ConvertException("collectiondecoder not support the type (" + type + ")"); + } + } + + @Override + public Collection convertFrom(Reader in) { + final int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) return null; + final Decodeable localdecoder = this.decoder; + final Collection result = this.creator.create(); + if (len == Reader.SIGN_NOLENGTH) { + while (in.hasNext()) { + result.add(localdecoder.convertFrom(in)); + } + } else { + for (int i = 0; i < len; i++) { + result.add(localdecoder.convertFrom(in)); + } + } + in.readArrayE(); + return result; + } + + @Override + public Type getType() { + return type; + } + +} diff --git a/com/wentch/redkale/convert/CollectionEncoder.java b/com/wentch/redkale/convert/CollectionEncoder.java new file mode 100644 index 000000000..e823a5775 --- /dev/null +++ b/com/wentch/redkale/convert/CollectionEncoder.java @@ -0,0 +1,65 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.*; +import java.util.Collection; + +/** + * 对象集合的序列化. + * 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。 + * 支持一定程度的泛型。 + * + * @author zhangjx + * @param + */ +@SuppressWarnings("unchecked") +public final class CollectionEncoder implements Encodeable> { + + private final Type type; + + private final Encodeable encoder; + + public CollectionEncoder(final Factory factory, final Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + Type t = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (t instanceof TypeVariable) { + this.encoder = factory.getAnyEncoder(); + } else { + this.encoder = factory.loadEncoder(t); + } + } else { + this.encoder = factory.getAnyEncoder(); + } + } + + @Override + public void convertTo(Writer out, Collection value) { + if (value == null) { + out.writeNull(); + return; + } + if (value.isEmpty()) { + out.writeArrayB(0); + out.writeArrayE(); + return; + } + out.writeArrayB(value.size()); + boolean first = true; + for (Object v : value) { + if (!first) out.writeArrayMark(); + encoder.convertTo(out, v); + if (first) first = false; + } + out.writeArrayE(); + } + + @Override + public Type getType() { + return type; + } +} diff --git a/com/wentch/redkale/convert/Convert.java b/com/wentch/redkale/convert/Convert.java new file mode 100644 index 000000000..61b42ff63 --- /dev/null +++ b/com/wentch/redkale/convert/Convert.java @@ -0,0 +1,26 @@ +/* + * 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 com.wentch.redkale.convert; + +/** + * 序列化操作类 + * + * @author zhangjx + * @param + * @param + */ +public abstract class Convert { + + protected final Factory factory; + + protected Convert(Factory factory) { + this.factory = factory; + } + + public Factory getFactory() { + return this.factory; + } +} diff --git a/com/wentch/redkale/convert/ConvertColumn.java b/com/wentch/redkale/convert/ConvertColumn.java new file mode 100644 index 000000000..1a868f0c4 --- /dev/null +++ b/com/wentch/redkale/convert/ConvertColumn.java @@ -0,0 +1,44 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * 依附在setter、getter方法、字段进行简单的配置 + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +@Repeatable(ConvertColumns.class) +public @interface ConvertColumn { + + /** + * 给字段取个别名, 只对JSON有效 + * + * @return + */ + String name() default ""; + + /** + * 解析/序列化时是否屏蔽该字段 + * + * @return + */ + boolean ignore() default false; + + /** + * 解析/序列化定制化的TYPE + * + * @return + */ + ConvertType type() default ConvertType.ALL; +} diff --git a/com/wentch/redkale/convert/ConvertColumnEntry.java b/com/wentch/redkale/convert/ConvertColumnEntry.java new file mode 100644 index 000000000..c5339b17a --- /dev/null +++ b/com/wentch/redkale/convert/ConvertColumnEntry.java @@ -0,0 +1,67 @@ +/* + * 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 com.wentch.redkale.convert; + +/** + * ConvertColumn 对应的实体类 + * + * @author zhangjx + */ +public final class ConvertColumnEntry { + + private String name = ""; + + private boolean ignore; + + private ConvertType convertType; + + public ConvertColumnEntry() { + } + + public ConvertColumnEntry(ConvertColumn column) { + if (column == null) return; + this.name = column.name(); + this.ignore = column.ignore(); + this.convertType = column.type(); + } + + public ConvertColumnEntry(String name, boolean ignore) { + this.name = name; + this.ignore = ignore; + this.convertType = ConvertType.ALL; + } + + public ConvertColumnEntry(String name, boolean ignore, ConvertType convertType) { + this.name = name; + this.ignore = ignore; + this.convertType = convertType; + } + + public String name() { + return name == null ? "" : name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean ignore() { + return ignore; + } + + public void setIgnore(boolean ignore) { + this.ignore = ignore; + } + + public ConvertType type() { + return convertType == null ? ConvertType.ALL : convertType; + } + + public void setConvertType(ConvertType convertType) { + this.convertType = convertType; + } + +} diff --git a/com/wentch/redkale/convert/ConvertColumns.java b/com/wentch/redkale/convert/ConvertColumns.java new file mode 100644 index 000000000..a5e613ad0 --- /dev/null +++ b/com/wentch/redkale/convert/ConvertColumns.java @@ -0,0 +1,24 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * ConvertColumn 的多用类 + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +public @interface ConvertColumns { + + ConvertColumn[] value(); +} diff --git a/com/wentch/redkale/convert/ConvertEntity.java b/com/wentch/redkale/convert/ConvertEntity.java new file mode 100644 index 000000000..99bd4330c --- /dev/null +++ b/com/wentch/redkale/convert/ConvertEntity.java @@ -0,0 +1,25 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 用于类名的别名, 类似javax.persistence.Table + * 该值必须是全局唯一 + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ConvertEntity { + + String value(); +} diff --git a/com/wentch/redkale/convert/ConvertException.java b/com/wentch/redkale/convert/ConvertException.java new file mode 100644 index 000000000..338e1f44e --- /dev/null +++ b/com/wentch/redkale/convert/ConvertException.java @@ -0,0 +1,28 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.convert; + +/** + * + * @author zhangjx + */ +public class ConvertException extends RuntimeException { + + public ConvertException() { + super(); + } + + public ConvertException(String s) { + super(s); + } + + public ConvertException(String message, Throwable cause) { + super(message, cause); + } + + public ConvertException(Throwable cause) { + super(cause); + } +} diff --git a/com/wentch/redkale/convert/ConvertType.java b/com/wentch/redkale/convert/ConvertType.java new file mode 100644 index 000000000..7871a01d8 --- /dev/null +++ b/com/wentch/redkale/convert/ConvertType.java @@ -0,0 +1,28 @@ +/* + * 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 com.wentch.redkale.convert; + +/** + * + * @author zhangjx + */ +public enum ConvertType { + + JSON(1), + BSON(2), + ALL(127); + + private final int value; + + private ConvertType(int v) { + this.value = v; + } + + public boolean contains(ConvertType type) { + if (type == null) return false; + return this.value >= type.value && (this.value & type.value) > 0; + } +} diff --git a/com/wentch/redkale/convert/DeMember.java b/com/wentch/redkale/convert/DeMember.java new file mode 100644 index 000000000..2897e7d23 --- /dev/null +++ b/com/wentch/redkale/convert/DeMember.java @@ -0,0 +1,64 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.util.Attribute; + +/** + * + * @author zhangjx + * @param + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class DeMember implements Comparable> { + + protected final Attribute attribute; + + protected Decodeable decoder; + + public DeMember(final Attribute attribute) { + this.attribute = attribute; + } + + public DeMember(Attribute attribute, Decodeable decoder) { + this(attribute); + this.decoder = decoder; + } + + public final void read(R in, T obj) { + this.attribute.set(obj, decoder.convertFrom(in)); + } + + public Attribute getAttribute() { + return this.attribute; + } + + @Override + public final int compareTo(DeMember o) { + if (o == null) return 1; + return this.attribute.field().compareTo(o.attribute.field()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof DeMember)) return false; + DeMember other = (DeMember) obj; + return compareTo(other) == 0; + } + + @Override + public int hashCode() { + return this.attribute.field().hashCode(); + } + + @Override + public String toString() { + return "DeMember{" + "attribute=" + attribute.field() + ", decoder=" + decoder + '}'; + } +} diff --git a/com/wentch/redkale/convert/Decodeable.java b/com/wentch/redkale/convert/Decodeable.java new file mode 100644 index 000000000..c6b58899a --- /dev/null +++ b/com/wentch/redkale/convert/Decodeable.java @@ -0,0 +1,27 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.Type; + +/** + * + * @author zhangjx + * @param + * @param + */ +public interface Decodeable { + + public T convertFrom(final R in); + + /** + * 泛型映射接口 + * + * @return + */ + public Type getType(); + +} diff --git a/com/wentch/redkale/convert/EnMember.java b/com/wentch/redkale/convert/EnMember.java new file mode 100644 index 000000000..beb71576e --- /dev/null +++ b/com/wentch/redkale/convert/EnMember.java @@ -0,0 +1,76 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.util.Attribute; + +/** + * + * @author zhangjx + * @param + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class EnMember implements Comparable> { + + private final Attribute attribute; + + final Encodeable encoder; + + private final boolean istring; + + //private final boolean isnumber; + private final boolean isbool; + + public EnMember(Attribute attribute, Encodeable encoder) { + this.attribute = attribute; + this.encoder = encoder; + Class t = attribute.type(); + this.istring = CharSequence.class.isAssignableFrom(t); + this.isbool = t == Boolean.class || t == boolean.class; + //this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive()); + } + + public boolean write(final W out, final boolean comma, final T obj) { + F value = attribute.get(obj); + if (value == null) return comma; + if (out.isTiny()) { + if (istring) { + if (((CharSequence) value).length() == 0) return comma; + } else if (isbool) { + if (!((Boolean) value)) return comma; + } + } + out.writeField(comma, attribute); + encoder.convertTo(out, value); + return true; + } + + @Override + public final int compareTo(EnMember o) { + if (o == null) return 1; + return this.attribute.field().compareTo(o.attribute.field()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof EnMember)) return false; + EnMember other = (EnMember) obj; + return compareTo(other) == 0; + } + + @Override + public int hashCode() { + return this.attribute.field().hashCode(); + } + + @Override + public String toString() { + return "EnMember{" + "attribute=" + attribute.field() + ", encoder=" + encoder + '}'; + } +} diff --git a/com/wentch/redkale/convert/Encodeable.java b/com/wentch/redkale/convert/Encodeable.java new file mode 100644 index 000000000..595383445 --- /dev/null +++ b/com/wentch/redkale/convert/Encodeable.java @@ -0,0 +1,27 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.Type; + +/** + * + * @author zhangjx + * @param + * @param + */ +public interface Encodeable { + + public void convertTo(final W out, T value); + + /** + * 泛型映射接口 + * + * @return + */ + public Type getType(); + +} diff --git a/com/wentch/redkale/convert/Factory.java b/com/wentch/redkale/convert/Factory.java new file mode 100644 index 000000000..c27cdfc75 --- /dev/null +++ b/com/wentch/redkale/convert/Factory.java @@ -0,0 +1,386 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.convert.ext.*; +import com.wentch.redkale.util.*; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Map; +import java.lang.reflect.*; +import java.math.BigInteger; +import java.net.*; +import static com.wentch.redkale.convert.ext.InetAddressSimpledCoder.*; +import com.wentch.redkale.util.Creator.Creators; +import java.util.*; +import java.util.concurrent.*; +import java.util.regex.*; + +/** + * + * @author zhangjx + * @param + * @param + */ +@SuppressWarnings("unchecked") +public abstract class Factory { + + private final Factory parent; + + protected Convert convert; + + protected boolean tiny; + + private final Encodeable anyEncoder = new AnyEncoder(this); + + //----------------------------------------------------------------------------------- + private final HashedMap creators = new HashedMap(); + + private final Map entitys = new ConcurrentHashMap(); + + private final HashedMap> decoders = new HashedMap(); + + private final HashedMap> encoders = new HashedMap(); + + private final HashMap columnEntrys = new HashMap(); + + private final Set skipIgnores = new HashSet(); + + private boolean skipAllIgnore = false; + + protected Factory(Factory parent, boolean tiny) { + this.tiny = tiny; + this.parent = parent; + if (parent == null) { + //--------------------------------------------------------- + this.register(boolean.class, BoolSimpledCoder.instance); + this.register(Boolean.class, BoolSimpledCoder.instance); + + this.register(byte.class, ByteSimpledCoder.instance); + this.register(Byte.class, ByteSimpledCoder.instance); + + this.register(short.class, ShortSimpledCoder.instance); + this.register(Short.class, ShortSimpledCoder.instance); + + this.register(char.class, CharSimpledCoder.instance); + this.register(Character.class, CharSimpledCoder.instance); + + this.register(int.class, IntSimpledCoder.instance); + this.register(Integer.class, IntSimpledCoder.instance); + + this.register(long.class, LongSimpledCoder.instance); + this.register(Long.class, LongSimpledCoder.instance); + + this.register(float.class, FloatSimpledCoder.instance); + this.register(Float.class, FloatSimpledCoder.instance); + + this.register(double.class, DoubleSimpledCoder.instance); + this.register(Double.class, DoubleSimpledCoder.instance); + + this.register(Number.class, NumberSimpledCoder.instance); + this.register(String.class, StringSimpledCoder.instance); + this.register(java.util.Date.class, DateSimpledCoder.instance); + this.register(BigInteger.class, BigIntegerSimpledCoder.instance); + this.register(InetAddress.class, InetAddressSimpledCoder.instance); + this.register(DLong.class, DLongSimpledCoder.instance); + this.register(Class.class, TypeSimpledCoder.instance); + this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance); + this.register(Pattern.class, PatternSimpledCoder.instance); + //--------------------------------------------------------- + this.register(boolean[].class, BoolArraySimpledCoder.instance); + this.register(byte[].class, ByteArraySimpledCoder.instance); + this.register(short[].class, ShortArraySimpledCoder.instance); + this.register(char[].class, CharArraySimpledCoder.instance); + this.register(int[].class, IntArraySimpledCoder.instance); + this.register(long[].class, LongArraySimpledCoder.instance); + this.register(float[].class, FloatArraySimpledCoder.instance); + this.register(double[].class, DoubleArraySimpledCoder.instance); + this.register(String[].class, StringArraySimpledCoder.instance); + //--------------------------------------------------------- + } + } + + public Factory parent() { + return this.parent; + } + + public abstract ConvertType getConvertType(); + + public abstract boolean isReversible(); + + public abstract Factory createChild(); + + public abstract Factory createChild(boolean tiny); + + public Convert getConvert() { + return convert; + } + + public void setTiny(boolean tiny) { + this.tiny = tiny; + } + + public ConvertColumnEntry findRef(AccessibleObject field) { + if (field == null) return null; + ConvertColumnEntry en = this.columnEntrys.get(field); + if (en != null) return en; + final ConvertType ct = this.getConvertType(); + for (ConvertColumn ref : field.getAnnotationsByType(ConvertColumn.class)) { + if (ref.type().contains(ct)) { + ConvertColumnEntry entry = new ConvertColumnEntry(ref); + if (skipAllIgnore) { + entry.setIgnore(false); + return entry; + } + if (skipIgnores.isEmpty()) return entry; + if (skipIgnores.contains(((Member) field).getDeclaringClass())) entry.setIgnore(false); + return entry; + } + } + return null; + } + + final String getEntity(Class clazz) { + ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class); + if (ce != null && findEntity(ce.value()) == null) entitys.put(ce.value(), clazz); + return ce == null ? clazz.getName() : ce.value(); + } + + private Class findEntity(String name) { + Class clazz = entitys.get(name); + return parent == null ? clazz : parent.findEntity(name); + } + + final Class getEntity(String name) { + Class clazz = findEntity(name); + try { + return clazz == null ? Class.forName(name) : clazz; + } catch (Exception ex) { + throw new ConvertException("convert entity is " + name, ex); + } + } + + /** + * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + *

+ * @param skipIgnore + */ + public final void registerSkipAllIgnore(final boolean skipIgnore) { + this.skipAllIgnore = skipIgnore; + } + + /** + * 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + *

+ * @param type + */ + public final void registerSkipIgnore(final Class type) { + skipIgnores.add(type); + } + + public final void register(final Class type, boolean ignore, String... columns) { + for (String column : columns) { + register(type, column, new ConvertColumnEntry(column, ignore)); + } + } + + public final boolean register(final Class type, String column, ConvertColumnEntry entry) { + if (type == null || column == null || entry == null) return false; + try { + final Field field = type.getDeclaredField(column); + String get = "get"; + if (field.getType() == boolean.class || field.getType() == Boolean.class) get = "is"; + char[] cols = column.toCharArray(); + cols[0] = Character.toUpperCase(cols[0]); + String col2 = new String(cols); + try { + register(type.getMethod(get + col2), entry); + } catch (Exception ex) { + } + try { + register(type.getMethod("set" + col2, field.getType()), entry); + } catch (Exception ex) { + } + return register(field, entry); + } catch (Exception e) { + return false; + } + } + + public final boolean register(final AccessibleObject field, final ConvertColumnEntry entry) { + if (field == null || entry == null) return false; + this.columnEntrys.put(field, entry); + return true; + } + + public final void register(final Class clazz, final Creator creator) { + creators.put(clazz, creator); + } + + public final Creator findCreator(Class type) { + Creator creator = creators.get(type); + if (creator != null) return creator; + return this.parent == null ? null : this.parent.findCreator(type); + } + + public final Creator loadCreator(Class type) { + Creator result = findCreator(type); + if (result == null) { + result = Creators.create(type); + creators.put(type, result); + } + return result; + } + + //---------------------------------------------------------------------- + public final Encodeable getAnyEncoder() { + return (Encodeable) anyEncoder; + } + + public final void register(final Type clazz, final SimpledCoder coder) { + decoders.put(clazz, coder); + encoders.put(clazz, coder); + } + + public final void register(final Type clazz, final Decodeable decoder) { + decoders.put(clazz, decoder); + } + + public final void register(final Type clazz, final Encodeable printer) { + encoders.put(clazz, printer); + } + + public final Decodeable findDecoder(final Type type) { + Decodeable rs = (Decodeable) decoders.get(type); + if (rs != null) return rs; + return this.parent == null ? null : this.parent.findDecoder(type); + } + + public final Encodeable findEncoder(final Type type) { + Encodeable rs = (Encodeable) encoders.get(type); + if (rs != null) return rs; + return this.parent == null ? null : this.parent.findEncoder(type); + } + + public final Decodeable loadDecoder(final Type type) { + Decodeable decoder = findDecoder(type); + if (decoder != null) return decoder; + if (type instanceof GenericArrayType) return new ArrayDecoder(this, type); + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + decoder = findDecoder(clazz); + if (decoder != null) return decoder; + return createDecoder(type, clazz); + } + + public final Decodeable createDecoder(final Type type) { + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + return createDecoder(type, clazz); + } + + private Decodeable createDecoder(final Type type, final Class clazz) { + Decodeable decoder = null; + ObjectDecoder od = null; + if (clazz.isEnum()) { + decoder = new EnumSimpledCoder(clazz); + } else if (clazz.isArray()) { + decoder = new ArrayDecoder(this, type); + } else if (Collection.class.isAssignableFrom(clazz)) { + decoder = new CollectionDecoder(this, type); + } else if (Map.class.isAssignableFrom(clazz)) { + decoder = new MapDecoder(this, type); + } else if (clazz == Object.class) { + od = new ObjectDecoder(type); + decoder = od; + } else if (!clazz.getName().startsWith("java.")) { + od = new ObjectDecoder(type); + decoder = od; + } + if (decoder == null) throw new ConvertException("not support the type (" + type + ")"); + register(type, decoder); + if (od != null) od.init(this); + return decoder; + } + + public final Encodeable loadEncoder(final Type type) { + Encodeable encoder = findEncoder(type); + if (encoder != null) return encoder; + if (type instanceof GenericArrayType) return new ArrayEncoder(this, type); + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof TypeVariable) { + TypeVariable tv = (TypeVariable) type; + Type t = Object.class; + if (tv.getBounds().length == 1) { + t = tv.getBounds()[0]; + } + if (!(t instanceof Class)) t = Object.class; + clazz = (Class) t; + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + encoder = findEncoder(clazz); + if (encoder != null) return encoder; + return createEncoder(type, clazz); + } + + public final Encodeable createEncoder(final Type type) { + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + return createEncoder(type, clazz); + } + + private Encodeable createEncoder(final Type type, final Class clazz) { + Encodeable encoder = null; + ObjectEncoder oe = null; + if (clazz.isEnum()) { + encoder = new EnumSimpledCoder(clazz); + } else if (clazz.isArray()) { + encoder = new ArrayEncoder(this, type); + } else if (Collection.class.isAssignableFrom(clazz)) { + encoder = new CollectionEncoder(this, type); + } else if (Map.class.isAssignableFrom(clazz)) { + encoder = new MapEncoder(this, type); + } else if (clazz == Object.class) { + return (Encodeable) this.anyEncoder; + } else if (!clazz.getName().startsWith("java.")) { + oe = new ObjectEncoder(type); + encoder = oe; + } + if (encoder == null) throw new ConvertException("not support the type (" + type + ")"); + register(type, encoder); + if (oe != null) oe.init(this); + return encoder; + + } + +} diff --git a/com/wentch/redkale/convert/HashedMap.java b/com/wentch/redkale/convert/HashedMap.java new file mode 100644 index 000000000..2236f85f8 --- /dev/null +++ b/com/wentch/redkale/convert/HashedMap.java @@ -0,0 +1,68 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.convert; + +/** + * 只增不减的伪Map类 + * + * @author zhangjx + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class HashedMap { + + protected final transient Entry[] table; + + public HashedMap() { + this(128); + } + + public HashedMap(int initCapacity) { + this.table = new Entry[Math.max(initCapacity, 16)]; + } + + public final V get(final K key) { + final K k = key; + final Entry[] data = this.table; + Entry entry = data[k.hashCode() & (data.length - 1)]; + while (entry != null) { + if (k == entry.key) return entry.value; + entry = entry.next; + } + return null; + } + + public final V put(K key, V value) { + final K k = key; + final Entry[] data = this.table; + final int index = k.hashCode() & (data.length - 1); + Entry entry = data[index]; + while (entry != null) { + if (k == entry.key) { + entry.value = value; + return entry.value; + } + entry = entry.next; + } + data[index] = new Entry(key, value, data[index]); + return null; + } + + protected static final class Entry { + + protected V value; + + protected final K key; + + protected final Entry next; + + protected Entry(K key, V value, Entry next) { + this.key = key; + this.value = value; + this.next = next; + } + } +} diff --git a/com/wentch/redkale/convert/MapDecoder.java b/com/wentch/redkale/convert/MapDecoder.java new file mode 100644 index 000000000..dfd7c2c1e --- /dev/null +++ b/com/wentch/redkale/convert/MapDecoder.java @@ -0,0 +1,78 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.util.Creator; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * + * @author zhangjx + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class MapDecoder implements Decodeable> { + + private final Type type; + + private final Type keyType; + + private final Type valueType; + + protected Creator> creator; + + private final Decodeable keyDecoder; + + private final Decodeable valueDecoder; + + public MapDecoder(final Factory factory, final Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.keyType = pt.getActualTypeArguments()[0]; + this.valueType = pt.getActualTypeArguments()[1]; + this.creator = factory.loadCreator((Class) pt.getRawType()); + factory.register(type, this); + this.keyDecoder = factory.loadDecoder(this.keyType); + this.valueDecoder = factory.loadDecoder(this.valueType); + } else { + throw new ConvertException("mapdecoder not support the type (" + type + ")"); + } + } + + @Override + public Map convertFrom(Reader in) { + final int len = in.readMapB(); + if (len == Reader.SIGN_NULL) return null; + final Map result = this.creator.create(); + if (len == Reader.SIGN_NOLENGTH) { + while (in.hasNext()) { + K key = keyDecoder.convertFrom(in); + in.skipBlank(); + V value = valueDecoder.convertFrom(in); + result.put(key, value); + } + } else { + for (int i = 0; i < len; i++) { + K key = keyDecoder.convertFrom(in); + in.skipBlank(); + V value = valueDecoder.convertFrom(in); + result.put(key, value); + } + } + in.readMapE(); + return result; + } + + @Override + public Type getType() { + return this.type; + } + +} diff --git a/com/wentch/redkale/convert/MapEncoder.java b/com/wentch/redkale/convert/MapEncoder.java new file mode 100644 index 000000000..b4c12ff39 --- /dev/null +++ b/com/wentch/redkale/convert/MapEncoder.java @@ -0,0 +1,62 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * + * @author zhangjx + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class MapEncoder implements Encodeable> { + + private final Type type; + + private final Encodeable keyencoder; + + private final Encodeable valencoder; + + public MapEncoder(final Factory factory, final Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + final Type[] pt = ((ParameterizedType) type).getActualTypeArguments(); + this.keyencoder = factory.loadEncoder(pt[0]); + this.valencoder = factory.loadEncoder(pt[1]); + } else { + this.keyencoder = factory.getAnyEncoder(); + this.valencoder = factory.getAnyEncoder(); + } + } + + @Override + public void convertTo(Writer out, Map value) { + final Map values = value; + if (values == null) { + out.writeNull(); + return; + } + out.writeMapB(values.size()); + boolean first = true; + for (Map.Entry en : values.entrySet()) { + if (!first) out.writeArrayMark(); + this.keyencoder.convertTo(out, en.getKey()); + out.writeMapMark(); + this.valencoder.convertTo(out, en.getValue()); + if (first) first = false; + } + out.writeMapE(); + } + + @Override + public Type getType() { + return type; + } +} diff --git a/com/wentch/redkale/convert/ObjectDecoder.java b/com/wentch/redkale/convert/ObjectDecoder.java new file mode 100644 index 000000000..d62e7572f --- /dev/null +++ b/com/wentch/redkale/convert/ObjectDecoder.java @@ -0,0 +1,137 @@ +/* + * 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 com.wentch.redkale.convert; + +import static com.wentch.redkale.convert.ObjectEncoder.TYPEZERO; +import com.wentch.redkale.util.Creator; +import java.lang.reflect.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * @author zhangjx + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class ObjectDecoder implements Decodeable { + + protected final Type type; + + protected final Class typeClass; + + protected Creator creator; + + protected DeMember[] members; + + protected Factory factory; + + protected ObjectDecoder(Type type) { + this.type = ((type instanceof Class) && ((Class) type).isInterface()) ? Object.class : type; + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.typeClass = (Class) pt.getRawType(); + } else { + this.typeClass = (Class) type; + } + this.members = new DeMember[0]; + } + + public void init(final Factory factory) { + this.factory = factory; + if (type == Object.class) return; + + Class clazz = null; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (!(type instanceof Class)) { + throw new ConvertException("[" + type + "] is no a class"); + } else { + clazz = (Class) type; + } + final Type[] virGenericTypes = this.typeClass.getTypeParameters(); + final Type[] realGenericTypes = (type instanceof ParameterizedType) ? ((ParameterizedType) type).getActualTypeArguments() : TYPEZERO; + this.creator = factory.loadCreator(clazz); + final Set list = new HashSet(); + try { + ConvertColumnEntry ref; + for (final Field field : clazz.getFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + ref = factory.findRef(field); + if (ref != null && ref.ignore()) continue; + Type t = ObjectEncoder.makeGenericType(field.getGenericType(), virGenericTypes, realGenericTypes); + list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t))); + } + final boolean reversible = factory.isReversible(); + 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().length() < 4) continue; + if (!method.getName().startsWith("set")) continue; + if (method.getParameterTypes().length != 1) continue; + if (method.getReturnType() != void.class) continue; + if (reversible) { + boolean is = method.getParameterTypes()[0] == boolean.class || method.getParameterTypes()[0] == Boolean.class; + try { + clazz.getMethod(method.getName().replaceFirst("set", is ? "is" : "get")); + } catch (Exception e) { + continue; + } + } + ref = factory.findRef(method); + if (ref != null && ref.ignore()) continue; + Type t = ObjectEncoder.makeGenericType(method.getGenericParameterTypes()[0], virGenericTypes, realGenericTypes); + list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t))); + } + this.members = list.toArray(new DeMember[list.size()]); + Arrays.sort(this.members); + } catch (Exception ex) { + throw new ConvertException(ex); + } + } + + /** + * 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2] + * + * @param in + * @return + */ + @Override + public final T convertFrom(final R in) { + final String clazz = in.readClassName(); + if (clazz != null && !clazz.isEmpty()) return (T) factory.loadDecoder(factory.getEntity(clazz)).convertFrom(in); + if (in.readObjectB() == Reader.SIGN_NULL) return null; + final T result = this.creator.create(); + final AtomicInteger index = new AtomicInteger(); + while (in.hasNext()) { + DeMember member = in.readField(index, members); + in.skipBlank(); + if (member == null) { + in.skipValue(); //跳过该属性的值 + } else { + member.read(in, result); + } + index.incrementAndGet(); + } + in.readObjectE(); + return result; + } + + @Override + public final Type getType() { + return this.type; + } + + @Override + public String toString() { + return "ObjectDecoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}'; + } +} diff --git a/com/wentch/redkale/convert/ObjectEncoder.java b/com/wentch/redkale/convert/ObjectEncoder.java new file mode 100644 index 000000000..f5560d6ce --- /dev/null +++ b/com/wentch/redkale/convert/ObjectEncoder.java @@ -0,0 +1,214 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.util.Attribute; +import com.wentch.redkale.util.Attribute.Attributes; +import java.lang.reflect.*; +import java.util.*; + +/** + * + * @author zhangjx + * @param + * @param + */ +@SuppressWarnings("unchecked") +public final class ObjectEncoder implements Encodeable { + + static final Type[] TYPEZERO = new Type[0]; + + protected final Type type; + + protected final Class typeClass; + + protected EnMember[] members; + + protected Factory factory; + + protected ObjectEncoder(Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.typeClass = (Class) pt.getRawType(); + } else { + this.typeClass = (Class) type; + } + this.members = new EnMember[0]; + } + + static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) { + if (type instanceof Class) { + return type; + } else if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + Type[] paramTypes = pt.getActualTypeArguments(); + final Type[] newTypes = new Type[paramTypes.length]; + int count = 0; + for (int i = 0; i < newTypes.length; i++) { + newTypes[i] = makeGenericType(paramTypes[i], virGenericTypes, realGenericTypes); + if (paramTypes[i] == newTypes[i]) count++; + } + if (count == paramTypes.length) return pt; + return new ParameterizedType() { + + @Override + public Type[] getActualTypeArguments() { + return newTypes; + } + + @Override + public Type getRawType() { + return pt.getRawType(); + } + + @Override + public Type getOwnerType() { + return pt.getOwnerType(); + } + + }; + } + if (realGenericTypes == null) return type; + if (type instanceof WildcardType) { + final WildcardType wt = (WildcardType) type; + for (Type f : wt.getUpperBounds()) { + for (int i = 0; i < virGenericTypes.length; i++) { + if (virGenericTypes[i] == f) return realGenericTypes.length == 0 ? Object.class : realGenericTypes[i]; + } + } + } else if (type instanceof TypeVariable) { + for (int i = 0; i < virGenericTypes.length; i++) { + if (virGenericTypes[i] == type) return i >= realGenericTypes.length ? Object.class : realGenericTypes[i]; + } + } + return type; + } + + private static String readGetSetFieldName(Method method) { + if (method == null) return null; + String fname = method.getName(); + if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; + fname = fname.substring(fname.startsWith("is") ? 2 : 3); + if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) { + fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1); + } else if (fname.length() == 1) { + fname = "" + Character.toLowerCase(fname.charAt(0)); + } + return fname; + } + + static Attribute createAttribute(final Factory factory, Class clazz, final Field field, final Method getter, final Method setter) { + String fieldalias = null; + if (field != null) { // public field + ConvertColumnEntry ref = factory.findRef(field); + fieldalias = ref == null || ref.name().isEmpty() ? field.getName() : ref.name(); + } else if (getter != null) { + ConvertColumnEntry ref = factory.findRef(getter); + String mfieldname = readGetSetFieldName(getter); + if (ref == null) { + try { + ref = factory.findRef(clazz.getDeclaredField(mfieldname)); + } catch (Exception e) { + } + } + fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); + } else { // setter != null + ConvertColumnEntry ref = factory.findRef(setter); + String mfieldname = readGetSetFieldName(setter); + if (ref == null) { + try { + ref = factory.findRef(clazz.getDeclaredField(mfieldname)); + } catch (Exception e) { + } + } + fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); + } + return Attributes.create(clazz, fieldalias, field, getter, setter); + } + + public void init(final Factory factory) { + this.factory = factory; + 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 HashSet(); + final Type[] virGenericTypes = this.typeClass.getTypeParameters(); + final Type[] realGenericTypes = (type instanceof ParameterizedType) ? ((ParameterizedType) type).getActualTypeArguments() : null; + if (realGenericTypes != null) { + // println(type + "," + Arrays.toString(virGenericTypes) + ", " + Arrays.toString(realGenericTypes)); + } + try { + ConvertColumnEntry ref; + for (final Field field : clazz.getFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + ref = factory.findRef(field); + if (ref != null && ref.ignore()) continue; + Type t = makeGenericType(field.getGenericType(), virGenericTypes, realGenericTypes); + list.add(new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t))); + } + final boolean reversible = factory.isReversible(); + 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().length() < 3) continue; + if (method.getName().equals("getClass")) continue; + if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue; + if (method.getParameterTypes().length != 0) continue; + if (method.getReturnType() == void.class) continue; + if (reversible) { + 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(method); + if (ref != null && ref.ignore()) continue; + Type t = makeGenericType(method.getGenericReturnType(), virGenericTypes, realGenericTypes); + list.add(new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t))); + } + this.members = list.toArray(new EnMember[list.size()]); + Arrays.sort(this.members); + + } catch (Exception ex) { + throw new ConvertException(ex); + } + } + + @Override + public final void convertTo(W out, T value) { + if (value == null) { + out.wirteClassName(null); + out.writeNull(); + return; + } + if (value != null && value.getClass() != this.typeClass) { + final Class clz = value.getClass(); + out.wirteClassName(factory.getEntity(clz)); + factory.loadEncoder(clz).convertTo(out, value); + return; + } + out.writeObjectB(members.length, value); + boolean comma = false; + for (EnMember member : members) { + comma = member.write(out, comma, value); + } + out.writeObjectE(value); + } + + @Override + public final Type getType() { + return this.type; + } + + @Override + public String toString() { + return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}'; + } +} diff --git a/com/wentch/redkale/convert/Reader.java b/com/wentch/redkale/convert/Reader.java new file mode 100644 index 000000000..ba40ed756 --- /dev/null +++ b/com/wentch/redkale/convert/Reader.java @@ -0,0 +1,112 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * @author zhangjx + */ +public interface Reader { + + public static final short SIGN_NULL = -1; + + public static final short SIGN_NOLENGTH = -2; + + /** + * 是否还存在下个元素或字段 + * + * @return + */ + public boolean hasNext(); + + /** + * 跳过值(不包含值前面的字段) + */ + public void skipValue(); + + /** + * /跳过字段与值之间的多余内容, json就是跳过:符, map跳过: + */ + public void skipBlank(); + + /** + * 读取对象的开头 返回字段数 + * + * @return + */ + public int readObjectB(); + + /** + * 读取对象的尾端 + * + */ + public void readObjectE(); + + /** + * 读取数组的开头并返回数组的长度 + * + * @return + */ + public int readArrayB(); + + /** + * 读取数组的尾端 + * + */ + public void readArrayE(); + + /** + * 读取map的开头并返回map的size + * + * @return + */ + public int readMapB(); + + /** + * 读取数组的尾端 + * + */ + public void readMapE(); + + /** + * 根据字段读取字段对应的DeMember + * + * @param index + * @param members + * @return + */ + public DeMember readField(final AtomicInteger index, final DeMember[] members); + + public boolean readBoolean(); + + public byte readByte(); + + public char readChar(); + + public short readShort(); + + public int readInt(); + + public long readLong(); + + public float readFloat(); + + public double readDouble(); + + /** + * 读取无转义字符长度不超过255的字符串, 例如枚举值、字段名、类名字符串等 + * + * @return + */ + public String readSmallString(); + + public String readClassName(); + + public String readString(); + +} diff --git a/com/wentch/redkale/convert/SimpledCoder.java b/com/wentch/redkale/convert/SimpledCoder.java new file mode 100644 index 000000000..db0e42657 --- /dev/null +++ b/com/wentch/redkale/convert/SimpledCoder.java @@ -0,0 +1,42 @@ +/* + * 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 com.wentch.redkale.convert; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * + * @author zhangjx + * @param + * @param + * @param + */ +public abstract class SimpledCoder implements Decodeable, Encodeable { + + private Type type; + + @Override + public abstract void convertTo(final W out, final T value); + + @Override + public abstract T convertFrom(final R in); + + @Override + @SuppressWarnings("unchecked") + public Class getType() { + if (type == null) { + Type[] ts = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments(); + type = ts[ts.length - 1]; + } + return (Class) type; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } +} diff --git a/com/wentch/redkale/convert/Writer.java b/com/wentch/redkale/convert/Writer.java new file mode 100644 index 000000000..adcba5e91 --- /dev/null +++ b/com/wentch/redkale/convert/Writer.java @@ -0,0 +1,120 @@ +/* + * 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 com.wentch.redkale.convert; + +import com.wentch.redkale.util.Attribute; + +/** + * + * @author zhangjx + */ +public interface Writer { + + /** + * 当tiny=true时, 字符串为空、boolean为false的字段值都会被跳过, 不会输出。 + *

+ * @return + */ + public boolean isTiny(); + + /** + * 输出null值 + */ + public void writeNull(); + + /** + * + * @param clazz + */ + public void wirteClassName(String clazz); + + /** + * 输出一个对象前的操作 + * + * @param fieldCount 字段个数 + * + * @param obj + */ + public void writeObjectB(int fieldCount, Object obj); + + /** + * 输出一个对象后的操作 + * + * @param obj + */ + public void writeObjectE(Object obj); + + /** + * 输出一个数组前的操作 + * + * @param size 数组长度 + */ + public void writeArrayB(int size); + + /** + * 输出数组元素间的间隔符 + * + */ + public void writeArrayMark(); + + /** + * 输出一个数组后的操作 + * + */ + public void writeArrayE(); + + /** + * 输出一个Map前的操作 + * + * @param size map大小 + */ + public void writeMapB(int size); + + /** + * 输出一个Map中key与value间的间隔符 + * + */ + public void writeMapMark(); + + /** + * 输出一个Map后的操作 + * + */ + public void writeMapE(); + + /** + * 输出一个字段 + * + * @param comma 是否非第一个字段 + * @param attribute + */ + public void writeField(boolean comma, Attribute attribute); + + public void writeBoolean(boolean value); + + public void writeByte(byte value); + + public void writeChar(char value); + + public void writeShort(short value); + + public void writeInt(int value); + + public void writeLong(long value); + + public void writeFloat(float value); + + public void writeDouble(double value); + + /** + * 写入无转义字符长度不超过255的字符串, 例如枚举值、字段名、类名字符串等 * + * + * @param value + */ + public void writeSmallString(String value); + + public void writeString(String value); +} diff --git a/com/wentch/redkale/convert/bson/BsonConvert.java b/com/wentch/redkale/convert/bson/BsonConvert.java new file mode 100644 index 000000000..7f2961461 --- /dev/null +++ b/com/wentch/redkale/convert/bson/BsonConvert.java @@ -0,0 +1,104 @@ +/* + * 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 com.wentch.redkale.convert.bson; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.util.*; +import java.lang.reflect.*; +import java.nio.*; + +/** + * BSON协议格式: + * 1). 基本数据类型: 直接转换成byte[] + * 2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。 + * 3). String: length(4 bytes) + byte[](utf8); + * 4). 数组: length(4 bytes) + byte[]... + * 5). Object: + * 1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值) + * 2. 空字符串(SmallString) + * 3. SIGN_OBJECTB 标记位,值固定为0xBB (short) + * 4. 循环字段值: + * 4.1 SIGN_HASNEXT 标记位,值固定为1 (byte) + * 4.2 字段类型; 1-9为基本类型&字符串; 101-109为基本类型&字符串的数组; 127为Object + * 4.3 字段名 (SmallString) + * 4.4 字段的值Object + * 5. SIGN_NONEXT 标记位,值固定为0 (byte) + * 6. SIGN_OBJECTE 标记位,值固定为0xEE (short) + * + * @author zhangjx + */ +public final class BsonConvert extends Convert { + + private static final ObjectPool readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", 16)); + + private static final ObjectPool writerPool = BsonWriter.createPool(Integer.getInteger("convert.bson.pool.size", 16)); + + private final boolean tiny; + + protected BsonConvert(Factory factory, boolean tiny) { + super(factory); + this.tiny = tiny; + } + + public T convertFrom(final Type type, final byte[] bytes) { + if (bytes == null) return null; + return convertFrom(type, bytes, 0, bytes.length); + } + + public T convertFrom(final Type type, final byte[] bytes, int start, int len) { + if (type == null) return null; + final BsonReader in = readerPool.poll(); + in.setBytes(bytes, start, len); + @SuppressWarnings("unchecked") + T rs = (T) factory.loadDecoder(type).convertFrom(in); + readerPool.offer(in); + return rs; + } + + public byte[] convertTo(final Type type, Object value) { + if (type == null) return null; + final BsonWriter out = writerPool.poll(); + out.setTiny(tiny); + factory.loadEncoder(type).convertTo(out, value); + byte[] result = out.toArray(); + writerPool.offer(out); + return result; + } + + public byte[] convertTo(Object value) { + if (value == null) { + final BsonWriter out = writerPool.poll(); + out.setTiny(tiny); + out.writeNull(); + byte[] result = out.toArray(); + writerPool.offer(out); + return result; + } + return convertTo(value.getClass(), value); + } + + public ByteBuffer convertToBuffer(final Type type, Object value) { + if (type == null) return null; + final BsonWriter out = writerPool.poll(); + out.setTiny(tiny); + factory.loadEncoder(type).convertTo(out, value); + ByteBuffer result = out.toBuffer(); + writerPool.offer(out); + return result; + } + + public ByteBuffer convertToBuffer(Object value) { + if (value == null) { + final BsonWriter out = writerPool.poll(); + out.setTiny(tiny); + out.writeNull(); + ByteBuffer result = out.toBuffer(); + writerPool.offer(out); + return result; + } + return convertToBuffer(value.getClass(), value); + } +} diff --git a/com/wentch/redkale/convert/bson/BsonFactory.java b/com/wentch/redkale/convert/bson/BsonFactory.java new file mode 100644 index 000000000..8097f6cab --- /dev/null +++ b/com/wentch/redkale/convert/bson/BsonFactory.java @@ -0,0 +1,62 @@ +/* + * 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 com.wentch.redkale.convert.bson; + +import com.wentch.redkale.convert.*; +import java.io.Serializable; + +/** + * + * @author zhangjx + */ +public final class BsonFactory extends Factory { + + private static final BsonFactory instance = new BsonFactory(null, Boolean.getBoolean("convert.bson.tiny")); + + static final Decodeable objectDecoder = instance.loadDecoder(Object.class); + + static final Encodeable objectEncoder = instance.loadEncoder(Object.class); + + static { + instance.register(Serializable.class, objectDecoder); + instance.register(Serializable.class, objectEncoder); + } + + private BsonFactory(BsonFactory parent, boolean tiny) { + super(parent, tiny); + } + + public static BsonFactory root() { + return instance; + } + + @Override + public final BsonConvert getConvert() { + if (convert == null) convert = new BsonConvert(this, tiny); + return (BsonConvert) convert; + } + + @Override + public BsonFactory createChild() { + return new BsonFactory(this, this.tiny); + } + + @Override + public BsonFactory createChild(boolean tiny) { + return new BsonFactory(this, tiny); + } + + @Override + public ConvertType getConvertType() { + return ConvertType.BSON; + } + + @Override + public boolean isReversible() { + return true; + } + +} diff --git a/com/wentch/redkale/convert/bson/BsonReader.java b/com/wentch/redkale/convert/bson/BsonReader.java new file mode 100644 index 000000000..f2d23d815 --- /dev/null +++ b/com/wentch/redkale/convert/bson/BsonReader.java @@ -0,0 +1,311 @@ +/* + * 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 com.wentch.redkale.convert.bson; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.convert.ext.*; +import com.wentch.redkale.util.*; +import java.util.concurrent.atomic.*; + +/** + * + * @author zhangjx + */ +public final class BsonReader implements Reader { + + public static final short SIGN_OBJECTB = (short) 0xBB; + + public static final short SIGN_OBJECTE = (short) 0xEE; + + public static final byte SIGN_HASNEXT = 1; + + public static final byte SIGN_NONEXT = 0; + + public static final byte VERBOSE_NO = 1; + + public static final byte VERBOSE_YES = 2; + + private int position = -1; + + private byte typeval; //字段的类型值 对应 BsonWriter.writeField + + private byte[] content; + + public BsonReader() { + } + + public static ObjectPool createPool(int max) { + return new ObjectPool(max, new Creator() { + + @Override + public BsonReader create(Object... params) { + return new BsonReader(); + } + }, null, new Predicate() { + + @Override + public boolean test(BsonReader t) { + return t.recycle(); + } + }); + } + + public BsonReader(byte[] bytes) { + setBytes(bytes, 0, bytes.length); + } + + public BsonReader(byte[] bytes, int start, int len) { + setBytes(bytes, start, len); + } + + public final void setBytes(byte[] bytes) { + setBytes(bytes, 0, bytes.length); + } + + public final void setBytes(byte[] bytes, int start, int len) { + this.content = bytes; + this.position = start - 1; + //this.limit = start + len - 1; + } + + protected boolean recycle() { + this.position = -1; + this.typeval = 0; + //this.limit = -1; + this.content = null; + return true; + } + + public void close() { + this.recycle(); + } + + /** + * 跳过属性的值 + */ + @Override + public final void skipValue() { + if (typeval == 0) return; + final byte val = this.typeval; + this.typeval = 0; + switch (val) { + case 1: readBoolean(); + break; + case 2: readByte(); + break; + case 3: readShort(); + break; + case 4: readChar(); + break; + case 5: readInt(); + break; + case 6: readLong(); + break; + case 7: readFloat(); + break; + case 8: readDouble(); + break; + case 9: readString(); + break; + case 101: + BoolArraySimpledCoder.instance.convertFrom(this); + break; + case 102: + ByteArraySimpledCoder.instance.convertFrom(this); + break; + case 103: + ShortArraySimpledCoder.instance.convertFrom(this); + break; + case 104: + CharArraySimpledCoder.instance.convertFrom(this); + break; + case 105: + IntArraySimpledCoder.instance.convertFrom(this); + break; + case 106: + LongArraySimpledCoder.instance.convertFrom(this); + break; + case 107: + FloatArraySimpledCoder.instance.convertFrom(this); + break; + case 108: + DoubleArraySimpledCoder.instance.convertFrom(this); + break; + case 109: + StringArraySimpledCoder.instance.convertFrom(this); + break; + case 127: + BsonFactory.objectDecoder.convertFrom(this); + break; + } + } + + /** + * 判断下一个非空白字节是否为{ + * + */ + @Override + public int readObjectB() { + short bt = readShort(); + if (bt == Reader.SIGN_NULL) return bt; + if (bt != SIGN_OBJECTB) { + throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB) + + " (position = " + position + ") but '" + this.content[this.position] + "'"); + } + return bt; + } + + @Override + public void readObjectE() { + if (readShort() != SIGN_OBJECTE) { + throw new ConvertException("a bson object must end with " + (SIGN_OBJECTE) + + " (position = " + position + ") but '" + this.content[this.position] + "'"); + } + } + + @Override + public int readMapB() { + return readArrayB(); + } + + @Override + public void readMapE() { + } + + /** + * 判断下一个非空白字节是否为[ + * + * @return + */ + @Override + public int readArrayB() { + return readInt(); + } + + @Override + public void readArrayE() { + } + + /** + * 判断下一个非空白字节是否: + */ + @Override + public void skipBlank() { + } + + /** + * 判断对象是否存在下一个属性或者数组是否存在下一个元素 + * + * @return + */ + @Override + public boolean hasNext() { + byte b = readByte(); + if (b == SIGN_HASNEXT) return true; + if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT) + + " or " + (SIGN_NONEXT) + ") but '" + b + "' at position(" + this.position + ")"); + return false; + } + + @Override + public DeMember readField(final AtomicInteger index, final DeMember[] members) { + final String exceptedfield = readSmallString(); + this.typeval = readByte(); + final int len = members.length; + int v = index.get(); + if (v >= len) { + v = 0; + index.set(0); + } + for (int k = v; k < len; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + index.set(k); + return members[k]; + } + } + for (int k = 0; k < v; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + index.set(k); + return members[k]; + } + } + return null; + } + + //------------------------------------------------------------ + @Override + public boolean readBoolean() { + return content[++this.position] == 1; + } + + @Override + public byte readByte() { + return content[++this.position]; + } + + @Override + public char readChar() { + return (char) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position])); + } + + @Override + public short readShort() { + return (short) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position])); + } + + @Override + public int readInt() { + return ((content[++this.position] & 0xff) << 24) | ((content[++this.position] & 0xff) << 16) + | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); + } + + @Override + public long readLong() { + return ((((long) content[++this.position] & 0xff) << 56) + | (((long) content[++this.position] & 0xff) << 48) + | (((long) content[++this.position] & 0xff) << 40) + | (((long) content[++this.position] & 0xff) << 32) + | (((long) content[++this.position] & 0xff) << 24) + | (((long) content[++this.position] & 0xff) << 16) + | (((long) content[++this.position] & 0xff) << 8) + | (((long) content[++this.position] & 0xff))); + } + + @Override + public float readFloat() { + return Float.intBitsToFloat(readInt()); + } + + @Override + public double readDouble() { + return Double.longBitsToDouble(readLong()); + } + + @Override + public String readClassName() { + return readSmallString(); + } + + @Override + public String readSmallString() { + int len = 0xff & readByte(); + if (len == 0) return ""; + String value = new String(content, ++this.position, len); + this.position += len - 1; + return value; + } + + @Override + public String readString() { + int len = readInt(); + if (len == SIGN_NULL) return null; + if (len == 0) return ""; + String value = new String(Utility.decodeUTF8(content, ++this.position, len)); + this.position += len - 1; + return value; + } + +} diff --git a/com/wentch/redkale/convert/bson/BsonSimpledCoder.java b/com/wentch/redkale/convert/bson/BsonSimpledCoder.java new file mode 100644 index 000000000..840e1fca3 --- /dev/null +++ b/com/wentch/redkale/convert/bson/BsonSimpledCoder.java @@ -0,0 +1,17 @@ +/* + * 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 com.wentch.redkale.convert.bson; + +import com.wentch.redkale.convert.SimpledCoder; + +/** + * + * @author zhangjx + * @param + */ +public abstract class BsonSimpledCoder extends SimpledCoder { + +} diff --git a/com/wentch/redkale/convert/bson/BsonWriter.java b/com/wentch/redkale/convert/bson/BsonWriter.java new file mode 100644 index 000000000..62dc08927 --- /dev/null +++ b/com/wentch/redkale/convert/bson/BsonWriter.java @@ -0,0 +1,300 @@ +/* + * 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 com.wentch.redkale.convert.bson; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.util.*; +import java.nio.*; + +/** + * + * @author zhangjx + */ +public final class BsonWriter implements Writer { + + private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", 1024); + + protected int count; + + private byte[] content; + + protected boolean tiny; + + public static ObjectPool createPool(int max) { + return new ObjectPool(max, new Creator() { + + @Override + public BsonWriter create(Object... params) { + return new BsonWriter(); + } + }, null, new Predicate() { + + @Override + public boolean test(BsonWriter t) { + return t.recycle(); + } + }); + } + + public byte[] toArray() { + if (count == content.length) return content; + byte[] newdata = new byte[count]; + System.arraycopy(content, 0, newdata, 0, count); + return newdata; + } + + public ByteBuffer toBuffer() { + return ByteBuffer.wrap(content, 0, count); + } + + public BsonWriter() { + this(defaultSize); + } + + public BsonWriter(int size) { + this.content = new byte[size > 32 ? size : 32]; + } + + @Override + public boolean isTiny() { + return tiny; + } + + public void setTiny(boolean tiny) { + this.tiny = tiny; + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + /** + * 返回指定至少指定长度的缓冲区 + * + * @param len + * @return + */ + public byte[] expand(int len) { + int newcount = count + len; + if (newcount <= content.length) return content; + byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)]; + System.arraycopy(content, 0, newdata, 0, count); + this.content = newdata; + return newdata; + } + + public void writeTo(final byte ch) { + expand(1); + content[count++] = ch; + } + + public void writeTo(final byte... chs) { + int len = chs.length; + expand(len); + System.arraycopy(chs, 0, content, count, len); + count += len; + } + + public void writeTo(final byte[] chs, final int start, final int end) { + int len = end - start; + expand(len); + System.arraycopy(chs, start, content, count, len); + count += len; + } + + protected boolean recycle() { + this.count = 0; + if (this.content.length > defaultSize) { + this.content = new byte[defaultSize]; + } + return true; + } + + //------------------------------------------------------------------------ + public final int count() { + return this.count; + } + + public final void count(int count) { + if (count >= 0) this.count = count; + } + + //----------------------------------------------------------------------- + @Override + public String toString() { + return new String(content, 0, count); + } + + @Override + public void writeBoolean(boolean value) { + writeTo(value ? (byte) 1 : (byte) 0); + } + + @Override + public void writeByte(byte value) { + writeTo(value); + } + + @Override + public void writeChar(final char value) { + writeTo((byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF)); + } + + @Override + public void writeShort(short value) { + writeTo((byte) (value >> 8), (byte) value); + } + + @Override + public void writeInt(int value) { + writeTo((byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value); + } + + @Override + public void writeLong(long value) { + writeTo((byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32), + (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value); + } + + @Override + public void writeFloat(float value) { + writeInt(Float.floatToIntBits(value)); + } + + @Override + public void writeDouble(double value) { + writeLong(Double.doubleToLongBits(value)); + } + + @Override + public void wirteClassName(String clazz) { + writeSmallString(clazz == null ? "" : clazz); + } + + @Override + public final void writeObjectB(int fieldCount, Object obj) { + writeSmallString(""); + writeShort(BsonReader.SIGN_OBJECTB); + } + + @Override + public final void writeObjectE(Object obj) { + writeByte(BsonReader.SIGN_NONEXT); + writeShort(BsonReader.SIGN_OBJECTE); + } + + @Override + public final void writeField(boolean comma, Attribute attribute) { + writeByte(BsonReader.SIGN_HASNEXT); + writeSmallString(attribute.field()); + byte typeval = 127; //字段的类型值 + final Class type = attribute.type(); + if (type == boolean.class || type == Boolean.class) { + typeval = 1; + } else if (type == byte.class || type == Byte.class) { + typeval = 2; + } else if (type == short.class || type == Short.class) { + typeval = 3; + } else if (type == char.class || type == Character.class) { + typeval = 4; + } else if (type == int.class || type == Integer.class) { + typeval = 5; + } else if (type == long.class || type == Long.class) { + typeval = 6; + } else if (type == float.class || type == Float.class) { + typeval = 7; + } else if (type == double.class || type == Double.class) { + typeval = 8; + } else if (type == String.class) { + typeval = 9; + } else if (type == boolean[].class || type == Boolean[].class) { + typeval = 101; + } else if (type == byte[].class || type == Byte[].class) { + typeval = 102; + } else if (type == short[].class || type == Short[].class) { + typeval = 103; + } else if (type == char[].class || type == Character[].class) { + typeval = 104; + } else if (type == int[].class || type == Integer[].class) { + typeval = 105; + } else if (type == long[].class || type == Long[].class) { + typeval = 106; + } else if (type == float[].class || type == Float[].class) { + typeval = 107; + } else if (type == double[].class || type == Double[].class) { + typeval = 108; + } else if (type == String[].class) { + typeval = 109; + } + writeByte(typeval); + } + + /** + * 对于类的字段名、枚举值这些长度一般不超过255且不会出现双字节字符的字符串采用writeSmallString处理, readSmallString用于读取 + * + * @param value + */ + @Override + public void writeSmallString(String value) { + if (value.isEmpty()) { + writeTo((byte) 0); + return; + } + char[] chars = Utility.charArray(value); + if (chars.length > 255) throw new ConvertException("'" + value + "' has very long length"); + byte[] bytes = new byte[chars.length + 1]; + bytes[0] = (byte) chars.length; + for (int i = 0; i < chars.length; i++) { + if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' has double-word"); + bytes[i + 1] = (byte) chars[i]; + } + writeTo(bytes); + } + + @Override + public void writeString(String value) { + if (value == null) { + writeInt(Reader.SIGN_NULL); + return; + } else if (value.isEmpty()) { + writeInt(0); + return; + } + byte[] bytes = Utility.encodeUTF8(value); + writeInt(bytes.length); + writeTo(bytes); + } + + @Override + public final void writeNull() { + writeShort(Reader.SIGN_NULL); + } + + @Override + public void writeArrayB(int size) { + writeInt(size); + } + + @Override + public void writeArrayMark() { + } + + @Override + public void writeArrayE() { + } + + @Override + public void writeMapB(int size) { + writeArrayB(size); + } + + @Override + public void writeMapMark() { + } + + @Override + public void writeMapE() { + } + +} diff --git a/com/wentch/redkale/convert/ext/BigIntegerSimpledCoder.java b/com/wentch/redkale/convert/ext/BigIntegerSimpledCoder.java new file mode 100644 index 000000000..735e85c3e --- /dev/null +++ b/com/wentch/redkale/convert/ext/BigIntegerSimpledCoder.java @@ -0,0 +1,38 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; +import com.wentch.redkale.convert.Reader; +import java.math.BigInteger; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class BigIntegerSimpledCoder extends SimpledCoder { + + public static final BigIntegerSimpledCoder instance = new BigIntegerSimpledCoder(); + + @Override + public void convertTo(W out, BigInteger value) { + if (value == null) { + out.writeNull(); + return; + } + ByteArraySimpledCoder.instance.convertTo(out, value.toByteArray()); + } + + @Override + public BigInteger convertFrom(R in) { + byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); + return bytes == null ? null : new BigInteger(bytes); + } + +} diff --git a/com/wentch/redkale/convert/ext/BoolArraySimpledCoder.java b/com/wentch/redkale/convert/ext/BoolArraySimpledCoder.java new file mode 100644 index 000000000..696386202 --- /dev/null +++ b/com/wentch/redkale/convert/ext/BoolArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class BoolArraySimpledCoder extends SimpledCoder { + + public static final BoolArraySimpledCoder instance = new BoolArraySimpledCoder(); + + @Override + public void convertTo(W out, boolean[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (boolean v : values) { + if (flag) out.writeArrayMark(); + out.writeBoolean(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public boolean[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + boolean[] data = new boolean[8]; + while (in.hasNext()) { + if (size >= data.length) { + boolean[] newdata = new boolean[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readBoolean(); + } + in.readArrayE(); + boolean[] newdata = new boolean[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + boolean[] values = new boolean[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readBoolean(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/BoolSimpledCoder.java b/com/wentch/redkale/convert/ext/BoolSimpledCoder.java new file mode 100644 index 000000000..4b13eb923 --- /dev/null +++ b/com/wentch/redkale/convert/ext/BoolSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class BoolSimpledCoder extends SimpledCoder { + + public static final BoolSimpledCoder instance = new BoolSimpledCoder(); + + @Override + public void convertTo(W out, Boolean value) { + out.writeBoolean(value); + } + + @Override + public Boolean convertFrom(R in) { + return in.readBoolean(); + } + +} diff --git a/com/wentch/redkale/convert/ext/ByteArraySimpledCoder.java b/com/wentch/redkale/convert/ext/ByteArraySimpledCoder.java new file mode 100644 index 000000000..ffd09ccce --- /dev/null +++ b/com/wentch/redkale/convert/ext/ByteArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class ByteArraySimpledCoder extends SimpledCoder { + + public static final ByteArraySimpledCoder instance = new ByteArraySimpledCoder(); + + @Override + public void convertTo(W out, byte[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (byte v : values) { + if (flag) out.writeArrayMark(); + out.writeByte(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public byte[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + byte[] data = new byte[8]; + while (in.hasNext()) { + if (size >= data.length) { + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readByte(); + } + in.readArrayE(); + byte[] newdata = new byte[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + byte[] values = new byte[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readByte(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/ByteSimpledCoder.java b/com/wentch/redkale/convert/ext/ByteSimpledCoder.java new file mode 100644 index 000000000..e76a6e84c --- /dev/null +++ b/com/wentch/redkale/convert/ext/ByteSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class ByteSimpledCoder extends SimpledCoder { + + public static final ByteSimpledCoder instance = new ByteSimpledCoder(); + + @Override + public void convertTo(W out, Byte value) { + out.writeByte(value); + } + + @Override + public Byte convertFrom(R in) { + return in.readByte(); + } + +} diff --git a/com/wentch/redkale/convert/ext/CharArraySimpledCoder.java b/com/wentch/redkale/convert/ext/CharArraySimpledCoder.java new file mode 100644 index 000000000..9b5be5edb --- /dev/null +++ b/com/wentch/redkale/convert/ext/CharArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class CharArraySimpledCoder extends SimpledCoder { + + public static final CharArraySimpledCoder instance = new CharArraySimpledCoder(); + + @Override + public void convertTo(W out, char[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (char v : values) { + if (flag) out.writeArrayMark(); + out.writeChar(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public char[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + char[] data = new char[8]; + while (in.hasNext()) { + if (size >= data.length) { + char[] newdata = new char[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readChar(); + } + in.readArrayE(); + char[] newdata = new char[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + char[] values = new char[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readChar(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/CharSimpledCoder.java b/com/wentch/redkale/convert/ext/CharSimpledCoder.java new file mode 100644 index 000000000..26b501043 --- /dev/null +++ b/com/wentch/redkale/convert/ext/CharSimpledCoder.java @@ -0,0 +1,33 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class CharSimpledCoder extends SimpledCoder { + + public static final CharSimpledCoder instance = new CharSimpledCoder(); + + @Override + public void convertTo(W out, Character value) { + out.writeChar(value); + } + + @Override + public Character convertFrom(R in) { + return in.readChar(); + } + +} diff --git a/com/wentch/redkale/convert/ext/DLongSimpledCoder.java b/com/wentch/redkale/convert/ext/DLongSimpledCoder.java new file mode 100644 index 000000000..b6fce842a --- /dev/null +++ b/com/wentch/redkale/convert/ext/DLongSimpledCoder.java @@ -0,0 +1,40 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.Writer; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.util.DLong; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class DLongSimpledCoder extends SimpledCoder { + + public static final DLongSimpledCoder instance = new DLongSimpledCoder(); + + @Override + public void convertTo(final W out, final DLong value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.getFirst() + "_" + value.getSecond()); + } + } + + @Override + public DLong convertFrom(R in) { + String str = in.readString(); + if (str == null) return null; + int pos = str.indexOf('_'); + return new DLong(Long.parseLong(str.substring(0, pos)), Long.parseLong(str.substring(pos + 1))); + } + +} diff --git a/com/wentch/redkale/convert/ext/DateSimpledCoder.java b/com/wentch/redkale/convert/ext/DateSimpledCoder.java new file mode 100644 index 000000000..4df76df2f --- /dev/null +++ b/com/wentch/redkale/convert/ext/DateSimpledCoder.java @@ -0,0 +1,33 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; +import java.util.Date; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class DateSimpledCoder extends SimpledCoder { + + public static final DateSimpledCoder instance = new DateSimpledCoder(); + + @Override + public void convertTo(W out, Date value) { + out.writeLong(value.getTime()); + } + + @Override + public Date convertFrom(R in) { + return new Date(in.readLong()); + } + +} diff --git a/com/wentch/redkale/convert/ext/DoubleArraySimpledCoder.java b/com/wentch/redkale/convert/ext/DoubleArraySimpledCoder.java new file mode 100644 index 000000000..04aa3a543 --- /dev/null +++ b/com/wentch/redkale/convert/ext/DoubleArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class DoubleArraySimpledCoder extends SimpledCoder { + + public static final DoubleArraySimpledCoder instance = new DoubleArraySimpledCoder(); + + @Override + public void convertTo(W out, double[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (double v : values) { + if (flag) out.writeArrayMark(); + out.writeDouble(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public double[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + double[] data = new double[8]; + while (in.hasNext()) { + if (size >= data.length) { + double[] newdata = new double[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readDouble(); + } + in.readArrayE(); + double[] newdata = new double[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + double[] values = new double[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readDouble(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/DoubleSimpledCoder.java b/com/wentch/redkale/convert/ext/DoubleSimpledCoder.java new file mode 100644 index 000000000..a56456562 --- /dev/null +++ b/com/wentch/redkale/convert/ext/DoubleSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class DoubleSimpledCoder extends SimpledCoder { + + public static final DoubleSimpledCoder instance = new DoubleSimpledCoder(); + + @Override + public void convertTo(W out, Double value) { + out.writeDouble(value); + } + + @Override + public Double convertFrom(R in) { + return in.readDouble(); + } + +} diff --git a/com/wentch/redkale/convert/ext/EnumSimpledCoder.java b/com/wentch/redkale/convert/ext/EnumSimpledCoder.java new file mode 100644 index 000000000..323b2b25e --- /dev/null +++ b/com/wentch/redkale/convert/ext/EnumSimpledCoder.java @@ -0,0 +1,44 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + * @param + */ +public final class EnumSimpledCoder extends SimpledCoder { + + private final Class type; + + public EnumSimpledCoder(Class type) { + this.type = type; + } + + @Override + public void convertTo(final W out, final E value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + @SuppressWarnings("unchecked") + public E convertFrom(final R in) { + String value = in.readSmallString(); + if (value == null) return null; + return (E) Enum.valueOf(type, value); + } + +} diff --git a/com/wentch/redkale/convert/ext/FloatArraySimpledCoder.java b/com/wentch/redkale/convert/ext/FloatArraySimpledCoder.java new file mode 100644 index 000000000..ccba72bae --- /dev/null +++ b/com/wentch/redkale/convert/ext/FloatArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class FloatArraySimpledCoder extends SimpledCoder { + + public static final FloatArraySimpledCoder instance = new FloatArraySimpledCoder(); + + @Override + public void convertTo(W out, float[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (float v : values) { + if (flag) out.writeArrayMark(); + out.writeFloat(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public float[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + float[] data = new float[8]; + while (in.hasNext()) { + if (size >= data.length) { + float[] newdata = new float[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readFloat(); + } + in.readArrayE(); + float[] newdata = new float[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + float[] values = new float[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readFloat(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/FloatSimpledCoder.java b/com/wentch/redkale/convert/ext/FloatSimpledCoder.java new file mode 100644 index 000000000..a2b9cbed5 --- /dev/null +++ b/com/wentch/redkale/convert/ext/FloatSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class FloatSimpledCoder extends SimpledCoder { + + public static final FloatSimpledCoder instance = new FloatSimpledCoder(); + + @Override + public void convertTo(W out, Float value) { + out.writeFloat(value); + } + + @Override + public Float convertFrom(R in) { + return in.readFloat(); + } + +} diff --git a/com/wentch/redkale/convert/ext/InetAddressSimpledCoder.java b/com/wentch/redkale/convert/ext/InetAddressSimpledCoder.java new file mode 100644 index 000000000..92c2050a1 --- /dev/null +++ b/com/wentch/redkale/convert/ext/InetAddressSimpledCoder.java @@ -0,0 +1,70 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; +import com.wentch.redkale.convert.Reader; +import java.net.*; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class InetAddressSimpledCoder extends SimpledCoder { + + public static final InetAddressSimpledCoder instance = new InetAddressSimpledCoder(); + + @Override + public void convertTo(W out, InetAddress value) { + if (value == null) { + out.writeNull(); + return; + } + ByteArraySimpledCoder.instance.convertTo(out, value.getAddress()); + } + + @Override + public InetAddress convertFrom(R in) { + byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); + if (bytes == null) return null; + try { + return InetAddress.getByAddress(bytes); + } catch (Exception ex) { + return null; + } + } + + public final static class InetSocketAddressSimpledCoder extends SimpledCoder { + + public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder(); + + @Override + public void convertTo(W out, InetSocketAddress value) { + if (value == null) { + out.writeNull(); + return; + } + ByteArraySimpledCoder.instance.convertTo(out, value.getAddress().getAddress()); + out.writeInt(value.getPort()); + } + + @Override + public InetSocketAddress convertFrom(R in) { + byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); + if (bytes == null) return null; + int port = in.readInt(); + try { + return new InetSocketAddress(InetAddress.getByAddress(bytes), port); + } catch (Exception ex) { + return null; + } + } + + } +} diff --git a/com/wentch/redkale/convert/ext/IntArraySimpledCoder.java b/com/wentch/redkale/convert/ext/IntArraySimpledCoder.java new file mode 100644 index 000000000..8a8c9248a --- /dev/null +++ b/com/wentch/redkale/convert/ext/IntArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class IntArraySimpledCoder extends SimpledCoder { + + public static final IntArraySimpledCoder instance = new IntArraySimpledCoder(); + + @Override + public void convertTo(W out, int[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (int v : values) { + if (flag) out.writeArrayMark(); + out.writeInt(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public int[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + int[] data = new int[8]; + while (in.hasNext()) { + if (size >= data.length) { + int[] newdata = new int[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readInt(); + } + in.readArrayE(); + int[] newdata = new int[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + int[] values = new int[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readInt(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/IntSimpledCoder.java b/com/wentch/redkale/convert/ext/IntSimpledCoder.java new file mode 100644 index 000000000..e67c1d710 --- /dev/null +++ b/com/wentch/redkale/convert/ext/IntSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class IntSimpledCoder extends SimpledCoder { + + public static final IntSimpledCoder instance = new IntSimpledCoder(); + + @Override + public void convertTo(W out, Integer value) { + out.writeInt(value); + } + + @Override + public Integer convertFrom(R in) { + return in.readInt(); + } + +} diff --git a/com/wentch/redkale/convert/ext/LongArraySimpledCoder.java b/com/wentch/redkale/convert/ext/LongArraySimpledCoder.java new file mode 100644 index 000000000..2ea858b96 --- /dev/null +++ b/com/wentch/redkale/convert/ext/LongArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class LongArraySimpledCoder extends SimpledCoder { + + public static final LongArraySimpledCoder instance = new LongArraySimpledCoder(); + + @Override + public void convertTo(W out, long[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (long v : values) { + if (flag) out.writeArrayMark(); + out.writeLong(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public long[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + long[] data = new long[8]; + while (in.hasNext()) { + if (size >= data.length) { + long[] newdata = new long[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readInt(); + } + in.readArrayE(); + long[] newdata = new long[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + long[] values = new long[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readInt(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/LongSimpledCoder.java b/com/wentch/redkale/convert/ext/LongSimpledCoder.java new file mode 100644 index 000000000..203523b0b --- /dev/null +++ b/com/wentch/redkale/convert/ext/LongSimpledCoder.java @@ -0,0 +1,33 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class LongSimpledCoder extends SimpledCoder { + + public static final LongSimpledCoder instance = new LongSimpledCoder(); + + @Override + public void convertTo(W out, Long value) { + out.writeLong(value); + } + + @Override + public Long convertFrom(R in) { + return in.readLong(); + } + +} diff --git a/com/wentch/redkale/convert/ext/NumberSimpledCoder.java b/com/wentch/redkale/convert/ext/NumberSimpledCoder.java new file mode 100644 index 000000000..aa9e369c7 --- /dev/null +++ b/com/wentch/redkale/convert/ext/NumberSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class NumberSimpledCoder extends SimpledCoder { + + public static final NumberSimpledCoder instance = new NumberSimpledCoder(); + + @Override + public void convertTo(W out, Number value) { + out.writeLong(value == null ? 0L : value.longValue()); + } + + @Override + public Number convertFrom(R in) { + return in.readLong(); + } + +} diff --git a/com/wentch/redkale/convert/ext/PatternSimpledCoder.java b/com/wentch/redkale/convert/ext/PatternSimpledCoder.java new file mode 100644 index 000000000..a225ff9cf --- /dev/null +++ b/com/wentch/redkale/convert/ext/PatternSimpledCoder.java @@ -0,0 +1,38 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.*; +import java.util.regex.*; + +/** + * + * @author zhangjx + * @param + * @param + */ +public class PatternSimpledCoder extends SimpledCoder { + + public static final PatternSimpledCoder instance = new PatternSimpledCoder(); + + @Override + public void convertTo(W out, Pattern value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.flags() + "," + value.pattern()); + } + } + + @Override + public Pattern convertFrom(R in) { + String value = in.readString(); + if (value == null) return null; + int pos = value.indexOf(','); + return Pattern.compile(value.substring(pos + 1), Integer.parseInt(value.substring(0, pos))); + } + +} diff --git a/com/wentch/redkale/convert/ext/ShortArraySimpledCoder.java b/com/wentch/redkale/convert/ext/ShortArraySimpledCoder.java new file mode 100644 index 000000000..8c73f94e1 --- /dev/null +++ b/com/wentch/redkale/convert/ext/ShortArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class ShortArraySimpledCoder extends SimpledCoder { + + public static final ShortArraySimpledCoder instance = new ShortArraySimpledCoder(); + + @Override + public void convertTo(W out, short[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (short v : values) { + if (flag) out.writeArrayMark(); + out.writeShort(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public short[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + short[] data = new short[8]; + while (in.hasNext()) { + if (size >= data.length) { + short[] newdata = new short[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readShort(); + } + in.readArrayE(); + short[] newdata = new short[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + short[] values = new short[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readShort(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/ShortSimpledCoder.java b/com/wentch/redkale/convert/ext/ShortSimpledCoder.java new file mode 100644 index 000000000..50c91b1c9 --- /dev/null +++ b/com/wentch/redkale/convert/ext/ShortSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class ShortSimpledCoder extends SimpledCoder { + + public static final ShortSimpledCoder instance = new ShortSimpledCoder(); + + @Override + public void convertTo(W out, Short value) { + out.writeShort(value); + } + + @Override + public Short convertFrom(R in) { + return in.readShort(); + } + +} diff --git a/com/wentch/redkale/convert/ext/StringArraySimpledCoder.java b/com/wentch/redkale/convert/ext/StringArraySimpledCoder.java new file mode 100644 index 000000000..8cbc06cf7 --- /dev/null +++ b/com/wentch/redkale/convert/ext/StringArraySimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class StringArraySimpledCoder extends SimpledCoder { + + public static final StringArraySimpledCoder instance = new StringArraySimpledCoder(); + + @Override + public void convertTo(W out, String[] values) { + if (values == null) { + out.writeNull(); + return; + } + out.writeArrayB(values.length); + boolean flag = false; + for (String v : values) { + if (flag) out.writeArrayMark(); + out.writeString(v); + flag = true; + } + out.writeArrayE(); + } + + @Override + public String[] convertFrom(R in) { + int len = in.readArrayB(); + if (len == Reader.SIGN_NULL) { + return null; + } else if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + String[] data = new String[8]; + while (in.hasNext()) { + if (size >= data.length) { + String[] newdata = new String[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readString(); + } + in.readArrayE(); + String[] newdata = new String[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + String[] values = new String[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readString(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/com/wentch/redkale/convert/ext/StringSimpledCoder.java b/com/wentch/redkale/convert/ext/StringSimpledCoder.java new file mode 100644 index 000000000..aeafee793 --- /dev/null +++ b/com/wentch/redkale/convert/ext/StringSimpledCoder.java @@ -0,0 +1,32 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.SimpledCoder; +import com.wentch.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class StringSimpledCoder extends SimpledCoder { + + public static final StringSimpledCoder instance = new StringSimpledCoder(); + + @Override + public void convertTo(W out, String value) { + out.writeString(value); + } + + @Override + public String convertFrom(R in) { + return in.readString(); + } + +} diff --git a/com/wentch/redkale/convert/ext/TypeSimpledCoder.java b/com/wentch/redkale/convert/ext/TypeSimpledCoder.java new file mode 100644 index 000000000..dc6397252 --- /dev/null +++ b/com/wentch/redkale/convert/ext/TypeSimpledCoder.java @@ -0,0 +1,42 @@ +/* + * 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 com.wentch.redkale.convert.ext; + +import com.wentch.redkale.convert.Reader; +import com.wentch.redkale.convert.Writer; +import com.wentch.redkale.convert.SimpledCoder; + +/** + * + * @author zhangjx + * @param + * @param + */ +public class TypeSimpledCoder extends SimpledCoder { + + public static final TypeSimpledCoder instance = new TypeSimpledCoder(); + + @Override + public void convertTo(final W out, final Class value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.getName()); + } + } + + @Override + public Class convertFrom(R in) { + String str = in.readSmallString(); + if (str == null) return null; + try { + return Class.forName(str); + } catch (Exception e) { + return null; + } + } + +} diff --git a/com/wentch/redkale/convert/json/InetAddressJsonSimpledCoder.java b/com/wentch/redkale/convert/json/InetAddressJsonSimpledCoder.java new file mode 100644 index 000000000..0e0e95eb8 --- /dev/null +++ b/com/wentch/redkale/convert/json/InetAddressJsonSimpledCoder.java @@ -0,0 +1,68 @@ +/* + * 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 com.wentch.redkale.convert.json; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.convert.ext.*; +import java.net.*; + +/** + * + * @author zhangjx + * @param + * @param + */ +public final class InetAddressJsonSimpledCoder extends SimpledCoder { + + public static final InetAddressJsonSimpledCoder instance = new InetAddressJsonSimpledCoder(); + + @Override + public void convertTo(JsonWriter out, InetAddress value) { + if (value == null) { + out.writeNull(); + return; + } + StringSimpledCoder.instance.convertTo(out, value.getHostAddress()); + } + + @Override + public InetAddress convertFrom(JsonReader in) { + String str = StringSimpledCoder.instance.convertFrom(in); + if (str == null) return null; + try { + return InetAddress.getByName(str); + } catch (Exception ex) { + return null; + } + } + + public final static class InetSocketAddressJsonSimpledCoder extends SimpledCoder { + + public static final InetSocketAddressJsonSimpledCoder instance = new InetSocketAddressJsonSimpledCoder(); + + @Override + public void convertTo(JsonWriter out, InetSocketAddress value) { + if (value == null) { + out.writeNull(); + return; + } + StringSimpledCoder.instance.convertTo(out, value.getHostString() + ":" + value.getPort()); + } + + @Override + public InetSocketAddress convertFrom(JsonReader in) { + String str = StringSimpledCoder.instance.convertFrom(in); + if (str == null) return null; + try { + int pos = str.indexOf(':'); + return new InetSocketAddress(str.substring(0, pos), Integer.parseInt(str.substring(pos + 1))); + } catch (Exception ex) { + return null; + } + } + + } +} diff --git a/com/wentch/redkale/convert/json/JsonConvert.java b/com/wentch/redkale/convert/json/JsonConvert.java new file mode 100644 index 000000000..d965935e3 --- /dev/null +++ b/com/wentch/redkale/convert/json/JsonConvert.java @@ -0,0 +1,88 @@ +/* + * 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 com.wentch.redkale.convert.json; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.util.*; +import java.lang.reflect.*; + +/** + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public final class JsonConvert extends Convert { + + public static final Type TYPE_MAP_STRING_STRING = new TypeToken>() { + }.getType(); + + private static final ObjectPool readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16)); + + private static final ObjectPool writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", 16)); + + private final boolean tiny; + + protected JsonConvert(JsonFactory factory, boolean tiny) { + super(factory); + this.tiny = tiny; + } + + @Override + public JsonFactory getFactory() { + return (JsonFactory) factory; + } + + public T convertFrom(final Type type, final String text) { + if (text == null) return null; + return convertFrom(type, Utility.charArray(text)); + } + + public T convertFrom(final Type type, final char[] text) { + if (text == null) return null; + return convertFrom(type, text, 0, text.length); + } + + public T convertFrom(final Type type, final char[] text, int start, int len) { + if (text == null || type == null) return null; + final JsonReader in = readerPool.poll(); + in.setText(text, start, len); + T rs = (T) factory.loadDecoder(type).convertFrom(in); + readerPool.offer(in); + return rs; + } + + public String convertTo(final Type type, Object value) { + if (type == null) return null; + if (value == null) return "null"; + final JsonWriter out = writerPool.poll(); + out.setTiny(tiny); + factory.loadEncoder(type).convertTo(out, value); + String result = out.toString(); + writerPool.offer(out); + return result; + } + + public String convertTo(Object value) { + if (value == null) return "null"; + return convertTo(value.getClass(), value); + } + + public byte[] convertToUTF8Bytes(Object value) { + if (value == null) return new byte[]{110, 117, 108, 108}; + return convertToUTF8Bytes(value.getClass(), value); + } + + public byte[] convertToUTF8Bytes(final Type type, Object value) { + if (type == null) return null; + if (value == null) return new byte[]{110, 117, 108, 108}; + final JsonWriter out = writerPool.poll(); + out.setTiny(tiny); + factory.loadEncoder(type).convertTo(out, value); + byte[] result = out.toUTF8Bytes(); + writerPool.offer(out); + return result; + } +} diff --git a/com/wentch/redkale/convert/json/JsonFactory.java b/com/wentch/redkale/convert/json/JsonFactory.java new file mode 100644 index 000000000..0b7f5c9f7 --- /dev/null +++ b/com/wentch/redkale/convert/json/JsonFactory.java @@ -0,0 +1,60 @@ +/* + * 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 com.wentch.redkale.convert.json; + +import com.wentch.redkale.convert.ConvertType; +import com.wentch.redkale.convert.Factory; +import java.io.Serializable; +import java.net.*; + +/** + * + * @author zhangjx + */ +public final class JsonFactory extends Factory { + + private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny")); + + static { + instance.register(InetAddress.class, InetAddressJsonSimpledCoder.instance); + instance.register(InetSocketAddress.class, InetAddressJsonSimpledCoder.InetSocketAddressJsonSimpledCoder.instance); + instance.register(Serializable.class, instance.loadEncoder(Object.class)); + } + + private JsonFactory(JsonFactory parent, boolean tiny) { + super(parent, tiny); + } + + public static JsonFactory root() { + return instance; + } + + @Override + public final JsonConvert getConvert() { + if (convert == null) convert = new JsonConvert(this, tiny); + return (JsonConvert) convert; + } + + @Override + public JsonFactory createChild() { + return new JsonFactory(this, this.tiny); + } + + @Override + public JsonFactory createChild(boolean tiny) { + return new JsonFactory(this, tiny); + } + + @Override + public ConvertType getConvertType() { + return ConvertType.JSON; + } + + @Override + public boolean isReversible() { + return false; + } +} diff --git a/com/wentch/redkale/convert/json/JsonReader.java b/com/wentch/redkale/convert/json/JsonReader.java new file mode 100644 index 000000000..2e399e3bb --- /dev/null +++ b/com/wentch/redkale/convert/json/JsonReader.java @@ -0,0 +1,575 @@ +/* + * 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 com.wentch.redkale.convert.json; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.util.*; +import java.util.concurrent.atomic.*; + +/** + * + * @author zhangjx + */ +public final class JsonReader implements Reader { + + private int position = -1; + + private char[] text; + + private int limit; + + public static ObjectPool createPool(int max) { + return new ObjectPool(max, new Creator() { + + @Override + public JsonReader create(Object... params) { + return new JsonReader(); + } + }, null, new Predicate() { + + @Override + public boolean test(JsonReader t) { + return t.recycle(); + } + }); + } + + public JsonReader() { + } + + public JsonReader(String json) { + setText(Utility.charArray(json)); + } + + public JsonReader(char[] text) { + setText(text, 0, text.length); + } + + public JsonReader(char[] text, int start, int len) { + setText(text, start, len); + } + + public final void setText(String text) { + JsonReader.this.setText(Utility.charArray(text)); + } + + public final void setText(char[] text) { + setText(text, 0, text.length); + } + + public final void setText(char[] text, int start, int len) { + this.text = text; + this.position = start - 1; + this.limit = start + len - 1; + } + + protected boolean recycle() { + this.position = -1; + this.limit = -1; + this.text = null; + return true; + } + + public void close() { + this.recycle(); + } + + /** + * 找到指定的属性值 例如: {id : 1, data : { name : 'a', items : [1,2,3]}} seek('data.items') 直接跳转到 [1,2,3]; + * + * @param key + */ + public final void seek(String key) { + if (key == null || key.length() < 1) return; + final String[] keys = key.split("\\."); + nextGoodChar(); //读掉 { [ + for (String key1 : keys) { + while (this.hasNext()) { + String field = this.readSmallString(); + skipBlank(); + if (key1.equals(field)) break; + skipValue(); + } + } + + } + + /** + * 跳过属性的值 + */ + @Override + public final void skipValue() { + final char ch = nextGoodChar(); + if (ch == '"' || ch == '\'') { + backChar(ch); + readString(); + } else if (ch == '{') { + while (hasNext()) { + this.readSmallString(); //读掉field + this.skipBlank(); + this.skipValue(); + } + } else if (ch == '[') { + while (hasNext()) { + this.skipValue(); + } + } else { + char c; + for (;;) { + c = nextChar(); + if (c <= ' ') return; + if (c == '}' || c == ']' || c == ',' || c == ':') { + backChar(c); + return; + } + } + } + } + + /** + * 读取下一个字符, 不跳过空白字符 + * + * @return + */ + protected char nextChar() { + return this.text[++this.position]; + } + + /** + * 跳过空白字符, 返回一个非空白字符 + * + * @return + */ + protected char nextGoodChar() { + char c = nextChar(); + if (c > ' ') return c; + for (;;) { + c = nextChar(); + if (c > ' ') return c; + } + } + + /** + * 回退最后读取的字符 + * + * @param ch + */ + protected void backChar(char ch) { + this.position--; + } + + /** + * 判断下一个非空白字符是否为{ + * + */ + @Override + public int readObjectB() { + char ch = this.text[++this.position]; + if (ch == '{') return SIGN_NOLENGTH; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == '{') return SIGN_NOLENGTH; + } + if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL; + if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL; + throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + + @Override + public void readObjectE() { + } + + /** + * 判断下一个非空白字符是否为{ + * + */ + @Override + public int readMapB() { + return readArrayB(); + } + + @Override + public void readMapE() { + } + + /** + * 判断下一个非空白字符是否为[ + * + * @return + */ + @Override + public int readArrayB() { + char ch = this.text[++this.position]; + if (ch == '[') return SIGN_NOLENGTH; + if (ch == '{') return SIGN_NOLENGTH; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == '[') return SIGN_NOLENGTH; + if (ch == '{') return SIGN_NOLENGTH; + } + if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL; + if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL; + throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + + @Override + public void readArrayE() { + } + + /** + * 判断下一个非空白字符是否: + */ + @Override + public void skipBlank() { + char ch = this.text[++this.position]; + if (ch == ':') return; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == ':') return; + } + throw new ConvertException("'" + new String(text) + "'expected a ':' but '" + ch + "'(position = " + position + ") in (" + new String(this.text) + ")"); + } + + /** + * 判断对象是否存在下一个属性或者数组是否存在下一个元素 + * + * @return + */ + @Override + public boolean hasNext() { + char ch = this.text[++this.position]; + if (ch == ',') return true; + if (ch == '}' || ch == ']') return false; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == ',') return true; + if (ch == '}' || ch == ']') return false; + } + this.position--; + return true; + } + + @Override + public String readClassName() { + return null; + } + + @Override + public String readSmallString() { + final int eof = this.limit; + if (this.position == eof) return null; + final char[] text0 = this.text; + int currpos = this.position; + char ch = text0[++currpos]; + if (ch <= ' ') { + for (;;) { + ch = text0[++currpos]; + if (ch > ' ') break; + } + } + if (ch == '"' || ch == '\'') { + final char quote = ch; + final int start = currpos + 1; + for (;;) { + ch = text0[++currpos]; + if (ch == '\\') { + this.position = currpos - 1; + return readEscapeValue(quote, start); + } else if (ch == quote) { + break; + } + } + this.position = currpos; + char[] chs = new char[currpos - start]; + System.arraycopy(text0, start, chs, 0, chs.length); + return new String(chs); + } else { + int start = currpos; + for (;;) { + if (currpos == eof) break; + ch = text0[++currpos]; + if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') break; + } + int len = currpos - start; + if (len < 1) { + this.position = currpos; + return String.valueOf(ch); + } + this.position = currpos - 1; + if (len == 4 && text0[start] == 'n' && text0[start + 1] == 'u' && text0[start + 2] == 'l' && text0[start + 3] == 'l') return null; + return new String(text0, start, len); + } + } + + /** + * 读取一个int + * + * @return + */ + @Override + public final int readInt() { + final char[] text0 = this.text; + final int eof = this.limit; + int currpos = this.position; + char firstchar = text0[++currpos]; + if (firstchar <= ' ') { + for (;;) { + firstchar = text0[++currpos]; + if (firstchar > ' ') break; + } + } + if (firstchar == '"' || firstchar == '\'') { + firstchar = text0[++currpos]; + if (firstchar == '"' || firstchar == '\'') { + this.position = currpos; + return 0; + } + } + int value = 0; + final boolean negative = firstchar == '-'; + if (!negative) { + if (firstchar < '0' || firstchar > '9') throw new NumberFormatException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); + value = firstchar - '0'; + } + for (;;) { + if (currpos == eof) break; + char ch = text0[++currpos]; + if (ch >= '0' && ch <= '9') { + value = (value << 3) + (value << 1) + (ch - '0'); + } else if (ch == '"' || ch == '\'') { + } else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') { + break; + } else { + throw new NumberFormatException("illegal escape(" + ch + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); + } + } + this.position = currpos - 1; + return negative ? -value : value; + } + + /** + * 读取一个long + * + * @return + */ + @Override + public final long readLong() { + final char[] text0 = this.text; + final int eof = this.limit; + int currpos = this.position; + char firstchar = text0[++currpos]; + if (firstchar <= ' ') { + for (;;) { + firstchar = text0[++currpos]; + if (firstchar > ' ') break; + } + } + if (firstchar == '"' || firstchar == '\'') { + firstchar = text0[++currpos]; + if (firstchar == '"' || firstchar == '\'') { + this.position = currpos; + return 0L; + } + } + long value = 0; + final boolean negative = firstchar == '-'; + if (!negative) { + if (firstchar < '0' || firstchar > '9') throw new NumberFormatException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); + value = firstchar - '0'; + } + for (;;) { + if (currpos == eof) break; + char ch = text0[++currpos]; + if (ch >= '0' && ch <= '9') { + value = (value << 3) + (value << 1) + (ch - '0'); + } else if (ch == '"' || ch == '\'') { + } else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') { + break; + } else { + throw new NumberFormatException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + } + this.position = currpos - 1; + return negative ? -value : value; + } + + @Override + public DeMember readField(final AtomicInteger index, final DeMember[] members) { + final String exceptedfield = this.readSmallString(); + final int len = members.length; + int v = index.get(); + if (v >= len) { + v = 0; + index.set(0); + } + for (int k = v; k < len; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + index.set(k); + return members[k]; + } + } + for (int k = 0; k < v; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + index.set(k); + return members[k]; + } + } + return null; + //if (result == null && len == 1 && text0[start] == '@') return REFER; + } +//------------------------------------------------------------ + + @Override + public boolean readBoolean() { + return "true".equalsIgnoreCase(this.readSmallString()); + } + + @Override + public byte readByte() { + return (byte) readInt(); + } + + @Override + public char readChar() { + return (char) readInt(); + } + + @Override + public short readShort() { + return (short) readInt(); + } + + @Override + public float readFloat() { + String chars = readSmallString(); + if (chars == null || chars.isEmpty()) return 0.f; + return Float.parseFloat(chars); + } + + @Override + public double readDouble() { + String chars = readSmallString(); + if (chars == null || chars.isEmpty()) return 0.0; + return Double.parseDouble(chars); + } + + /** + * 读取字符串, 必须是"或者'包围的字符串值 + * + * @return + */ + @Override + public String readString() { + final char[] text0 = this.text; + int currpos = this.position; + char expected = text0[++currpos]; + if (expected <= ' ') { + for (;;) { + expected = text0[++currpos]; + if (expected > ' ') break; + } + } + if (expected != '"' && expected != '\'') { + if (expected == 'n' && text0.length > currpos + 3) { + if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') { + this.position = currpos; + if (text0.length > currpos + 4) { + char ch = text0[currpos + 1]; + if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null; + } else { + return null; + } + } + } else { + final int start = currpos; + for (;;) { + char ch = text0[currpos]; + if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break; + currpos++; + } + if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); + this.position = currpos - 1; + return new String(text0, start, currpos - start); + } + this.position = currpos; + throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); + } + final int start = ++currpos; + for (;;) { + char ch = text0[currpos]; + if (ch == expected) { + break; + } else if (ch == '\\') { + this.position = currpos - 1; + return readEscapeValue(expected, start); + } + currpos++; + } + this.position = currpos; + return new String(text0, start, currpos - start); + } + + private String readEscapeValue(final char expected, int start) { + StringBuilder array = new StringBuilder(); + final char[] text0 = this.text; + int pos = this.position; + array.append(text0, start, pos + 1 - start); + char c; + for (;;) { + c = text0[++pos]; + if (c == expected) { + this.position = pos; + return array.toString(); + } else if (c == '\\') { + c = text0[++pos]; + switch (c) { + case '"': + case '\'': + case '\\': + case '/': + array.append(c); + break; + case 'n': + array.append('\n'); + break; + case 'r': + array.append('\r'); + break; + case 'u': + array.append((char) Integer.parseInt(new String(new char[]{text0[++pos], text0[++pos], text0[++pos], text0[++pos]}), 16)); + break; + case 't': + array.append('\t'); + break; + case 'b': + array.append('\b'); + break; + case 'f': + array.append('\f'); + break; + default: + this.position = pos; + throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ") in (" + new String(this.text) + ")"); + } + } else { + array.append(c); + } + } + } + +} diff --git a/com/wentch/redkale/convert/json/JsonSimpledCoder.java b/com/wentch/redkale/convert/json/JsonSimpledCoder.java new file mode 100644 index 000000000..cd28528ff --- /dev/null +++ b/com/wentch/redkale/convert/json/JsonSimpledCoder.java @@ -0,0 +1,17 @@ +/* + * 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 com.wentch.redkale.convert.json; + +import com.wentch.redkale.convert.SimpledCoder; + +/** + * + * @author zhangjx + * @param + */ +public abstract class JsonSimpledCoder extends SimpledCoder { + +} diff --git a/com/wentch/redkale/convert/json/JsonWriter.java b/com/wentch/redkale/convert/json/JsonWriter.java new file mode 100644 index 000000000..34f6481a5 --- /dev/null +++ b/com/wentch/redkale/convert/json/JsonWriter.java @@ -0,0 +1,299 @@ +/* + * 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 com.wentch.redkale.convert.json; + +import com.wentch.redkale.convert.*; +import com.wentch.redkale.util.*; + +/** + * + * writeTo系列的方法输出的字符不能含特殊字符 + * + * @author zhangjx + */ +public final class JsonWriter implements Writer { + + private static final char[] CHARS_TUREVALUE = "true".toCharArray(); + + private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); + + private static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", 1024); + + protected int count; + + private char[] content; + + protected boolean tiny; + + public static ObjectPool createPool(int max) { + return new ObjectPool(max, new Creator() { + + @Override + public JsonWriter create(Object... params) { + return new JsonWriter(); + } + }, null, new Predicate() { + + @Override + public boolean test(JsonWriter t) { + return t.recycle(); + } + }); + } + + public JsonWriter() { + this(defaultSize); + } + + public JsonWriter(int size) { + this.content = new char[size > 128 ? size : 128]; + } + + @Override + public boolean isTiny() { + return tiny; + } + + public void setTiny(boolean tiny) { + this.tiny = tiny; + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + /** + * 返回指定至少指定长度的缓冲区 + * + * @param len + * @return + */ + public char[] expand(int len) { + int newcount = count + len; + if (newcount <= content.length) return content; + char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)]; + System.arraycopy(content, 0, newdata, 0, count); + this.content = newdata; + return newdata; + } + + public void writeTo(final char ch) { + expand(1); + content[count++] = ch; + } + + public void writeTo(final char... chs) { + int len = chs.length; + expand(len); + System.arraycopy(chs, 0, content, count, len); + count += len; + } + + public void writeTo(final char[] chs, final int start, final int end) { + int len = end - start; + expand(len); + System.arraycopy(chs, start, content, count, len); + count += len; + } + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String + * + * @param quote + * @param value + */ + public void writeTo(final boolean quote, final String value) { + int len = value.length(); + expand(len + (quote ? 2 : 0)); + if (quote) content[count++] = '"'; + value.getChars(0, len, content, count); + count += len; + if (quote) content[count++] = '"'; + } + + protected boolean recycle() { + this.count = 0; + if (this.content.length > defaultSize) { + this.content = new char[defaultSize]; + } + return true; + } + + public char[] toArray() { + if (count == content.length) return content; + char[] newdata = new char[count]; + System.arraycopy(content, 0, newdata, 0, count); + return newdata; + } + + public byte[] toUTF8Bytes() { + return Utility.encodeUTF8(content, 0, count); + } + + //------------------------------------------------------------------------ + public final int count() { + return this.count; + } + + public final void count(int count) { + if (count >= 0) this.count = count; + } + +// @SuppressWarnings("unchecked") +// public final boolean writeRefer(final Object value) { +// if (stack == null) return false; +// int index = stack.indexOf(value); +// if (index > -1) { +// int deep = stack.size() - index; +// if (deep < 0) throw new ConvertException("the refer deep value(" + deep + ") is illegal"); +// writeTo('{', '"', '@', '"', ':', '"'); +// for (int i = 0; i < deep; i++) { +// writeTo('@'); +// } +// writeTo('"', '}'); +// return true; +// } +// return false; +// } + //----------------------------------------------------------------------- + @Override + public String toString() { + return new String(content, 0, count); + } + + @Override + public void writeBoolean(boolean value) { + writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE); + } + + @Override + public void writeByte(byte value) { + writeInt(value); + } + + @Override + public void writeChar(char value) { + writeInt(value); + } + + @Override + public void writeShort(short value) { + writeInt(value); + } + + @Override + public void writeInt(int value) { + writeSmallString(String.valueOf(value)); + } + + @Override + public void writeLong(long value) { + writeSmallString(String.valueOf(value)); + } + + @Override + public void writeFloat(float value) { + writeSmallString(String.valueOf(value)); + } + + @Override + public void writeDouble(double value) { + writeSmallString(String.valueOf(value)); + } + + @Override + public void writeString(String value) { + if (value == null) { + writeNull(); + return; + } + expand(value.length() * 2 + 2); + content[count++] = '"'; + for (char ch : Utility.charArray(value)) { + switch (ch) { + case '\n': content[count++] = '\\'; + content[count++] = 'n'; + break; + case '\r': content[count++] = '\\'; + content[count++] = 'r'; + break; + case '\t': content[count++] = '\\'; + content[count++] = 't'; + break; + case '\\': content[count++] = '\\'; + content[count++] = ch; + break; + case '"': content[count++] = '\\'; + content[count++] = ch; + break; + default: content[count++] = ch; + break; + } + } + content[count++] = '"'; + } + + @Override + public void wirteClassName(String clazz) { + } + + @Override + public final void writeObjectB(int fieldCount, Object obj) { + writeTo('{'); + } + + @Override + public final void writeObjectE(Object obj) { + writeTo('}'); + } + + @Override + public final void writeField(boolean comma, Attribute attribute) { + if (comma) writeTo(','); + writeTo('"'); + writeSmallString(attribute.field()); + writeTo('"'); + writeTo(':'); + } + + @Override + public final void writeNull() { + writeTo('n', 'u', 'l', 'l'); + } + + @Override + public void writeArrayB(int size) { + writeTo('['); + } + + @Override + public void writeArrayMark() { + writeTo(','); + } + + @Override + public void writeArrayE() { + writeTo(']'); + } + + @Override + public void writeMapB(int size) { + writeTo('{'); + } + + @Override + public void writeMapMark() { + writeTo(':'); + } + + @Override + public void writeMapE() { + writeTo('}'); + } + + @Override + public void writeSmallString(String value) { + writeTo(false, value); + } +} diff --git a/com/wentch/redkale/util/Attribute.java b/com/wentch/redkale/util/Attribute.java new file mode 100644 index 000000000..7bbae9781 --- /dev/null +++ b/com/wentch/redkale/util/Attribute.java @@ -0,0 +1,300 @@ +/* + * 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 com.wentch.redkale.util; + +import java.lang.reflect.*; +import static org.objectweb.asm.Opcodes.*; +import org.objectweb.asm.Type; + +/** + * 该类功能是动态映射一个Data类中成员对应的getter、setter方法; 代替低效的反射实现方式。 + * 映射Field时,field要么是public非final,要么存在对应的getter、setter方法。 + * + * @author zhangjx + * @param + * @param + */ +public interface Attribute { + + public Class type(); + + public Class declaringClass(); + + public String field(); + + public F get(T obj); + + public void set(T obj, F value); + + public static final class Attributes { + + public static Attribute create(final Field field) { + return create((Class) field.getDeclaringClass(), field.getName(), field, null, null); + } + + public static Attribute create(String fieldname, final Field field) { + return create((Class) field.getDeclaringClass(), fieldname, field, null, null); + } + + public static Attribute create(Class clazz, final String fieldname) { + try { + return create(clazz, fieldname, clazz.getDeclaredField(fieldname), null, null); + } catch (NoSuchFieldException ex) { + throw new RuntimeException(ex); + } catch (SecurityException ex2) { + throw new RuntimeException(ex2); + } + } + + public static Attribute create(Class clazz, final java.lang.reflect.Field field) { + return create(clazz, field.getName(), field); + } + + public static Attribute create(Class clazz, final String fieldname, final java.lang.reflect.Field field) { + return create(clazz, fieldname, field, null, null); + } + + public static Attribute create(final Method getter, final Method setter) { + return create((Class) (getter == null ? setter.getDeclaringClass() : getter.getDeclaringClass()), null, null, getter, setter); + } + + public static Attribute create(Class clazz, final Method getter, final Method setter) { + return create(clazz, null, null, getter, setter); + } + + public static Attribute create(Class clazz, final String fieldalias, final Method getter, final Method setter) { + return create(clazz, fieldalias, null, getter, setter); + } + + @SuppressWarnings("unchecked") + public static Attribute create(final Class clazz, String fieldalias0, final Field field0, Method getter0, Method setter0) { + if (fieldalias0 != null && fieldalias0.isEmpty()) fieldalias0 = null; + int mod = field0 == null ? Modifier.STATIC : field0.getModifiers(); + if (field0 != null && !Modifier.isStatic(mod) && !Modifier.isPublic(mod)) { + Class t = field0.getType(); + char[] fs = field0.getName().toCharArray(); + fs[0] = Character.toUpperCase(fs[0]); + String mn = new String(fs); + if (getter0 == null) { + String prefix = t == boolean.class || t == Boolean.class ? "is" : "get"; + try { + getter0 = clazz.getMethod(prefix + mn); + } catch (Exception ex) { + } + } + if (setter0 == null) { + try { + setter0 = clazz.getMethod("set" + mn, field0.getType()); + } catch (Exception ex) { + } + } + } + final Field field = field0 == null ? null : (!Modifier.isPublic(mod) || Modifier.isStatic(mod) ? null : field0); + final java.lang.reflect.Method getter = getter0; + final java.lang.reflect.Method setter = setter0; + if (fieldalias0 == null) { + if (field0 != null) { + fieldalias0 = field0.getName(); + } else { + String s; + if (getter0 != null) { + s = getter0.getName().substring(getter0.getName().startsWith("is") ? 2 : 3); + } else { + s = setter0.getName().substring(3); + } + char[] d = s.toCharArray(); + if (d.length < 2 || Character.isLowerCase(d[1])) { + d[0] = Character.toLowerCase(d[0]); + } + fieldalias0 = new String(d); + } + } + if (getter == null && setter == null && field == null) { + throw new RuntimeException("[" + clazz + "]have no public field or setter or getter"); + } + final String fieldname = fieldalias0; + Class column; + if (field != null) { // public field + column = field.getType(); + } else if (getter != null) { + column = getter.getReturnType(); + } else { // setter != null + column = setter.getParameterTypes()[0]; + } + final Class pcolumn = column; + if (column.isPrimitive()) column = Array.get(Array.newInstance(column, 1), 0).getClass(); + final String supDynName = Attribute.class.getName().replace('.', '/'); + final String interName = clazz.getName().replace('.', '/'); + final String columnName = column.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(clazz); + final String columnDesc = Type.getDescriptor(column); + + ClassLoader loader = Attribute.class.getClassLoader(); + String newDynName = supDynName + "_Dyn_" + clazz.getSimpleName() + "_" + + fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array"); + if (String.class.getClassLoader() != clazz.getClassLoader()) { + loader = clazz.getClassLoader(); + newDynName = interName + "_Dyn" + Attribute.class.getSimpleName() + "_" + + fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array"); + } + try { + return (Attribute) Class.forName(newDynName.replace('/', '.')).newInstance(); + } catch (Exception ex) { + } + //--------------------------------------------------- + final org.objectweb.asm.ClassWriter cw = new org.objectweb.asm.ClassWriter(0); + org.objectweb.asm.MethodVisitor mv; + + cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + columnDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + { //构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { //field 方法 + mv = cw.visitMethod(ACC_PUBLIC, "field", "()Ljava/lang/String;", null, null); + mv.visitLdcInsn(fieldname); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //type 方法 + mv = cw.visitMethod(ACC_PUBLIC, "type", "()Ljava/lang/Class;", null, null); + if (pcolumn == boolean.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == byte.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == char.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == short.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == int.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == float.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == long.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == double.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); + } else { + mv.visitLdcInsn(Type.getType(pcolumn)); + } + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //declaringClass 方法 + mv = cw.visitMethod(ACC_PUBLIC, "declaringClass", "()Ljava/lang/Class;", null, null); + mv.visitLdcInsn(Type.getType(clazz)); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //get 方法 + mv = cw.visitMethod(ACC_PUBLIC, "get", "(" + interDesc + ")" + columnDesc, null, null); + int m = 1; + if (getter == null) { + if (field == null) { + mv.visitInsn(ACONST_NULL); + } else { //public field + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, interName, field.getName(), Type.getDescriptor(pcolumn)); + if (pcolumn != column) { + mv.visitMethodInsn(INVOKESTATIC, columnName, "valueOf", "(" + Type.getDescriptor(pcolumn) + ")" + columnDesc, false); + } + } + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, interName, getter.getName(), Type.getMethodDescriptor(getter), false); + if (pcolumn != column) { + mv.visitMethodInsn(INVOKESTATIC, columnName, "valueOf", "(" + Type.getDescriptor(pcolumn) + ")" + columnDesc, false); + m = 2; + } + } + mv.visitInsn(ARETURN); + mv.visitMaxs(m, 2); + mv.visitEnd(); + } + { //set 方法 + mv = cw.visitMethod(ACC_PUBLIC, "set", "(" + interDesc + columnDesc + ")V", null, null); + int m = 2; + if (setter == null) { + if (field == null || Modifier.isFinal(field.getModifiers())) { + m = 0; + } else { //public field + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + if (pcolumn != column) { + try { + java.lang.reflect.Method pm = column.getMethod(pcolumn.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, columnName, pm.getName(), Type.getMethodDescriptor(pm), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + mv.visitFieldInsn(PUTFIELD, interName, field.getName(), Type.getDescriptor(pcolumn)); + } + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + if (pcolumn != column) { + try { + java.lang.reflect.Method pm = column.getMethod(pcolumn.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, columnName, pm.getName(), Type.getMethodDescriptor(pm), false); + m = 3; + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + mv.visitMethodInsn(INVOKEVIRTUAL, interName, setter.getName(), Type.getMethodDescriptor(setter), false); + } + mv.visitInsn(RETURN); + mv.visitMaxs(m, 3); + mv.visitEnd(); + } + { //虚拟get + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, interName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "get", "(" + interDesc + ")" + columnDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + {//虚拟set + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, interName); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, columnName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "set", "(" + interDesc + columnDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + + byte[] bytes = cw.toByteArray(); + Class creatorClazz = (Class) new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + try { + return creatorClazz.newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } +} diff --git a/com/wentch/redkale/util/BiPredicate.java b/com/wentch/redkale/util/BiPredicate.java new file mode 100644 index 000000000..20624a6c4 --- /dev/null +++ b/com/wentch/redkale/util/BiPredicate.java @@ -0,0 +1,27 @@ +/* + * 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 com.wentch.redkale.util; + +/** + * JDK 8 java.util.function.BiPredicate + * + * @author zhangjx + * @param + * @param + */ +public interface BiPredicate { + + /** + * Evaluates this predicate on the given arguments. + * + * @param t the first input argument + * @param u the second input argument + * @return {@code true} if the input arguments match the predicate, + * otherwise {@code false} + */ + boolean test(T t, U u); + +} diff --git a/com/wentch/redkale/util/ByteArray.java b/com/wentch/redkale/util/ByteArray.java new file mode 100644 index 000000000..4dc85bbbf --- /dev/null +++ b/com/wentch/redkale/util/ByteArray.java @@ -0,0 +1,161 @@ +/* + * 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 com.wentch.redkale.util; + +import java.nio.*; +import java.nio.charset.*; + +/** + * + * @author zhangjx + */ +public final class ByteArray { + + private byte[] content; + + private int count; + + public ByteArray() { + this(1024); + } + + public ByteArray(int size) { + content = new byte[Math.max(128, size)]; + } + + public void clear() { + this.count = 0; + } + + public int find(byte value) { + return find(0, value); + } + + public boolean equal(final byte[] bytes) { + if (bytes == null || count != bytes.length) return false; + for (int i = 0; i < count; i++) { + if (content[i] != bytes[i]) return false; + } + return true; + } + + public boolean isEmpty() { + return count == 0; + } + + public int count() { + return count; + } + + public void write(byte[] buf) { + System.arraycopy(this.content, 0, buf, 0, count); + } + + public byte[] directBytes() { + return content; + } + + public int find(int offset, char value) { + return find(offset, (byte) value); + } + + public int find(int offset, byte value) { + return find(offset, -1, value); + } + + public int find(int offset, int limit, char value) { + return find(offset, limit, (byte) value); + } + + public int find(int offset, int limit, byte value) { + byte[] bytes = this.content; + int end = limit > 0 ? limit : count; + for (int i = offset; i < end; i++) { + if (bytes[i] == value) return i; + } + return -1; + } + + public void removeLastByte() { + if (count > 0) count--; + } + + public void addInt(int value) { + add((byte) (value >> 24 & 0xFF), (byte) (value >> 16 & 0xFF), (byte) (value >> 8 & 0xFF), (byte) (value & 0xFF)); + } + + public void add(byte value) { + if (count >= content.length - 1) { + byte[] ns = new byte[content.length + 8]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + content[count++] = value; + } + + public void add(byte... values) { + if (count >= content.length - values.length) { + byte[] ns = new byte[content.length + values.length]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + System.arraycopy(content, count, values, 0, values.length); + count += values.length; + } + + public void add(ByteBuffer buffer, int len) { + if (len < 1) return; + if (count >= content.length - len) { + byte[] ns = new byte[content.length + len]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + buffer.get(content, count, len); + count += len; + } + + @Override + public String toString() { + return new String(content, 0, count); + } + + public String toString(final Charset charset) { + return toString(0, count, charset); + } + + public String toString(final int offset, int len, final Charset charset) { + if (charset == null) return new String(Utility.decodeUTF8(content, offset, len)); + return new String(content, offset, len, charset); + } + + public String toDecodeString(final int offset, int len, final Charset charset) { + int index = offset; + for (int i = offset; i < (offset + len); i++) { + if (content[i] == '+') { + content[index] = ' '; + } else if (content[i] == '%') { + content[index] = (byte) ((hexBit(content[++i]) * 16 + hexBit(content[++i]))); + } else { + content[index] = content[i]; + } + index++; + } + for (int i = index + 1; i < (offset + len); i++) { + content[i] = ' '; + } + len = index - offset; + if (charset == null) return new String(Utility.decodeUTF8(content, offset, len)); + return new String(content, offset, len, charset); + } + + private static int hexBit(byte b) { + if ('0' <= b && '9' >= b) return b - '0'; + if ('a' <= b && 'z' >= b) return b - 'a' + 10; + if ('A' <= b && 'Z' >= b) return b - 'A' + 10; + return b; + } + +} diff --git a/com/wentch/redkale/util/Consumer.java b/com/wentch/redkale/util/Consumer.java new file mode 100644 index 000000000..2ae5f6af6 --- /dev/null +++ b/com/wentch/redkale/util/Consumer.java @@ -0,0 +1,22 @@ +/* + * 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 com.wentch.redkale.util; + +/** + * JDK 8 java.util.function.Consumer + * + * @author zhangjx + * @param + */ +public interface Consumer { + + /** + * Performs this operation on the given argument. + * + * @param t the input argument + */ + void accept(T t); +} diff --git a/com/wentch/redkale/util/Creator.java b/com/wentch/redkale/util/Creator.java new file mode 100644 index 000000000..23efe455f --- /dev/null +++ b/com/wentch/redkale/util/Creator.java @@ -0,0 +1,210 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.util; + +import java.beans.ConstructorProperties; +import java.lang.reflect.*; +import java.util.*; +import org.objectweb.asm.*; +import static org.objectweb.asm.Opcodes.*; +import org.objectweb.asm.Type; + +/** + * 实现一个类的构造方法。 代替低效的反射实现方式。 + * + * @author zhangjx + * @param + */ +public interface Creator { +// +// static class PooledCreator implements Creator { +// +// private final T defValue; +// +// private final Reproduce reproduce; +// +// private final ReferenceQueue refQueue = new ReferenceQueue(); +// +// private final Queue queue; +// +// private final Creator creator; +// +// public PooledCreator(int max, Class clazz, Creator creator) { +// this.creator = creator; +// this.defValue = creator.create(); +// this.reproduce = Reproduce.create(clazz, clazz); +// this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max)); +// new Thread() { +// { +// setDaemon(true); +// setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler"); +// } +// +// @Override +// public void run() { +// try { +// for (;;) { +// T r = refQueue.remove().get(); +// if (r == null) continue; +// reproduce.copy(r, defValue); +// queue.offer(r); +// } +// } catch (Exception e) { +// //do nothind +// } +// } +// }.start(); +// } +// +// @Override +// public T create(Object... params) { +// T rs = queue.poll(); +// if (rs == null) { +// rs = creator.create(params); +// } +// return new WeakReference<>(rs, refQueue).get(); +// } +// +// } +// +// @SuppressWarnings("unchecked") +// public static Creator create(int max, Class clazz) { +// return new PooledCreator<>(max, clazz, create(clazz)); +// } +// +// @SuppressWarnings("unchecked") +// public static Creator create(int max, Class clazz, Creator creator) { +// return new PooledCreator<>(max, clazz, creator); +// } + + public T create(Object... params); + + public static final class Creators { + + @SuppressWarnings("unchecked") + public static Creator create(Class clazz) { + if (clazz.isAssignableFrom(ArrayList.class)) { + clazz = (Class) ArrayList.class; + } else if (clazz.isAssignableFrom(HashMap.class)) { + clazz = (Class) HashMap.class; + } else if (clazz.isAssignableFrom(HashSet.class)) { + clazz = (Class) HashSet.class; + } + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { + throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator."); + } + final String supDynName = Creator.class.getName().replace('.', '/'); + final String interName = clazz.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(clazz); + ClassLoader loader = Creator.class.getClassLoader(); + String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000); + if (String.class.getClassLoader() != clazz.getClassLoader()) { + loader = clazz.getClassLoader(); + newDynName = interName + "_Dyn" + Creator.class.getSimpleName(); + } + try { + return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance(); + } catch (Exception ex) { + } + Constructor constructor = null; + for (Constructor c : clazz.getConstructors()) { + if (c.getParameterCount() == 0) { + constructor = c; + break; + } + } + if (constructor == null) { + for (Constructor c : clazz.getConstructors()) { + if (c.getAnnotation(ConstructorProperties.class) != null) { + constructor = c; + break; + } + } + } + if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor."); + //------------------------------------------------------------- + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + {//构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class); + if (cps != null) { + av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true); + AnnotationVisitor av1 = av0.visitArray("value"); + for (String n : cps.value()) { + av1.visit(null, n); + } + av1.visitEnd(); + av0.visitEnd(); + } + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + {//create 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); + mv.visitTypeInsn(NEW, interName); + mv.visitInsn(DUP); + //--------------------------------------- + { + Parameter[] params = constructor.getParameters(); + final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5}; + for (int i = 0; i < params.length; i++) { + mv.visitVarInsn(ALOAD, 1); + if (i < 6) { + mv.visitInsn(iconsts[i]); + } else { + mv.visitIntInsn(BIPUSH, i); + } + mv.visitInsn(AALOAD); + Class ct = params[i].getType(); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct)); + if (ct.isPrimitive()) { + Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass(); + try { + Method pm = ct.getMethod(ct.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + } + } + //--------------------------------------- + mv.visitMethodInsn(INVOKESPECIAL, interName, "", Type.getConstructorDescriptor(constructor), false); + mv.visitInsn(ARETURN); + mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2); + mv.visitEnd(); + } + { //虚拟 create 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class creatorClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + try { + return (Creator) creatorClazz.newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } +} diff --git a/com/wentch/redkale/util/DLong.java b/com/wentch/redkale/util/DLong.java new file mode 100644 index 000000000..919e31568 --- /dev/null +++ b/com/wentch/redkale/util/DLong.java @@ -0,0 +1,81 @@ +/* + * 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 com.wentch.redkale.util; + +/** + * + * @author zhangjx + */ +public final class DLong extends Number implements Comparable { + + private final long first; + + private final long second; + + public DLong(long one, long two) { + this.first = one; + this.second = two; + } + + public long getFirst() { + return first; + } + + public long getSecond() { + return second; + } + + public boolean compare(long one, long two) { + return this.first == one && this.second == two; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final DLong other = (DLong) obj; + return (this.first == other.first && this.second == other.second); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 89 * hash + (int) (this.first ^ (this.first >>> 32)); + hash = 89 * hash + (int) (this.second ^ (this.second >>> 32)); + return hash; + } + + @Override + public String toString() { + return this.first + "_" + this.second; + } + + @Override + public int intValue() { + return (int) longValue(); + } + + @Override + public long longValue() { + return first ^ second; + } + + @Override + public float floatValue() { + return (float) longValue(); + } + + @Override + public double doubleValue() { + return (double) longValue(); + } + + @Override + public int compareTo(DLong o) { + return (int) (first == o.first ? (second - o.second) : (first - o.first)); + } + +} diff --git a/com/wentch/redkale/util/DebugMethodVisitor.java b/com/wentch/redkale/util/DebugMethodVisitor.java new file mode 100644 index 000000000..7c8cc848b --- /dev/null +++ b/com/wentch/redkale/util/DebugMethodVisitor.java @@ -0,0 +1,143 @@ +/* + * 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 com.wentch.redkale.util; + +import java.util.*; +import org.objectweb.asm.*; + +/** + * + * @author zhangjx + */ +public class DebugMethodVisitor { + + private final MethodVisitor visitor; + + private boolean debug = false; + + public void setDebug(boolean d) { + debug = d; + } + + private final Map labels = new LinkedHashMap(); + + private static final String[] opcodes = new String[200]; //0 -18 + + static { + try { + for (java.lang.reflect.Field field : Opcodes.class.getFields()) { + String name = field.getName(); + if (name.startsWith("ASM")) continue; + if (name.startsWith("V1_")) continue; + if (name.startsWith("ACC_")) continue; + if (name.startsWith("T_")) continue; + if (name.startsWith("H_")) continue; + if (name.startsWith("F_")) continue; + if (field.getType() != int.class) continue; + opcodes[(int) (Integer) field.get(null)] = name; + } + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + + public DebugMethodVisitor(MethodVisitor visitor) { + //super(Opcodes.ASM5, visitor); + this.visitor = visitor; + } + + public AnnotationVisitor visitAnnotation(String desc, boolean flag) { + AnnotationVisitor av = visitor.visitAnnotation(desc, flag); + if (debug) System.out.println("mv.visitAnnotation(\"" + desc + "\", " + flag + ");"); + return av; + } + + public void visitParameter(String name, int access) { + visitor.visitParameter(name, access); + if (debug) System.out.println("mv.visitParameter(" + name + ", " + access + ");"); + } + + public void visitVarInsn(int opcode, int var) { + visitor.visitVarInsn(opcode, var); + if (debug) System.out.println("mv.visitVarInsn(" + opcodes[opcode] + ", " + var + ");"); + } + + public void visitJumpInsn(int opcode, Label var) { //调用次方法的 ClassWriter 必须由 COMPUTE_FRAMES 构建 + visitor.visitJumpInsn(opcode, var); + if (debug) { + Integer index = labels.get(var); + if (index == null) { + index = labels.size(); + labels.put(var, index); + System.out.println("Label l" + index + " = new Label();"); + } + System.out.println("mv.visitJumpInsn(" + opcodes[opcode] + ", l" + index + ");"); + } + } + + public void visitCode() { + visitor.visitCode(); + if (debug) System.out.println("mv.visitCode();"); + } + + public void visitLabel(Label var) { + visitor.visitLabel(var); + if (debug) { + Integer index = labels.get(var); + if (index == null) { + index = labels.size(); + labels.put(var, index); + System.out.println("Label l" + index + " = new Label();"); + } + System.out.println("mv.visitLabel(l" + index + ");"); + } + } + + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + visitor.visitMethodInsn(opcode, owner, name, desc, itf); + if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\", " + itf + ");"); + } + + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + visitor.visitFieldInsn(opcode, owner, name, desc); + if (debug) System.out.println("mv.visitFieldInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\");"); + } + + public void visitTypeInsn(int opcode, String type) { + visitor.visitTypeInsn(opcode, type); + if (debug) System.out.println("mv.visitTypeInsn(" + opcodes[opcode] + ", \"" + type + "\");"); + } + + public void visitInsn(int opcode) { + visitor.visitInsn(opcode); + if (debug) System.out.println("mv.visitInsn(" + opcodes[opcode] + ");"); + } + + public void visitIntInsn(int opcode, int value) { + visitor.visitIntInsn(opcode, value); + if (debug) System.out.println("mv.visitIntInsn(" + opcodes[opcode] + ", " + value + ");"); + } + + public void visitIincInsn(int opcode, int value) { + visitor.visitIincInsn(opcode, value); + if (debug) System.out.println("mv.visitIincInsn(" + opcode + ", " + value + ");"); + } + + public void visitLdcInsn(Object o) { + visitor.visitLdcInsn(o); + if (debug) System.out.println("mv.visitLdcInsn(" + o + ");"); + } + + public void visitMaxs(int maxStack, int maxLocals) { + visitor.visitMaxs(maxStack, maxLocals); + if (debug) System.out.println("mv.visitMaxs(" + maxStack + ", " + maxLocals + ");"); + } + + public void visitEnd() { + visitor.visitEnd(); + if (debug) System.out.println("mv.visitEnd();\r\n\r\n\r\n"); + } +} diff --git a/com/wentch/redkale/util/ObjectPool.java b/com/wentch/redkale/util/ObjectPool.java new file mode 100644 index 000000000..44e74496e --- /dev/null +++ b/com/wentch/redkale/util/ObjectPool.java @@ -0,0 +1,95 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.util; + +import com.wentch.redkale.util.Creator.Creators; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; + +/** + * + * @author zhangjx + * @param + */ +public final class ObjectPool { + + private static final Logger logger = Logger.getLogger(ObjectPool.class.getSimpleName()); + + private final boolean debug; + + private final Queue queue; + + private Creator creator; + + private final Consumer prepare; + + private final Predicate recycler; + + private final AtomicLong creatCounter; + + private final AtomicLong cycleCounter; + + public ObjectPool(Class clazz, Consumer prepare, Predicate recycler) { + this(2, clazz, prepare, recycler); + } + + public ObjectPool(int max, Class clazz, Consumer prepare, Predicate recycler) { + this(max, Creators.create(clazz), prepare, recycler); + } + + public ObjectPool(Creator creator, Consumer prepare, Predicate recycler) { + this(2, creator, prepare, recycler); + } + + public ObjectPool(int max, Creator creator, Consumer prepare, Predicate recycler) { + this(null, null, max, creator, prepare, recycler); + } + + public ObjectPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { + this.creatCounter = creatCounter; + this.cycleCounter = cycleCounter; + this.creator = creator; + this.prepare = prepare; + this.recycler = recycler; + this.queue = new ArrayBlockingQueue(Math.max(Runtime.getRuntime().availableProcessors() * 2, max)); + this.debug = logger.isLoggable(Level.FINER); + } + + public void setCreator(Creator creator) { + this.creator = creator; + } + + public T poll() { + T result = queue.poll(); + if (result == null) { + if (creatCounter != null) creatCounter.incrementAndGet(); + result = this.creator.create(); + } + if (prepare != null) prepare.accept(result); + return result; + } + + public void offer(final T e) { + if (e != null && recycler.test(e)) { + if (cycleCounter != null) cycleCounter.incrementAndGet(); + if (debug) { + for (T t : queue) { + if (t == e) logger.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] repeat offer the same object(" + e + ")", new Exception()); + } + } + queue.offer(e); + } + } + + public long getCreatCount() { + return creatCounter.longValue(); + } + + public long getCycleCount() { + return cycleCounter.longValue(); + } +} diff --git a/com/wentch/redkale/util/Predicate.java b/com/wentch/redkale/util/Predicate.java new file mode 100644 index 000000000..9bf0b0639 --- /dev/null +++ b/com/wentch/redkale/util/Predicate.java @@ -0,0 +1,24 @@ +/* + * 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 com.wentch.redkale.util; + +/** + * JDK 8 java.util.function.Predicate + * + * @author zhangjx + * @param + */ +public interface Predicate { + + /** + * Evaluates this predicate on the given argument. + * + * @param t the input argument + * @return {@code true} if the input argument matches the predicate, + * otherwise {@code false} + */ + boolean test(T t); +} diff --git a/com/wentch/redkale/util/TypeToken.java b/com/wentch/redkale/util/TypeToken.java new file mode 100644 index 000000000..63b67ed02 --- /dev/null +++ b/com/wentch/redkale/util/TypeToken.java @@ -0,0 +1,26 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.util; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * + * @author zhangjx + * @param + */ +public abstract class TypeToken { + + private final Type type; + + public TypeToken() { + type = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + } + + public final Type getType() { + return type; + } +} diff --git a/com/wentch/redkale/util/Utility.java b/com/wentch/redkale/util/Utility.java new file mode 100644 index 000000000..71ef6054a --- /dev/null +++ b/com/wentch/redkale/util/Utility.java @@ -0,0 +1,508 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.util; + +import java.io.*; +import java.lang.reflect.Field; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.*; +import java.time.*; +import java.util.*; +import javax.net.ssl.*; + +/** + * + * @author zhangjx + */ +public final class Utility { + + private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL"; + + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + private static final char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static final sun.misc.Unsafe UNSAFE; + + private static final long strvaloffset; + + private static final javax.net.ssl.SSLContext DEFAULTSSL_CONTEXT; + + static { + sun.misc.Unsafe usafe = null; + long fd = 0L; + try { + Field safeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + safeField.setAccessible(true); + usafe = (sun.misc.Unsafe) safeField.get(null); + fd = usafe.objectFieldOffset(String.class.getDeclaredField("value")); + } catch (Exception e) { + throw new RuntimeException(e); //不可能会发生 + } + UNSAFE = usafe; + strvaloffset = fd; + + try { + DEFAULTSSL_CONTEXT = javax.net.ssl.SSLContext.getInstance("SSL"); + DEFAULTSSL_CONTEXT.init(null, new javax.net.ssl.TrustManager[]{new javax.net.ssl.X509TrustManager() { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { + } + + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { + } + }}, null); + } catch (Exception e) { + throw new RuntimeException(e); //不可能会发生 + } + } + + private Utility() { + } + + public static String now() { + return String.format(format, System.currentTimeMillis()); + } + + public static void println(String string, ByteBuffer buffer) { + if (buffer == null || !buffer.hasRemaining()) return; + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + buffer.flip(); + println(string, bytes); + } + + public static void println(String string, byte... bytes) { + if (bytes == null) return; + StringBuilder sb = new StringBuilder(); + if (string != null) sb.append(string); + sb.append(bytes.length).append(".["); + boolean last = false; + for (byte b : bytes) { + if (last) sb.append(','); + int v = b & 0xff; + if (v < 16) sb.append('0'); + sb.append(Integer.toHexString(v)); + last = true; + } + sb.append(']'); + (System.out).println(sb); + } + + /** + * 返回本机的第一个内网IPv4地址, 没有则返回null + *

+ * @return + */ + public static InetAddress localInetAddress() { + InetAddress back = null; + try { + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + while (nifs.hasMoreElements()) { + NetworkInterface nif = nifs.nextElement(); + if (!nif.isUp()) continue; + Enumeration eis = nif.getInetAddresses(); + while (eis.hasMoreElements()) { + InetAddress ia = eis.nextElement(); + if (ia.isLoopbackAddress()) back = ia; + if (ia.isSiteLocalAddress()) return ia; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return back; + } + + public static int today() { + java.time.LocalDate today = java.time.LocalDate.now(); + return today.getYear() * 10000 + today.getMonthValue() * 100 + today.getDayOfMonth(); + } + + //时间点所在星期的周一 + public static long monday(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate(); + ld = ld.minusDays(ld.getDayOfWeek().getValue() - 1); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + //时间点所在星期的周日 + public static long sunday(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate(); + ld = ld.plusDays(7 - ld.getDayOfWeek().getValue()); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + //时间点所在月份的1号 + public static long monthFirstDay(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate().withDayOfMonth(1); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + public static String binToHexString(byte[] bytes) { + return new String(binToHex(bytes)); + } + + public static char[] binToHex(byte[] bytes) { + return binToHex(bytes, 0, bytes.length); + } + + public static String binToHexString(byte[] bytes, int offset, int len) { + return new String(binToHex(bytes, offset, len)); + } + + public static char[] binToHex(byte[] bytes, int offset, int len) { + final char[] sb = new char[len * 2]; + final int end = offset + len; + int index = 0; + final char[] hexs = hex; + for (int i = offset; i < end; i++) { + byte b = bytes[i]; + sb[index++] = (hexs[((b >> 4) & 0xF)]); + sb[index++] = hexs[((b) & 0xF)]; + } + return sb; + } + + public static byte[] hexToBin(CharSequence src) { + return hexToBin(src, 0, src.length()); + } + + public static byte[] hexToBin(CharSequence src, int offset, int len) { + final int size = (len + 1) / 2; + final byte[] bytes = new byte[size]; + final int end = offset + len; + String digits = "0123456789abcdef"; + for (int i = 0; i < size; i++) { + int ch1 = src.charAt(offset + i * 2); + if ('A' <= ch1 && 'F' >= ch1) ch1 = ch1 - 'A' + 'a'; + int ch2 = src.charAt(offset + i * 2 + 1); + if ('A' <= ch2 && 'F' >= ch2) ch2 = ch2 - 'A' + 'a'; + int pos1 = digits.indexOf(ch1); + if (pos1 < 0) throw new NumberFormatException(); + int pos2 = digits.indexOf(ch2); + if (pos2 < 0) throw new NumberFormatException(); + bytes[i] = (byte) (pos1 * 0x10 + pos2); + } + return bytes; + } + + public static byte[] hexToBin(char[] src) { + return hexToBin(src, 0, src.length); + } + + public static byte[] hexToBin(char[] src, int offset, int len) { + final int size = (len + 1) / 2; + final byte[] bytes = new byte[size]; + final int end = offset + len; + String digits = "0123456789abcdef"; + for (int i = 0; i < size; i++) { + int ch1 = src[offset + i * 2]; + if ('A' <= ch1 && 'F' >= ch1) ch1 = ch1 - 'A' + 'a'; + int ch2 = src[offset + i * 2 + 1]; + if ('A' <= ch2 && 'F' >= ch2) ch2 = ch2 - 'A' + 'a'; + int pos1 = digits.indexOf(ch1); + if (pos1 < 0) throw new NumberFormatException(); + int pos2 = digits.indexOf(ch2); + if (pos2 < 0) throw new NumberFormatException(); + bytes[i] = (byte) (pos1 * 0x10 + pos2); + } + return bytes; + } + + //----------------------------------------------------------------------------- + public static char[] decodeUTF8(final byte[] array) { + return decodeUTF8(array, 0, array.length); + } + + public static char[] decodeUTF8(final byte[] array, final int start, final int len) { + byte b; + int size = len; + final byte[] bytes = array; + final int limit = start + len; + for (int i = start; i < limit; i++) { + b = bytes[i]; + if ((b >> 5) == -2) { + size--; + } else if ((b >> 4) == -2) { + size -= 2; + } + } + final char[] text = new char[size]; + size = 0; + for (int i = start; i < limit;) { + b = bytes[i++]; + if (b >= 0) { + text[size++] = (char) b; + } else if ((b >> 5) == -2) { + text[size++] = (char) (((b << 6) ^ bytes[i++]) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80))); + } else if ((b >> 4) == -2) { + text[size++] = (char) ((b << 12) ^ (bytes[i++] << 6) ^ (bytes[i++] ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); + } + } + return text; + } + + public static byte[] encodeUTF8(final String value) { + if (value == null) return new byte[0]; + return encodeUTF8((char[]) UNSAFE.getObject(value, strvaloffset)); + } + + public static byte[] encodeUTF8(final char[] array) { + return encodeUTF8(array, 0, array.length); + } + + public static byte[] encodeUTF8(final char[] text, final int start, final int len) { + char c; + int size = 0; + final char[] chars = text; + final int limit = start + len; + for (int i = start; i < limit; i++) { + c = chars[i]; + if (c < 0x80) { + size++; + } else if (c < 0x800) { + size += 2; + } else { + size += 3; + } + } + final byte[] bytes = new byte[size]; + size = 0; + for (int i = start; i < limit; i++) { + c = chars[i]; + if (c < 0x80) { + bytes[size++] = (byte) c; + } else if (c < 0x800) { + bytes[size++] = (byte) (0xc0 | (c >> 6)); + bytes[size++] = (byte) (0x80 | (c & 0x3f)); + } else { + bytes[size++] = (byte) (0xe0 | ((c >> 12))); + bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + bytes[size++] = (byte) (0x80 | (c & 0x3f)); + } + } + return bytes; + } + + public static char[] charArray(String value) { + return value == null ? null : (char[]) UNSAFE.getObject(value, strvaloffset); + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] array) { + return encodeUTF8(buffer, array, 0, array.length); + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] array) { + return encodeUTF8(buffer, bytesLength, array, 0, array.length); + } + + public static int encodeUTF8Length(String value) { + if (value == null) return -1; + return encodeUTF8Length((char[]) UNSAFE.getObject(value, strvaloffset)); + } + + public static int encodeUTF8Length(final char[] text) { + return encodeUTF8Length(text, 0, text.length); + } + + public static int encodeUTF8Length(final char[] text, final int start, final int len) { + char c; + int size = 0; + final char[] chars = text; + final int limit = start + len; + for (int i = start; i < limit; i++) { + c = chars[i]; + size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3)); + } + return size; + } + + /** + * 将两个数字组装成一个long + *

+ * @param high + * @param low + * @return + */ + public static long merge(long high, long low) { + return high << 32 | low; + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] text, final int start, final int len) { + return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len); + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] text, final int start, final int len) { + char c; + char[] chars = text; + final int limit = start + len; + int remain = buffer.remaining(); + final ByteBuffer buffer2 = remain >= bytesLength ? null : ByteBuffer.allocate(bytesLength - remain + 3); //最差情况buffer最后两byte没有填充 + ByteBuffer buf = buffer; + for (int i = start; i < limit; i++) { + c = chars[i]; + if (c < 0x80) { + if (buf.remaining() < 1) buf = buffer2; + buf.put((byte) c); + } else if (c < 0x800) { + if (buf.remaining() < 2) buf = buffer2; + buf.put((byte) (0xc0 | (c >> 6))); + buf.put((byte) (0x80 | (c & 0x3f))); + } else { + if (buf.remaining() < 3) buf = buffer2; + buf.put((byte) (0xe0 | ((c >> 12)))); + buf.put((byte) (0x80 | ((c >> 6) & 0x3f))); + buf.put((byte) (0x80 | (c & 0x3f))); + } + } + if (buffer2 != null) buffer2.flip(); + return buffer2; + } + + //----------------------------------------------------------------------------- + public static Socket createDefaultSSLSocket(InetSocketAddress address) throws IOException { + return createDefaultSSLSocket(address.getAddress(), address.getPort()); + } + + public static Socket createDefaultSSLSocket(InetAddress host, int port) throws IOException { + Socket socket = DEFAULTSSL_CONTEXT.getSocketFactory().createSocket(host, port); + + return socket; + } + + public static String postHttpContent(String url) throws IOException { + return remoteHttpContent(null, "POST", url, null, null).toString("UTF-8"); + } + + public static String postHttpContent(String url, String body) throws IOException { + return remoteHttpContent(null, "POST", url, null, body).toString("UTF-8"); + } + + public static String postHttpContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent(null, "POST", url, headers, body).toString("UTF-8"); + } + + public static String postHttpContent(SSLContext ctx, String url) throws IOException { + return remoteHttpContent(ctx, "POST", url, null, null).toString("UTF-8"); + } + + public static String postHttpContent(SSLContext ctx, String url, String body) throws IOException { + return remoteHttpContent(ctx, "POST", url, null, body).toString("UTF-8"); + } + + public static String postHttpContent(SSLContext ctx, String url, Map headers, String body) throws IOException { + return remoteHttpContent(ctx, "POST", url, headers, body).toString("UTF-8"); + } + + public static byte[] postHttpBytesContent(String url) throws IOException { + return remoteHttpContent(null, "POST", url, null, null).toByteArray(); + } + + public static byte[] postHttpBytesContent(SSLContext ctx, String url) throws IOException { + return remoteHttpContent(ctx, "POST", url, null, null).toByteArray(); + } + + public static byte[] postHttpBytesContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent(null, "POST", url, headers, body).toByteArray(); + } + + public static byte[] postHttpBytesContent(SSLContext ctx, String url, Map headers, String body) throws IOException { + return remoteHttpContent(ctx, "POST", url, headers, body).toByteArray(); + } + + public static String getHttpContent(String url) throws IOException { + return remoteHttpContent(null, "GET", url, null, null).toString("UTF-8"); + } + + public static String getHttpContent(SSLContext ctx, String url) throws IOException { + return remoteHttpContent(ctx, "GET", url, null, null).toString("UTF-8"); + } + + public static String getHttpContent(SSLContext ctx, String url, Map headers, String body) throws IOException { + return remoteHttpContent(ctx, "GET", url, headers, body).toString("UTF-8"); + } + + public static String getHttpContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent(null, "GET", url, headers, body).toString("UTF-8"); + } + + public static byte[] getHttpBytesContent(String url) throws IOException { + return remoteHttpContent(null, "GET", url, null, null).toByteArray(); + } + + public static byte[] getHttpBytesContent(SSLContext ctx, String url) throws IOException { + return remoteHttpContent(ctx, "GET", url, null, null).toByteArray(); + } + + public static byte[] getHttpBytesContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent(null, "GET", url, headers, body).toByteArray(); + } + + public static byte[] getHttpBytesContent(SSLContext ctx, String url, Map headers, String body) throws IOException { + return remoteHttpContent(ctx, "GET", url, headers, body).toByteArray(); + } + + protected static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, Map headers, String body) throws IOException { + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + conn.setConnectTimeout(3000); + conn.setReadTimeout(3000); + if (conn instanceof HttpsURLConnection) ((HttpsURLConnection) conn).setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory()); + conn.setRequestMethod(method); + if (headers != null) { + for (Map.Entry en : headers.entrySet()) { + conn.setRequestProperty(en.getKey(), en.getValue()); + } + } + if (body != null) { + conn.setDoInput(true); + conn.setDoOutput(true); + conn.getOutputStream().write(body.getBytes(UTF_8)); + } + conn.connect(); + int rs = conn.getResponseCode(); + if (rs == 301 || rs == 302) { + String newurl = conn.getHeaderField("Location"); + conn.disconnect(); + return remoteHttpContent(ctx, method, newurl, headers, body); + } + InputStream in = conn.getInputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] bytes = new byte[1024]; + int pos; + while ((pos = in.read(bytes)) != -1) { + out.write(bytes, 0, pos); + } + conn.disconnect(); + return out; + } + + public static String read(InputStream in) throws IOException { + return read(in, "UTF-8"); + } + + public static String read(InputStream in, String charsetName) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] bytes = new byte[1024]; + int pos; + while ((pos = in.read(bytes)) != -1) { + out.write(bytes, 0, pos); + } + return charsetName == null ? out.toString() : out.toString(charsetName); + } +}