org.nustaq.kson.KsonSerializer Maven / Gradle / Ivy
Show all versions of fst Show documentation
/*
* Copyright 2014 Ruediger Moeller.
*
* 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 org.nustaq.kson;
import org.nustaq.serialization.FSTClazzInfo;
import org.nustaq.serialization.FSTConfiguration;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
*
* Date: 20.12.13
* Time: 12:03
*
* Created by ruedi on 07.08.2014.
*/
public class KsonSerializer {
protected KsonCharOutput out;
protected KsonTypeMapper mapper;
protected boolean pretty = true;
protected boolean writeNull = false;
protected FSTConfiguration conf;
public KsonSerializer(KsonCharOutput out, KsonTypeMapper mapper, FSTConfiguration conf) {
this.out = out;
this.mapper = mapper;
this.conf = conf;
}
public void writeObject(Object o) throws Exception {
writeObjectInternal(null, null, o, 0);
}
protected void writeObjectInternal(Class expectedClass, Class expectedValueClass, Object o, int indent) throws Exception {
if (o == null) {
if (indent >= 0) writeIndent(indent);
out.writeString("null");
if (indent >= 0) writeln();
return;
}
if (o instanceof Character) {
if (indent >= 0) writeIndent(indent);
final char ch = ((Character) o).charValue();
if (ch <128 && ch > 32)
out.writeString(o.toString());
else
out.writeString(Integer.toString(ch));
if (indent >= 0) writeln();
} else if (o instanceof Number || o instanceof Boolean) {
if (indent >= 0) writeIndent(indent);
out.writeString(o.toString());
if (indent >= 0) writeln();
} else if (o instanceof String) {
if (indent >= 0) writeIndent(indent);
writeString((String) o);
if (indent >= 0) writeln();
} else if (o instanceof Map) {
Map map = (Map) o;
writeIndent(indent);
out.writeString("{");
writeln();
for (Iterator iterator = map.keySet().iterator(); iterator.hasNext(); ) {
Object next = iterator.next();
Object value = map.get(next);
boolean valueSingleLine = isSingleLine(null, value);
boolean keySingleLine = isSingleLine(null, next);
if (keySingleLine) {
writeIndent(indent + 1);
if ( next instanceof String ) {
writeKey((String) next);
} else
writeObjectInternal(expectedClass, null, next, -1);
out.writeString(" : ");
if (!valueSingleLine)
writeln();
} else {
writeObjectInternal(expectedClass, null, next, indent + 1);
writeIndent(indent + 1);
out.writeString(":");
if (!valueSingleLine)
writeln();
}
if (valueSingleLine) {
out.writeChar(' ');
writeObjectInternal(expectedValueClass, null, value, -1);
if (iterator.hasNext())
writeListSep();
writeln();
} else {
writeObjectInternal(expectedValueClass, null, value, indent + 1);
if (iterator.hasNext())
writeListSep();
}
}
writeIndent(indent);
out.writeChar('}');
writeln();
} else if (o instanceof Collection) {
Collection coll = (Collection) o;
writeIndent(indent);
writeListStart();
writeln();
for (Iterator iterator = coll.iterator(); iterator.hasNext(); ) {
Object next = iterator.next();
writeObjectInternal(expectedClass, null, next, indent + 1);
if (iterator.hasNext())
writeListSep();
}
writeIndent(indent);
writeListEnd();
writeln();
} else if (o.getClass().isArray()) {
writeIndent(indent);
writeListStart();
int len = Array.getLength(o);
boolean lastWasSL = true;
Class expect = o.getClass().getComponentType();
for (int ii = 0; ii < len; ii++) {
Object val = Array.get(o, ii);
if (lastWasSL && (isSingleLine(null, val)||isSingleLineCLazz(expect))) {
writeObjectInternal(expect, null, val, -1);
out.writeChar(' ');
if (ii < len-1)
writeListSep();
} else {
if (ii == 0)
writeln();
writeObjectInternal(expect, null, val, indent + 1);
if (ii < len-1)
writeListSep();
lastWasSL = false;
}
}
if (!lastWasSL)
writeIndent(indent);
writeListEnd();
writeln();
} else {
writeIndent(indent);
writeClazzTag(expectedClass, o);
writeln();
FSTClazzInfo clInfo = conf.getCLInfoRegistry().getCLInfo(o.getClass(), conf);
FSTClazzInfo.FSTFieldInfo[] fieldInfo = clInfo.getFieldInfo();
for (int i = 0; i < fieldInfo.length; i++) {
FSTClazzInfo.FSTFieldInfo fstFieldInfo = fieldInfo[i];
Class expectedKey = Kson.fumbleOutGenericKeyType(fstFieldInfo.getField());
Class expectedValue = Kson.fumbleOutGenericValueType(fstFieldInfo.getField());
Object fieldValue = fstFieldInfo.getField().get(o);
// fieldValue = mapper.coerceWriting(fieldValue);
if (!isNullValue(fstFieldInfo, fieldValue) || writeNull) {
writeIndent(indent + 1);
final String name = fstFieldInfo.getName();
writeKey(name);
out.writeChar(':');
if (isSingleLine(fstFieldInfo, fieldValue)) {
out.writeString(" ");
writeObjectInternal(expectedKey, expectedValue, fieldValue, 0);
writeListSep();
} else {
writeln();
writeObjectInternal(expectedKey, expectedValue, fieldValue, indent + 2);
writeListSep();
}
}
}
removeLastListSep();
writeIndent(indent);
out.writeChar('}');
writeln();
}
}
/**
* called when writing a key of an key:value inside an object or map
* @param name
*/
protected void writeKey(String name) {
out.writeString(name);
}
/**
* determines classname tagging. Overrifing can enforce class tags always or (JSon) write as
* special attribute
* @param expectedClass
* @param o
*/
protected void writeClazzTag(Class expectedClass, Object o) {
if (expectedClass == o.getClass()) {
out.writeString("{");
} else {
String stringForType = mapper.getStringForType(o.getClass());
out.writeString(stringForType + " {");
}
}
protected void writeListEnd() {
out.writeChar(']');
}
protected void writeListStart() {
out.writeString("[ ");
}
// quirksmode: true
protected void removeLastListSep() {
}
protected void writeListSep() {
}
private boolean isSingleLine(FSTClazzInfo.FSTFieldInfo fstFieldInfo, Object fieldValue) {
if (fstFieldInfo == null) {
if (fieldValue instanceof Class) { // now that's dirty ..
Class clz = (Class) fieldValue;
return isSingleLineCLazz(clz);
}
return fieldValue==null || isSingleLineCLazz(fieldValue.getClass());
}
if (fstFieldInfo.isArray() && isSingleLine(null, fstFieldInfo.getType().getComponentType())) {
return true;
}
return (fieldValue==null || isSingleLineCLazz(fieldValue.getClass())) || isSingleLineCLazz(fstFieldInfo.getType());
}
private boolean isSingleLineCLazz(Class clz) {
return String.class.isAssignableFrom(clz) || Number.class.isAssignableFrom(clz) || clz.isPrimitive() || clz == Boolean.class || clz == Character.class;
}
static Character zeroC = Character.valueOf((char) 0);
private boolean isNullValue(FSTClazzInfo.FSTFieldInfo fstFieldInfo, Object fieldValue) {
if (fieldValue==null)
return true;
if (writeNull)
return false;
if (fstFieldInfo != null ) {
if (fstFieldInfo.getType().isPrimitive()) {
if (fieldValue instanceof Number) {
return ((Number) fieldValue).doubleValue() == 0.0;
}
return fieldValue.equals(zeroC) || fieldValue.equals(Boolean.FALSE);
}
}
return false;
}
protected void writeln() {
if (pretty)
out.writeChar('\n');
}
public void writeString(String string) {
if (string == null) {
out.writeString("null");
return;
}
if (string.length() == 0) {
out.writeString("\"\"");
return;
}
char b;
char c = 0;
int i;
int len = string.length();
String t;
boolean ws = shouldQuote(string);
if (ws)
out.writeChar('\"');
for (i = 0; i < len; i += 1) {
b = c;
c = string.charAt(i);
switch (c) {
case '\\':
case '"':
out.writeChar('\\');
out.writeChar(c);
break;
case '/':
if (b == '<') {
out.writeChar('\\');
}
out.writeChar(c);
break;
case '\b':
out.writeString("\\b");
break;
case '\t':
out.writeString("\\t");
break;
case '\n':
out.writeString("\\n");
break;
case '\f':
out.writeString("\\f");
break;
case '\r':
out.writeString("\\r");
break;
default:
if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
(c >= '\u2000' && c < '\u2100')) {
t = "000" + Integer.toHexString(c);
out.writeString("\\u" + t.substring(t.length() - 4));
} else {
out.writeChar(c);
}
}
}
if (ws)
out.writeChar('"');
}
protected boolean shouldQuote(String string) {
boolean ws = false;
for (int ii = 0; ii < string.length(); ii++) {
final char ch = string.charAt(ii);
if (ii==0 && !Character.isLetter(ch) )
return true;
if (!Character.isLetterOrDigit(ch) && ch != '_' && ch != '-' && ch != '(' && ch != ')' && ch != '[' && ch != ']' && ch != '.') {
ws = true;
break;
}
}
return ws;
}
protected void writeIndent(int indent) {
if (pretty) {
for (int i = 0; i < indent * 2; i++) {
out.writeChar(' ');
}
}
}
public boolean isWriteNull() {
return writeNull;
}
public void setWriteNull(boolean writeNull) {
this.writeNull = writeNull;
}
public void writeObject(Object o, Class aClass) throws Exception {
writeObjectInternal(aClass, null, o, 0);
}
}