| /*
|
| * Copyright 1999-2011 Alibaba Group.
|
| *
|
| * Licensed under the Apache License, Version 2.0 (the "License");
|
| * you may not use this file except in compliance with the License.
|
| * You may obtain a copy of the License at
|
| *
|
| * http://www.apache.org/licenses/LICENSE-2.0
|
| *
|
| * Unless required by applicable law or agreed to in writing, software
|
| * distributed under the License is distributed on an "AS IS" BASIS,
|
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| * See the License for the specific language governing permissions and
|
| * limitations under the License.
|
| */
|
| package com.alibaba.dubbo.common.serialize.support.dubbo; |
| |
| import java.io.IOException;
|
| import java.io.InputStream;
|
| import java.io.OutputStream;
|
| import java.io.Serializable;
|
| import java.lang.reflect.Constructor;
|
| import java.lang.reflect.Field;
|
| import java.lang.reflect.Method;
|
| import java.lang.reflect.Modifier;
|
| import java.util.ArrayList;
|
| import java.util.Arrays;
|
| import java.util.Collection;
|
| import java.util.Comparator;
|
| import java.util.Date;
|
| import java.util.HashMap;
|
| import java.util.HashSet;
|
| import java.util.List;
|
| import java.util.Map;
|
| import java.util.Set;
|
| import java.util.concurrent.ConcurrentHashMap;
|
| import java.util.concurrent.atomic.AtomicLong;
|
| import java.util.regex.Matcher;
|
|
|
| import com.alibaba.dubbo.common.bytecode.ClassGenerator;
|
| import com.alibaba.dubbo.common.io.ClassDescriptorMapper;
|
| import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
|
| import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
|
| import com.alibaba.dubbo.common.logger.Logger;
|
| import com.alibaba.dubbo.common.logger.LoggerFactory;
|
| import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectInputStream;
|
| import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectOutputStream;
|
| import com.alibaba.dubbo.common.utils.ClassHelper;
|
| import com.alibaba.dubbo.common.utils.IOUtils;
|
| import com.alibaba.dubbo.common.utils.ReflectUtils;
|
| import com.alibaba.dubbo.common.utils.StringUtils;
|
| |
| /** |
| * Builder. |
| * |
| * @author qian.lei |
| * |
| * @param <T> type. |
| */ |
| |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public abstract class Builder<T> implements GenericDataFlags |
| { |
| // Must be protected. by qian.lei |
| protected static Logger logger = LoggerFactory.getLogger(Builder.class); |
| |
| private static final AtomicLong BUILDER_CLASS_COUNTER = new AtomicLong(0); |
| |
| private static final String BUILDER_CLASS_NAME = Builder.class.getName(); |
| |
| private static final Map<Class<?>, Builder<?>> BuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>(); |
| private static final Map<Class<?>, Builder<?>> nonSerializableBuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>(); |
| |
| private static final String FIELD_CONFIG_SUFFIX = ".fc"; |
| |
| private static final int MAX_FIELD_CONFIG_FILE_SIZE = 16 * 1024; |
| |
| private static final Comparator<String> FNC = new Comparator<String>(){ |
| public int compare(String n1, String n2){ return compareFieldName(n1, n2); } |
| }; |
| |
| private static final Comparator<Field> FC = new Comparator<Field>(){ |
| public int compare(Field f1, Field f2){ return compareFieldName(f1.getName(), f2.getName()); } |
| }; |
| |
| private static final Comparator<Constructor> CC = new Comparator<Constructor>(){ |
| public int compare(Constructor o1, Constructor o2){ return o1.getParameterTypes().length - o2.getParameterTypes().length; } |
| }; |
| |
| // class-descriptor mapper |
| private static final List<String> mDescList = new ArrayList<String>(); |
| |
| private static final Map<String, Integer> mDescMap = new ConcurrentHashMap<String, Integer>(); |
| |
| public static ClassDescriptorMapper DEFAULT_CLASS_DESCRIPTOR_MAPPER = new ClassDescriptorMapper(){ |
| public String getDescriptor(int index) |
| { |
| if( index < 0 || index >= mDescList.size() ) |
| return null; |
| return mDescList.get(index); |
| } |
| |
| public int getDescriptorIndex(String desc) |
| { |
| Integer ret = mDescMap.get(desc); |
| return ret == null ? -1 : ret.intValue(); |
| } |
| }; |
| |
| protected Builder(){} |
| |
| abstract public Class<T> getType(); |
| |
| public void writeTo(T obj, OutputStream os) throws IOException |
| { |
| GenericObjectOutput out = new GenericObjectOutput(os); |
| writeTo(obj, out); |
| out.flushBuffer(); |
| } |
| |
| public T parseFrom(byte[] b) throws IOException |
| { |
| return parseFrom(new UnsafeByteArrayInputStream(b)); |
| } |
| |
| public T parseFrom(InputStream is) throws IOException |
| { |
| return parseFrom(new GenericObjectInput(is)); |
| } |
| |
| abstract public void writeTo(T obj, GenericObjectOutput out) throws IOException; |
| |
| abstract public T parseFrom(GenericObjectInput in) throws IOException; |
| |
| public static <T> Builder<T> register(Class<T> c, boolean isAllowNonSerializable) |
| { |
| if( c == Object.class || c.isInterface() ) |
| return (Builder<T>)GenericBuilder; |
| if( c == Object[].class ) |
| return (Builder<T>)GenericArrayBuilder; |
| |
| Builder<T> b = (Builder<T>)BuilderMap.get(c); |
| if(null != b) return b; |
| |
| boolean isSerializable = Serializable.class.isAssignableFrom(c); |
| if(!isAllowNonSerializable && !isSerializable) { |
| throw new IllegalStateException("Serialized class " + c.getName() + |
| " must implement java.io.Serializable (dubbo codec setting: isAllowNonSerializable = false)"); |
| } |
| |
| b = (Builder<T>)nonSerializableBuilderMap.get(c); |
| if(null != b) return b; |
| |
| b = newBuilder(c); |
| if(isSerializable) |
| BuilderMap.put(c, b); |
| else |
| nonSerializableBuilderMap.put(c, b); |
| |
| return b; |
| } |
| |
| public static <T> Builder<T> register(Class<T> c) |
| { |
| return register(c, false); |
| } |
| |
| public static <T> void register(Class<T> c, Builder<T> b) |
| { |
| if(Serializable.class.isAssignableFrom(c)) |
| BuilderMap.put(c, b); |
| else |
| nonSerializableBuilderMap.put(c, b); |
| } |
| |
| private static <T> Builder<T> newBuilder(Class<T> c) |
| { |
| if( c.isPrimitive() ) |
| throw new RuntimeException("Can not create builder for primitive type: " + c); |
| |
| if( logger.isInfoEnabled() ) |
| logger.info("create Builder for class: " + c); |
| |
| Builder<?> builder; |
| if( c.isArray() ) |
| builder = newArrayBuilder(c); |
| else |
| builder = newObjectBuilder(c); |
| return (Builder<T>)builder; |
| } |
| |
| private static Builder<?> newArrayBuilder(Class<?> c) |
| { |
| Class<?> cc = c.getComponentType(); |
| if( cc.isInterface() ) |
| return GenericArrayBuilder; |
| |
| ClassLoader cl = ClassHelper.getClassLoader(c); |
| |
| String cn = ReflectUtils.getName(c), ccn = ReflectUtils.getName(cc); // get class name as int[][], double[]. |
| String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement(); |
| |
| int ix = cn.indexOf(']'); |
| String s1 = cn.substring(0, ix), s2 = cn.substring(ix); // if name='int[][]' then s1='int[', s2='][]' |
| |
| StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code. |
| StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code. |
| |
| cwt.append("if( $1 == null ){ $2.write0(OBJECT_NULL); return; }"); |
| cwt.append(cn).append(" v = (").append(cn).append(")$1; int len = v.length; $2.write0(OBJECT_VALUES); $2.writeUInt(len); for(int i=0;i<len;i++){ "); |
| |
| cpf.append("byte b = $1.read0(); if( b == OBJECT_NULL ) return null; if( b != OBJECT_VALUES ) throw new java.io.IOException(\"Input format error, expect OBJECT_NULL|OBJECT_VALUES, get \" + b + \".\");"); |
| cpf.append("int len = $1.readUInt(); if( len == 0 ) return new ").append(s1).append('0').append(s2).append("; "); |
| cpf.append(cn).append(" ret = new ").append(s1).append("len").append(s2).append("; for(int i=0;i<len;i++){ "); |
| |
| Builder<?> builder = null; |
| if( cc.isPrimitive() ) |
| { |
| if( cc == boolean.class ) |
| { |
| cwt.append("$2.writeBool(v[i]);"); |
| cpf.append("ret[i] = $1.readBool();"); |
| } |
| else if( cc == byte.class ) |
| { |
| cwt.append("$2.writeByte(v[i]);"); |
| cpf.append("ret[i] = $1.readByte();"); |
| } |
| else if( cc == char.class ) |
| { |
| cwt.append("$2.writeShort((short)v[i]);"); |
| cpf.append("ret[i] = (char)$1.readShort();"); |
| } |
| else if( cc == short.class ) |
| { |
| cwt.append("$2.writeShort(v[i]);"); |
| cpf.append("ret[i] = $1.readShort();"); |
| } |
| else if( cc == int.class ) |
| { |
| cwt.append("$2.writeInt(v[i]);"); |
| cpf.append("ret[i] = $1.readInt();"); |
| } |
| else if( cc == long.class ) |
| { |
| cwt.append("$2.writeLong(v[i]);"); |
| cpf.append("ret[i] = $1.readLong();"); |
| } |
| else if( cc == float.class ) |
| { |
| cwt.append("$2.writeFloat(v[i]);"); |
| cpf.append("ret[i] = $1.readFloat();"); |
| } |
| else if( cc == double.class ) |
| { |
| cwt.append("$2.writeDouble(v[i]);"); |
| cpf.append("ret[i] = $1.readDouble();"); |
| } |
| } |
| else |
| { |
| builder = register(cc); |
| |
| cwt.append("builder.writeTo(v[i], $2);"); |
| cpf.append("ret[i] = (").append(ccn).append(")builder.parseFrom($1);"); |
| } |
| cwt.append(" } }"); |
| cpf.append(" } return ret; }"); |
| |
| ClassGenerator cg = ClassGenerator.newInstance(cl); |
| cg.setClassName(bcn); |
| cg.setSuperClass(Builder.class); |
| cg.addDefaultConstructor(); |
| if( builder != null ) |
| cg.addField("public static " + BUILDER_CLASS_NAME + " builder;"); |
| cg.addMethod("public Class getType(){ return " + cn + ".class; }"); |
| cg.addMethod(cwt.toString()); |
| cg.addMethod(cpf.toString()); |
| try |
| { |
| Class<?> wc = cg.toClass(); |
| // set static field. |
| if( builder != null ) |
| wc.getField("builder").set(null, builder); |
| return (Builder<?>)wc.newInstance(); |
| } |
| catch(RuntimeException e) |
| { |
| throw e; |
| } |
| catch(Throwable e) |
| { |
| throw new RuntimeException(e.getMessage()); |
| } |
| finally |
| { |
| cg.release(); |
| } |
| } |
| |
| private static Builder<?> newObjectBuilder(final Class<?> c) |
| { |
| if( c.isEnum() ) |
| return newEnumBuilder(c); |
| |
| if( c.isAnonymousClass() ) |
| throw new RuntimeException("Can not instantiation anonymous class: " + c); |
| |
| if( c.getEnclosingClass() != null && !Modifier.isStatic(c.getModifiers()) ) |
| throw new RuntimeException("Can not instantiation inner and non-static class: " + c); |
| |
| if( Throwable.class.isAssignableFrom(c) ) |
| return SerializableBuilder; |
| |
| ClassLoader cl = ClassHelper.getClassLoader(c); |
| |
| // is same package. |
| boolean isp; |
| String cn = c.getName(), bcn; |
| if( c.getClassLoader() == null ) // is system class. if( cn.startsWith("java.") || cn.startsWith("javax.") || cn.startsWith("sun.") ) |
| { |
| isp = false; |
| bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement(); |
| } |
| else |
| { |
| isp = true; |
| bcn = cn + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement(); |
| } |
| |
| // is Collection, is Map, is Serializable. |
| boolean isc = Collection.class.isAssignableFrom(c); |
| boolean ism = !isc && Map.class.isAssignableFrom(c); |
| boolean iss = !( isc || ism ) && Serializable.class.isAssignableFrom(c); |
| |
| // deal with fields. |
| String[] fns = null; // fix-order fields names |
| InputStream is = c.getResourceAsStream(c.getSimpleName() + FIELD_CONFIG_SUFFIX); // load field-config file. |
| if( is != null ) |
| { |
| try |
| { |
| int len = is.available(); |
| if( len > 0 ) |
| { |
| if( len > MAX_FIELD_CONFIG_FILE_SIZE ) |
| throw new RuntimeException("Load [" + c.getName() + "] field-config file error: File-size too larger"); |
| |
| String[] lines = IOUtils.readLines(is); |
| if( lines != null && lines.length > 0 ) |
| { |
| List<String> list = new ArrayList<String>(); |
| for(int i=0;i<lines.length;i++) |
| { |
| fns = lines[i].split(","); |
| Arrays.sort(fns, FNC); |
| for(int j=0;j<fns.length;j++) |
| list.add(fns[j]); |
| } |
| fns = list.toArray(new String[0]); |
| } |
| } |
| } |
| catch(IOException e) |
| { |
| throw new RuntimeException("Load [" + c.getName() + "] field-config file error: " + e.getMessage() ); |
| } |
| finally |
| { |
| try{ is.close(); } |
| catch(IOException e){} |
| } |
| } |
| |
| Field f, fs[]; |
| if( fns != null ) |
| { |
| fs = new Field[fns.length]; |
| for(int i=0;i<fns.length;i++) |
| { |
| String fn = fns[i]; |
| try |
| { |
| f = c.getDeclaredField(fn); |
| int mod = f.getModifiers(); |
| if( Modifier.isStatic(mod) || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod)) ) |
| throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is static/final field."); |
| if( Modifier.isTransient(mod) ) |
| { |
| if( iss ) |
| return SerializableBuilder; |
| throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is transient field."); |
| } |
| f.setAccessible(true); |
| fs[i] = f; |
| } |
| catch(SecurityException e) |
| { |
| throw new RuntimeException(e.getMessage()); |
| } |
| catch(NoSuchFieldException e) |
| { |
| throw new RuntimeException("Field [" + c.getName() + "." + fn + "] not found."); |
| } |
| } |
| } |
| else |
| { |
| Class<?> t = c; |
| List<Field> fl = new ArrayList<Field>(); |
| do |
| { |
| fs = t.getDeclaredFields(); |
| for( Field tf : fs ) |
| { |
| int mod = tf.getModifiers(); |
| if (Modifier.isStatic(mod)
|
| || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod))
|
| || tf.getName().equals("this$0") // skip static or inner-class's 'this$0' field.
|
| || ! Modifier.isPublic(tf.getType().getModifiers()) ) //skip private inner-class field |
| continue; |
| if( Modifier.isTransient(mod) ) |
| { |
| if( iss ) |
| return SerializableBuilder; |
| continue; |
| } |
| tf.setAccessible(true); |
| fl.add(tf); |
| } |
| t = t.getSuperclass(); |
| } |
| while( t != Object.class ); |
| |
| fs = fl.toArray(new Field[0]); |
| if( fs.length > 1 ) |
| Arrays.sort(fs, FC); |
| } |
| |
| // deal with constructors. |
| Constructor<?>[] cs = c.getDeclaredConstructors(); |
| if( cs.length == 0 ) |
| { |
| Class<?> t = c; |
| do |
| { |
| t = t.getSuperclass(); |
| if( t == null ) |
| throw new RuntimeException("Can not found Constructor?"); |
| cs = c.getDeclaredConstructors(); |
| } |
| while( cs.length == 0 ); |
| } |
| if( cs.length > 1 ) |
| Arrays.sort(cs, CC); |
| |
| // writeObject code. |
| StringBuilder cwf = new StringBuilder("protected void writeObject(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); |
| cwf.append(cn).append(" v = (").append(cn).append(")$1; "); |
| cwf.append("$2.writeInt(fields.length);"); |
| |
| // readObject code. |
| StringBuilder crf = new StringBuilder("protected void readObject(Object ret, ").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); |
| crf.append("int fc = $2.readInt();"); |
| crf.append("if( fc != ").append(fs.length).append(" ) throw new IllegalStateException(\"Deserialize Class [").append(cn).append("], field count not matched. Expect ").append(fs.length).append(" but get \" + fc +\".\");"); |
| crf.append(cn).append(" ret = (").append(cn).append(")$1;"); |
| |
| // newInstance code. |
| StringBuilder cni = new StringBuilder("protected Object newInstance(").append(GenericObjectInput.class.getName()).append(" in){ return "); |
| Constructor<?> con = cs[0]; |
| int mod = con.getModifiers(); |
| boolean dn = Modifier.isPublic(mod) || ( isp && !Modifier.isPrivate(mod) ); |
| if( dn ) |
| { |
| cni.append("new ").append(cn).append("("); |
| } |
| else |
| { |
| con.setAccessible(true); |
| cni.append("constructor.newInstance(new Object[]{"); |
| } |
| Class<?>[] pts = con.getParameterTypes(); |
| for(int i=0;i<pts.length;i++) |
| { |
| if( i > 0 ) |
| cni.append(','); |
| cni.append(defaultArg(pts[i])); |
| } |
| if( !dn ) |
| cni.append("}"); // close object array. |
| cni.append("); }"); |
| |
| // get bean-style property metadata. |
| Map<String, PropertyMetadata> pms = propertyMetadatas(c); |
| List<Builder<?>> builders = new ArrayList<Builder<?>>(fs.length); |
| String fn, ftn; // field name, field type name. |
| Class<?> ft; // field type. |
| boolean da; // direct access. |
| PropertyMetadata pm; |
| for(int i=0;i<fs.length;i++) |
| { |
| f = fs[i]; |
| fn = f.getName(); |
| ft = f.getType(); |
| ftn = ReflectUtils.getName(ft); |
| da = isp && ( f.getDeclaringClass() == c ) && ( Modifier.isPrivate(f.getModifiers()) == false ); |
| if( da ) |
| { |
| pm = null; |
| } |
| else |
| { |
| pm = pms.get(fn); |
| if( pm != null && ( pm.type != ft || pm.setter == null || pm.getter == null ) ) |
| pm = null; |
| } |
| |
| crf.append("if( fc == ").append(i).append(" ) return;"); |
| if( ft.isPrimitive() ) |
| { |
| if( ft == boolean.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeBool(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readBool();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeBool(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readBool());"); |
| } |
| else |
| { |
| cwf.append("$2.writeBool(((Boolean)fields[").append(i).append("].get($1)).booleanValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readBool());"); |
| } |
| } |
| else if( ft == byte.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeByte(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readByte();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeByte(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readByte());"); |
| } |
| else |
| { |
| cwf.append("$2.writeByte(((Byte)fields[").append(i).append("].get($1)).byteValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readByte());"); |
| } |
| } |
| else if( ft == char.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeShort((short)v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = (char)$2.readShort();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeShort((short)v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("((char)$2.readShort());"); |
| } |
| else |
| { |
| cwf.append("$2.writeShort((short)((Character)fields[").append(i).append("].get($1)).charValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)((char)$2.readShort()));"); |
| } |
| } |
| else if( ft == short.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeShort(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readShort();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeShort(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readShort());"); |
| } |
| else |
| { |
| cwf.append("$2.writeShort(((Short)fields[").append(i).append("].get($1)).shortValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readShort());"); |
| } |
| } |
| else if( ft == int.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeInt(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readInt();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeInt(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readInt());"); |
| } |
| else |
| { |
| cwf.append("$2.writeInt(((Integer)fields[").append(i).append("].get($1)).intValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readInt());"); |
| } |
| } |
| else if( ft == long.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeLong(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readLong();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeLong(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readLong());"); |
| } |
| else |
| { |
| cwf.append("$2.writeLong(((Long)fields[").append(i).append("].get($1)).longValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readLong());"); |
| } |
| } |
| else if( ft == float.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeFloat(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readFloat();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeFloat(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readFloat());"); |
| } |
| else |
| { |
| cwf.append("$2.writeFloat(((Float)fields[").append(i).append("].get($1)).floatValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readFloat());"); |
| } |
| } |
| else if( ft == double.class ) |
| { |
| if( da ) |
| { |
| cwf.append("$2.writeDouble(v.").append(fn).append(");"); |
| crf.append("ret.").append(fn).append(" = $2.readDouble();"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("$2.writeDouble(v.").append(pm.getter).append("());"); |
| crf.append("ret.").append(pm.setter).append("($2.readDouble());"); |
| } |
| else |
| { |
| cwf.append("$2.writeDouble(((Double)fields[").append(i).append("].get($1)).doubleValue());"); |
| crf.append("fields[").append(i).append("].set(ret, ($w)$2.readDouble());"); |
| } |
| } |
| } |
| else if( ft == c ) |
| { |
| if( da ) |
| { |
| cwf.append("this.writeTo(v.").append(fn).append(", $2);"); |
| crf.append("ret.").append(fn).append(" = (").append(ftn).append(")this.parseFrom($2);"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("this.writeTo(v.").append(pm.getter).append("(), $2);"); |
| crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")this.parseFrom($2));"); |
| } |
| else |
| { |
| cwf.append("this.writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);"); |
| crf.append("fields[").append(i).append("].set(ret, this.parseFrom($2));"); |
| } |
| } |
| else |
| { |
| int bc = builders.size(); |
| builders.add( register(ft) ); |
| |
| if( da ) |
| { |
| cwf.append("builders[").append(bc).append("].writeTo(v.").append(fn).append(", $2);"); |
| crf.append("ret.").append(fn).append(" = (").append(ftn).append(")builders[").append(bc).append("].parseFrom($2);"); |
| } |
| else if( pm != null ) |
| { |
| cwf.append("builders[").append(bc).append("].writeTo(v.").append(pm.getter).append("(), $2);"); |
| crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")builders[").append(bc).append("].parseFrom($2));"); |
| } |
| else |
| { |
| cwf.append("builders[").append(bc).append("].writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);"); |
| crf.append("fields[").append(i).append("].set(ret, builders[").append(bc).append("].parseFrom($2));"); |
| } |
| } |
| } |
| |
| // skip any fields. |
| crf.append("for(int i=").append(fs.length).append(";i<fc;i++) $2.skipAny();"); |
| |
| // collection or map |
| if( isc ) |
| { |
| cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.iterator();it.hasNext();){ $2.writeObject(it.next()); }"); |
| crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.add($2.readObject());"); |
| } |
| else if( ism ) |
| { |
| cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.entrySet().iterator();it.hasNext();){ java.util.Map.Entry entry = (java.util.Map.Entry)it.next(); $2.writeObject(entry.getKey()); $2.writeObject(entry.getValue()); }"); |
| crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.put($2.readObject(), $2.readObject());"); |
| } |
| cwf.append(" }"); |
| crf.append(" }"); |
| |
| ClassGenerator cg = ClassGenerator.newInstance(cl); |
| cg.setClassName(bcn); |
| cg.setSuperClass(AbstractObjectBuilder.class); |
| cg.addDefaultConstructor(); |
| cg.addField("public static java.lang.reflect.Field[] fields;"); |
| cg.addField("public static " + BUILDER_CLASS_NAME + "[] builders;"); |
| if( !dn ) |
| cg.addField("public static java.lang.reflect.Constructor constructor;"); |
| cg.addMethod("public Class getType(){ return " + cn + ".class; }"); |
| cg.addMethod(cwf.toString()); |
| cg.addMethod(crf.toString()); |
| cg.addMethod(cni.toString()); |
| try |
| { |
| Class<?> wc = cg.toClass(); |
| // set static field |
| wc.getField("fields").set(null, fs); |
| wc.getField("builders").set(null, builders.toArray(new Builder<?>[0])); |
| if( !dn ) |
| wc.getField("constructor").set(null, con); |
| return (Builder<?>)wc.newInstance(); |
| } |
| catch(RuntimeException e) |
| { |
| throw e; |
| } |
| catch(Throwable e) |
| { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| cg.release(); |
| } |
| } |
| |
| private static Builder<?> newEnumBuilder(Class<?> c) |
| { |
| ClassLoader cl = ClassHelper.getClassLoader(c); |
| |
| String cn = c.getName(); |
| String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement(); |
| |
| StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code. |
| cwt.append(cn).append(" v = (").append(cn).append(")$1;"); |
| cwt.append("if( $1 == null ){ $2.writeUTF(null); }else{ $2.writeUTF(v.name()); } }"); |
| |
| StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code. |
| cpf.append("String name = $1.readUTF(); if( name == null ) return null; return (").append(cn).append(")Enum.valueOf(").append(cn).append(".class, name); }"); |
| |
| ClassGenerator cg = ClassGenerator.newInstance(cl); |
| cg.setClassName(bcn); |
| cg.setSuperClass(Builder.class); |
| cg.addDefaultConstructor(); |
| cg.addMethod("public Class getType(){ return " + cn + ".class; }"); |
| cg.addMethod(cwt.toString()); |
| cg.addMethod(cpf.toString()); |
| try |
| { |
| Class<?> wc = cg.toClass(); |
| return (Builder<?>)wc.newInstance(); |
| } |
| catch(RuntimeException e) |
| { |
| throw e; |
| } |
| catch(Throwable e) |
| { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| cg.release(); |
| } |
| } |
| |
| private static Map<String, PropertyMetadata> propertyMetadatas(Class<?> c) |
| { |
| Map<String, Method> mm = new HashMap<String, Method>(); // method map. |
| Map<String, PropertyMetadata> ret = new HashMap<String, PropertyMetadata>(); // property metadata map. |
| |
| // All public method. |
| for( Method m : c.getMethods() ) |
| { |
| if( m.getDeclaringClass() == Object.class ) // Ignore Object's method. |
| continue; |
| mm.put(ReflectUtils.getDesc(m), m); |
| } |
| |
| Matcher matcher; |
| for( Map.Entry<String,Method> entry : mm.entrySet() ) |
| { |
| String desc = entry.getKey(); |
| Method method = entry.getValue(); |
| if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() || |
| ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(desc) ).matches() ) |
| { |
| String pn = propertyName(matcher.group(1)); |
| Class<?> pt = method.getReturnType(); |
| PropertyMetadata pm = ret.get(pn); |
| if( pm == null ) |
| { |
| pm = new PropertyMetadata(); |
| pm.type = pt; |
| ret.put(pn, pm); |
| } |
| else |
| { |
| if( pm.type != pt ) |
| continue; |
| } |
| pm.getter = method.getName(); |
| } |
| else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() ) |
| { |
| String pn = propertyName(matcher.group(1)); |
| Class<?> pt = method.getParameterTypes()[0]; |
| PropertyMetadata pm = ret.get(pn); |
| if( pm == null ) |
| { |
| pm = new PropertyMetadata(); |
| pm.type = pt; |
| ret.put(pn, pm); |
| } |
| else |
| { |
| if( pm.type != pt ) |
| continue; |
| } |
| pm.setter = method.getName(); |
| } |
| } |
| return ret; |
| } |
| |
| private static String propertyName(String s) |
| { |
| return s.length() == 1 || Character.isLowerCase(s.charAt(1)) ? Character.toLowerCase(s.charAt(0)) + s.substring(1) : s; |
| }
|
|
|
| private static boolean serializeIgnoreFinalModifier(Class cl)
|
| {
|
| // if (cl.isAssignableFrom(BigInteger.class)) return false;
|
| // for performance
|
| // if (cl.getName().startsWith("java")) return true;
|
| // if (cl.getName().startsWith("javax")) return true;
|
|
|
| return false;
|
| }
|
|
|
| @SuppressWarnings("unused")
|
| private static boolean isPrimitiveOrPrimitiveArray1(Class<?> cl)
|
| {
|
| if (cl.isPrimitive()){
|
| return true;
|
| } else {
|
| Class clazz = cl.getClass().getComponentType();
|
| if (clazz!=null && clazz.isPrimitive()){
|
| return true;
|
| }
|
| }
|
| return false;
|
| }
|
| |
| private static String defaultArg(Class<?> cl) |
| { |
| if( boolean.class == cl ) return "false"; |
| if( int.class == cl ) return "0"; |
| if( long.class == cl ) return "0l"; |
| if( double.class == cl ) return "(double)0"; |
| if( float.class == cl ) return "(float)0"; |
| if( short.class == cl ) return "(short)0"; |
| if( char.class == cl ) return "(char)0"; |
| if( byte.class == cl ) return "(byte)0";
|
| if( byte[].class == cl ) return "new byte[]{0}";
|
| if( !cl.isPrimitive() ) return "null"; |
| throw new UnsupportedOperationException(); |
| } |
| |
| private static int compareFieldName(String n1, String n2) |
| { |
| int l = Math.min(n1.length(), n2.length()); |
| for(int i=0;i<l;i++) |
| { |
| int t = n1.charAt(i) - n2.charAt(i); |
| if( t != 0 ) |
| return t; |
| } |
| return n1.length() - n2.length(); |
| } |
| |
| private static void addDesc(Class<?> c) |
| { |
| String desc = ReflectUtils.getDesc(c); |
| int index = mDescList.size(); |
| mDescList.add(desc); |
| mDescMap.put(desc, index); |
| } |
| |
| static class PropertyMetadata |
| { |
| Class<?> type; |
| String setter, getter; |
| } |
| |
| public static abstract class AbstractObjectBuilder<T> extends Builder<T> |
| { |
| abstract public Class<T> getType(); |
| |
| public void writeTo(T obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| int ref = out.getRef(obj); |
| if( ref < 0 ) |
| { |
| out.addRef(obj); |
| out.write0(OBJECT); |
| writeObject(obj, out); |
| } |
| else |
| { |
| out.write0(OBJECT_REF); |
| out.writeUInt(ref); |
| } |
| } |
| } |
| |
| public T parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| switch( b ) |
| { |
| case OBJECT: |
| { |
| T ret = newInstance(in); |
| in.addRef(ret); |
| readObject(ret, in); |
| return ret; |
| } |
| case OBJECT_REF: |
| return (T)in.getRef(in.readUInt()); |
| case OBJECT_NULL: |
| return null; |
| default: |
| throw new IOException("Input format error, expect OBJECT|OBJECT_REF|OBJECT_NULL, get " + b); |
| } |
| } |
| |
| abstract protected void writeObject(T obj, GenericObjectOutput out) throws IOException; |
| |
| abstract protected T newInstance(GenericObjectInput in) throws IOException; |
| |
| abstract protected void readObject(T ret, GenericObjectInput in) throws IOException; |
| } |
| |
| static final Builder<Object> GenericBuilder = new Builder<Object>(){ |
| @Override |
| public Class<Object> getType(){ return Object.class; } |
| @Override |
| public void writeTo(Object obj, GenericObjectOutput out) throws IOException{ out.writeObject(obj); } |
| @Override |
| public Object parseFrom(GenericObjectInput in) throws IOException{ return in.readObject(); } |
| }; |
| |
| static final Builder<Object[]> GenericArrayBuilder = new AbstractObjectBuilder<Object[]>(){ |
| @Override |
| public Class<Object[]> getType(){ return Object[].class; } |
| @Override |
| protected Object[] newInstance(GenericObjectInput in) throws IOException |
| { |
| return new Object[in.readUInt()]; |
| } |
| @Override |
| protected void readObject(Object[] ret, GenericObjectInput in) throws IOException |
| { |
| for(int i=0;i<ret.length;i++) |
| ret[i] = in.readObject(); |
| } |
| @Override |
| protected void writeObject(Object[] obj, GenericObjectOutput out) throws IOException |
| { |
| out.writeUInt(obj.length); |
| for( Object item : obj ) |
| out.writeObject(item); |
| } |
| }; |
| |
| static final Builder<Serializable> SerializableBuilder = new Builder<Serializable>(){ |
| @Override |
| public Class<Serializable> getType() |
| { |
| return Serializable.class; |
| } |
| @Override |
| public void writeTo(Serializable obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_STREAM); |
| UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(); |
| CompactedObjectOutputStream oos = new CompactedObjectOutputStream(bos); |
| oos.writeObject(obj); |
| oos.flush(); |
| bos.close(); |
| byte[] b = bos.toByteArray(); |
| out.writeUInt(b.length); |
| out.write0(b, 0, b.length); |
| } |
| } |
| @Override |
| public Serializable parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_STREAM ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_STREAM, get " + b + "."); |
| |
| UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(in.read0(in.readUInt())); |
| CompactedObjectInputStream ois = new CompactedObjectInputStream(bis); |
| try{ return (Serializable)ois.readObject(); } |
| catch(ClassNotFoundException e){ throw new IOException(StringUtils.toString(e)); } |
| } |
| }; |
| |
| static |
| { |
| addDesc(boolean[].class); |
| addDesc(byte[].class); |
| addDesc(char[].class); |
| addDesc(short[].class); |
| addDesc(int[].class); |
| addDesc(long[].class); |
| addDesc(float[].class); |
| addDesc(double[].class); |
| |
| addDesc(Boolean.class); |
| addDesc(Byte.class); |
| addDesc(Character.class); |
| addDesc(Short.class); |
| addDesc(Integer.class); |
| addDesc(Long.class); |
| addDesc(Float.class); |
| addDesc(Double.class); |
| |
| addDesc(String.class); |
| addDesc(String[].class); |
| |
| addDesc(ArrayList.class); |
| addDesc(HashMap.class); |
| addDesc(HashSet.class); |
| addDesc(Date.class); |
| addDesc(java.sql.Date.class); |
| addDesc(java.sql.Time.class); |
| addDesc(java.sql.Timestamp.class); |
| addDesc(java.util.LinkedList.class); |
| addDesc(java.util.LinkedHashMap.class); |
| addDesc(java.util.LinkedHashSet.class); |
| |
| register(byte[].class, new Builder<byte[]>(){ |
| @Override |
| public Class<byte[]> getType(){ return byte[].class; } |
| @Override |
| public void writeTo(byte[] obj, GenericObjectOutput out) throws IOException{ out.writeBytes(obj); } |
| @Override |
| public byte[] parseFrom(GenericObjectInput in) throws IOException{ return in.readBytes(); } |
| }); |
| register(Boolean.class, new Builder<Boolean>(){ |
| @Override |
| public Class<Boolean> getType(){ return Boolean.class; } |
| @Override |
| public void writeTo(Boolean obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| out.write0(VARINT_N1); |
| else if( obj.booleanValue() ) |
| out.write0(VARINT_1); |
| else |
| out.write0(VARINT_0); |
| } |
| @Override |
| public Boolean parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| switch( b ) |
| { |
| case VARINT_N1: return null; |
| case VARINT_0: return Boolean.FALSE; |
| case VARINT_1: return Boolean.TRUE; |
| default: throw new IOException("Input format error, expect VARINT_N1|VARINT_0|VARINT_1, get " + b + "."); |
| } |
| } |
| }); |
| register(Byte.class, new Builder<Byte>(){ |
| @Override |
| public Class<Byte> getType(){ return Byte.class; } |
| @Override |
| public void writeTo(Byte obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeByte(obj.byteValue()); |
| } |
| } |
| @Override |
| public Byte parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return Byte.valueOf(in.readByte()); |
| } |
| }); |
| register(Character.class, new Builder<Character>(){ |
| @Override |
| public Class<Character> getType(){ return Character.class; } |
| @Override |
| public void writeTo(Character obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeShort((short)obj.charValue()); |
| } |
| } |
| @Override |
| public Character parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return Character.valueOf((char)in.readShort()); |
| } |
| }); |
| register(Short.class, new Builder<Short>(){ |
| @Override |
| public Class<Short> getType(){ return Short.class; } |
| @Override |
| public void writeTo(Short obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeShort(obj.shortValue()); |
| } |
| } |
| @Override |
| public Short parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return Short.valueOf(in.readShort()); |
| } |
| }); |
| register(Integer.class, new Builder<Integer>(){ |
| @Override |
| public Class<Integer> getType(){ return Integer.class; } |
| @Override |
| public void writeTo(Integer obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeInt(obj.intValue()); |
| } |
| } |
| @Override |
| public Integer parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return Integer.valueOf(in.readInt()); |
| } |
| }); |
| register(Long.class, new Builder<Long>(){ |
| @Override |
| public Class<Long> getType(){ return Long.class; } |
| @Override |
| public void writeTo(Long obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeLong(obj.longValue()); |
| } |
| } |
| @Override |
| public Long parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return Long.valueOf(in.readLong()); |
| } |
| }); |
| register(Float.class, new Builder<Float>(){ |
| @Override |
| public Class<Float> getType(){ return Float.class; } |
| @Override |
| public void writeTo(Float obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeFloat(obj.floatValue()); |
| } |
| } |
| @Override |
| public Float parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return new Float(in.readFloat()); |
| } |
| }); |
| register(Double.class, new Builder<Double>(){ |
| @Override |
| public Class<Double> getType(){ return Double.class; } |
| @Override |
| public void writeTo(Double obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeDouble(obj.doubleValue()); |
| } |
| } |
| @Override |
| public Double parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return new Double(in.readDouble()); |
| } |
| }); |
| register(String.class, new Builder<String>(){ |
| @Override |
| public Class<String> getType(){ return String.class; } |
| @Override |
| public String parseFrom(GenericObjectInput in) throws IOException{ return in.readUTF(); } |
| @Override |
| public void writeTo(String obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj); } |
| }); |
| register(StringBuilder.class, new Builder<StringBuilder>(){ |
| @Override |
| public Class<StringBuilder> getType(){ return StringBuilder.class; } |
| @Override |
| public StringBuilder parseFrom(GenericObjectInput in) throws IOException{ return new StringBuilder(in.readUTF()); } |
| @Override |
| public void writeTo(StringBuilder obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); } |
| }); |
| register(StringBuffer.class, new Builder<StringBuffer>(){ |
| @Override |
| public Class<StringBuffer> getType(){ return StringBuffer.class; } |
| @Override |
| public StringBuffer parseFrom(GenericObjectInput in) throws IOException{ return new StringBuffer(in.readUTF()); } |
| @Override |
| public void writeTo(StringBuffer obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); } |
| }); |
| |
| // java.util |
| register(ArrayList.class, new Builder<ArrayList>(){ |
| @Override |
| public Class<ArrayList> getType(){ return ArrayList.class; } |
| @Override |
| public void writeTo(ArrayList obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUES); |
| out.writeUInt(obj.size()); |
| for( Object item : obj ) |
| out.writeObject(item); |
| } |
| } |
| @Override |
| public ArrayList parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUES ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + "."); |
| |
| int len = in.readUInt(); |
| ArrayList ret = new ArrayList(len); |
| for(int i=0;i<len;i++) |
| ret.add(in.readObject()); |
| return ret; |
| } |
| }); |
| register(HashMap.class, new Builder<HashMap>(){ |
| @Override |
| public Class<HashMap> getType(){ return HashMap.class; } |
| @Override |
| public void writeTo(HashMap obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_MAP); |
| out.writeUInt(obj.size()); |
| for( Map.Entry entry : (Set<Map.Entry>)obj.entrySet() ) |
| { |
| out.writeObject(entry.getKey()); |
| out.writeObject(entry.getValue()); |
| } |
| } |
| } |
| @Override |
| public HashMap parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_MAP ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_MAP, get " + b + "."); |
| |
| int len = in.readUInt(); |
| HashMap ret = new HashMap(len); |
| for(int i=0;i<len;i++) |
| ret.put(in.readObject(), in.readObject()); |
| return ret; |
| } |
| }); |
| register(HashSet.class, new Builder<HashSet>(){ |
| @Override |
| public Class<HashSet> getType(){ return HashSet.class; } |
| @Override |
| public void writeTo(HashSet obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUES); |
| out.writeUInt(obj.size()); |
| for( Object item : obj ) |
| out.writeObject(item); |
| } |
| } |
| @Override |
| public HashSet parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUES ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + "."); |
| |
| int len = in.readUInt(); |
| HashSet ret = new HashSet(len); |
| for(int i=0;i<len;i++) |
| ret.add(in.readObject()); |
| return ret; |
| } |
| }); |
| |
| register(Date.class, new Builder<Date>(){ |
| @Override |
| public Class<Date> getType(){ return Date.class; } |
| @Override |
| public void writeTo(Date obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeLong(obj.getTime()); |
| } |
| } |
| @Override |
| public Date parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return new Date(in.readLong()); |
| } |
| }); |
| |
| // java.sql |
| register(java.sql.Date.class, new Builder<java.sql.Date>(){ |
| @Override |
| public Class<java.sql.Date> getType(){ return java.sql.Date.class; } |
| @Override |
| public void writeTo(java.sql.Date obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeLong(obj.getTime()); |
| } |
| } |
| @Override |
| public java.sql.Date parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return new java.sql.Date(in.readLong()); |
| } |
| }); |
| register(java.sql.Timestamp.class, new Builder<java.sql.Timestamp>(){ |
| @Override |
| public Class<java.sql.Timestamp> getType(){ return java.sql.Timestamp.class; } |
| @Override |
| public void writeTo(java.sql.Timestamp obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeLong(obj.getTime()); |
| } |
| } |
| @Override |
| public java.sql.Timestamp parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return new java.sql.Timestamp(in.readLong()); |
| } |
| }); |
| register(java.sql.Time.class, new Builder<java.sql.Time>(){ |
| @Override |
| public Class<java.sql.Time> getType(){ return java.sql.Time.class; } |
| @Override |
| public void writeTo(java.sql.Time obj, GenericObjectOutput out) throws IOException |
| { |
| if( obj == null ) |
| { |
| out.write0(OBJECT_NULL); |
| } |
| else |
| { |
| out.write0(OBJECT_VALUE); |
| out.writeLong(obj.getTime()); |
| } |
| } |
| @Override |
| public java.sql.Time parseFrom(GenericObjectInput in) throws IOException |
| { |
| byte b = in.read0(); |
| if( b == OBJECT_NULL ) |
| return null; |
| if( b != OBJECT_VALUE ) |
| throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + "."); |
| |
| return new java.sql.Time(in.readLong()); |
| } |
| }); |
| } |
| } |