package net.acomputerdog.core.java;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * Reflection utilities
 */
public class Reflect {
    /**
     * Map of FieldInstances to Fields
     */
    private static final Map<FieldID, Field> fieldMap = new HashMap<FieldID, Field>();

    /**
     * Gets the value of a field, even if it is private
     *
     * @param cls      The class containing the field
     * @param instance An instance of the class to get from, or null if static
     * @param name     The name of the field
     * @param <T>      The type of the field
     * @return Return a T representing the value of the field
     */
    public static <T> T getFieldValue(Class cls, Object instance, String name) {
        FieldID fid = new FieldID(cls, name);
        Field field = fieldMap.get(fid);
        if (field == null) {
            try {
                field = cls.getDeclaredField(name);
                field.setAccessible(true);
            } catch (NoSuchFieldException e) {
                throw new IllegalArgumentException("Field does not exist!", e);
            }
            fieldMap.put(fid, field);
        }
        try {
            return (T) field.get(instance);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Unable to access field!", e);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Field does not contain a value of type T!", e);
        }
    }

    /**
     * Class used to hold a class and field name.  Used for caching hashmap.
     */
    private static class FieldID {
        /**
         * The class of the field
         */
        private final Class cls;
        /**
         * The name of the field
         */
        private final String name;

        private FieldID(Class cls, String name) {
            if (cls == null || name == null) {
                throw new IllegalArgumentException("Class and Name cannot be null!");
            }
            this.cls = cls;
            this.name = name;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof FieldID)) return false;

            FieldID fieldID = (FieldID) o;

            if (!cls.equals(fieldID.cls)) return false;

            return name.equals(fieldID.name);

        }

        @Override
        public int hashCode() {
            int result = cls.hashCode();
            result = 31 * result + (name.hashCode());
            return result;
        }
    }
}

@@ -248,33 +248,39 @@
 final Map%3CFieldI
-D
+nstance
 , Field%3E fieldMa
@@ -301,17 +301,23 @@
 p%3CFieldI
-D
+nstance
 , Field%3E
@@ -780,17 +780,23 @@
   FieldI
-D
+nstance
  fid = n
@@ -804,17 +804,23 @@
 w FieldI
-D
+nstance
 (cls, na
@@ -1647,17 +1647,23 @@
 s FieldI
-D
+nstance
  %7B%0A     
@@ -1863,17 +1863,23 @@
 e FieldI
-D
+nstance
 (Class c
@@ -2250,17 +2250,23 @@
 f FieldI
-D
+nstance
 )) retur
@@ -2297,17 +2297,29 @@
 eldI
-D fieldID
+nstance fieldInstance
  = (
@@ -2324,17 +2324,23 @@
  (FieldI
-D
+nstance
 ) o;%0A%0A  
@@ -2371,17 +2371,23 @@
 s(fieldI
-D
+nstance
 .cls)) r
@@ -2437,17 +2437,23 @@
 s(fieldI
-D
+nstance
 .name);%0A

Rename internal class of Reflect.class