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

com.sleepycat.persist.impl.ReflectionAccessor Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.persist.impl;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.List;

import com.sleepycat.compat.DbCompat;

/**
 * Implements Accessor using reflection.
 *
 * @author Mark Hayes
 */
class ReflectionAccessor implements Accessor {

    private static final FieldAccess[] EMPTY_KEYS = {};

    private Class type;
    private Accessor superAccessor;
    private Constructor constructor;
    private FieldAccess priKey;
    private FieldAccess[] secKeys;
    private FieldAccess[] nonKeys;

    private ReflectionAccessor(Class type, Accessor superAccessor) {
        this.type = type;
        this.superAccessor = superAccessor;
        try {
            constructor = type.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw DbCompat.unexpectedState(type.getName());
        }
        if (!Modifier.isPublic(type.getModifiers()) || 
            !Modifier.isPublic(constructor.getModifiers())) {
            setAccessible(constructor, type.getName() + "()");
        }
    }

    /**
     * Creates an accessor for a complex type.
     */
    ReflectionAccessor(Catalog catalog,
                       Class type,
                       Accessor superAccessor,
                       FieldInfo priKeyField,
                       List secKeyFields,
                       List nonKeyFields) {
        this(type, superAccessor);
        if (priKeyField != null) {
            priKey = getField(catalog, priKeyField,
                              true /*isRequiredKeyField*/);
        } else {
            priKey = null;
        }
        if (secKeyFields.size() > 0) {
            secKeys = getFields(catalog, secKeyFields,
                                false /*isRequiredKeyField*/);
        } else {
            secKeys = EMPTY_KEYS;
        }
        if (nonKeyFields.size() > 0) {
            nonKeys = getFields(catalog, nonKeyFields,
                                false /*isRequiredKeyField*/);
        } else {
            nonKeys = EMPTY_KEYS;
        }
    }

    /**
     * Creates an accessor for a composite key type.
     */
    ReflectionAccessor(Catalog catalog,
                       Class type,
                       List fieldInfos) {
        this(type, null);
        priKey = null;
        secKeys = EMPTY_KEYS;
        nonKeys = getFields(catalog, fieldInfos, true /*isRequiredKeyField*/);
    }

    private FieldAccess[] getFields(Catalog catalog,
                                    List fieldInfos,
                                    boolean isRequiredKeyField) {
        int index = 0;
        FieldAccess[] fields = new FieldAccess[fieldInfos.size()];
        for (FieldInfo info : fieldInfos) {
            fields[index] = getField(catalog, info, isRequiredKeyField);
            index += 1;
        }
        return fields;
    }

    private FieldAccess getField(Catalog catalog,
                                 FieldInfo fieldInfo,
                                 boolean isRequiredKeyField) {
        Field field;
        try {
            field = type.getDeclaredField(fieldInfo.getName());
        } catch (NoSuchFieldException e) {
            throw DbCompat.unexpectedException(e);
        }
        if (!Modifier.isPublic(type.getModifiers()) || 
            !Modifier.isPublic(field.getModifiers())) {
            setAccessible(field, field.getName());
        }
        Class fieldCls = field.getType();
        if (fieldCls.isPrimitive()) {
            assert SimpleCatalog.isSimpleType(fieldCls);
            return new PrimitiveAccess
                (field, (SimpleFormat) catalog.getFormat(fieldCls.getName()));
        } else if (isRequiredKeyField) {
            Format format = catalog.getFormat(fieldInfo.getClassName());
            assert format != null;
            return new KeyObjectAccess(field, format);
        } else if (fieldCls == String.class) {
            return new StringAccess(field);
        } else {
            return new ObjectAccess(field);
        }
    }

    private void setAccessible(AccessibleObject object, String memberName) {
        try {
            object.setAccessible(true);
        } catch (SecurityException e) {
            throw new IllegalStateException
                ("Unable to access non-public member: " +
                 type.getName() + '.' + memberName +
                 ". Please configure the Java Security Manager setting: " +
                 " ReflectPermission suppressAccessChecks", e);
        }
    }

    public Object newInstance() {
        try {
            return constructor.newInstance();
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        } catch (InstantiationException e) {
            throw DbCompat.unexpectedException(e);
        } catch (InvocationTargetException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public Object newArray(int len) {
        return Array.newInstance(type, len);
    }

    public boolean isPriKeyFieldNullOrZero(Object o) {
        try {
            if (priKey != null) {
                return priKey.isNullOrZero(o);
            } else if (superAccessor != null) {
                return superAccessor.isPriKeyFieldNullOrZero(o);
            } else {
                throw DbCompat.unexpectedState("No primary key field");
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void writePriKeyField(Object o, EntityOutput output)
        throws RefreshException {

        try {
            if (priKey != null) {
                priKey.write(o, output);
            } else if (superAccessor != null) {
                superAccessor.writePriKeyField(o, output);
            } else {
                throw DbCompat.unexpectedState("No primary key field");
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void readPriKeyField(Object o, EntityInput input)
        throws RefreshException {

        try {
            if (priKey != null) {
                priKey.read(o, input);
            } else if (superAccessor != null) {
                superAccessor.readPriKeyField(o, input);
            } else {
                throw DbCompat.unexpectedState("No primary key field");
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void writeSecKeyFields(Object o, EntityOutput output)
        throws RefreshException {

        try {
        
            /* 
             * In JE 5.0, String is treated as primitive type, so String does
             * not need to be registered. [#19247]
             */
            if (priKey != null && !priKey.isPrimitive && !priKey.isString) {
                output.registerPriKeyObject(priKey.field.get(o));
            }
            if (superAccessor != null) {
                superAccessor.writeSecKeyFields(o, output);
            }
            for (int i = 0; i < secKeys.length; i += 1) {
                secKeys[i].write(o, output);
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void readSecKeyFields(Object o,
                                 EntityInput input,
                                 int startField,
                                 int endField,
                                 int superLevel)
        throws RefreshException {

        try {
            if (priKey != null && !priKey.isPrimitive && !priKey.isString) {
                input.registerPriKeyObject(priKey.field.get(o));
            } else if (priKey != null && priKey.isString) {
                input.registerPriStringKeyObject(priKey.field.get(o));
            }
            if (superLevel != 0 && superAccessor != null) {
                superAccessor.readSecKeyFields
                    (o, input, startField, endField, superLevel - 1);
            } else {
                if (superLevel > 0) {
                    throw DbCompat.unexpectedState
                        ("Superclass does not exist");
                }
            }
            if (superLevel <= 0) {
                for (int i = startField;
                     i <= endField && i < secKeys.length;
                     i += 1) {
                    secKeys[i].read(o, input);
                }
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void writeNonKeyFields(Object o, EntityOutput output)
        throws RefreshException {

        try {
            if (superAccessor != null) {
                superAccessor.writeNonKeyFields(o, output);
            }
            for (int i = 0; i < nonKeys.length; i += 1) {
                nonKeys[i].write(o, output);
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void readNonKeyFields(Object o,
                                 EntityInput input,
                                 int startField,
                                 int endField,
                                 int superLevel)
        throws RefreshException {

        try {
            if (superLevel != 0 && superAccessor != null) {
                superAccessor.readNonKeyFields
                    (o, input, startField, endField, superLevel - 1);
            } else {
                if (superLevel > 0) {
                    throw DbCompat.unexpectedState
                        ("Superclass does not exist");
                }
            }
            if (superLevel <= 0) {
                for (int i = startField;
                     i <= endField && i < nonKeys.length;
                     i += 1) {
                    nonKeys[i].read(o, input);
                }
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void writeCompositeKeyFields(Object o, EntityOutput output)
        throws RefreshException {

        try {
            for (int i = 0; i < nonKeys.length; i += 1) {
                nonKeys[i].write(o, output);
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void readCompositeKeyFields(Object o, EntityInput input)
        throws RefreshException {

        try {
            for (int i = 0; i < nonKeys.length; i += 1) {
                nonKeys[i].read(o, input);
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public Object getField(Object o,
                           int field,
                           int superLevel,
                           boolean isSecField) {
        if (superLevel > 0) {
            return superAccessor.getField
                (o, field, superLevel - 1, isSecField);
        }
        try {
            Field fld =
                isSecField ? secKeys[field].field : nonKeys[field].field;
            return fld.get(o);
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    public void setField(Object o,
                         int field,
                         int superLevel,
                         boolean isSecField,
                         Object value) {
        if (superLevel > 0) {
            superAccessor.setField
                (o, field, superLevel - 1, isSecField, value);
            return;
        }
        try {
            Field fld =
                isSecField ? secKeys[field].field : nonKeys[field].field;
            fld.set(o, value);
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }
    
    public void setPriField(Object o, Object value) {
        try {
            if (priKey != null) {
                priKey.field.set(o, value);
            } else if (superAccessor != null) {
                superAccessor.setPriField(o, value);
            } else {
                throw DbCompat.unexpectedState("No primary key field");
            }
        } catch (IllegalAccessException e) {
            throw DbCompat.unexpectedException(e);
        }
    }

    /**
     * Abstract base class for field access classes.
     */
    private static abstract class FieldAccess {

        Field field;
        boolean isPrimitive;
        boolean isString = false;

        FieldAccess(Field field) {
            this.field = field;
            isPrimitive = field.getType().isPrimitive();
            isString = 
                field.getType().getName().equals(String.class.getName());
        }

        /**
         * Writes a field.
         */
        abstract void write(Object o, EntityOutput out)
            throws IllegalAccessException, RefreshException;

        /**
         * Reads a field.
         */
        abstract void read(Object o, EntityInput in)
            throws IllegalAccessException, RefreshException;

        /**
         * Returns whether a field is null (for reference types) or zero (for
         * primitive integer types).  This implementation handles the reference
         * types.
         */
        boolean isNullOrZero(Object o)
            throws IllegalAccessException {

            return field.get(o) == null;
        }
    }

    /**
     * Access for fields with object types.
     */
    private static class ObjectAccess extends FieldAccess {

        ObjectAccess(Field field) {
            super(field);
        }

        @Override
        void write(Object o, EntityOutput out)
            throws IllegalAccessException, RefreshException {

            out.writeObject(field.get(o), null);
        }

        @Override
        void read(Object o, EntityInput in)
            throws IllegalAccessException, RefreshException {

            field.set(o, in.readObject());
        }
    }

    /**
     * Access for primary key fields and composite key fields with object
     * types.
     */
    private static class KeyObjectAccess extends FieldAccess {

        private Format format;

        KeyObjectAccess(Field field, Format format) {
            super(field);
            this.format = format;
        }

        @Override
        void write(Object o, EntityOutput out)
            throws IllegalAccessException, RefreshException {

            out.writeKeyObject(field.get(o), format);
        }

        @Override
        void read(Object o, EntityInput in)
            throws IllegalAccessException, RefreshException {

            field.set(o, in.readKeyObject(format));
        }
    }

    /**
     * Access for String fields, that are not primary key fields or composite
     * key fields with object types.
     */
    private static class StringAccess extends FieldAccess {
        StringAccess(Field field) {
            super(field);
        }

        @Override
        void write(Object o, EntityOutput out)
            throws IllegalAccessException, RefreshException {
            
            out.writeString((String) field.get(o));
        }

        @Override
        void read(Object o, EntityInput in)
            throws IllegalAccessException, RefreshException {

            field.set(o, in.readStringObject());
        }
    }

    /**
     * Access for fields with primitive types.
     */
    private static class PrimitiveAccess extends FieldAccess {

        private SimpleFormat format;

        PrimitiveAccess(Field field, SimpleFormat format) {
            super(field);
            this.format = format;
        }

        @Override
        void write(Object o, EntityOutput out)
            throws IllegalAccessException {

            format.writePrimitiveField(o, out, field);
        }

        @Override
        void read(Object o, EntityInput in)
            throws IllegalAccessException, RefreshException {

            format.readPrimitiveField(o, in, field);
        }

        @Override
        boolean isNullOrZero(Object o)
            throws IllegalAccessException {

            return field.getLong(o) == 0;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy