package io.nettythrift.utils.json;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.thrift.TFieldIdEnum;
import org.apache.thrift.meta_data.FieldMetaData;
import org.apache.thrift.meta_data.FieldValueMetaData;
import org.apache.thrift.meta_data.ListMetaData;
import org.apache.thrift.meta_data.MapMetaData;
import org.apache.thrift.meta_data.SetMetaData;
import org.apache.thrift.meta_data.StructMetaData;
import org.apache.thrift.protocol.TField;
import org.apache.thrift.protocol.TType;
/**
* Common class for List,Set,Map, and Struct.(Struct is similar with Map)
*
* @author houkx
*
*/
public class BaseArray {
private int fieldIndex;
private final ArrayJson obj;
private FieldValueMetaData metaData;
private final int addStep;
private final int ARRAY_SIZE;
// ----- fields for Struct --------------
private Map<String, Map.Entry<TFieldIdEnum, FieldMetaData>> elementMetas;
private int createIndex = 0;
// field for JsonArray
private Map.Entry<TFieldIdEnum, FieldMetaData>[] elementMetaArr;
// ---------------------------------------
@SuppressWarnings("unchecked")
public BaseArray(FieldValueMetaData meta, ArrayJson obj) {
this.obj = obj;
this.metaData = meta;
ARRAY_SIZE = obj.arraySize();
int addStep = 1;
if (meta.type == TType.STRUCT) {
StructMetaData sm = (StructMetaData) meta;
Map<TFieldIdEnum, FieldMetaData> map = (Map<TFieldIdEnum, FieldMetaData>) FieldMetaData
.getStructMetaDataMap(sm.structClass);
if (obj instanceof JSONObject) {
this.fieldIndex = 1;
addStep = 2;
if (map != null && map.size() > 0) {
elementMetas = new HashMap<String, Map.Entry<TFieldIdEnum, FieldMetaData>>(map.size());
for (Map.Entry<TFieldIdEnum, FieldMetaData> m : map.entrySet()) {
TFieldIdEnum k = m.getKey();
// fieldName <-> metaData
elementMetas.put(k.getFieldName(), m);
// id <-> metaData
elementMetas.put(String.valueOf(k.getThriftFieldId()), m);
}
}
String fieldName = obj.getString(0);
if (!useId && fieldName.length() > 0) {
char c0 = fieldName.charAt(0);
useId = c0 >= '0' && c0 <= '9';
}
} else {
elementMetaArr = map.entrySet().toArray(new Map.Entry[0]);
Arrays.sort(elementMetaArr, new Comparator<Map.Entry<TFieldIdEnum, FieldMetaData>>() {
@Override
public int compare(Entry<TFieldIdEnum, FieldMetaData> o1, Entry<TFieldIdEnum, FieldMetaData> o2) {
return o1.getKey().getThriftFieldId() - o2.getKey().getThriftFieldId();
}
});
}
}
this.addStep = addStep;
}
public int length() {
return obj.length();
}
protected int currentIndex() {
int cur = fieldIndex;
if (cur >= ARRAY_SIZE) {
cur = ARRAY_SIZE - 1;
} else {
fieldIndex += addStep;
}
return cur;
}
public Object get() {
return obj.get(currentIndex());
}
public boolean getBoolean() {
return obj.getBoolean(currentIndex());
}
public double getDouble() {
return obj.getDouble(currentIndex());
}
public <E extends Enum<E>> E getEnum(Class<E> clazz) {
return obj.getEnum(clazz, currentIndex());
}
public BigDecimal getBigDecimal() {
return obj.getBigDecimal(currentIndex());
}
public BigInteger getBigInteger() {
return obj.getBigInteger(currentIndex());
}
public int getInt() {
return obj.getInt(currentIndex());
}
public long getLong() {
return obj.getLong(currentIndex());
}
public String getString() {
return obj.getString(currentIndex());
}
// 元素类型还是数组(或map)
public BaseArray getArray() {
return arr(metaData);
}
protected BaseArray arr(FieldValueMetaData m) {
switch (m.type) {
case TType.LIST: {
ArrayJson o = (ArrayJson) obj.get(currentIndex());
ListMetaData lm = (ListMetaData) m;
FieldValueMetaData em = lm.elemMetaData;
return new BaseArray(em, o);
}
case TType.SET: {
ArrayJson o = (ArrayJson) obj.get(currentIndex());
SetMetaData sm = (SetMetaData) m;
FieldValueMetaData em = sm.elemMetaData;
return new BaseArray(em, o);
}
case TType.MAP: {
int cur = currentIndex();
ArrayJson o = (ArrayJson) obj.get(cur);
MapMetaData mm = (MapMetaData) m;
FieldValueMetaData em = cur % 2 == 0 ? mm.keyMetaData : mm.valueMetaData;
return new BaseArray(em, o);
}
case TType.STRUCT: {
int cur = currentIndex();
ArrayJson o = (ArrayJson) obj.get(cur);
FieldMetaData fm = prevFieldMetaData;
return new BaseArray(fm.valueMetaData, o);
}
default:
throw new RuntimeException("ILLegal access:getArray():type=" + m.type);
}
}
public FieldValueMetaData getMetaData() {
return metaData;
}
private FieldMetaData prevFieldMetaData;
private boolean useId;
public boolean useId() {
return useId;
}
/**
* Struct use only.
*
* @return
*/
public TField newField() {
if (createIndex < obj.length()) {
Map.Entry<TFieldIdEnum, FieldMetaData> entry = null;
if (addStep == 2) {
String fieldName = obj.getString(createIndex << 1);
entry = elementMetas.get(fieldName);
createIndex++;
} else {
int i = createIndex;
Object o;
while (i < obj.length() && ((o = obj.get(i)) == null || o == JSONObject.NULL)) {
currentIndex();// array index: +1
i++;
}
entry = elementMetaArr[i];
createIndex = i + 1;
}
FieldMetaData fm = entry.getValue();
prevFieldMetaData = fm;
return new TField(fm.fieldName, fm.valueMetaData.type, entry.getKey().getThriftFieldId());
}
return null;
}
}