Package co.paralleluniverse.data.record

Source Code of co.paralleluniverse.data.record.RecordType$ClassInfo

/*
* Copyright (c) 2013, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*   or (per the licensee's choosing)
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.data.record;

import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
*
* @author pron
*/
public class RecordType<R> {
    public enum Mode {
        /**
         * About 2.5 times slower than REFLECTION in Java 7, but doesn't use boxing and doesn't generate garbage. The default.
         */
        METHOD_HANDLE,
        /**
         * About 8x slower than UNSAFE and GENERATION
         */
        REFLECTION,
        /**
         * Just a little slower than GENERATION. Can only work on fields; doesn't work if there are getters/setters.
         */
        UNSAFE,
        /**
         * The fastest method (as fast as direct settings of fields), but can only be used if both the target object's class
         * as well as the fields or getters/setters are public.
         */
        GENERATION
    };
    private final List<Field<R, ?>> fields;
    private int fieldIndex;
    private boolean sealed;
    private Set<Field<? super R, ?>> fieldSet;
    private int primitiveIndex;
    private int primitiveOffset;
    private int objectIndex;
    private int objectOffset;
    private int[] offsets;
    private final ThreadLocal<Mode> currentMode = new ThreadLocal<Mode>();
    private final ClassValue<ClassInfo> vtables;

    public RecordType() {
        this.fields = new ArrayList<Field<R, ?>>();
        this.fieldIndex = 0;
        this.vtables = new ClassValue<ClassInfo>() {
            @Override
            protected ClassInfo computeValue(Class<?> type) {
                seal();
                return new ClassInfo(currentMode.get(), type, fields);
            }
        };
    }

    public Field.BooleanField<R> booleanField(String name) {
        return addField(new Field.BooleanField<R>(name, -1));
    }

    public Field.ByteField<R> byteField(String name) {
        return addField(new Field.ByteField<R>(name, -1));
    }

    public Field.ShortField<R> shortField(String name) {
        return addField(new Field.ShortField<R>(name, -1));
    }

    public Field.IntField<R> intField(String name) {
        return addField(new Field.IntField<R>(name, -1));
    }

    public Field.LongField<R> longField(String name) {
        return addField(new Field.LongField<R>(name, -1));
    }

    public Field.FloatField<R> floatField(String name) {
        return addField(new Field.FloatField<R>(name, -1));
    }

    public Field.DoubleField<R> doubleField(String name) {
        return addField(new Field.DoubleField<R>(name, -1));
    }

    public Field.CharField<R> charField(String name) {
        return addField(new Field.CharField<R>(name, -1));
    }

    public <V> Field.ObjectField<R, V> objectField(String name, Class<V> type) {
        return addField(new Field.ObjectField<R, V>(name, type, -1));
    }

    public <V> Field.ObjectField<R, V> objectField(String name, TypeToken<V> type) {
        return addField(new Field.ObjectField<R, V>(name, type.getRawType(), -1));
    }

    public Field.BooleanArrayField<R> booleanArrayField(String name, int length) {
        return addField(new Field.BooleanArrayField<R>(name, length, -1));
    }

    public Field.ByteArrayField<R> byteArrayField(String name, int length) {
        return addField(new Field.ByteArrayField<R>(name, length, -1));
    }

    public Field.ShortArrayField<R> shortArrayField(String name, int length) {
        return addField(new Field.ShortArrayField<R>(name, length, -1));
    }

    public Field.IntArrayField<R> intArrayField(String name, int length) {
        return addField(new Field.IntArrayField<R>(name, length, -1));
    }

    public Field.LongArrayField<R> longArrayField(String name, int length) {
        return addField(new Field.LongArrayField<R>(name, length, -1));
    }

    public Field.FloatArrayField<R> floatArrayField(String name, int length) {
        return addField(new Field.FloatArrayField<R>(name, length, -1));
    }

    public Field.DoubleArrayField<R> doubleArrayField(String name, int length) {
        return addField(new Field.DoubleArrayField<R>(name, length, -1));
    }

    public Field.CharArrayField<R> charArrayField(String name, int length) {
        return addField(new Field.CharArrayField<R>(name, length, -1));
    }

    public <V> Field.ObjectArrayField<R, V> objectArrayField(String name, Class<V> type, int length) {
        return addField(new Field.ObjectArrayField<R, V>(name, type, length, -1));
    }

    private <F extends Field<R, ?>> F addField(F field) {
        if (sealed)
            throw new IllegalStateException("Cannot add fields once a record has been instantiated");

        assert field.id < 0;
        final int id = fieldIndex;
        this.fieldIndex++;

        final Field<R, ?> f;
        switch (field.type()) {
            case Field.BOOLEAN:
                f = Field.booleanField(field.name(), id);
                break;
            case Field.BYTE:
                f = Field.byteField(field.name(), id);
                break;
            case Field.SHORT:
                f = Field.shortField(field.name(), id);
                break;
            case Field.INT:
                f = Field.intField(field.name(), id);
                break;
            case Field.LONG:
                f = Field.longField(field.name(), id);
                break;
            case Field.FLOAT:
                f = Field.floatField(field.name(), id);
                break;
            case Field.DOUBLE:
                f = Field.doubleField(field.name(), id);
                break;
            case Field.CHAR:
                f = Field.charField(field.name(), id);
                break;
            case Field.BOOLEAN_ARRAY:
                f = Field.booleanArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.BYTE_ARRAY:
                f = Field.byteArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.SHORT_ARRAY:
                f = Field.shortArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.INT_ARRAY:
                f = Field.intArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.LONG_ARRAY:
                f = Field.longArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.FLOAT_ARRAY:
                f = Field.floatArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.DOUBLE_ARRAY:
                f = Field.doubleArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.CHAR_ARRAY:
                f = Field.charArrayField(field.name(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            case Field.OBJECT:
                f = Field.objectField(field.name(), (Class)field.typeClass(), id);
                break;
            case Field.OBJECT_ARRAY:
                f = Field.objectArrayField(field.name(), field.typeClass().getComponentType(), ((Field.ArrayField<R, ?>) field).length, id);
                break;
            default:
                throw new AssertionError();
        }
        fields.add(f);
        return (F) f;
    }

    private void seal() {
        if (!sealed) {
            this.sealed = true;
            this.fieldSet = (Set) ImmutableSet.copyOf(fields);
            this.offsets = new int[fields.size()];
            for (Field<?, ?> field : fields) {
                final int offset;
                if (field.type() == Field.OBJECT || field.type() == Field.OBJECT_ARRAY) {
                    offset = objectOffset;
                    objectOffset += (field instanceof Field.ArrayField ? ((Field.ArrayField) field).length : 1);
                    objectIndex++;
                } else {
                    offset = primitiveOffset;
                    primitiveOffset += field.size();
                    primitiveIndex++;
                }
                offsets[field.id()] = offset;
            }
        }
    }

    static class ClassInfo {
        final Entry[] table;
        final Mode mode;
        final Set<Field<?, ?>> fieldSet;

        private ClassInfo(Mode mode, Class<?> type, Collection<? extends Field<?, ?>> fields) {
            try {
                if (mode == null) {
                    boolean unsafePossible = true;
                    boolean generationPossible = true;

                    for (Field<?, ?> field : fields) {
                        final Method getter = field instanceof Field.ArrayField ? getIndexedGetter(type, field) : getGetter(type, field);
                        final Method setter = field instanceof Field.ArrayField ? getIndexedSetter(type, field) : getSetter(type, field);
                        final java.lang.reflect.Field f = getter == null ? getField(type, field) : null;

                        if (f == null && getter != null)
                            unsafePossible = false;

                        if (getter == null && ((f.getModifiers() & Modifier.PUBLIC) == 0))
                            generationPossible = false;
                    }

                    if (unsafePossible)
                        mode = Mode.UNSAFE;
                    else if (generationPossible)
                        mode = Mode.GENERATION;
                    else
                        mode = Mode.METHOD_HANDLE;
                }

                this.mode = mode;
                this.table = new Entry[fields.size()];
                final List<Field<?, ?>> implementedFields = new ArrayList<>();
                for (Field<?, ?> field : fields) {
                    final Method getter = field instanceof Field.ArrayField ? getIndexedGetter(type, field) : getGetter(type, field);
                    final Method setter = field instanceof Field.ArrayField ? getIndexedSetter(type, field) : getSetter(type, field);
                    final java.lang.reflect.Field f = getter == null ? getField(type, field) : null;
                    final boolean indexed = f == null && field instanceof Field.ArrayField;

                    final MethodHandle getterHandle;
                    final MethodHandle setterHandle;
                    if (mode == Mode.METHOD_HANDLE) {
                        getterHandle = DynamicMethodHandleRecord.getGetterMethodHandle(field, f, getter);
                        setterHandle = DynamicMethodHandleRecord.getSetterMethodHandle(field, f, setter);
                    } else {
                        getterHandle = null;
                        setterHandle = null;
                    }

                    final long offset;
                    if (mode == Mode.UNSAFE) {
                        if (f == null && getter != null)
                            throw new RuntimeException("Cannot use UNSAFE mode for class " + type.getName() + " because field " + field.name + " has a getter and/or a setter");
                        offset = DynamicUnsafeRecord.getFieldOffset(type, f);
                    } else
                        offset = -1L;

                    final DynamicGeneratedRecord.Accessor accessor;
                    if (mode == Mode.GENERATION) {
                        if ((type.getModifiers() & Modifier.PUBLIC) == 0)
                            throw new RuntimeException("Cannot use GENERATION mode because class " + type.getName() + " is not public.");
                        if (f != null && (f.getModifiers() & Modifier.PUBLIC) == 0)
                            throw new RuntimeException("Cannot use GENERATION mode because field " + f.getName() + " in class " + type.getName() + " is not public.");

                        accessor = DynamicGeneratedRecord.generateAccessor(type, field, f, getter, setter);
                    } else
                        accessor = null;

                    if (f != null || getter != null)
                        implementedFields.add(field);
                    table[field.id()] = new Entry(f, getter, setter, getterHandle, setterHandle, offset, accessor, indexed);
                }

                this.fieldSet = (Set) ImmutableSet.copyOf(implementedFields);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static java.lang.reflect.Field getField(Class<?> type, Field field) {
        java.lang.reflect.Field f = null;
        try {
            f = type.getField(field.name());
        } catch (NoSuchFieldException e) {
        }
        try {
            f = type.getDeclaredField(field.name());
        } catch (NoSuchFieldException e) {
        }
        if (f != null) {
            f.setAccessible(true);
        }
        return f;
    }

    private static Method getGetter(Class<?> type, Field field) {
        try {
            return type.getMethod("get" + capitalize(field.name()));
        } catch (NoSuchMethodException e) {
        }
        if (field.type() == Field.BOOLEAN) {
            try {
                return type.getMethod("is" + capitalize(field.name()));
            } catch (NoSuchMethodException e) {
            }
        }
        return null;
    }

    private static Method getSetter(Class<?> type, Field field) {
        try {
            return type.getMethod("set" + capitalize(field.name()), field.typeClass());
        } catch (NoSuchMethodException e) {
        }
        return null;
    }

    private static Method getIndexedGetter(Class<?> type, Field field) {
        assert field instanceof Field.ArrayField;
        try {
            return type.getMethod("get" + capitalize(field.name()), int.class);
        } catch (NoSuchMethodException e) {
        }
        if (field.type() == Field.BOOLEAN_ARRAY) {
            try {
                return type.getMethod("is" + capitalize(field.name()), int.class);
            } catch (NoSuchMethodException e) {
            }
        }
        return null;
    }

    private static Method getIndexedSetter(Class<?> type, Field field) {
        assert field instanceof Field.ArrayField;
        try {
            return type.getMethod("set" + capitalize(field.name()), int.class, field.typeClass().getComponentType());
        } catch (NoSuchMethodException e) {
        }
        return null;
    }

    private static String capitalize(String str) {
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }

    static class Entry {
        final java.lang.reflect.Field field;
        final Method getter;
        final Method setter;
        final MethodHandle getterHandle;
        final MethodHandle setterHandle;
        final long offset;
        final DynamicGeneratedRecord.Accessor accessor;
        final boolean indexed;

        public Entry(java.lang.reflect.Field field, Method getter, Method setter, MethodHandle getterHandle, MethodHandle setterHandle, long offset, DynamicGeneratedRecord.Accessor accessor, boolean indexed) {
            this.field = field;
            this.getter = getter;
            this.setter = setter;
            this.getterHandle = getterHandle;
            this.setterHandle = setterHandle;
            this.offset = offset;
            this.accessor = accessor;
            this.indexed = indexed;
        }
    }

    public Set<Field<? super R, ?>> fields() {
        seal();
        return fieldSet;
    }

    ClassInfo getClassInfo(Class<?> clazz) {
        return vtables.get(clazz);
    }

    int getPrimitiveIndex() {
        return primitiveIndex;
    }

    int getObjectIndex() {
        return objectIndex;
    }

    int getPrimitiveOffset() {
        return primitiveOffset;
    }

    int getObjectOffset() {
        return objectOffset;
    }

    int[] getOffsets() {
        return offsets;
    }

    public Record<R> newInstance() {
        seal();
        return new SimpleRecord<R>(this);
    }

    public Record<R> newInstance(Object target) {
        return newInstance(target, null);
    }

    public Record<R> newInstance(Object target, Mode mode) {
        seal();
        currentMode.set(mode);
        ClassInfo ci = vtables.get(target.getClass());
        if (mode == null)
            mode = ci.mode;
        if (mode != Mode.REFLECTION && ci.mode != mode)
            throw new IllegalStateException("Target's class, " + target.getClass().getName() + ", has been mirrored with a different, incompatible mode, " + ci.mode);
        switch (mode) {
            case METHOD_HANDLE:
                return new DynamicMethodHandleRecord<R>(this, target);
            case REFLECTION:
                return new DynamicReflectionRecord<R>(this, target);
            case UNSAFE:
                return new DynamicUnsafeRecord<R>(this, target);
            case GENERATION:
                return new DynamicGeneratedRecord<R>(this, target);
        }
        throw new AssertionError("unreachable");
    }

    public RecordArray<R> newArray(int size) {
        seal();
        return new SimpleRecordArray<R>(this, size);
    }

    @Override
    public String toString() {
        return fields.toString();
    }
}
TOP

Related Classes of co.paralleluniverse.data.record.RecordType$ClassInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.