| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.weex.wson; |
| |
| |
| import android.support.v4.util.LruCache; |
| |
| import com.alibaba.fastjson.JSON; |
| import com.alibaba.fastjson.JSONArray; |
| import com.alibaba.fastjson.JSONObject; |
| import com.alibaba.fastjson.annotation.JSONField; |
| import org.apache.weex.utils.WXLogUtils; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * fast binary json format for parse map and serialize map |
| * Created by efurture on 2017/8/16. |
| */ |
| public class Wson { |
| |
| /** |
| * skip map null values |
| * */ |
| public static final boolean WriteMapNullValue = false; |
| /** |
| * wson data type |
| * */ |
| private static final byte NULL_TYPE = '0'; |
| |
| private static final byte STRING_TYPE = 's'; |
| |
| private static final byte BOOLEAN_TYPE_TRUE = 't'; |
| |
| private static final byte BOOLEAN_TYPE_FALSE = 'f'; |
| |
| private static final byte NUMBER_INT_TYPE = 'i'; |
| |
| private static final byte NUMBER_LONG_TYPE = 'l'; |
| |
| private static final byte NUMBER_BIG_INTEGER_TYPE = 'g'; |
| |
| private static final byte NUMBER_BIG_DECIMAL_TYPE = 'e'; |
| |
| private static final byte NUMBER_DOUBLE_TYPE = 'd'; |
| |
| private static final byte NUMBER_FLOAT_TYPE = 'F'; |
| |
| private static final byte ARRAY_TYPE = '['; |
| |
| private static final byte MAP_TYPE = '{'; |
| |
| /** |
| * StringUTF-16, byte order with native byte order |
| * */ |
| private static final boolean IS_NATIVE_LITTLE_ENDIAN = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); |
| |
| |
| /** |
| * parse wson data to object, please use WXJsonUtils.parseWson |
| * @param data byte array |
| * */ |
| public static final Object parse(byte[] data){ |
| if(data == null){ |
| return null; |
| } |
| try{ |
| Parser parser = new Parser(data); |
| Object object = parser.parse(); |
| parser.close(); |
| return object; |
| }catch (Exception e){ |
| WXLogUtils.e("parseWson", e); |
| return null; |
| } |
| } |
| |
| |
| /** |
| * serialize object to wson data, please use WXJsonUtils.toWsonOrJsonWXJSObject |
| * */ |
| public static final byte[] toWson(Object object){ |
| if(object == null){ |
| return null; |
| } |
| Builder builder = new Builder(); |
| byte[] bts = builder.toWson(object); |
| builder.close(); |
| return bts; |
| } |
| |
| |
| /** |
| * wson data parser |
| * */ |
| private static final class Parser { |
| |
| private int position = 0; |
| private byte[] buffer; |
| private char[] charsBuffer; |
| |
| private Parser(byte[] buffer) { |
| this.buffer = buffer; |
| charsBuffer = localCharsBufferCache.get(); |
| if(charsBuffer != null){ |
| localCharsBufferCache.set(null); |
| }else{ |
| charsBuffer = new char[512]; |
| } |
| } |
| |
| |
| private final Object parse(){ |
| return readObject(); |
| } |
| |
| private final void close(){ |
| position = 0; |
| buffer = null; |
| if(charsBuffer != null){ |
| localCharsBufferCache.set(charsBuffer); |
| } |
| charsBuffer = null; |
| } |
| |
| private final Object readObject(){ |
| byte type = readType(); |
| switch (type){ |
| case STRING_TYPE: |
| return readUTF16String(); |
| case NUMBER_INT_TYPE : |
| return readVarInt(); |
| case NUMBER_FLOAT_TYPE : |
| return readFloat(); |
| case MAP_TYPE: |
| return readMap(); |
| case ARRAY_TYPE: |
| return readArray(); |
| case NUMBER_DOUBLE_TYPE : |
| return readDouble(); |
| case NUMBER_LONG_TYPE : |
| return readLong(); |
| case NUMBER_BIG_INTEGER_TYPE : |
| return new BigInteger(readUTF16String()); |
| case NUMBER_BIG_DECIMAL_TYPE : |
| return new BigDecimal(readUTF16String()); |
| case BOOLEAN_TYPE_FALSE: |
| return Boolean.FALSE; |
| case BOOLEAN_TYPE_TRUE: |
| return Boolean.TRUE; |
| case NULL_TYPE: |
| return null; |
| default: |
| throw new RuntimeException("wson unhandled type " + type + " " + |
| position + " length " + buffer.length); |
| } |
| } |
| |
| |
| |
| private final Object readMap(){ |
| int size = readUInt(); |
| Map<String, Object> object = new JSONObject();; |
| for(int i=0; i<size; i++){ |
| String key = readMapKeyUTF16(); |
| Object value = readObject(); |
| object.put(key, value); |
| } |
| return object; |
| } |
| |
| private final Object readArray(){ |
| int length = readUInt(); |
| List<Object> array = new JSONArray(length); |
| for(int i=0; i<length; i++){ |
| array.add(readObject()); |
| } |
| return array; |
| } |
| |
| private final byte readType(){ |
| byte type = buffer[position]; |
| position ++; |
| return type; |
| } |
| |
| |
| private final String readMapKeyUTF16() { |
| int length = readUInt(); |
| length = length/2; |
| if(charsBuffer.length < length){ |
| charsBuffer = new char[length]; |
| } |
| int hash = 5381; |
| if(IS_NATIVE_LITTLE_ENDIAN){ |
| for(int i=0; i<length; i++){ |
| char ch = (char) ((buffer[position] & 0xFF) + |
| (buffer[position + 1] << 8)); |
| charsBuffer[i] = (ch); |
| hash = ((hash << 5) + hash) + ch; |
| position+=2; |
| } |
| }else{ |
| for(int i=0; i<length; i++){ |
| char ch = (char) ((buffer[position + 1] & 0xFF) + |
| (buffer[position] << 8)); |
| charsBuffer[i] = (ch); |
| hash = ((hash << 5) + hash) + ch; |
| position+=2; |
| } |
| } |
| int globalIndex = (globalStringBytesCache.length - 1)&hash; |
| String cache = globalStringBytesCache[globalIndex]; |
| if(cache != null |
| && cache.length() == length){ |
| boolean isStringEqual = true; |
| for(int i=0; i<length; i++){ |
| if(charsBuffer[i] != cache.charAt(i)){ |
| isStringEqual = false; |
| break; |
| } |
| } |
| if(isStringEqual) { |
| return cache; |
| } |
| } |
| cache = new String(charsBuffer, 0, length); |
| if(length < 64) { |
| globalStringBytesCache[globalIndex] = cache; |
| } |
| return cache; |
| } |
| |
| private final String readUTF16String(){ |
| int length = readUInt()/2; |
| if(charsBuffer.length < length){ |
| charsBuffer = new char[length]; |
| } |
| if(IS_NATIVE_LITTLE_ENDIAN){ |
| for(int i=0; i<length; i++){ |
| char ch = (char) ((buffer[position] & 0xFF) + |
| (buffer[position + 1] << 8)); |
| charsBuffer[i] = (ch); |
| position+=2; |
| } |
| }else{ |
| for(int i=0; i<length; i++){ |
| char ch = (char) ((buffer[position + 1] & 0xFF) + |
| (buffer[position] << 8)); |
| charsBuffer[i] = (ch); |
| position+=2; |
| } |
| } |
| return new String(charsBuffer, 0, length); |
| } |
| |
| |
| |
| |
| |
| private final int readVarInt(){ |
| int raw = readUInt(); |
| // This undoes the trick in putVarInt() |
| int num = (((raw << 31) >> 31) ^ raw) >> 1; |
| // This extra step lets us deal with the largest signed values by treating |
| // negative results from read unsigned methods as like unsigned values. |
| // Must re-flip the top bit if the original read value had it set. |
| return num ^ (raw & (1 << 31)); |
| } |
| |
| private final int readUInt(){ |
| int value = 0; |
| int i = 0; |
| int b; |
| while (((b = buffer[position]) & 0x80) != 0) { |
| value |= (b & 0x7F) << i; |
| i += 7; |
| position+=1; |
| if (i > 35) { |
| throw new IllegalArgumentException("Variable length quantity is too long"); |
| } |
| } |
| position+=1; |
| return value | (b << i); |
| } |
| |
| private final long readLong(){ |
| long number = (((buffer[position + 7] & 0xFFL) ) + |
| ((buffer[position + 6] & 0xFFL) << 8) + |
| ((buffer[position + 5] & 0xFFL) << 16) + |
| ((buffer[position + 4] & 0xFFL) << 24) + |
| ((buffer[position + 3] & 0xFFL) << 32) + |
| ((buffer[position + 2] & 0xFFL) << 40) + |
| ((buffer[position + 1] & 0xFFL) << 48) + |
| (((long) buffer[position]) << 56)); |
| position += 8; |
| return number; |
| } |
| |
| private final Object readDouble(){ |
| double number = Double.longBitsToDouble(readLong()); |
| if(number > Integer.MAX_VALUE){ |
| long numberLong = (long) number; |
| double doubleLong = (numberLong); |
| if(number - doubleLong < Double.MIN_NORMAL){ |
| return numberLong; |
| } |
| } |
| return number; |
| } |
| |
| private Object readFloat() { |
| int number = (((buffer[position + 3] & 0xFF) ) + |
| ((buffer[position + 2] & 0xFF) << 8) + |
| ((buffer[position + 1] & 0xFF) << 16) + |
| ((buffer[position ] & 0xFF) << 24)); |
| position +=4; |
| return Float.intBitsToFloat(number); |
| } |
| } |
| |
| /** |
| * wson builder |
| * */ |
| private static final class Builder { |
| |
| private byte[] buffer; |
| private int position; |
| private ArrayList refs; |
| private final static ThreadLocal<byte[]> bufLocal = new ThreadLocal<byte[]>(); |
| private final static ThreadLocal<ArrayList> refsLocal = new ThreadLocal<ArrayList>(); |
| |
| |
| |
| private Builder(){ |
| buffer = bufLocal.get(); |
| if(buffer != null) { |
| bufLocal.set(null); |
| }else{ |
| buffer = new byte[1024]; |
| } |
| refs = refsLocal.get(); |
| if(refs != null){ |
| refsLocal.set(null); |
| }else{ |
| refs = new ArrayList<>(16); |
| } |
| } |
| |
| |
| private final byte[] toWson(Object object){ |
| writeObject(object); |
| byte[] bts = new byte[position]; |
| System.arraycopy(buffer, 0, bts, 0, position); |
| return bts; |
| } |
| |
| private final void close(){ |
| if(buffer.length <= 1024*16){ |
| bufLocal.set(buffer); |
| } |
| if(refs.isEmpty()){ |
| refsLocal.set(refs); |
| }else{ |
| refs.clear(); |
| } |
| refs = null; |
| buffer = null; |
| position = 0; |
| } |
| |
| private final void writeObject(Object object) { |
| if(object instanceof CharSequence){ |
| ensureCapacity(2); |
| writeByte(STRING_TYPE); |
| writeUTF16String((CharSequence) object); |
| return; |
| }else if (object instanceof Map){ |
| if(refs.contains(object)){ |
| ensureCapacity(2); |
| writeByte(NULL_TYPE); |
| return; |
| } |
| refs.add(object); |
| Map map = (Map) object; |
| writeMap(map); |
| refs.remove(refs.size()-1); |
| return; |
| }else if (object instanceof List){ |
| if(refs.contains(object)){ |
| ensureCapacity(2); |
| writeByte(NULL_TYPE); |
| return; |
| } |
| refs.add(object); |
| ensureCapacity(8); |
| List list = (List) object; |
| writeByte(ARRAY_TYPE); |
| writeUInt(list.size()); |
| for(Object value : list){ |
| writeObject(value); |
| } |
| refs.remove(refs.size()-1); |
| return; |
| }else if (object instanceof Number){ |
| Number number = (Number) object; |
| writeNumber(number); |
| return; |
| }else if (object instanceof Boolean){ |
| ensureCapacity(2); |
| Boolean value = (Boolean) object; |
| if(value){ |
| writeByte(BOOLEAN_TYPE_TRUE); |
| }else{ |
| writeByte(BOOLEAN_TYPE_FALSE); |
| } |
| return; |
| }else if(object == null){ |
| ensureCapacity(2); |
| writeByte(NULL_TYPE); |
| return; |
| }else if (object.getClass().isArray()){ |
| if(refs.contains(object)){ |
| ensureCapacity(2); |
| writeByte(NULL_TYPE); |
| return; |
| } |
| refs.add(object); |
| ensureCapacity(8); |
| int length = Array.getLength(object); |
| writeByte(ARRAY_TYPE); |
| writeUInt(length); |
| for(int i=0; i<length; i++){ |
| Object value = Array.get(object, i); |
| writeObject(value); |
| } |
| refs.remove(refs.size()-1); |
| return; |
| }else if(object instanceof Date){ |
| ensureCapacity(10); |
| double date = ((Date)object).getTime(); |
| writeByte(NUMBER_DOUBLE_TYPE); |
| writeDouble(date); |
| }else if(object instanceof Calendar){ |
| ensureCapacity(10); |
| double date = ((Calendar)object).getTime().getTime(); |
| writeByte(NUMBER_DOUBLE_TYPE); |
| writeDouble(date); |
| }else if(object instanceof Collection){ |
| if(refs.contains(object)){ |
| ensureCapacity(2); |
| writeByte(NULL_TYPE); |
| return; |
| } |
| refs.add(object); |
| ensureCapacity(8); |
| Collection list = (Collection) object; |
| writeByte(ARRAY_TYPE); |
| writeUInt(list.size()); |
| for(Object value : list){ |
| writeObject(value); |
| } |
| refs.remove(refs.size()-1); |
| }else{ |
| if(refs.contains(object)){ |
| ensureCapacity(2); |
| writeByte(NULL_TYPE); |
| }else { |
| refs.add(object); |
| if(object.getClass().isEnum()){ |
| writeObject(JSON.toJSONString(object)); |
| }else{ |
| writeAdapterObject(object); |
| } |
| refs.remove(refs.size()-1); |
| } |
| return; |
| } |
| } |
| |
| private final void writeNumber(Number number) { |
| ensureCapacity(12); |
| if(number instanceof Integer){ |
| writeByte(NUMBER_INT_TYPE); |
| writeVarInt(number.intValue()); |
| return; |
| } |
| |
| if(number instanceof Float){ |
| writeByte(NUMBER_FLOAT_TYPE); |
| writeFloat(number.floatValue()); |
| return; |
| } |
| if(number instanceof Double){ |
| writeByte(NUMBER_DOUBLE_TYPE); |
| writeDouble(number.doubleValue()); |
| return; |
| } |
| |
| if(number instanceof Long){ |
| writeByte(NUMBER_LONG_TYPE); |
| writeLong(number.longValue()); |
| return; |
| } |
| |
| if(number instanceof Short |
| || number instanceof Byte){ |
| writeByte(NUMBER_INT_TYPE); |
| writeVarInt(number.intValue()); |
| return; |
| } |
| |
| if(number instanceof BigInteger){ |
| writeByte(NUMBER_BIG_INTEGER_TYPE); |
| writeUTF16String(number.toString()); |
| return; |
| } |
| |
| if(number instanceof BigDecimal){ |
| String value = number.toString(); |
| double doubleValue = number.doubleValue(); |
| if(value.equals(Double.toString(doubleValue))){ |
| writeByte(NUMBER_DOUBLE_TYPE); |
| writeDouble(doubleValue); |
| }else { |
| writeByte(NUMBER_BIG_DECIMAL_TYPE); |
| writeUTF16String(value); |
| } |
| return; |
| } |
| writeByte(STRING_TYPE); |
| writeUTF16String(number.toString()); |
| |
| } |
| |
| private final void writeMap(Map map) { |
| if(WriteMapNullValue){ |
| ensureCapacity(8); |
| writeByte(MAP_TYPE); |
| writeUInt(map.size()); |
| Set<Map.Entry<Object,Object>> entries = map.entrySet(); |
| for(Map.Entry<Object,Object> entry : entries){ |
| writeMapKeyUTF16(entry.getKey().toString()); |
| writeObject(entry.getValue()); |
| } |
| }else{ |
| Set<Map.Entry<Object,Object>> entries = map.entrySet(); |
| int nullValueSize = 0; |
| for(Map.Entry<Object,Object> entry : entries){ |
| if(entry.getValue() == null){ |
| nullValueSize++; |
| } |
| } |
| |
| ensureCapacity(8); |
| writeByte(MAP_TYPE); |
| writeUInt(map.size()-nullValueSize); |
| for(Map.Entry<Object,Object> entry : entries){ |
| if(entry.getValue() == null){ |
| continue; |
| } |
| writeMapKeyUTF16(entry.getKey().toString()); |
| writeObject(entry.getValue()); |
| } |
| } |
| } |
| |
| |
| private final void writeByte(byte type){ |
| buffer[position] = type; |
| position++; |
| } |
| |
| private final void writeAdapterObject(Object object){ |
| if(specialClass.get(object.getClass().getName()) != null){ |
| writeObject(JSON.toJSON(object)); |
| return; |
| } |
| try{ |
| writeMap(toMap(object)); |
| }catch (Exception e){ |
| specialClass.put(object.getClass().getName(), true); |
| writeObject(JSON.toJSON(object)); |
| } |
| } |
| |
| private final Map toMap(Object object){ |
| Map map = new JSONObject(); |
| try { |
| Class<?> targetClass = object.getClass(); |
| String key = targetClass.getName(); |
| List<Method> methods = getBeanMethod(key, targetClass); |
| for (Method method : methods) { |
| String methodName = method.getName(); |
| if (methodName.startsWith(METHOD_PREFIX_GET)) { |
| Object value = method.invoke(object); |
| if(value != null){ |
| StringBuilder builder = new StringBuilder(method.getName().substring(3)); |
| builder.setCharAt(0, Character.toLowerCase(builder.charAt(0))); |
| map.put(builder.toString(), (Object) value); |
| } |
| }else if(methodName.startsWith(METHOD_PREFIX_IS)){ |
| Object value = method.invoke(object); |
| if(value != null){ |
| StringBuilder builder = new StringBuilder(method.getName().substring(2)); |
| builder.setCharAt(0, Character.toLowerCase(builder.charAt(0))); |
| map.put(builder.toString(), value); |
| } |
| } |
| } |
| List<Field> fields = getBeanFields(key, targetClass); |
| for(Field field : fields){ |
| String fieldName = field.getName(); |
| if(map.containsKey(fieldName)){ |
| continue; |
| } |
| Object value = field.get(object); |
| if(value == null){ |
| continue; |
| } |
| map.put(fieldName, value); |
| } |
| }catch (Exception e){ |
| if(e instanceof RuntimeException){ |
| throw (RuntimeException)e; |
| }else{ |
| throw new RuntimeException(e); |
| } |
| } |
| return map; |
| } |
| |
| private final void writeMapKeyUTF16(String value){ |
| writeUTF16String(value); |
| } |
| |
| |
| |
| |
| /** |
| * writeString UTF-16 |
| * */ |
| private final void writeUTF16String(CharSequence value){ |
| int length = value.length(); |
| ensureCapacity(length*2 + 8); |
| writeUInt(length*2); |
| if(IS_NATIVE_LITTLE_ENDIAN){ |
| for(int i=0; i<length; i++){ |
| char ch = value.charAt(i); |
| buffer[position] = (byte) (ch); |
| buffer[position+1] = (byte) (ch >>> 8); |
| position+=2; |
| } |
| }else{ |
| for(int i=0; i<length; i++){ |
| char ch = value.charAt(i); |
| buffer[position + 1] = (byte) (ch ); |
| buffer[position] = (byte) (ch >>> 8); |
| position+=2; |
| } |
| } |
| } |
| |
| |
| private final void writeDouble(double value){ |
| writeLong(Double.doubleToLongBits(value)); |
| } |
| |
| private final void writeFloat(float value){ |
| int val = Float.floatToIntBits(value); |
| buffer[position + 3] = (byte) (val ); |
| buffer[position + 2] = (byte) (val >>> 8); |
| buffer[position + 1] = (byte) (val >>> 16); |
| buffer[position ] = (byte) (val >>> 24); |
| position += 4; |
| } |
| |
| private final void writeLong(long val){ |
| buffer[position + 7] = (byte) (val ); |
| buffer[position + 6] = (byte) (val >>> 8); |
| buffer[position + 5] = (byte) (val >>> 16); |
| buffer[position + 4] = (byte) (val >>> 24); |
| buffer[position + 3] = (byte) (val >>> 32); |
| buffer[position + 2] = (byte) (val >>> 40); |
| buffer[position + 1] = (byte) (val >>> 48); |
| buffer[position ] = (byte) (val >>> 56); |
| position += 8; |
| } |
| |
| private final void writeVarInt(int value){ |
| writeUInt((value << 1) ^ (value >> 31)); |
| } |
| |
| private final void writeUInt(int value){ |
| while ((value & 0xFFFFFF80) != 0) { |
| buffer[position] = (byte)((value & 0x7F) | 0x80); |
| position++; |
| value >>>= 7; |
| } |
| buffer[position] = (byte)(value & 0x7F); |
| position++; |
| } |
| |
| |
| private final void ensureCapacity(int minCapacity) { |
| minCapacity += position; |
| // overflow-conscious code |
| if (minCapacity - buffer.length > 0){ |
| int oldCapacity = buffer.length; |
| int newCapacity = oldCapacity << 1; |
| if(newCapacity < 1024*16){ |
| newCapacity = 1024*16; |
| } |
| if (newCapacity - minCapacity < 0) { |
| newCapacity = minCapacity; |
| } |
| buffer = Arrays.copyOf(buffer, newCapacity); |
| } |
| } |
| } |
| |
| |
| /** |
| * cache json property key, most of them all same |
| * */ |
| private static final int GLOBAL_STRING_CACHE_SIZE = 2*1024; |
| private static final ThreadLocal<char[]> localCharsBufferCache = new ThreadLocal<>(); |
| private static final String[] globalStringBytesCache = new String[GLOBAL_STRING_CACHE_SIZE]; |
| |
| |
| |
| |
| /** |
| * lru cache, to map helper |
| * */ |
| private static final String METHOD_PREFIX_GET = "get"; |
| private static final String METHOD_PREFIX_IS = "is"; |
| private static LruCache<String, List<Method>> methodsCache = new LruCache<>(128); |
| private static LruCache<String, List<Field>> fieldsCache = new LruCache<>(128); |
| private static LruCache<String, Boolean> specialClass = new LruCache<>(16); |
| |
| |
| private static final List<Method> getBeanMethod(String key, Class targetClass){ |
| List<Method> methods = methodsCache.get(key); |
| if(methods == null){ |
| methods = new ArrayList<>(); |
| Method[] allMethods = targetClass.getMethods(); |
| for(Method method : allMethods){ |
| if(method.getDeclaringClass() == Object.class){ |
| continue; |
| } |
| if( (method.getModifiers() & Modifier.STATIC) != 0){ |
| continue; |
| } |
| String methodName = method.getName(); |
| if(methodName.startsWith(METHOD_PREFIX_GET) |
| || methodName.startsWith(METHOD_PREFIX_IS)) { |
| if(method.getAnnotation(JSONField.class) != null){ |
| throw new UnsupportedOperationException("getBeanMethod JSONField Annotation Not Handled, Use toJSON"); |
| } |
| methods.add(method); |
| } |
| } |
| methodsCache.put(key, methods); |
| } |
| return methods; |
| } |
| |
| |
| |
| private static final List<Field> getBeanFields(String key, Class targetClass){ |
| List<Field> fieldList = fieldsCache.get(key); |
| if(fieldList == null) { |
| Field[] fields = targetClass.getFields(); |
| fieldList = new ArrayList<>(fields.length); |
| for(Field field : fields){ |
| if((field.getModifiers() & Modifier.STATIC) != 0){ |
| continue; |
| } |
| if(field.getAnnotation(JSONField.class) != null){ |
| throw new UnsupportedOperationException("getBeanMethod JSONField Annotation Not Handled, Use toJSON"); |
| } |
| fieldList.add(field); |
| } |
| fieldsCache.put(key, fieldList); |
| } |
| return fieldList; |
| } |
| |
| } |