org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.util.DynamicObject.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.util.DynamicObject.java

Source

/*******************************************************************************
 * Copyright (c) 2012, 2014 Obeo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.util;

import static com.google.common.base.Throwables.propagate;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * Utility class to access (R/W) field in super class hierarchy.
 * <p>
 * It has decent performance as it LRU-caches reflective call.
 * 
 * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
 */
public class DynamicObject {

    private final LoadingCache<String, Field> fFieldCache;

    private final LoadingCache<MethodKey, Method> fMethodCache;

    private final Object fTarget;

    private final Class<?> fTargetClass;

    /**
     * 
     */
    public DynamicObject(Object target) {
        fTarget = target;
        fTargetClass = target.getClass();
        fFieldCache = CacheBuilder.newBuilder().maximumSize(16).build(new CacheLoader<String, Field>() {
            @Override
            public Field load(String key) throws Exception {
                return getField(fTargetClass, key);
            }
        });
        fMethodCache = CacheBuilder.newBuilder().maximumSize(16).build(new CacheLoader<MethodKey, Method>() {
            @Override
            public Method load(MethodKey key) throws Exception {
                return getMethod(fTargetClass, key.methodName, key.parameterTypes);
            }
        });
    }

    public Object get(String fieldName) {
        Object ret = null;
        try {
            Field field = fFieldCache.get(fieldName);
            ret = field.get(fTarget);
        } catch (Exception e) {
            handleException(e);
        }
        return ret;
    }

    public void set(String fieldName, Object value) {
        try {
            Field field = fFieldCache.get(fieldName);
            field.set(fTarget, value);
        } catch (Exception e) {
            handleException(e);
        }
    }

    public double getDouble(String fieldName) {
        double ret = Double.NaN;
        try {
            Field field = fFieldCache.get(fieldName);
            ret = field.getDouble(fTarget);
        } catch (Exception e) {
            handleException(e);
        }
        return ret;
    }

    public void setDouble(String fieldName, double value) {
        try {
            Field field = fFieldCache.get(fieldName);
            field.setDouble(fTarget, value);
        } catch (Exception e) {
            handleException(e);
        }
    }

    public int getInt(String fieldName) {
        int ret = 0;
        try {
            Field field = fFieldCache.get(fieldName);
            ret = field.getInt(fTarget);
        } catch (Exception e) {
            handleException(e);
        }
        return ret;
    }

    public void setInt(String fieldName, int value) {
        try {
            Field field = fFieldCache.get(fieldName);
            field.setInt(fTarget, value);
        } catch (Exception e) {
            handleException(e);
        }
    }

    public Object invoke(String methodName, Class<?>[] parameterTypes, Object... args) {
        Object ret = null;
        try {
            Method method = fMethodCache.get(MethodKey.create(methodName, parameterTypes));
            ret = method.invoke(fTarget, args);
        } catch (Exception e) {
            handleException(e);
        }
        return ret;
    }

    /**
     * @param cause
     */
    protected void handleException(Throwable cause) {
        propagate(cause);
    }

    private static Field getField(Class<?> clazz, String fieldName) {
        try {
            Field declaredField = clazz.getDeclaredField(fieldName);
            makeAccessible(declaredField);
            return declaredField;
        } catch (NoSuchFieldException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass == null) {
                propagate(e);
            } else {
                return getField(superClass, fieldName);
            }
        }
        return null;
    }

    private static Method getMethod(Class<?> clazz, String name, Class<?>[] parameterTypes) {
        Method method = null;
        try {
            method = clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException nsme) {
            if (!clazz.isInterface()) {
                Class<?> superClass = clazz.getSuperclass();
                if (superClass != null) {
                    method = getMethod(superClass, name, parameterTypes);
                }
            }

            if (method == null) {
                for (Class<?> superInterface : clazz.getInterfaces()) {
                    method = getMethod(superInterface, name, parameterTypes);
                    if (method != null) {
                        break;
                    }
                }
            }
        } catch (Exception e) {
            propagate(e);
        }

        if (method != null && !method.isAccessible()) {
            makeAccessible(method);
        }

        return method;
    }

    private static void makeAccessible(Member member) {
        final boolean instanceOfAccesibleAndNotAccessible = member instanceof AccessibleObject
                && !((AccessibleObject) member).isAccessible();
        if (instanceOfAccesibleAndNotAccessible || !Modifier.isPublic(member.getModifiers())
                || !Modifier.isPublic(member.getDeclaringClass().getModifiers())) {
            if (member instanceof AccessibleObject) {
                ((AccessibleObject) member).setAccessible(true);
            } else {
                throw new RuntimeException("Can not set accessible " + member); //$NON-NLS-1$
            }
        }
    }

    private static class MethodKey {
        final String methodName;

        final Class<?>[] parameterTypes;

        private MethodKey(String methodName, Class<?>[] parameterTypes) {
            this.methodName = methodName;
            this.parameterTypes = parameterTypes;
        }

        static MethodKey create(String methodName, Class<?>[] parameterTypes) {
            return new MethodKey(methodName, parameterTypes);
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
            result = prime * result + Arrays.hashCode(parameterTypes);
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            MethodKey other = (MethodKey) obj;
            if (methodName == null) {
                if (other.methodName != null) {
                    return false;
                }
            } else if (!methodName.equals(other.methodName)) {
                return false;
            }
            if (!Arrays.equals(parameterTypes, other.parameterTypes)) {
                return false;
            }
            return true;
        }

    }
}