All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nustaq.serialization.FSTClazzInfo Maven / Gradle / Ivy

Go to download

A fast java serialization drop in-replacement and some serialization based utils such as Structs and OffHeap Memory.

The newest version!
/*
 * 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.annotations.*;
import org.nustaq.serialization.util.FSTMap;
import org.nustaq.serialization.util.FSTUtil;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA.
 * User: Möller
 * Date: 03.11.12
 * Time: 13:08
 * To change this template use File | Settings | File Templates.
 */
public final class FSTClazzInfo {

    // cache constructor per class (big saving in permspace)
    public static boolean BufferConstructorMeta = true;
    // cache and share class.getDeclaredFields amongst all fstconfigs
    public static boolean BufferFieldMeta = true;

    /**
     * cache + share j.reflect.Field. This can be cleared in case it gets too fat/leaks mem (e.g. class reloading)
     */
    public static ConcurrentHashMap sharedFieldSets = new ConcurrentHashMap<>();

    public static final Comparator defFieldComparator = new Comparator() {
        @Override
        public int compare(FSTFieldInfo o1, FSTFieldInfo o2) {
            int res = 0;

            if ( o1.getVersion() != o2.getVersion() ) {
                return o1.getVersion() < o2.getVersion() ? -1 : 1;
            }

            // order: version, boolean, primitives, conditionals, object references
            if (o1.getType() == boolean.class && o2.getType() != boolean.class) {
                return -1;
            }
            if (o1.getType() != boolean.class && o2.getType() == boolean.class) {
                return 1;
            }

            if (o1.isConditional() && !o2.isConditional()) {
                res = 1;
            } else if (!o1.isConditional() && o2.isConditional()) {
                res = -1;
            } else if (o1.isPrimitive() && !o2.isPrimitive()) {
                res = -1;
            } else if (!o1.isPrimitive() && o2.isPrimitive())
                res = 1;
//                if (res == 0) // 64 bit / 32 bit issues
//                    res = (int) (o1.getMemOffset() - o2.getMemOffset());
            if (res == 0)
                res = o1.getType().getSimpleName().compareTo(o2.getType().getSimpleName());
            if (res == 0)
                res = o1.getName().compareTo(o2.getName());
            if (res == 0) {
                return o1.getField().getDeclaringClass().getName().compareTo(o2.getField().getDeclaringClass().getName());
            }
            return res;
        }
    };
    Class[] predict;
    private boolean ignoreAnn;
    FSTMap fieldMap;
    Method writeReplaceMethod, readResolveMethod;
    FSTMap compInfo;

    Object decoderAttached; // for decoders

    public Object getDecoderAttached() {
        return decoderAttached;
    }

    public void setDecoderAttached(Object decoderAttached) {
        this.decoderAttached = decoderAttached;
    }

    boolean requiresCompatibleMode;
    boolean externalizable;
    boolean flat; // never share instances of this class
    boolean isAsciiNameShortString = false;
    boolean requiresInit = false;
    boolean hasTransient;
    FSTObjectSerializer ser;
    FSTFieldInfo fieldInfo[]; // serializable fields

    Class clazz;
    Object[] enumConstants;
    Constructor cons;
    int clzId = -1;
    int structSize = 0;


    FSTConfiguration conf;
    protected FSTClassInstantiator instantiator; // initialized from FSTConfiguration in constructor
    boolean crossPlatform;

    public FSTClazzInfo(FSTConfiguration conf, Class clazz, FSTClazzInfoRegistry infoRegistry, boolean ignoreAnnotations) {
        this.conf = conf; // fixme: historically was not bound to conf but now is. Remove redundant state + refs (note: may still be useful because of less pointerchasing)
        crossPlatform = conf.isCrossPlatform();
        this.clazz = clazz;
        enumConstants = clazz.getEnumConstants();
        ignoreAnn = ignoreAnnotations;
        createFields(clazz);

        instantiator = conf.getInstantiator(clazz);
        if (Externalizable.class.isAssignableFrom(clazz)) {
            externalizable = true;
            cons = instantiator.findConstructorForExternalize(clazz);
        } else if (Serializable.class.isAssignableFrom(clazz) || clazz == Object.class) {
            externalizable = false;
            cons = instantiator.findConstructorForSerializable(clazz);
        } else {
            if (!conf.isStructMode()) {
                if ( conf.isForceSerializable() || getSer() != null ) {
                    externalizable = false;
                    cons = instantiator.findConstructorForSerializable(clazz);
                } else {
                    throw new RuntimeException("Class " + clazz.getName() + " does not implement Serializable or externalizable");
                }
            } else {
                cons = instantiator.findConstructorForSerializable(clazz);
            }
        }
        if (!ignoreAnnotations) {
            Predict annotation = (Predict) clazz.getAnnotation(Predict.class);
            if (annotation != null) {
                predict = annotation.value();
            }
            flat = clazz.isAnnotationPresent(Flat.class);
        }

        if (cons != null) {
            cons.setAccessible(true);
        }

        final String name = clazz.getName();
        if (name.length() < 127) {
            isAsciiNameShortString = true;
            for (int i = 0; i < name.length(); i++) {
                if (name.charAt(i) > 127) {
                    isAsciiNameShortString = false;
                    break;
                }
            }
        }

        requiresInit = isExternalizable() || useCompatibleMode() || hasTransient || conf.isForceClzInit();

        if (useCompatibleMode() && crossPlatform && getSer() == null && !clazz.isEnum())
            throw new RuntimeException("cannot support legacy JDK serialization methods in crossplatform mode. Define a serializer for this class " + clazz.getName());
    }

    byte[] bufferedName;

    public byte[] getBufferedName() {
        if (bufferedName == null) {
            bufferedName = getClazz().getName().getBytes();
        }
        return bufferedName;
    }

    @Override
    public String toString() {
        return "FSTClazzInfo{" +
                "clazz=" + clazz +
                '}';
    }

    public boolean isAsciiNameShortString() {
        return isAsciiNameShortString;
    }

    public int getClzId() {
        return clzId;
    }

    public void setClzId(int clzId) {
        this.clzId = clzId;
    }

    public int getNumBoolFields() {
        FSTFieldInfo[] fis = getFieldInfo();
        for (int i = 0; i < fis.length; i++) {
            FSTFieldInfo fstFieldInfo = fis[i];
            if (fstFieldInfo.getType() != boolean.class) {
                return i;
            }
        }
        return fis.length;
    }

    public boolean isExternalizable() {
        return externalizable;
    }

    public final boolean isFlat() {
        return flat;
    }

    public final Class[] getPredict() {
        return predict;
    }

    public final Object newInstance(boolean doesRequireInit) {
        return instantiator.newInstance(clazz, cons, doesRequireInit || requiresInit, conf.isForceSerializable() );
    }

    /**
     * Sideeffect: sets hasTransient
     *
     * @param c
     * @param res
     * @return
     */
    static ReentrantLock shareLock = new ReentrantLock(false);
    public final List getAllFields(Class c, List res) {
        try {
            if ( BufferFieldMeta ) {
                shareLock.lock();
            }
            if (res == null) {
                res = new ArrayList();
            }
            if (c == null) {
                return res;
            }
            Field[] declaredFields = BufferFieldMeta && !conf.isStructMode() ? sharedFieldSets.get(c) : null;
            if (declaredFields == null) {
                declaredFields = c.getDeclaredFields();
                if (BufferFieldMeta && !conf.isStructMode())
                    sharedFieldSets.put(c, declaredFields);
            }
            List c1 = Arrays.asList(declaredFields);
            Collections.reverse(c1);
            for (int i = 0; i < c1.size(); i++) {
                Field field = c1.get(i);
                res.add(0, field);
            }
            for (int i = 0; i < res.size(); i++) {
                Field field = res.get(i);
                if (Modifier.isStatic(field.getModifiers()) || isTransient(c, field)) {
                    if (isTransient(c, field)) {
                        hasTransient = true;
                    }
                    res.remove(i);
                    i--;
                }
            }
            List allFields = getAllFields(c.getSuperclass(), res);
            return new ArrayList<>(allFields);
        } finally {
            if ( BufferFieldMeta )
                shareLock.unlock();
        }
    }

    private boolean isTransient(Class c, Field field) {
        if (Modifier.isTransient(field.getModifiers()))
            return true;
        while (c.getName().indexOf("$") >= 0) {
            c = c.getSuperclass(); // patch fuer reallive queries, kontraktor spore
        }
        if ( field.getName().startsWith("this$") && c.getAnnotation(AnonymousTransient.class) != null )
            return true;
        return (c.getAnnotation(Transient.class) != null && field.getAnnotation(Serialize.class) == null);
    }

    public final FSTFieldInfo[] getFieldInfo() {
        return fieldInfo;
    }

    public final FSTFieldInfo[] getFieldInfoFiltered(Class... toRemove) {
        FSTFieldInfo[] fis = getFieldInfo();
        int count = 0;
        for (int i = 0; i < fis.length; i++) {
            FSTFieldInfo fi = fis[i];
            boolean skip = false;
            for (int j = 0; j < toRemove.length; j++) {
                Class aClass = toRemove[j];
                if (fi.getField().getDeclaringClass() == aClass) {
                    skip = true;
                    break;
                }
            }
            if (!skip) {
                count++;
            }
        }
        FSTFieldInfo res[] = new FSTFieldInfo[count];
        count = 0;
        for (int i = 0; i < fis.length; i++) {
            FSTFieldInfo fi = fis[i];
            boolean skip = false;
            for (int j = 0; j < toRemove.length; j++) {
                Class aClass = toRemove[j];
                if (fi.getField().getDeclaringClass() == aClass) {
                    skip = true;
                    break;
                }
            }
            if (!skip) {
                res[count++] = fis[i];
            }
        }
        return res;
    }

    public final FSTFieldInfo getFieldInfo(String name, Class declaringClass) {
        if ( fieldMap != null ) {
            if (declaringClass == null) {
                return fieldMap.get(name);
            }
            return fieldMap.get(declaringClass.getName() + "#" + name); //FIXME: THIS IS VERY SLOW (only used by JSON / compatibility mode)
        } else {
            synchronized (this) {
                fieldMap = buildFieldMap();
                return getFieldInfo(name,declaringClass);
            }
        }
    }

    private FSTMap buildFieldMap() {
        FSTMap res = new FSTMap<>(fieldInfo.length);
        for (int i = 0; i < fieldInfo.length; i++) {
            Field field = fieldInfo[i].getField();
            if ( field != null ) {
                res.put(field.getDeclaringClass().getName() + "#" + field.getName(), fieldInfo[i]);
                res.put(field.getName(), fieldInfo[i]);
            }
        }
        return res;
    }

    private void createFields(Class c) {
        if (c.isInterface() || c.isPrimitive()) {
            return;
        }
        List fields = getAllFields(c, null);
        fieldInfo = new FSTFieldInfo[fields.size()];
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            fieldInfo[i] = createFieldInfo(field);
        }

        // compatibility info sort order
        Comparator infocomp = new Comparator() {
            @Override
            public int compare(FSTFieldInfo o1, FSTFieldInfo o2) {
                int res = 0;
                res = o1.getType().getSimpleName().compareTo(o2.getType().getSimpleName());
                if (res == 0)
                    res = o1.getType().getName().compareTo(o2.getType().getName());
                if (res == 0) {
                    Class declaringClass = o1.getType().getDeclaringClass();
                    Class declaringClass1 = o2.getType().getDeclaringClass();
                    if (declaringClass == null && declaringClass1 == null) {
                        return 0;
                    }
                    if (declaringClass != null && declaringClass1 == null) {
                        return 1;
                    }
                    if (declaringClass == null && declaringClass1 != null) {
                        return -1;
                    }
                    if (res == 0) {
                        return declaringClass.getName().compareTo(declaringClass1.getName());
                    }
                }
                return res;
            }
        };

        // check if we actually need to build up compatibility info (memory intensive)
        boolean requiresCompatibilityData = false;
        if ( ! Externalizable.class.isAssignableFrom(c) && getSerNoStore() == null ) {
            Class tmpCls = c;
            while( tmpCls != Object.class ) {
                if ( FSTUtil.findPrivateMethod(tmpCls, "writeObject", new Class[]{ObjectOutputStream.class}, Void.TYPE) != null ||
                     FSTUtil.findPrivateMethod(tmpCls, "readObject", new Class[]{ObjectInputStream.class},Void.TYPE) != null ||
                     FSTUtil.findDerivedMethod(tmpCls, "writeReplace", null, Object.class) != null ||
                     FSTUtil.findDerivedMethod(tmpCls, "readResolve", null, Object.class) != null ) {
                     requiresCompatibilityData = true;
                     break;
                }
                tmpCls = tmpCls.getSuperclass();
            }
        }

        if (!conf.isStructMode() && requiresCompatibilityData ) {
            getCompInfo();
            fieldMap = buildFieldMap();
            Class curCl = c;
            fields.clear();
            while (curCl != Object.class) {
                ObjectStreamClass os = null;
                try {
                    os = ObjectStreamClass.lookup(curCl);
                } catch (Exception e) {
                    FSTUtil.rethrow(e);
                }
                if (os != null) {
                    final ObjectStreamField[] fi = os.getFields();
                    List curClzFields = new ArrayList();
                    if (fi != null) {
                        for (int i = 0; i < fi.length; i++) {
                            ObjectStreamField objectStreamField = fi[i];
                            String ff = objectStreamField.getName();
                            final FSTFieldInfo fstFieldInfo = fieldMap.get(curCl.getName() + "#" + ff);
                            if (fstFieldInfo != null && fstFieldInfo.getField() != null) {
                                curClzFields.add(fstFieldInfo);
                                fields.add(fstFieldInfo.getField());
                            } else {
                                FSTFieldInfo fake = new FSTFieldInfo(null, null, true );
                                fake.type = objectStreamField.getType();
                                fake.fakeName = objectStreamField.getName();
                                curClzFields.add(fake);
                            }
                        }
                    }
                    Collections.sort(curClzFields, infocomp);
                    FSTCompatibilityInfo info = new FSTCompatibilityInfo(curClzFields, curCl);
                    getCompInfo().put(curCl, info);
                    if (info.needsCompatibleMode()) {
                        requiresCompatibleMode = true;
                    }
                }
                curCl = curCl.getSuperclass();
            }
        }

        // default sort order
        Comparator comp = defFieldComparator;
        if (!conf.isStructMode())
            Arrays.sort(fieldInfo, comp);
        int off = 8; // object header: length + clzId
        for (int i = 0; i < fieldInfo.length; i++) {
            FSTFieldInfo fstFieldInfo = fieldInfo[i];
            fstFieldInfo.setStructOffset(off);
            off += fstFieldInfo.getStructSize();
        }
        structSize = off;
        writeReplaceMethod = FSTUtil.findDerivedMethod(
            c, "writeReplace", null, Object.class);
        readResolveMethod = FSTUtil.findDerivedMethod(
            c, "readResolve", null, Object.class);
        if (writeReplaceMethod != null) {
            writeReplaceMethod.setAccessible(true);
        }
        if (readResolveMethod != null) {
            readResolveMethod.setAccessible(true);
        }
        for (int i = 0; i < fieldInfo.length; i++) {
            FSTFieldInfo fstFieldInfo = fieldInfo[i];
            fstFieldInfo.indexId = i;
        }
    }

    public int getStructSize() {
        return structSize;
    }

    public boolean useCompatibleMode() {
        return requiresCompatibleMode || writeReplaceMethod != null || readResolveMethod != null;
    }


    static AtomicInteger fiCount = new AtomicInteger(0);
    static AtomicInteger missCount = new AtomicInteger(0);
    protected FSTFieldInfo createFieldInfo(Field field) {
        FSTConfiguration.FieldKey key = null;
        if ( conf.fieldInfoCache != null ) {
            key = new FSTConfiguration.FieldKey(field.getDeclaringClass(), field.getName());
            FSTFieldInfo res = conf.fieldInfoCache.get(key);
            if ( res != null ) {
                fiCount.incrementAndGet();
                return res;
            }
        }
        field.setAccessible(true);
        Predict predict = crossPlatform ? null : field.getAnnotation(Predict.class); // needs to be iognored cross platform
        FSTFieldInfo result = new FSTFieldInfo(predict != null ? predict.value() : null, field, ignoreAnn);
        if ( conf.fieldInfoCache != null && key != null ) {
            conf.fieldInfoCache.put(key,result);
        }
        missCount.incrementAndGet();
        return result;
    }

    public final Method getReadResolveMethod() {
        return readResolveMethod;
    }

    public final Method getWriteReplaceMethod() {
        return writeReplaceMethod;
    }

    public final Class getClazz() {
        return clazz;
    }

    public Object[] getEnumConstants() {
        return enumConstants;
    }

    public FSTMap getCompInfo() {
        if (compInfo == null)
            compInfo = new FSTMap<>(3); // just avoid edge case NPE's
        return compInfo;
    }

    public final static class FSTFieldInfo {

        final public static int BOOL = 1;
        final public static int BYTE = 2;
        final public static int CHAR = 3;
        final public static int SHORT = 4;
        final public static int INT = 5;
        final public static int LONG = 6;
        final public static int FLOAT = 7;
        final public static int DOUBLE = 8;

        Class possibleClasses[];
        FSTClazzInfo lastInfo; // cache last class stored (can save a hash lookup)
        String oneOf[] = null;

        int arrayDim;
        Class arrayType;
        boolean flat = false;
        boolean isConditional = false;

        final Field field;
        Class type;
        boolean integral = false;
        boolean primitive = false;
        boolean isArr = false;
        byte version;
        int integralType;
        long memOffset = -1;
        boolean isAndroid = FSTConfiguration.isAndroid; // hope for better locality

        int structOffset = 0;
        int indexId; // position in serializable fields array
        int align = 0;
        int alignPad = 0;
        Object bufferedName; // cache byte rep of field name (used for cross platform)

        // hack required for compatibility with ancient JDK mechanics (cross JDK, e.g. Android <=> OpenJDK ).
        // in rare cases, a field used in putField is not present as a real field
        // in this case only these of a fieldinfo are set
        public String fakeName;

        public FSTFieldInfo(Class[] possibleClasses, Field fi, boolean ignoreAnnotations) {
            this.possibleClasses = possibleClasses;
            field = fi;
            if (fi == null) {
                isArr = false;
            } else {
                isArr = field.getType().isArray();
                type = fi.getType();
                primitive = type.isPrimitive();
                if (FSTUtil.unFlaggedUnsafe != null ) {
                    fi.setAccessible(true);
                    if (!Modifier.isStatic(fi.getModifiers())) {
                        try {
                            memOffset = (int) FSTUtil.unFlaggedUnsafe.objectFieldOffset(fi);
                        } catch (Throwable th) {
                            //throw FSTUtil.rethrow(th);
                        }
                    }
                }
            }
            if (isArray()) {
                String clName = field.getType().getName();
                arrayDim = 1 + clName.lastIndexOf('[');
                arrayType = calcComponentType(field.getType());
            }
            calcIntegral();
            if (fi != null && !ignoreAnnotations) {
                version = (byte) (fi.isAnnotationPresent(Version.class) ? fi.getAnnotation(Version.class).value() : 0);
                flat = fi.isAnnotationPresent(Flat.class);
                isConditional = fi.isAnnotationPresent(Conditional.class);
                if (isIntegral()) {
                    isConditional = false;
                }
                OneOf annotation = fi.getAnnotation(OneOf.class);
                if (annotation != null) {
                    oneOf = annotation.value();
                }
            }

        }

        public byte getVersion() {
            return version;
        }

        public Object getBufferedName() {
            return bufferedName;
        }

        public void setBufferedName(Object bufferedName) {
            this.bufferedName = bufferedName;
        }

        public int align(int off) {
            while ((off / align) * align != off)
                off++;
            return off;
        }

        public int getIndexId() {
            return indexId;
        }

        public int getStructOffset() {
            return structOffset;
        }

        public void setStructOffset(int structOffset) {
            this.structOffset = structOffset;
        }

        public String[] getOneOf() {
            return oneOf;
        }

        public long getMemOffset() {
            return memOffset;
        }

        public int getAlign() {
            return align;
        }

        public int getAlignPad() {
            return alignPad;
        }

        public boolean isConditional() {
            return isConditional;
        }

        public FSTClazzInfo getLastInfo() {
            return lastInfo;
        }

        public void setLastInfo(FSTClazzInfo lastInfo) {
            this.lastInfo = lastInfo;
        }

        Class calcComponentType(Class c) {
            if (c.isArray()) {
                return calcComponentType(c.getComponentType());
            }
            return c;
        }

        public boolean isVolatile() {
            return Modifier.isVolatile(getField().getModifiers());
        }

        public final Class getType() {
            return type;
        }

        public boolean isArray() {
            return isArr;
        }

        public int getArrayDepth() {
            return arrayDim;
        }

        public Class getArrayType() {
            return arrayType;
        }

        public Class[] getPossibleClasses() {
            return possibleClasses;
        }

        void setPossibleClasses(Class[] possibleClasses) {
            this.possibleClasses = possibleClasses;
        }

        public Field getField() {
            return field;
        }

        public void calcIntegral() {
            if (field == null) {
                return;
            }
            if (isArray()) {
                integral = isIntegral(getArrayType());
            } else {
                integral = isIntegral(field.getType());

                Class type = field.getType();
                integralType = getIntegralCode(type);
            }
        }

        public static int getIntegralCode(Class type) {
            if (type == boolean.class) {
                return BOOL;
            } else if (type == byte.class) {
                return BYTE;
            } else if (type == char.class) {
                return CHAR;
            } else if (type == short.class) {
                return SHORT;
            } else if (type == int.class) {
                return INT;
            } else if (type == long.class) {
                return LONG;
            } else if (type == float.class) {
                return FLOAT;
            } else if (type == double.class) {
                return DOUBLE;
            }
            return 0;
        }

        /**
         * only set if is not an array, but a direct native field type
         *
         * @return
         */
        public int getIntegralType() {
            return integralType;
        }

        public boolean isIntegral(Class type) {
            return type.isPrimitive();
        }

        /**
         * @return wether this is primitive or an array of primitives
         */
        public boolean isIntegral() {
            return integral;
        }

        public String getDesc() {
            return field != null ? "<" + field.getName() + " of " + field.getDeclaringClass().getSimpleName() + ">" : "";
        }

        public String toString() {
            return getDesc();
        }

        public boolean isFlat() {
            return flat;
        }

        public int getComponentStructSize() {
            if (arrayType == boolean.class || arrayType == byte.class)
                return 1;
            if (arrayType == char.class || arrayType == short.class)
                return 2;
            if (arrayType == int.class || arrayType == float.class)
                return 4;
            if (arrayType == long.class || arrayType == double.class)
                return 8;
            return 0; // object => cannot decide
        }

        public int getStructSize() {
            if (type == boolean.class || type == byte.class)
                return 1;
            if (type == char.class || type == short.class)
                return 2;
            if (type == int.class || type == float.class)
                return 4;
            if (type == long.class || type == double.class)
                return 8;
            if (isArray()) {
                if (isIntegral())
                    return 8; // pointer+length
                else // object array
                    return 16; // pointer+length+elemsiz+pointertype
            }
            return 4;
        }

        public boolean isPrimitive() {
            return primitive;
        }

        public final int getByteValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getByte(obj, memOffset);
            }
            return field.getByte(obj);
        }

        public final int getCharValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getChar(obj, memOffset);
            }
            return field.getChar(obj);
        }

        public final int getShortValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getShort(obj, memOffset);
            }
            return field.getShort(obj);
        }

        public final int getIntValueUnsafe(Object obj) throws IllegalAccessException {
            return FSTUtil.unFlaggedUnsafe.getInt(obj, memOffset);
        }


        public final long getLongValueUnsafe(Object obj) throws IllegalAccessException {
            return FSTUtil.unFlaggedUnsafe.getLong(obj, memOffset);
        }

        public final boolean getBooleanValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getBoolean(obj, memOffset);
            }
            return field.getBoolean(obj);
        }

        /**
         * Warning: crashes if not an object ref !
         * use getField().get() for a safe version ..
         *
         * @param obj
         * @return
         * @throws IllegalAccessException
         */
        public final Object getObjectValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getObject(obj, memOffset);
            }
            return field.get(obj);
        }

        public final float getFloatValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getFloat(obj, memOffset);
            }
            return field.getFloat(obj);
        }

        public final void setCharValue(Object newObj, char c) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putChar(newObj, memOffset, c);
                return;
            }
            field.setChar(newObj, c);
        }

        public final void setShortValue(Object newObj, short i1) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putShort(newObj, memOffset, i1);
                return;
            }
            field.setShort(newObj, i1);
        }

        public final void setObjectValue(Object target, Object value) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putObject(target, memOffset, value);
                return;
            }
            field.set(target, value);
        }

        public final void setFloatValue(Object newObj, float l) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putFloat(newObj, memOffset, l);
                return;
            }
            field.setFloat(newObj, l);
        }

        public final void setDoubleValue(Object newObj, double l) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putDouble(newObj, memOffset, l);
                return;
            }
            field.setDouble(newObj, l);
        }

        public final void setLongValue(Object newObj, long i1) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putLong(newObj, memOffset, i1);
                return;
            }
            field.setLong(newObj, i1);
        }

        public final long getLongValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getLong(obj, memOffset);
            }
            return field.getLong(obj);
        }

        public final double getDoubleValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getDouble(obj, memOffset);
            }
            return field.getDouble(obj);
        }

        public final void setIntValue(Object newObj, int i1) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putInt(newObj, memOffset, i1);
                return;
            }
            field.setInt(newObj, i1);
        }

        public final int getIntValue(Object obj) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                return FSTUtil.unFlaggedUnsafe.getInt(obj, memOffset);
            }
            return field.getInt(obj);
        }

        public final void setBooleanValue(Object newObj, boolean i1) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putBoolean(newObj, memOffset, i1);
                return;
            }
            field.setBoolean(newObj, i1);
        }

        public final void setByteValue(Object newObj, byte b) throws IllegalAccessException {
            if (!isAndroid && memOffset >= 0) {
                FSTUtil.unFlaggedUnsafe.putByte(newObj, memOffset, b);
                return;
            }
            field.setByte(newObj, b);
        }

        public String getName() {
            return field != null ? field.getName() : fakeName;
        }
    }

    /**
     * sideeffecting: if no ser is found, next lookup will return null immediate
     * @return
     */
    public FSTObjectSerializer getSer() {
        if (ser == null) {
            if (clazz == null) {
                return null;
            }
            ser = getSerNoStore();
            if (ser == null) {
                ser = FSTSerializerRegistry.NULL;
            }
        }
        if (ser == FSTSerializerRegistry.NULL) {
            return null;
        }
        return ser;
    }

    // no sideffecting lookup
    public FSTObjectSerializer getSerNoStore() {
        return conf.getCLInfoRegistry().getSerializerRegistry().getSerializer(clazz);
    }

    static class FSTCompatibilityInfo {
        Method writeMethod, readMethod;
        ObjectStreamClass objectStreamClass;
        List infos;
        Class clazz;
        FSTFieldInfo infoArr[];

        public FSTCompatibilityInfo(List inf, Class c) {
            readClazz(c);
            infos = inf;
            clazz = c;
        }

        public List getFields() {
            return infos;
        }

        public FSTFieldInfo[] getFieldArray() {
            if (infoArr == null) {
                List fields = getFields();
                final FSTFieldInfo[] fstFieldInfos = new FSTFieldInfo[fields.size()];
                fields.toArray(fstFieldInfos);
                Arrays.sort(fstFieldInfos, defFieldComparator);
                infoArr = fstFieldInfos;
            }
            return infoArr;
        }

        public Class getClazz() {
            return clazz;
        }

        public boolean needsCompatibleMode() {
            return writeMethod != null || readMethod != null;
        }

        public void readClazz(Class c) {
            writeMethod = FSTUtil.findPrivateMethod(c, "writeObject",
                                                    new Class[]{ObjectOutputStream.class},
                                                    Void.TYPE);
            readMethod = FSTUtil.findPrivateMethod(c, "readObject",
                                                   new Class[]{ObjectInputStream.class},
                                                   Void.TYPE);
            if (writeMethod != null) {
                writeMethod.setAccessible(true);
            }
            if (readMethod != null) {
                readMethod.setAccessible(true);
            }
        }

        public Method getReadMethod() {
            return readMethod;
        }

        public void setReadMethod(Method readMethod) {
            this.readMethod = readMethod;
        }

        public Method getWriteMethod() {
            return writeMethod;
        }

        public void setWriteMethod(Method writeMethod) {
            this.writeMethod = writeMethod;
        }

        public boolean isAsymmetric() {
            return (getReadMethod() == null && getWriteMethod() != null) || (getWriteMethod() == null && getReadMethod() != null);
        }

    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy