org.nustaq.serialization.FSTObjectInput Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fst Show documentation
Show all versions of fst Show documentation
A fast java serialization drop in-replacement and some serialization based utils such as Structs and OffHeap Memory.
/*
* 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.serialization;
import org.nustaq.serialization.coders.Unknown;
import org.nustaq.serialization.minbin.MBObject;
import org.nustaq.serialization.util.FSTUtil;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: Möller
* Date: 04.11.12
* Time: 11:53
*/
/**
* replacement of ObjectInputStream
*/
public class FSTObjectInput implements ObjectInput {
public static boolean REGISTER_ENUMS_READ = false; // do not register enums on read. Flag is saver in case things brake somewhere
public static ByteArrayInputStream emptyStream = new ByteArrayInputStream(new byte[0]);
protected FSTDecoder codec;
protected FSTObjectRegistry objects;
protected Stack debugStack;
protected int curDepth;
protected ArrayList callbacks;
// FSTConfiguration conf;
// mirrored from conf
protected boolean ignoreAnnotations;
protected FSTClazzInfoRegistry clInfoRegistry;
// done
protected ConditionalCallback conditionalCallback;
protected int readExternalReadAHead = 8000;
protected VersionConflictListener versionConflictListener;
protected FSTConfiguration conf;
// copied values from conf
protected boolean isCrossPlatform;
public FSTConfiguration getConf() {
return conf;
}
@Override
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
getCodec().readPlainBytes(b, off, len);
}
@Override
public int skipBytes(int n) throws IOException {
getCodec().skip(n);
return n;
}
@Override
public boolean readBoolean() throws IOException {
return getCodec().readFByte() == 0 ? false : true;
}
@Override
public byte readByte() throws IOException {
return getCodec().readFByte();
}
@Override
public int readUnsignedByte() throws IOException {
return ((int) getCodec().readFByte()+256) & 0xff;
}
@Override
public short readShort() throws IOException {
return getCodec().readFShort();
}
@Override
public int readUnsignedShort() throws IOException {
return ((int)readShort()+65536) & 0xffff;
}
@Override
public char readChar() throws IOException {
return getCodec().readFChar();
}
@Override
public int readInt() throws IOException {
return getCodec().readFInt();
}
@Override
public long readLong() throws IOException {
return getCodec().readFLong();
}
@Override
public float readFloat() throws IOException {
return getCodec().readFFloat();
}
@Override
public double readDouble() throws IOException {
return getCodec().readFDouble();
}
@Override
public String readLine() throws IOException {
throw new RuntimeException("not implemented");
}
@Override
public String readUTF() throws IOException {
return getCodec().readStringUTF();
}
public FSTDecoder getCodec() {
return codec;
}
protected void setCodec(FSTDecoder codec) {
this.codec = codec;
}
public boolean isClosed() {
return closed;
}
protected static class CallbackEntry {
ObjectInputValidation cb;
int prio;
CallbackEntry(ObjectInputValidation cb, int prio) {
this.cb = cb;
this.prio = prio;
}
}
public static interface ConditionalCallback {
public boolean shouldSkip(Object halfDecoded, int streamPosition, Field field);
}
public FSTObjectInput() throws IOException {
this(emptyStream, FSTConfiguration.getDefaultConfiguration());
}
public FSTObjectInput(FSTConfiguration conf) {
this(emptyStream, conf);
}
/**
* Creates a FSTObjectInput that uses the specified
* underlying InputStream.
*
* @param in the specified input stream
*/
public FSTObjectInput(InputStream in) throws IOException {
this(in, FSTConfiguration.getDefaultConfiguration());
}
/**
* Creates a FSTObjectInput that uses the specified
* underlying InputStream.
*
* Don't create a FSTConfiguration with each stream, just create one global static configuration and reuseit.
* FSTConfiguration is threadsafe.
*
* @param in the specified input stream
*/
public FSTObjectInput(InputStream in, FSTConfiguration conf) {
setCodec(conf.createStreamDecoder());
getCodec().setInputStream(in);
isCrossPlatform = conf.isCrossPlatform();
initRegistries(conf);
this.conf = conf;
}
public Class getClassForName(String name) throws ClassNotFoundException {
return getCodec().classForName(name);
}
protected void initRegistries(FSTConfiguration conf) {
ignoreAnnotations = conf.getCLInfoRegistry().isIgnoreAnnotations();
clInfoRegistry = conf.getCLInfoRegistry();
objects = (FSTObjectRegistry) conf.getCachedObject(FSTObjectRegistry.class);
if (objects == null) {
objects = new FSTObjectRegistry(conf);
} else {
objects.clearForRead(conf);
}
}
public ConditionalCallback getConditionalCallback() {
return conditionalCallback;
}
public void setConditionalCallback(ConditionalCallback conditionalCallback) {
this.conditionalCallback = conditionalCallback;
}
public int getReadExternalReadAHead() {
return readExternalReadAHead;
}
/**
* since the stock readXX methods on InputStream are final, i can't ensure sufficient readAhead on the inputStream
* before calling readExternal. Default value is 16000 bytes. If you make use of the externalizable interfac
* and write larger Objects a) cast the ObjectInput in readExternal to FSTObjectInput and call ensureReadAhead on this
* in your readExternal method b) set a sufficient maximum using this method before serializing.
* @param readExternalReadAHead
*/
public void setReadExternalReadAHead(int readExternalReadAHead) {
this.readExternalReadAHead = readExternalReadAHead;
}
@Override
public Object readObject() throws ClassNotFoundException, IOException {
try {
return readObject((Class[]) null);
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
public int read() throws IOException {
return getCodec().readIntByte();
}
@Override
public int read(byte[] b) throws IOException {
getCodec().readPlainBytes(b, 0, b.length);
return b.length;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
getCodec().readPlainBytes(b, off, len);
return b.length;
}
@Override
public long skip(long n) throws IOException {
getCodec().skip((int) n);
return n;
}
@Override
public int available() throws IOException {
return getCodec().available();
}
protected void processValidation() throws InvalidObjectException {
if (callbacks == null) {
return;
}
Collections.sort(callbacks, new Comparator() {
@Override
public int compare(CallbackEntry o1, CallbackEntry o2) {
return o2.prio - o1.prio;
}
});
for (int i = 0; i < callbacks.size(); i++) {
CallbackEntry callbackEntry = callbacks.get(i);
try {
callbackEntry.cb.validateObject();
} catch (Exception ex) {
FSTUtil.rethrow(ex);
}
}
}
public Object readObject(Class... possibles) throws Exception {
curDepth++;
if ( isCrossPlatform ) {
return readObjectInternal(null); // not supported cross platform
}
try {
if (possibles != null && possibles.length > 1 ) {
for (int i = 0; i < possibles.length; i++) {
Class possible = possibles[i];
getCodec().registerClass(possible);
}
}
Object res = readObjectInternal(possibles);
processValidation();
return res;
} catch (Throwable th) {
FSTUtil.rethrow(th);
} finally {
curDepth--;
}
return null;
}
protected FSTClazzInfo.FSTFieldInfo infoCache;
public Object readObjectInternal(Class... expected) throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
try {
FSTClazzInfo.FSTFieldInfo info = infoCache;
infoCache = null;
if (info == null )
info = new FSTClazzInfo.FSTFieldInfo(expected, null, ignoreAnnotations);
else
info.possibleClasses = expected;
Object res = readObjectWithHeader(info);
infoCache = info;
return res;
} catch (Throwable t) {
FSTUtil.rethrow(t);
}
return null;
}
public Object readObjectWithHeader(FSTClazzInfo.FSTFieldInfo referencee) throws Exception {
FSTClazzInfo clzSerInfo;
Class c;
final int readPos = getCodec().getInputPos();
byte code = getCodec().readObjectHeaderTag(); // NOTICE: THIS ADVANCES THE INPUT STREAM...
if (code == FSTObjectOutput.OBJECT ) {
// class name
clzSerInfo = readClass();
c = clzSerInfo.getClazz();
if ( c.isArray() )
return readArrayNoHeader(referencee,readPos,c);
// fall through
} else if ( code == FSTObjectOutput.TYPED ) {
c = referencee.getType();
clzSerInfo = getClazzInfo(c, referencee);
} else if ( code >= 1 ) {
try {
c = referencee.getPossibleClasses()[code - 1];
clzSerInfo = getClazzInfo(c, referencee);
} catch (Throwable th) {
clzSerInfo = null; c = null;
FSTUtil.rethrow(th);
}
} else {
Object res = instantiateSpecialTag(referencee, readPos, code);
return res;
}
try {
FSTObjectSerializer ser = clzSerInfo.getSer();
if (ser != null) {
Object res = instantiateAndReadWithSer(c, ser, clzSerInfo, referencee, readPos);
getCodec().readArrayEnd(clzSerInfo);
return res;
} else {
Object res = instantiateAndReadNoSer(c, clzSerInfo, referencee, readPos);
return res;
}
} catch (Exception e) {
FSTUtil.rethrow(e);
}
return null;
}
protected Object instantiateSpecialTag(FSTClazzInfo.FSTFieldInfo referencee, int readPos, byte code) throws Exception {
if ( code == FSTObjectOutput.STRING ) { // faster than switch, note: currently string tag not used by all codecs ..
String res = getCodec().readStringUTF();
objects.registerObjectForRead(res, readPos);
return res;
} else if ( code == FSTObjectOutput.BIG_INT ) {
return instantiateBigInt();
} else if ( code == FSTObjectOutput.NULL ) {
return null;
} else
{
switch (code) {
// case FSTObjectOutput.BIG_INT: { return instantiateBigInt(); }
case FSTObjectOutput.BIG_LONG: { return Long.valueOf(getCodec().readFLong()); }
case FSTObjectOutput.BIG_BOOLEAN_FALSE: { return Boolean.FALSE; }
case FSTObjectOutput.BIG_BOOLEAN_TRUE: { return Boolean.TRUE; }
case FSTObjectOutput.ONE_OF: { return referencee.getOneOf()[getCodec().readFByte()]; }
// case FSTObjectOutput.NULL: { return null; }
case FSTObjectOutput.DIRECT_ARRAY_OBJECT: {
Object directObject = getCodec().getDirectObject();
objects.registerObjectForRead(directObject,readPos);
return directObject;
}
case FSTObjectOutput.DIRECT_OBJECT: {
Object directObject = getCodec().getDirectObject();
if (directObject.getClass() == byte[].class) { // fixme. special for minibin, move it there
if ( referencee != null && referencee.getType() == boolean[].class )
{
byte[] ba = (byte[]) directObject;
boolean res[] = new boolean[ba.length];
for (int i = 0; i < res.length; i++) {
res[i] = ba[i] != 0;
}
directObject = res;
}
}
objects.registerObjectForRead(directObject,readPos);
return directObject;
}
// case FSTObjectOutput.STRING: return getCodec().readStringUTF();
case FSTObjectOutput.HANDLE: {
Object res = instantiateHandle(referencee);
getCodec().readObjectEnd();
return res;
}
case FSTObjectOutput.ARRAY: {
Object res = instantiateArray(referencee, readPos);
return res;
}
case FSTObjectOutput.ENUM: { return instantiateEnum(referencee, readPos); }
}
throw new RuntimeException("unknown object tag "+code);
}
}
protected FSTClazzInfo getClazzInfo(Class c, FSTClazzInfo.FSTFieldInfo referencee) {
FSTClazzInfo clzSerInfo;
FSTClazzInfo lastInfo = referencee.lastInfo;
if ( lastInfo != null && lastInfo.clazz == c && lastInfo.conf == conf) {
clzSerInfo = lastInfo;
} else {
clzSerInfo = clInfoRegistry.getCLInfo(c, conf);
referencee.lastInfo = clzSerInfo;
}
return clzSerInfo;
}
protected Object instantiateHandle(FSTClazzInfo.FSTFieldInfo referencee) throws IOException {
int handle = getCodec().readFInt();
Object res = objects.getReadRegisteredObject(handle);
if (res == null) {
throw new IOException("unable to ressolve handle " + handle + " " + referencee.getDesc() + " " + getCodec().getInputPos() );
}
return res;
}
protected Object instantiateArray(FSTClazzInfo.FSTFieldInfo referencee, int readPos) throws Exception {
Object res = readArray(referencee, readPos); // NEED TO PASS ALONG THE POS FOR THE ARRAY
/*
registerObjectForRead alerady gets called by readArray (and with the proper pos now). that said, I'm unclear
on the intent of the if ( ! referencee.isFlat() ) so I wanted to comment on that
if ( ! referencee.isFlat() ) {
objects.registerObjectForRead(res, readPos);
}
*/
return res;
}
protected Object instantiateEnum(FSTClazzInfo.FSTFieldInfo referencee, int readPos) throws IOException, ClassNotFoundException {
FSTClazzInfo clzSerInfo;
Class c;
clzSerInfo = readClass();
c = clzSerInfo.getClazz();
int ordinal = getCodec().readFInt();
Object[] enumConstants = clzSerInfo.getEnumConstants();
if ( enumConstants == null ) {
// pseudo enum of anonymous classes tom style ?
return null;
}
Object res = enumConstants[ordinal];
if ( REGISTER_ENUMS_READ ) {
if ( ! referencee.isFlat() ) { // should be unnecessary
objects.registerObjectForRead(res, readPos);
}
}
return res;
}
protected Object instantiateBigInt() throws IOException {
int val = getCodec().readFInt();
return Integer.valueOf(val);
}
protected Object instantiateAndReadWithSer(Class c, FSTObjectSerializer ser, FSTClazzInfo clzSerInfo, FSTClazzInfo.FSTFieldInfo referencee, int readPos) throws Exception {
boolean serInstance = false;
Object newObj = ser.instantiate(c, this, clzSerInfo, referencee, readPos);
if (newObj == null) {
newObj = clzSerInfo.newInstance(getCodec().isMapBased());
} else
serInstance = true;
if (newObj == null) {
throw new IOException(referencee.getDesc() + ":Failed to instantiate '" + c.getName() + "'. Register a custom serializer implementing instantiate or define empty constructor..");
}
if ( newObj == FSTObjectSerializer.REALLY_NULL ) {
newObj = null;
} else {
if (newObj.getClass() != c && ser == null ) {
// for advanced trickery (e.g. returning non-serializable from FSTSerializer)
// this hurts. so in case of FSTSerializers incoming clzInfo will refer to the
// original class, not the one actually instantiated
c = newObj.getClass();
clzSerInfo = clInfoRegistry.getCLInfo(c, conf);
}
if ( ! referencee.isFlat() && ! clzSerInfo.isFlat() && !ser.alwaysCopy()) {
objects.registerObjectForRead(newObj, readPos);
}
if ( !serInstance )
ser.readObject(this, newObj, clzSerInfo, referencee);
}
getCodec().consumeEndMarker(); //=> bug when writing objects unlimited
return newObj;
}
protected Object instantiateAndReadNoSer(Class c, FSTClazzInfo clzSerInfo, FSTClazzInfo.FSTFieldInfo referencee, int readPos) throws Exception {
Object newObj;
newObj = clzSerInfo.newInstance(getCodec().isMapBased());
if (newObj == null) {
throw new IOException(referencee.getDesc() + ":Failed to instantiate '" + c.getName() + "'. Register a custom serializer implementing instantiate or define empty constructor.");
}
//fixme: code below improves unshared decoding perf, however disables to run mixed mode (clients can decide)
//actually would need 2 flags for encode/decode
//tested with json mixed mode does not work anyway ...
final boolean needsRefLookup = conf.shareReferences && !referencee.isFlat() && !clzSerInfo.isFlat();
// previously :
// final boolean needsRefLookup = !referencee.isFlat() && !clzSerInfo.isFlat();
if (needsRefLookup) {
objects.registerObjectForRead(newObj, readPos);
}
if ( clzSerInfo.isExternalizable() )
{
int tmp = readPos;
getCodec().ensureReadAhead(readExternalReadAHead);
((Externalizable)newObj).readExternal(this);
getCodec().readExternalEnd();
if ( clzSerInfo.getReadResolveMethod() != null ) {
final Object prevNew = newObj;
newObj = handleReadRessolve(clzSerInfo, newObj);
if ( newObj != prevNew && needsRefLookup ) {
objects.replace(prevNew, newObj, tmp);
}
}
} else if (clzSerInfo.useCompatibleMode())
{
Object replaced = readObjectCompatible(referencee, clzSerInfo, newObj);
if (replaced != null && replaced != newObj) {
objects.replace(newObj, replaced, readPos);
newObj = replaced;
}
} else {
FSTClazzInfo.FSTFieldInfo[] fieldInfo = clzSerInfo.getFieldInfo();
readObjectFields(referencee, clzSerInfo, fieldInfo, newObj,0,0);
}
return newObj;
}
protected Object readObjectCompatible(FSTClazzInfo.FSTFieldInfo referencee, FSTClazzInfo serializationInfo, Object newObj) throws Exception {
Class cl = serializationInfo.getClazz();
readObjectCompatibleRecursive(referencee, newObj, serializationInfo, cl);
if (newObj != null &&
serializationInfo.getReadResolveMethod() != null) {
newObj = handleReadRessolve(serializationInfo, newObj);
}
return newObj;
}
protected Object handleReadRessolve(FSTClazzInfo serializationInfo, Object newObj) throws IllegalAccessException {
Object rep = null;
try {
rep = serializationInfo.getReadResolveMethod().invoke(newObj);
} catch (InvocationTargetException e) {
FSTUtil.rethrow(e);
}
newObj = rep;//FIXME: support this in call
return newObj;
}
protected void readObjectCompatibleRecursive(FSTClazzInfo.FSTFieldInfo referencee, Object toRead, FSTClazzInfo serializationInfo, Class cl) throws Exception {
FSTClazzInfo.FSTCompatibilityInfo fstCompatibilityInfo = serializationInfo.getCompInfo().get(cl);
if (!Serializable.class.isAssignableFrom(cl)) {
return; // ok here, as compatible mode will never be triggered for "forceSerializable"
}
readObjectCompatibleRecursive(referencee, toRead, serializationInfo, cl.getSuperclass());
if (fstCompatibilityInfo != null && fstCompatibilityInfo.getReadMethod() != null) {
try {
int tag = readByte(); // expect 55
if ( tag == 66 ) {
// no write method defined, but read method defined ...
// expect defaultReadObject
getCodec().moveTo(getCodec().getInputPos() - 1); // need to push back tag, cause defaultWriteObject on writer side does not write tag
// input.pos--;
}
ObjectInputStream objectInputStream = getObjectInputStream(cl, serializationInfo, referencee, toRead);
fstCompatibilityInfo.getReadMethod().invoke(toRead, objectInputStream);
fakeWrapper.pop();
} catch (Exception e) {
FSTUtil.rethrow(e);
}
} else {
if (fstCompatibilityInfo != null) {
int tag = readByte();
if ( tag == 55 )
{
// came from writeMethod, but no readMethod defined => assume defaultWriteObject
tag = readByte(); // consume tag of defaultwriteobject (99)
if ( tag == 77 ) // came from putfield
{
HashMap fieldMap = (HashMap) FSTObjectInput.this.readObjectInternal(HashMap.class);
final FSTClazzInfo.FSTFieldInfo[] fieldArray = fstCompatibilityInfo.getFieldArray();
for (int i = 0; i < fieldArray.length; i++) {
FSTClazzInfo.FSTFieldInfo fstFieldInfo = fieldArray[i];
final Object val = fieldMap.get(fstFieldInfo.getName());
if ( val != null ) {
fstFieldInfo.setObjectValue(toRead,val);
}
}
return;
}
}
readObjectFields(referencee, serializationInfo, fstCompatibilityInfo.getFieldArray(), toRead,0,0);
}
}
}
public void defaultReadObject(FSTClazzInfo.FSTFieldInfo referencee, FSTClazzInfo serializationInfo, Object newObj)
{
try {
readObjectFields(referencee,serializationInfo,serializationInfo.getFieldInfo(),newObj,0,-1); // -1 flag to indicate no object end should be called
} catch (Exception e) {
FSTUtil.rethrow(e);
}
}
protected void readObjectFields(FSTClazzInfo.FSTFieldInfo referencee, FSTClazzInfo serializationInfo, FSTClazzInfo.FSTFieldInfo[] fieldInfo, Object newObj, int startIndex, int version) throws Exception {
if ( getCodec().isMapBased() ) {
readFieldsMapBased(referencee, serializationInfo, newObj);
if ( version >= 0 /*&& newObj instanceof Unknown == false*/ ) {
getCodec().readObjectEnd();
}
return;
}
if ( version < 0 )
version = 0;
int booleanMask = 0;
int boolcount = 8;
final int length = fieldInfo.length;
int conditional = 0;
for (int i = startIndex; i < length; i++) {
try {
FSTClazzInfo.FSTFieldInfo subInfo = fieldInfo[i];
if (subInfo.getVersion() > version ) {
int nextVersion = getCodec().readVersionTag();
if ( nextVersion == 0 ) // old object read
{
oldVersionRead(newObj);
return;
}
if ( nextVersion != subInfo.getVersion() ) {
throw new RuntimeException("read version tag "+nextVersion+" fieldInfo has "+subInfo.getVersion());
}
readObjectFields(referencee,serializationInfo,fieldInfo,newObj,i,nextVersion);
return;
}
if (subInfo.isPrimitive()) {
int integralType = subInfo.getIntegralType();
if (integralType == FSTClazzInfo.FSTFieldInfo.BOOL) {
if (boolcount == 8) {
booleanMask = ((int) getCodec().readFByte() + 256) &0xff;
boolcount = 0;
}
boolean val = (booleanMask & 128) != 0;
booleanMask = booleanMask << 1;
boolcount++;
subInfo.setBooleanValue(newObj, val);
} else {
switch (integralType) {
case FSTClazzInfo.FSTFieldInfo.BYTE: subInfo.setByteValue(newObj, getCodec().readFByte()); break;
case FSTClazzInfo.FSTFieldInfo.CHAR: subInfo.setCharValue(newObj, getCodec().readFChar()); break;
case FSTClazzInfo.FSTFieldInfo.SHORT: subInfo.setShortValue(newObj, getCodec().readFShort()); break;
case FSTClazzInfo.FSTFieldInfo.INT: subInfo.setIntValue(newObj, getCodec().readFInt()); break;
case FSTClazzInfo.FSTFieldInfo.LONG: subInfo.setLongValue(newObj, getCodec().readFLong()); break;
case FSTClazzInfo.FSTFieldInfo.FLOAT: subInfo.setFloatValue(newObj, getCodec().readFFloat()); break;
case FSTClazzInfo.FSTFieldInfo.DOUBLE: subInfo.setDoubleValue(newObj, getCodec().readFDouble()); break;
}
}
} else {
if ( subInfo.isConditional() ) {
if ( conditional == 0 ) {
conditional = getCodec().readPlainInt();
if ( skipConditional(newObj, conditional, subInfo) ) {
getCodec().moveTo(conditional);
continue;
}
}
}
// object
Object subObject = readObjectWithHeader(subInfo);
subInfo.setObjectValue(newObj, subObject);
}
} catch (IllegalAccessException ex) {
throw new IOException(ex);
}
}
int debug = getCodec().readVersionTag();// just consume '0'
}
public VersionConflictListener getVersionConflictListener() {
return versionConflictListener;
}
/**
* see @Version annotation
* @param versionConflictListener
*/
public void setVersionConflictListener(VersionConflictListener versionConflictListener) {
this.versionConflictListener = versionConflictListener;
}
protected void oldVersionRead(Object newObj) {
if ( versionConflictListener != null )
versionConflictListener.onOldVersionRead(newObj);
}
protected void readFieldsMapBased(FSTClazzInfo.FSTFieldInfo referencee, FSTClazzInfo serializationInfo, Object newObj) throws Exception {
String name;
int len = getCodec().getObjectHeaderLen(); // check if len is known in advance
if ( len < 0 )
len = Integer.MAX_VALUE;
int count = 0;
boolean isUnknown = newObj.getClass() == Unknown.class; // json
boolean inArray = isUnknown && getCodec().inArray(); // json externalized/custom serialized
getCodec().startFieldReading(newObj);
// fixme: break up this loop into separate impls.
while( count < len ) {
if ( inArray ) {
// unknwon json object written by externalize or custom serializer
Object o = readObjectWithHeader(null);
if ( o != null && getCodec().isEndMarker(o.toString()) )
return;
((Unknown)newObj).add(o);
continue;
}
name= getCodec().readStringUTF();
//int debug = getCodec().getInputPos();
if ( len == Integer.MAX_VALUE && getCodec().isEndMarker(name) )
return;
count++;
if (isUnknown) {
FSTClazzInfo.FSTFieldInfo fakeField = new FSTClazzInfo.FSTFieldInfo(null, null, true);
fakeField.fakeName = name;
Object toSet = readObjectWithHeader(fakeField);
((Unknown)newObj).set(name, toSet);
} else
if ( newObj.getClass() == MBObject.class ) {
Object toSet = readObjectWithHeader(null);
((MBObject)newObj).put(name,toSet);
} else {
FSTClazzInfo.FSTFieldInfo fieldInfo = serializationInfo.getFieldInfo(name, null);
if (fieldInfo == null) {
System.out.println("warning: unknown field: " + name + " on class " + serializationInfo.getClazz().getName());
} else {
if (fieldInfo.isPrimitive()) {
// direct primitive field
switch (fieldInfo.getIntegralType()) {
case FSTClazzInfo.FSTFieldInfo.BOOL:
fieldInfo.setBooleanValue(newObj, getCodec().readFByte() == 0 ? false : true);
break;
case FSTClazzInfo.FSTFieldInfo.BYTE:
fieldInfo.setByteValue(newObj, getCodec().readFByte());
break;
case FSTClazzInfo.FSTFieldInfo.CHAR:
fieldInfo.setCharValue(newObj, getCodec().readFChar());
break;
case FSTClazzInfo.FSTFieldInfo.SHORT:
fieldInfo.setShortValue(newObj, getCodec().readFShort());
break;
case FSTClazzInfo.FSTFieldInfo.INT:
fieldInfo.setIntValue(newObj, getCodec().readFInt());
break;
case FSTClazzInfo.FSTFieldInfo.LONG:
fieldInfo.setLongValue(newObj, getCodec().readFLong());
break;
case FSTClazzInfo.FSTFieldInfo.FLOAT:
fieldInfo.setFloatValue(newObj, getCodec().readFFloat());
break;
case FSTClazzInfo.FSTFieldInfo.DOUBLE:
fieldInfo.setDoubleValue(newObj, getCodec().readFDouble());
break;
default:
throw new RuntimeException("unkown primitive type " + fieldInfo);
}
} else {
Object toSet = readObjectWithHeader(fieldInfo);
toSet = getCodec().coerceElement(fieldInfo.getType(), toSet);
fieldInfo.setObjectValue(newObj, toSet);
}
}
}
}
getCodec().endFieldReading(newObj);
}
protected boolean skipConditional(Object newObj, int conditional, FSTClazzInfo.FSTFieldInfo subInfo) {
if ( conditionalCallback != null ) {
return conditionalCallback.shouldSkip(newObj,conditional,subInfo.getField());
}
return false;
}
protected void readCompatibleObjectFields(FSTClazzInfo.FSTFieldInfo referencee, FSTClazzInfo serializationInfo, FSTClazzInfo.FSTFieldInfo[] fieldInfo, Map res) throws Exception {
int booleanMask = 0;
int boolcount = 8;
for (int i = 0; i < fieldInfo.length; i++) {
try {
FSTClazzInfo.FSTFieldInfo subInfo = fieldInfo[i];
if (subInfo.isIntegral() && !subInfo.isArray()) {
final Class subInfoType = subInfo.getType();
if (subInfoType == boolean.class) {
if (boolcount == 8) {
booleanMask = ((int) getCodec().readFByte() + 256) &0xff;
boolcount = 0;
}
boolean val = (booleanMask & 128) != 0;
booleanMask = booleanMask << 1;
boolcount++;
res.put(subInfo.getName(), val);
}
if (subInfoType == byte.class) {
res.put(subInfo.getName(), getCodec().readFByte());
} else if (subInfoType == char.class) {
res.put(subInfo.getName(), getCodec().readFChar());
} else if (subInfoType == short.class) {
res.put(subInfo.getName(), getCodec().readFShort());
} else if (subInfoType == int.class) {
res.put(subInfo.getName(), getCodec().readFInt());
} else if (subInfoType == double.class) {
res.put(subInfo.getName(), getCodec().readFDouble());
} else if (subInfoType == float.class) {
res.put(subInfo.getName(), getCodec().readFFloat());
} else if (subInfoType == long.class) {
res.put(subInfo.getName(), getCodec().readFLong());
}
} else {
// object
Object subObject = readObjectWithHeader(subInfo);
res.put(subInfo.getName(), subObject);
}
} catch (IllegalAccessException ex) {
throw new IOException(ex);
}
}
}
public String readStringUTF() throws IOException {
return getCodec().readStringUTF();
}
/**
* len < 127 !!!!!
* @return
* @throws IOException
*/
public String readStringAsc() throws IOException {
return getCodec().readStringAsc();
}
protected Object readArray(FSTClazzInfo.FSTFieldInfo referencee, int pos) throws Exception {
Object classOrArray = getCodec().readArrayHeader();
if (pos < 0)
pos = getCodec().getInputPos();
if ( classOrArray instanceof Class == false )
return classOrArray;
if ( classOrArray == null )
return null;
Object o = readArrayNoHeader(referencee, pos, (Class) classOrArray);
getCodec().readArrayEnd(null);
return o;
}
protected Object readArrayNoHeader(FSTClazzInfo.FSTFieldInfo referencee, int pos, Class arrCl) throws Exception {
final int len = getCodec().readFInt();
if (len == -1) {
return null;
}
Class arrType = arrCl.getComponentType();
if (!arrType.isArray()) {
Object array = Array.newInstance(arrType, len);
if ( ! referencee.isFlat() )
objects.registerObjectForRead(array, pos );
if (arrType.isPrimitive()) {
return getCodec().readFPrimitiveArray(array, arrType, len);
} else { // Object Array
Object arr[] = (Object[]) array;
for (int i = 0; i < len; i++) {
Object value = readObjectWithHeader(referencee);
value = getCodec().coerceElement(arrType, value);
arr[i] = value;
}
getCodec().readObjectEnd();
}
return array;
} else { // multidim array
Object array[] = (Object[]) Array.newInstance(arrType, len);
if ( ! referencee.isFlat() ) {
objects.registerObjectForRead(array, pos);
}
FSTClazzInfo.FSTFieldInfo ref1 = new FSTClazzInfo.FSTFieldInfo(referencee.getPossibleClasses(), null, clInfoRegistry.isIgnoreAnnotations());
for (int i = 0; i < len; i++) {
Object subArray = readArray(ref1, -1);
array[i] = subArray;
}
return array;
}
}
public void registerObject(Object o, int streamPosition, FSTClazzInfo info, FSTClazzInfo.FSTFieldInfo referencee) {
if ( ! objects.disabled && !referencee.isFlat() && (info == null || ! info.isFlat() ) ) {
objects.registerObjectForRead(o, streamPosition);
}
}
public FSTClazzInfo readClass() throws IOException, ClassNotFoundException {
return getCodec().readClass();
}
protected void resetAndClearRefs() {
try {
reset();
objects.clearForRead(conf);
} catch (IOException e) {
FSTUtil.rethrow(e);
}
}
public void reset() throws IOException {
getCodec().reset();
}
public void resetForReuse(InputStream in) throws IOException {
if ( closed ) {
throw new RuntimeException("can't reuse closed stream");
}
getCodec().reset();
getCodec().setInputStream(in);
objects.clearForRead(conf);
callbacks = null; //fix memory leak on reuse from default FstConfiguration
}
public void resetForReuseCopyArray(byte bytes[], int off, int len) throws IOException {
if ( closed ) {
throw new RuntimeException("can't reuse closed stream");
}
getCodec().reset();
objects.clearForRead(conf);
getCodec().resetToCopyOf(bytes, off, len);
callbacks = null; //fix memory leak on reuse from default FstConfiguration
}
public void resetForReuseUseArray(byte bytes[]) throws IOException {
resetForReuseUseArray(bytes, bytes.length);
}
public void resetForReuseUseArray(byte bytes[], int len) throws IOException {
if ( closed ) {
throw new RuntimeException("can't reuse closed stream");
}
objects.clearForRead(conf);
getCodec().resetWith(bytes, len);
callbacks = null; //fix memory leak on reuse from default FstConfiguration
}
public final int readFInt() throws IOException {
return getCodec().readFInt();
}
protected boolean closed = false;
@Override
public void close() throws IOException {
closed = true;
resetAndClearRefs();
conf.returnObject(objects);
getCodec().close();
}
////////////////////////////////////////////////////// epic compatibility hack /////////////////////////////////////////////////////////
protected MyObjectStream fakeWrapper; // some jdk classes hash for ObjectStream, so provide the same instance always
protected ObjectInputStream getObjectInputStream(final Class cl, final FSTClazzInfo clInfo, final FSTClazzInfo.FSTFieldInfo referencee, final Object toRead) throws IOException {
ObjectInputStream wrapped = new ObjectInputStream() {
@Override
public Object readObjectOverride() throws IOException, ClassNotFoundException {
try {
byte b = FSTObjectInput.this.readByte();
if ( b != FSTObjectOutput.SPECIAL_COMPATIBILITY_OBJECT_TAG ) {
Constructor>[] constructors = OptionalDataException.class.getDeclaredConstructors();
FSTObjectInput.this.pushBack(1);
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
Class[] typeParameters = constructor.getParameterTypes();
if ( typeParameters != null && typeParameters.length == 1 && typeParameters[0] == int.class) {
constructor.setAccessible(true);
OptionalDataException ode;
try {
ode = (OptionalDataException) constructor.newInstance(0);
throw ode;
} catch (InvocationTargetException e) {
break;
}
}
}
throw new EOFException("if your code relies on this, think");
}
return FSTObjectInput.this.readObjectInternal(referencee.getPossibleClasses());
} catch (IllegalAccessException e) {
throw new IOException(e);
} catch (InstantiationException e) {
throw new IOException(e);
}
}
@Override
public Object readUnshared() throws IOException, ClassNotFoundException {
try {
return FSTObjectInput.this.readObjectInternal(referencee.getPossibleClasses()); // fixme
} catch (IllegalAccessException e) {
throw new IOException(e);
} catch (InstantiationException e) {
throw new IOException(e);
}
}
@Override
public void defaultReadObject() throws IOException, ClassNotFoundException {
try {
int tag = readByte();
if ( tag == 77 ) // came from writeFields
{
fieldMap = (HashMap) FSTObjectInput.this.readObjectInternal(HashMap.class);
// object has been written with writeFields, is no read with defaultReadObjects,
// need to autoapply map to object vars.
// this might be redundant in case readObject() pulls a getFields() .. (see bitset testcase)
for (Iterator iterator = fieldMap.keySet().iterator(); iterator.hasNext(); ) {
String key = iterator.next();
FSTClazzInfo.FSTFieldInfo fieldInfo = clInfo.getFieldInfo(key, null);// in case fieldName is not unique => cannot recover/fix
if ( fieldInfo != null ) {
fieldInfo.setObjectValue(toRead,fieldMap.get(key));
}
}
} else {
FSTObjectInput.this.readObjectFields(
referencee,
clInfo,
clInfo.getCompInfo().get(cl).getFieldArray(),
toRead,
0,
0
); // FIXME: only fields of current class
}
} catch (Exception e) {
throw new IOException(e);
}
}
HashMap fieldMap;
@Override
public GetField readFields() throws IOException, ClassNotFoundException {
int tag = readByte();
try {
FSTClazzInfo.FSTCompatibilityInfo fstCompatibilityInfo = clInfo.getCompInfo().get(cl);
if (tag==99) { // came from defaultwriteobject
// Note: in case number and names of instance fields of reader/writer are different,
// this fails as code below implicitely assumes, fields of writer == fields of reader
// unfortunately one can use defaultWriteObject at writer side but use getFields at reader side
// in readObject(). if then fields differ, code below reads BS and fails.
// Its impossible to fix that except by always using putField + getField for
// JDK compatibility classes, however this will waste lots of performance. As
// it would be necessary to *always* write full metainformation (a map of fieldName => value pairs)
// see #53
fieldMap = new HashMap();
FSTObjectInput.this.readCompatibleObjectFields(referencee, clInfo, fstCompatibilityInfo.getFieldArray(), fieldMap);
getCodec().readVersionTag(); // consume dummy version tag as created by defaultWriteObject
} else if (tag == 66) { // has been written from writeObjectCompatible without writeMethod
fieldMap = new HashMap();
FSTObjectInput.this.readCompatibleObjectFields(referencee, clInfo, fstCompatibilityInfo.getFieldArray(), fieldMap);
getCodec().readVersionTag(); // consume dummy version tag as created by defaultWriteObject
} else {
fieldMap = (HashMap) FSTObjectInput.this.readObjectInternal(HashMap.class);
}
} catch (Exception e) {
FSTUtil.rethrow(e);
}
return new GetField() {
@Override
public ObjectStreamClass getObjectStreamClass() {
return ObjectStreamClass.lookup(cl);
}
@Override
public boolean defaulted(String name) throws IOException {
return fieldMap.get(name) == null;
}
@Override
public boolean get(String name, boolean val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Boolean) fieldMap.get(name)).booleanValue();
}
@Override
public byte get(String name, byte val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Byte) fieldMap.get(name)).byteValue();
}
@Override
public char get(String name, char val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Character) fieldMap.get(name)).charValue();
}
@Override
public short get(String name, short val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Short) fieldMap.get(name)).shortValue();
}
@Override
public int get(String name, int val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Integer) fieldMap.get(name)).intValue();
}
@Override
public long get(String name, long val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Long) fieldMap.get(name)).longValue();
}
@Override
public float get(String name, float val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Float) fieldMap.get(name)).floatValue();
}
@Override
public double get(String name, double val) throws IOException {
if (fieldMap.get(name) == null) {
return val;
}
return ((Double) fieldMap.get(name)).doubleValue();
}
@Override
public Object get(String name, Object val) throws IOException {
Object res = fieldMap.get(name);
if (res == null) {
return val;
}
return res;
}
};
}
@Override
public void registerValidation(ObjectInputValidation obj, int prio) throws NotActiveException, InvalidObjectException {
if (callbacks == null) {
callbacks = new ArrayList();
}
callbacks.add(new CallbackEntry(obj, prio));
}
@Override
public int read() throws IOException {
return getCodec().readFByte();
}
@Override
public int read(byte[] buf, int off, int len) throws IOException {
return FSTObjectInput.this.read(buf, off, len);
}
@Override
public int available() throws IOException {
return FSTObjectInput.this.available();
}
@Override
public void close() throws IOException {
}
@Override
public boolean readBoolean() throws IOException {
return FSTObjectInput.this.readBoolean();
}
@Override
public byte readByte() throws IOException {
return getCodec().readFByte();
}
@Override
public int readUnsignedByte() throws IOException {
return FSTObjectInput.this.readUnsignedByte();
}
@Override
public char readChar() throws IOException {
return getCodec().readFChar();
}
@Override
public short readShort() throws IOException {
return getCodec().readFShort();
}
@Override
public int readUnsignedShort() throws IOException {
return FSTObjectInput.this.readUnsignedShort();
}
@Override
public int readInt() throws IOException {
return getCodec().readFInt();
}
@Override
public long readLong() throws IOException {
return getCodec().readFLong();
}
@Override
public float readFloat() throws IOException {
return getCodec().readFFloat();
}
@Override
public double readDouble() throws IOException {
return getCodec().readFDouble();
}
@Override
public void readFully(byte[] buf) throws IOException {
FSTObjectInput.this.readFully(buf);
}
@Override
public void readFully(byte[] buf, int off, int len) throws IOException {
FSTObjectInput.this.readFully(buf, off, len);
}
@Override
public int skipBytes(int len) throws IOException {
return FSTObjectInput.this.skipBytes(len);
}
@Override
public String readUTF() throws IOException {
return getCodec().readStringUTF();
}
@Override
public String readLine() throws IOException {
return FSTObjectInput.this.readLine();
}
@Override
public int read(byte[] b) throws IOException {
return FSTObjectInput.this.read(b);
}
@Override
public long skip(long n) throws IOException {
return FSTObjectInput.this.skip(n);
}
@Override
public void mark(int readlimit) {
throw new RuntimeException("not implemented");
}
@Override
public void reset() throws IOException {
FSTObjectInput.this.reset();
}
@Override
public boolean markSupported() {
return false;
}
};
if ( fakeWrapper == null ) {
fakeWrapper = new MyObjectStream();
}
fakeWrapper.push(wrapped);
return fakeWrapper;
}
protected void pushBack(int i) {
getCodec().pushBack(i);
}
protected static class MyObjectStream extends ObjectInputStream {
ObjectInputStream wrapped;
ArrayDeque wrappedStack = new ArrayDeque();
public void push( ObjectInputStream in ) {
wrappedStack.push(in);
wrapped = in;
}
public void pop() {
wrapped = wrappedStack.pop();
}
MyObjectStream() throws IOException, SecurityException {
}
@Override
public Object readObjectOverride() throws IOException, ClassNotFoundException {
return wrapped.readObject();
}
@Override
public Object readUnshared() throws IOException, ClassNotFoundException {
return wrapped.readUnshared();
}
@Override
public void defaultReadObject() throws IOException, ClassNotFoundException {
wrapped.defaultReadObject();
}
@Override
public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException {
return wrapped.readFields();
}
@Override
public void registerValidation(ObjectInputValidation obj, int prio) throws NotActiveException, InvalidObjectException {
wrapped.registerValidation(obj,prio);
}
@Override
public int read() throws IOException {
return wrapped.read();
}
@Override
public int read(byte[] buf, int off, int len) throws IOException {
return wrapped.read(buf, off, len);
}
@Override
public int available() throws IOException {
return wrapped.available();
}
@Override
public void close() throws IOException {
wrapped.close();
}
@Override
public boolean readBoolean() throws IOException {
return wrapped.readBoolean();
}
@Override
public byte readByte() throws IOException {
return wrapped.readByte();
}
@Override
public int readUnsignedByte() throws IOException {
return wrapped.readUnsignedByte();
}
@Override
public char readChar() throws IOException {
return wrapped.readChar();
}
@Override
public short readShort() throws IOException {
return wrapped.readShort();
}
@Override
public int readUnsignedShort() throws IOException {
return wrapped.readUnsignedShort();
}
@Override
public int readInt() throws IOException {
return wrapped.readInt();
}
@Override
public long readLong() throws IOException {
return wrapped.readLong();
}
@Override
public float readFloat() throws IOException {
return wrapped.readFloat();
}
@Override
public double readDouble() throws IOException {
return wrapped.readDouble();
}
@Override
public void readFully(byte[] buf) throws IOException {
wrapped.readFully(buf);
}
@Override
public void readFully(byte[] buf, int off, int len) throws IOException {
wrapped.readFully(buf, off, len);
}
@Override
public int skipBytes(int len) throws IOException {
return wrapped.skipBytes(len);
}
@Override
public String readUTF() throws IOException {
return wrapped.readUTF();
}
@Override
public String readLine() throws IOException {
return wrapped.readLine();
}
@Override
public int read(byte[] b) throws IOException {
return wrapped.read(b);
}
@Override
public long skip(long n) throws IOException {
return wrapped.skip(n);
}
@Override
public void mark(int readlimit) {
wrapped.mark(readlimit);
}
@Override
public void reset() throws IOException {
wrapped.reset();
}
@Override
public boolean markSupported() {
return wrapped.markSupported();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy