com.addthis.codec.kv.CodecKV Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codec Show documentation
Show all versions of codec Show documentation
Codec serialization library
/*
* 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.addthis.codec.kv;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import com.addthis.basis.kv.KVPair;
import com.addthis.basis.kv.KVPairs;
import com.addthis.basis.util.Base64;
import com.addthis.basis.util.Bytes;
import com.addthis.codec.Codec;
import com.addthis.codec.reflection.Fields;
import com.addthis.codec.codables.SuperCodable;
import com.addthis.codec.reflection.CodableClassInfo;
import com.addthis.codec.reflection.CodableFieldInfo;
import com.addthis.codec.util.CodableStatistics;
public final class CodecKV extends Codec {
private CodecKV() {}
public static final CodecKV INSTANCE = new CodecKV();
@Override
public Object decode(Object shell, byte[] data) throws Exception {
return decodeString(Fields.getClassFieldMap(shell.getClass()), shell,
new KVPairs(Bytes.toString(data)));
}
@Override
public byte[] encode(Object obj) throws Exception {
return Bytes.toBytes(encodeString(obj));
}
@Override
public CodableStatistics statistics(Object obj) {
throw new UnsupportedOperationException();
}
@Override
public boolean storesNull(byte data[]) {
throw new UnsupportedOperationException();
}
// @SuppressWarnings("unchecked")
public static String encodeString(Object object) throws Exception {
if (object instanceof SuperCodable) {
((SuperCodable) object).preEncode();
}
KVPairs kv = new KVPairs();
CodableClassInfo fieldMap = Fields.getClassFieldMap(object.getClass());
if (fieldMap.size() == 0) {
return object.toString();
}
String stype = fieldMap.getClassName(object);
if (stype != null) {
kv.putValue(fieldMap.getClassField(), stype);
}
for (Iterator fields = fieldMap.values().iterator(); fields.hasNext(); ) {
CodableFieldInfo field = fields.next();
Object value = field.get(object);
if (value == null) {
continue;
}
String name = field.getName();
if (field.isArray()) {
int len = Array.getLength(value);
if (field.getType() == byte.class) {
kv.putValue(name, new String(Base64.encode((byte[]) value)));
} else {
for (int i = 0; i < len; i++) {
Object av = Array.get(value, i);
kv.putValue(name + i, encodeString(av));
}
}
} else if (field.isCollection()) {
Collection> coll = (Collection>) value;
int idx = 0;
for (Iterator> iter = coll.iterator(); iter.hasNext(); idx++) {
kv.putValue(name + idx, encodeString(iter.next()));
}
} else if (field.isMap()) {
Map, ?> map = (Map, ?>) value;
KVPairs nv = new KVPairs();
for (Entry, ?> entry : map.entrySet()) {
nv.putValue(entry.getKey().toString(), encodeString(entry.getValue()));
}
kv.putValue(field.getName(), nv.toString());
} else if (field.isNative()) {
kv.putValue(name, value.toString());
} else if (field.isEnum()) {
kv.putValue(name, value.toString());
} else if (field.isCodable()) {
kv.putValue(name, encodeString(value));
}
}
return kv.toString();
}
public static Object decodeArray(Class> type, KVPairs data, String name) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, Exception {
if (type == byte.class) {
String text = data.getValue(name);
if (text != null) {
return Base64.decode(text.toCharArray());
}
} else {
List arr = getList(data, name);
int size = arr.size();
if (size > 0) {
Object value = Array.newInstance(type, size);
for (int i = 0; i < size; i++) {
Array.set(value, i, decodeString(type, arr.get(i)));
}
return value;
}
}
return null;
}
public static Object decodeString(Class> type, String kv) throws Exception {
if (Fields.isNative(type)) {
return decodeNative(type, kv);
} else {
return decodeString(Fields.getClassFieldMap(type), type, kv);
}
}
public static Object decodeString(CodableClassInfo fieldMap, Class> type, String kv) throws Exception {
if (fieldMap.size() == 0) {
return decodeNative(type, kv);
}
return decodeKV(fieldMap, type, new KVPairs(kv));
}
public static Object decodeKV(Class> type, KVPairs data) throws Exception {
return decodeString(Fields.getClassFieldMap(type), type, data);
}
public static Object decodeKV(CodableClassInfo fieldMap, Class> type, KVPairs data) throws Exception {
String stype = data.getValue(fieldMap.getClassField());
Class> atype = stype != null ? fieldMap.getClass(stype) : type;
if (atype != null && atype != type) {
fieldMap = Fields.getClassFieldMap(atype);
type = atype;
}
Object object = type.newInstance();
return decodeString(fieldMap, object, data);
}
public static Object decodeString(CodableClassInfo fieldMap, Object object, KVPairs data) throws Exception {
for (Iterator fields = fieldMap.values().iterator(); fields.hasNext();) {
CodableFieldInfo field = fields.next();
String name = field.getName();
if (field.isArray()) {
field.set(object, decodeArray(field.getType(), data, name));
} else if (field.isCollection()) {
List arr = getList(data, name);
int size = arr.size();
if (size > 0) {
Collection