Advertisement
xtrafrancyz

Reflect

Jul 25th, 2017
350
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 31.87 KB | None | 0 0
  1. import sun.reflect.ReflectionFactory;
  2.  
  3. import java.lang.invoke.MethodHandles;
  4. import java.lang.reflect.Array;
  5. import java.lang.reflect.Constructor;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.Method;
  8. import java.lang.reflect.Modifier;
  9. import java.util.ArrayList;
  10. import java.util.Arrays;
  11. import java.util.HashMap;
  12. import java.util.List;
  13. import java.util.Map;
  14. import java.util.concurrent.ConcurrentHashMap;
  15. import java.util.logging.Level;
  16. import java.util.logging.Logger;
  17.  
  18. /**
  19.  * Класс предоставляет API для Java Reflection с кеширующими возможностями
  20.  *
  21.  * @author xtrafrancyz
  22.  */
  23. @SuppressWarnings({"unchecked", "unused"})
  24. public class Reflect {
  25.     private static final Map<String, ClassData> cache = new ConcurrentHashMap<>();
  26.    
  27.     private Reflect() {}
  28.    
  29.     /**
  30.      * Добавляет новый элемент в enum
  31.      *
  32.      * @param enumType класс енума, куда нужно добавить значение
  33.      * @param name     название нового значения
  34.      * @param args     агрументы конструктора
  35.      * @return новое значение енума
  36.      */
  37.     public static <T extends Enum<?>> T addEnum(Class<T> enumType, String name, Object... args) {
  38.         try {
  39.             ClassData<T> data = getClass(enumType);
  40.            
  41.             // Find field with all enums
  42.             Field field = null;
  43.             try {
  44.                 field = data.findFinalField("$VALUES");
  45.             } catch (UnableToFindFieldException ex) {
  46.                 try {
  47.                     field = data.findFinalField("ENUM$VALUES"); // Eclipse internal compiler
  48.                 } catch (UnableToFindFieldException ignored) {}
  49.             }
  50.             if (field == null) {
  51.                 int flags = Modifier.STATIC | Modifier.FINAL | 0x00001000 /*SYNTHETIC*/;
  52.                 String valueType = "[L" + enumType.getName().replace('.', '/') + ";";
  53.                 for (Field f : enumType.getDeclaredFields()) {
  54.                     if ((f.getModifiers() & flags) == flags && f.getType().getName().replace('.', '/').equals(valueType)) {
  55.                         field = f;
  56.                         field.setAccessible(true);
  57.                         ClassData.FIELD_MODIFIERS.set(field, field.getModifiers() & ~Modifier.FINAL);
  58.                         break;
  59.                     }
  60.                 }
  61.             }
  62.             if (field == null) {
  63.                 throw new UnableToFindFieldException(enumType, "$VALUES");
  64.             }
  65.            
  66.             // Getting previous enum values
  67.             T[] prev = (T[]) field.get(null);
  68.             List<T> values = new ArrayList<>(Arrays.asList(prev));
  69.            
  70.             // Adapting constructor parameters
  71.             Object[] params = new Object[args.length + 2];
  72.             params[0] = name;
  73.             params[1] = values.size();
  74.             System.arraycopy(args, 0, params, 2, args.length);
  75.            
  76.             // Creating new enum instance
  77.             ReflectionFactory rFactory = ReflectionFactory.getReflectionFactory();
  78.             T newValue = (T) rFactory.newConstructorAccessor(data.findConstructor(params)).newInstance(params);
  79.            
  80.             // Adding new enum
  81.             values.add(newValue);
  82.             field.set(null, values.toArray((T[]) Array.newInstance(enumType, 0)));
  83.            
  84.             Reflect.setFinal(Class.class, enumType, "enumConstants", null);
  85.             Reflect.setFinal(Class.class, enumType, "enumConstantDirectory", null);
  86.            
  87.             return newValue;
  88.         } catch (Exception e) {
  89.             error(e, "addEnum error");
  90.             return null;
  91.         }
  92.     }
  93.    
  94.     /**
  95.      * Создаёт объект класса clazz, при этом вызывая конструктор с аргументами args.
  96.      *
  97.      * @param clazz класс объекта
  98.      * @param args  аргументы конструктора
  99.      * @return новый объект
  100.      */
  101.     public static <E> E construct(Class<E> clazz, Object... args) {
  102.         try {
  103.             return getClass(clazz).construct(args);
  104.         } catch (Exception e) {
  105.             error(e, "Constructor error");
  106.             return null;
  107.         }
  108.     }
  109.    
  110.     /**
  111.      * Возвращает значение статического поля класса
  112.      *
  113.      * @param clazz класс
  114.      * @param field имя поля
  115.      * @return значение
  116.      */
  117.     public static <E> E get(Class clazz, String field) {
  118.         try {
  119.             return (E) getClass(clazz).get(null, field);
  120.         } catch (Exception e) {
  121.             error(e, "Get static field error");
  122.             return null;
  123.         }
  124.     }
  125.    
  126.     /**
  127.      * Возвращает значение поля объекта
  128.      *
  129.      * @param instance объект
  130.      * @param field    имя поля
  131.      * @return значение
  132.      */
  133.     public static <R> R get(Object instance, String field) {
  134.         try {
  135.             return (R) getClass(instance.getClass()).get(instance, field);
  136.         } catch (Exception e) {
  137.             error(e, "Get field error");
  138.             return null;
  139.         }
  140.     }
  141.    
  142.     /**
  143.      * Возвращает значение поля объекта
  144.      *
  145.      * @param clazz    класс, в котором есть это поле
  146.      * @param instance объект
  147.      * @param field    имя поля
  148.      * @return значение
  149.      */
  150.     public static <T, E> E get(Class<T> clazz, T instance, String field) {
  151.         try {
  152.             return (E) getClass(clazz).get(instance, field);
  153.         } catch (Exception e) {
  154.             error(e, "Get field error");
  155.             return null;
  156.         }
  157.     }
  158.    
  159.     /**
  160.      * Устанавливает новое значение статическому полю класса
  161.      *
  162.      * @param clazz класс
  163.      * @param field имя поля
  164.      * @param value новое значение
  165.      */
  166.     public static void set(Class clazz, String field, Object value) {
  167.         try {
  168.             getClass(clazz).set(null, field, value);
  169.         } catch (Exception e) {
  170.             error(e, "Set static field error");
  171.         }
  172.     }
  173.    
  174.     /**
  175.      * Устанавливает новое значение полю объекта
  176.      *
  177.      * @param instance объект
  178.      * @param field    имя поля
  179.      * @param value    новое значение
  180.      */
  181.     public static void set(Object instance, String field, Object value) {
  182.         try {
  183.             getClass(instance.getClass()).set(instance, field, value);
  184.         } catch (Exception e) {
  185.             error(e, "Set field error");
  186.         }
  187.     }
  188.    
  189.     /**
  190.      * Устанавливает новое значение полю объекта
  191.      *
  192.      * @param clazz    класс, в котором есть это поле
  193.      * @param instance объект
  194.      * @param field    имя поля
  195.      * @param value    новое значение
  196.      */
  197.     public static <T> void set(Class<T> clazz, T instance, String field, Object value) {
  198.         try {
  199.             getClass(clazz).set(instance, field, value);
  200.         } catch (Exception e) {
  201.             error(e, "Set field error");
  202.         }
  203.     }
  204.    
  205.     /**
  206.      * Устанавливает новое значение статическому финальному полю класса
  207.      *
  208.      * @param clazz класс
  209.      * @param field имя поля
  210.      * @param value новое значение
  211.      */
  212.     public static void setFinal(Class clazz, String field, Object value) {
  213.         try {
  214.             getClass(clazz).setFinal(null, field, value);
  215.         } catch (Exception e) {
  216.             error(e, "Set static final field error");
  217.         }
  218.     }
  219.    
  220.     /**
  221.      * Устанавливает новое значение финальному полю объекта
  222.      *
  223.      * @param instance объект
  224.      * @param field    имя поля
  225.      * @param value    новое значение
  226.      */
  227.     public static void setFinal(Object instance, String field, Object value) {
  228.         try {
  229.             getClass(instance.getClass()).setFinal(instance, field, value);
  230.         } catch (Exception e) {
  231.             error(e, "Set final field error");
  232.         }
  233.     }
  234.    
  235.     /**
  236.      * Устанавливает новое значение финальному полю объекта
  237.      *
  238.      * @param clazz    класс, в котором есть это поле
  239.      * @param instance объект
  240.      * @param field    имя поля
  241.      * @param value    новое значение
  242.      */
  243.     public static <T> void setFinal(Class<T> clazz, T instance, String field, Object value) {
  244.         try {
  245.             getClass(clazz).setFinal(instance, field, value);
  246.         } catch (Exception e) {
  247.             error(e, "Set final field error");
  248.         }
  249.     }
  250.    
  251.     /**
  252.      * Вызывает статический метод у класса
  253.      *
  254.      * @param clazz  класс
  255.      * @param method имя метода
  256.      * @param args   аргументы метода
  257.      * @return результат выполнения
  258.      */
  259.     public static <E> E invoke(Class clazz, String method, Object... args) {
  260.         try {
  261.             return (E) getClass(clazz).invoke(null, method, args);
  262.         } catch (Throwable e) {
  263.             error(e, "Invoke static error");
  264.             return null;
  265.         }
  266.     }
  267.    
  268.     /**
  269.      * Вызывает метод у объекта
  270.      *
  271.      * @param instance объект класса
  272.      * @param method   имя метода
  273.      * @param args     аргументы метода
  274.      * @return результат выполнения
  275.      */
  276.     public static <E> E invoke(Object instance, String method, Object... args) {
  277.         try {
  278.             return (E) getClass(instance.getClass()).invoke(instance, method, args);
  279.         } catch (Throwable e) {
  280.             error(e, "Invoke error");
  281.             return null;
  282.         }
  283.     }
  284.    
  285.     /**
  286.      * Вызывает метод у объекта
  287.      *
  288.      * @param clazz    класс, к которому принадлежит метод
  289.      * @param instance объект класса
  290.      * @param method   имя метода
  291.      * @param args     аргументы метода
  292.      * @return результат выполнения
  293.      */
  294.     public static <T, E> E invoke(Class<T> clazz, T instance, String method, Object... args) {
  295.         try {
  296.             return (E) getClass(clazz).invoke(instance, method, args);
  297.         } catch (Throwable e) {
  298.             error(e, "Invoke error");
  299.             return null;
  300.         }
  301.     }
  302.    
  303.     /**
  304.      * Проверяет, существует ли конструктор в классе
  305.      *
  306.      * @param clazz класс, в котором нужно искать конструктор
  307.      * @param args  аргументы
  308.      * @return true - если конструктор существует
  309.      */
  310.     public static <T> boolean isConstructorExist(Class<T> clazz, Class... args) {
  311.         return findConstructor(clazz, args) != null;
  312.     }
  313.    
  314.     /**
  315.      * Проверяет, существует ли метод в классе
  316.      *
  317.      * @param clazz  класс, в котором нужно искать метод
  318.      * @param method имя метода
  319.      * @param args   аргументы
  320.      * @return true - если метод существует
  321.      */
  322.     public static <T> boolean isMethodExist(Class<T> clazz, String method, Class... args) {
  323.         return findMethod(clazz, method, args) != null;
  324.     }
  325.    
  326.     /**
  327.      * Проверяет, существует ли поле в классе
  328.      *
  329.      * @param clazz класс, в котором нужно искать метод
  330.      * @param field имя поля
  331.      * @return true - если поле существует
  332.      */
  333.     public static <T> boolean isFieldExist(Class<T> clazz, String field) {
  334.         return findField(clazz, field) != null;
  335.     }
  336.    
  337.     /**
  338.      * Ищет конструктор в классе
  339.      *
  340.      * @param clazz класс, содержащий метод
  341.      * @param args  типы аргументов конструктора
  342.      * @return метод, который сразу можно вызывать
  343.      */
  344.     public static <T> Constructor<T> findConstructor(Class<T> clazz, Class... args) {
  345.         try {
  346.             return getClass(clazz).findConstructor0(args);
  347.         } catch (Exception ignored) {
  348.             return null;
  349.         }
  350.     }
  351.    
  352.     /**
  353.      * Ищет метод в классе
  354.      *
  355.      * @param clazz  класс, содержащий метод
  356.      * @param method имя метода
  357.      * @param args   типы аргументов метода
  358.      * @return метод, который сразу можно вызывать
  359.      */
  360.     public static <T> Method findMethod(Class<T> clazz, String method, Class... args) {
  361.         try {
  362.             return getClass(clazz).findMethod0(method, args);
  363.         } catch (Exception ignored) {
  364.             return null;
  365.         }
  366.     }
  367.    
  368.     /**
  369.      * Ищет поле в классе
  370.      *
  371.      * @param clazz класс, содержащий поле
  372.      * @param field имя поля
  373.      * @return поле, из которого сразу же можно получить данные
  374.      */
  375.     public static <T> Field findField(Class<T> clazz, String field) {
  376.         try {
  377.             return getClass(clazz).findField(field);
  378.         } catch (Exception ignored) {
  379.             return null;
  380.         }
  381.     }
  382.    
  383.     /**
  384.      * Ищет поле в классе. Сразу убирает с него флаг final.
  385.      *
  386.      * @param clazz класс, содержащий поле
  387.      * @param field имя поля
  388.      * @return поле с правами доступа и без final
  389.      */
  390.     public static <T> Field findFinalField(Class<T> clazz, String field) {
  391.         try {
  392.             return getClass(clazz).findFinalField(field);
  393.         } catch (Exception ignored) {
  394.             return null;
  395.         }
  396.     }
  397.    
  398.     /**
  399.      * Ищет класс по указанному полному названию не бросая исключения.
  400.      *
  401.      * @param name имя искомого класса
  402.      * @return класс, если найден, иначе - null
  403.      */
  404.     public static Class<?> findClass(String name) {
  405.         try {
  406.             return Class.forName(name);
  407.         } catch (ClassNotFoundException ignored) {
  408.             return null;
  409.         }
  410.     }
  411.    
  412.     /**
  413.      * Если будут использоваться перегруженные методы с одинаковым именем и одинаковым количеством аргументов, то
  414.      * необходимо установить значение true, иначе - false.<br/>
  415.      * При установленном значении true кэшироваться будут не только количество аргументов, а еще и их типы<br/>
  416.      * Замедляет вызов метода (0.002мс на каждый аргумент)
  417.      *
  418.      * @param clazz класс, в котором присутствуют перегруженные методы
  419.      * @param flag  true - включить агрессивную перегрузку, false - выключить
  420.      */
  421.     public static void setAggressiveMethodsOverloading(Class clazz, boolean flag) {
  422.         ClassData data = getClass(clazz);
  423.         if (data.aggressiveOverloading != flag) {
  424.             data.aggressiveOverloading = flag;
  425.             data.methods.clear();
  426.         }
  427.     }
  428.    
  429.     /**
  430.      * Возвращает MethodHandles.Lookup с максимальными правами. То есть будет игнорировать любые модификаторы доступа
  431.      */
  432.     public static MethodHandles.Lookup lookup() {
  433.         return get(MethodHandles.Lookup.class, "IMPL_LOOKUP");
  434.     }
  435.    
  436.     private static <T> ClassData<T> getClass(Class<T> clazz) {
  437.         ClassData<T> data = cache.get(clazz.getName());
  438.         if (data == null)
  439.             cache.put(clazz.getName(), data = new ClassData<>(clazz));
  440.         return data;
  441.     }
  442.    
  443.     //Для лёгкости переноса этого класса
  444.     private static void error(Throwable e, String message) {
  445.         Logger.getLogger("Reflect").log(Level.SEVERE, message, e);
  446.     }
  447.    
  448.     static class ClassData<K> {
  449.         private static Field FIELD_MODIFIERS = null;
  450.        
  451.         static {
  452.             try {
  453.                 FIELD_MODIFIERS = Field.class.getDeclaredField("modifiers");
  454.                 FIELD_MODIFIERS.setAccessible(true);
  455.             } catch (Exception ex) {
  456.                 error(ex, "Field modifiers field not found");
  457.             }
  458.         }
  459.        
  460.         private final Class<K> clazz;
  461.         private final Map<String, Field> fields = new HashMap<>();
  462.         private final Map<Object, Method> methods = new HashMap<>();
  463.         private final Map<Object, Constructor<K>> constructors = new HashMap<>();
  464.        
  465.         boolean aggressiveOverloading = false;
  466.        
  467.         public ClassData(Class<K> clazz) {
  468.             this.clazz = clazz;
  469.         }
  470.        
  471.         void set(Object instance, String field, Object value) throws Exception {
  472.             this.findField(field).set(instance, value);
  473.         }
  474.        
  475.         void setFinal(Object instance, String field, Object value) throws Exception {
  476.             findFinalField(field).set(instance, value);
  477.         }
  478.        
  479.         Object get(Object instance, String field) throws Exception {
  480.             return this.findField(field).get(instance);
  481.         }
  482.        
  483.         Object invoke(Object instance, String method, Object... args) throws Throwable {
  484.             return this.findMethod(method, args).invoke(instance, args);
  485.         }
  486.        
  487.         K construct(Object... args) throws Exception {
  488.             return this.findConstructor(args).newInstance(args);
  489.         }
  490.        
  491.         /**
  492.          * Поиск конструктора с заданными аргументами
  493.          *
  494.          * @param args аргументы
  495.          * @return конструктор или null, если таковой не найден
  496.          */
  497.         Constructor<K> findConstructor(Object... args) {
  498.             return findConstructor0(toTypes(args));
  499.         }
  500.        
  501.         /**
  502.          * Поиск конструктора с заданными типами аргументов
  503.          *
  504.          * @param types типы аргументов
  505.          * @return конструктор или null, если таковой не найден
  506.          */
  507.         Constructor<K> findConstructor0(Class... types) {
  508.             Object mapped = new ConstructorMapKey(types);
  509.             Constructor<K> con = constructors.get(mapped);
  510.             if (con == null) {
  511.                 constructorsLoop:
  512.                 for (Constructor c : clazz.getDeclaredConstructors()) {
  513.                     Class<?>[] ptypes = c.getParameterTypes();
  514.                     if (ptypes.length != types.length)
  515.                         continue;
  516.                    
  517.                     for (int i = 0; i < ptypes.length; i++)
  518.                         if (types[i] != null && ptypes[i] != types[i] && !ptypes[i].isAssignableFrom(types[i]))
  519.                             continue constructorsLoop;
  520.                    
  521.                     con = c;
  522.                     con.setAccessible(true);
  523.                     constructors.put(mapped, con);
  524.                     break;
  525.                 }
  526.                
  527.                 if (con == null)
  528.                     throw new UnableToFindConstructorException(clazz, types);
  529.             }
  530.             return con;
  531.         }
  532.        
  533.         /**
  534.          * Рекурсивный поиск метода в классе и всех суперклассам данного класса
  535.          *
  536.          * @param name имя метода
  537.          * @param args аргументы метода
  538.          * @return метод или null, если он не найден
  539.          */
  540.         Method findMethod(String name, Object... args) {
  541.             Class[] types = null;
  542.             Object mapped;
  543.             if (aggressiveOverloading) {
  544.                 types = toTypes(args);
  545.                 mapped = new AggressiveMethodMapKey(name, types);
  546.             } else {
  547.                 mapped = new MethodMapKey(name, args.length);
  548.             }
  549.            
  550.             Method method = methods.get(mapped);
  551.             if (method == null) {
  552.                 if (types == null)
  553.                     types = toTypes(args);
  554.                
  555.                 method = fastFindMethod(name, types);
  556.                
  557.                 if (method == null)
  558.                     throw new UnableToFindMethodException(clazz, name, types);
  559.                 else
  560.                     methods.put(mapped, method);
  561.             }
  562.             return method;
  563.         }
  564.        
  565.         /**
  566.          * Рекурсивный поиск метода в классе и всех суперклассам данного класса
  567.          *
  568.          * @param name  имя метода
  569.          * @param types типы аргументов метода
  570.          * @return метод или null, если он не найден
  571.          */
  572.         Method findMethod0(String name, Class... types) {
  573.             Object mapped;
  574.             if (aggressiveOverloading)
  575.                 mapped = new AggressiveMethodMapKey(name, types);
  576.             else
  577.                 mapped = new MethodMapKey(name, types.length);
  578.            
  579.             Method method = methods.get(mapped);
  580.             if (method == null) {
  581.                 method = fastFindMethod(name, types);
  582.                 if (method == null)
  583.                     throw new UnableToFindMethodException(clazz, name, types);
  584.                 else
  585.                     methods.put(mapped, method);
  586.             }
  587.             return method;
  588.         }
  589.        
  590.         /**
  591.          * Выполняет рекурсивный поиск метода в классе и всех суперклассам данного класса.
  592.          * Увеличение производительности обеспечивается за счет упрощенного сравнения аргументов
  593.          * метода.
  594.          *
  595.          * @param name  имя метода
  596.          * @param types типы аргументов, которые принимает данный метод
  597.          * @return метод или null, если он не найден
  598.          */
  599.         @SuppressWarnings("StringEquality")
  600.         private Method fastFindMethod(String name, Class... types) {
  601.             Method method = null;
  602.             name = name.intern();
  603.             Class clazz0 = clazz;
  604.             do {
  605.                 methodsLoop:
  606.                 for (Method m : clazz0.getDeclaredMethods()) {
  607.                     if (name != m.getName())
  608.                         continue;
  609.                    
  610.                     Class<?>[] ptypes = m.getParameterTypes();
  611.                     if (ptypes.length != types.length)
  612.                         continue;
  613.                    
  614.                     for (int i = 0; i < ptypes.length; i++)
  615.                         if (types[i] != null && ptypes[i] != types[i] && !ptypes[i].isAssignableFrom(types[i]))
  616.                             continue methodsLoop;
  617.                    
  618.                     method = m;
  619.                     break;
  620.                 }
  621.                 if (method != null) {
  622.                     method.setAccessible(true);
  623.                     break;
  624.                 }
  625.                 clazz0 = clazz0.getSuperclass();
  626.             } while (clazz0 != null);
  627.             return method;
  628.         }
  629.        
  630.         /**
  631.          * Рекурсивный поиск поля по классу и всем суперклассам данного класса<br/>
  632.          * Если поле находится, то у него убирается флаг final
  633.          *
  634.          * @param name имя поля
  635.          * @return поле или null, если оно не найдено
  636.          */
  637.         Field findFinalField(String name) throws Exception {
  638.             Field field = findField(name);
  639.             FIELD_MODIFIERS.set(field, field.getModifiers() & ~Modifier.FINAL);
  640.             return field;
  641.         }
  642.        
  643.         /**
  644.          * Рекурсивный поиск поля по классу и всем суперклассам данного класса
  645.          *
  646.          * @param name имя поля
  647.          * @return поле или null, если оно не найдено
  648.          */
  649.         Field findField(String name) {
  650.             Field field = fields.get(name);
  651.             if (field == null) {
  652.                 Class clazz0 = clazz;
  653.                 while (clazz0 != null) {
  654.                     try {
  655.                         field = clazz0.getDeclaredField(name);
  656.                         field.setAccessible(true);
  657.                         fields.put(name, field);
  658.                         break;
  659.                     } catch (Exception e) {
  660.                         clazz0 = clazz0.getSuperclass();
  661.                     }
  662.                 }
  663.                 if (field == null)
  664.                     throw new UnableToFindFieldException(clazz, name);
  665.             }
  666.             return field;
  667.         }
  668.        
  669.         /**
  670.          * Преобразует объекты в их классы
  671.          *
  672.          * @param objects объекты
  673.          * @return классы объектов
  674.          */
  675.         private Class[] toTypes(Object[] objects) {
  676.             if (objects.length == 0)
  677.                 return new Class[0];
  678.            
  679.             Class[] types = new Class[objects.length];
  680.             for (int i = 0; i < objects.length; i++) {
  681.                 if (objects[i] == null) {
  682.                     types[i] = null;
  683.                     continue;
  684.                 }
  685.                 Class type = objects[i].getClass();
  686.                 if (type == Integer.class)
  687.                     type = Integer.TYPE;
  688.                 else if (type == Double.class)
  689.                     type = Double.TYPE;
  690.                 else if (type == Boolean.class)
  691.                     type = Boolean.TYPE;
  692.                 else if (type == Float.class)
  693.                     type = Float.TYPE;
  694.                 else if (type == Long.class)
  695.                     type = Long.TYPE;
  696.                 else if (type == Character.class)
  697.                     type = Character.TYPE;
  698.                 else if (type == Byte.class)
  699.                     type = Byte.TYPE;
  700.                 else if (type == Short.class)
  701.                     type = Short.TYPE;
  702.                 types[i] = type;
  703.             }
  704.             return types;
  705.         }
  706.     }
  707.    
  708.     static class ConstructorMapKey {
  709.         Class[] types;
  710.        
  711.         public ConstructorMapKey(Class[] types) {
  712.             this.types = types;
  713.         }
  714.        
  715.         @Override
  716.         public int hashCode() {
  717.             return Arrays.hashCode(types);
  718.         }
  719.        
  720.         @Override
  721.         public boolean equals(Object obj) {
  722.             if (!(obj instanceof AggressiveMethodMapKey))
  723.                 return false;
  724.             AggressiveMethodMapKey other = (AggressiveMethodMapKey) obj;
  725.             if (types.length != other.types.length)
  726.                 return false;
  727.             for (int i = 0; i < types.length; i++)
  728.                 if (types[i] != other.types[i])
  729.                     return false;
  730.             return true;
  731.         }
  732.     }
  733.    
  734.     static class MethodMapKey {
  735.         String name;
  736.         int args;
  737.        
  738.         public MethodMapKey(String name, int args) {
  739.             this.name = name;
  740.             this.args = args;
  741.         }
  742.        
  743.         @Override
  744.         public int hashCode() {
  745.             return name.hashCode() + args;
  746.         }
  747.        
  748.         @Override
  749.         public boolean equals(Object obj) {
  750.             if (!(obj instanceof MethodMapKey))
  751.                 return false;
  752.             MethodMapKey other = (MethodMapKey) obj;
  753.             return other.args == args && other.name.equals(name);
  754.         }
  755.     }
  756.    
  757.     static class AggressiveMethodMapKey {
  758.         Class[] types;
  759.         String name;
  760.        
  761.         public AggressiveMethodMapKey(String name, Class[] types) {
  762.             this.name = name;
  763.             this.types = types;
  764.         }
  765.        
  766.         @Override
  767.         public int hashCode() {
  768.             int hash = name.hashCode();
  769.             hash = 31 * hash + Arrays.hashCode(types);
  770.             return hash;
  771.         }
  772.        
  773.         @Override
  774.         public boolean equals(Object obj) {
  775.             if (!(obj instanceof AggressiveMethodMapKey))
  776.                 return false;
  777.             AggressiveMethodMapKey other = (AggressiveMethodMapKey) obj;
  778.             if (types.length != other.types.length ||
  779.                 !other.name.equals(name))
  780.                 return false;
  781.             for (int i = 0; i < types.length; i++)
  782.                 if (types[i] != other.types[i])
  783.                     return false;
  784.             return true;
  785.         }
  786.     }
  787.    
  788.     /**
  789.      * Преобразует массив классов в строку, почти как дескриптор в байткоде
  790.      */
  791.     private static String classesToString(Class[] classes) {
  792.         int iMax = classes.length - 1;
  793.         if (iMax == -1)
  794.             return "()";
  795.        
  796.         StringBuilder b = new StringBuilder();
  797.         b.append('(');
  798.         for (int i = 0; ; i++) {
  799.             b.append(classes[i].getName());
  800.             if (i == iMax)
  801.                 return b.append(')').toString();
  802.             b.append(',');
  803.         }
  804.     }
  805.    
  806.     private static class UnableToFindFieldException extends RuntimeException {
  807.         private String fieldName;
  808.         private String className;
  809.        
  810.         public UnableToFindFieldException(Class clazz, String fieldName) {
  811.             super();
  812.             this.fieldName = fieldName;
  813.             this.className = clazz.getName();
  814.         }
  815.        
  816.         @Override
  817.         public String getMessage() {
  818.             return toString();
  819.         }
  820.        
  821.         @Override
  822.         public String toString() {
  823.             return "Unable to find field '" + fieldName + "' in class '" + className + "'";
  824.         }
  825.     }
  826.    
  827.     private static class UnableToFindMethodException extends RuntimeException {
  828.         protected String methodName;
  829.         protected String className;
  830.         protected Class[] types;
  831.        
  832.         public UnableToFindMethodException(Class clazz, String methodName, Class[] types) {
  833.             super();
  834.             this.methodName = methodName;
  835.             this.className = clazz.getName();
  836.             this.types = types;
  837.         }
  838.        
  839.         @Override
  840.         public String getMessage() {
  841.             return toString();
  842.         }
  843.        
  844.         @Override
  845.         public String toString() {
  846.             return "Unable to find method '" + className + "." + methodName + classesToString(types) + "'";
  847.         }
  848.     }
  849.    
  850.     private static class UnableToFindConstructorException extends UnableToFindMethodException {
  851.         public UnableToFindConstructorException(Class clazz, Class[] types) {
  852.             super(clazz, null, types);
  853.         }
  854.        
  855.         @Override
  856.         public String toString() {
  857.             return "Unable to find constructor '" + className + ".<init>" + classesToString(types) + "'";
  858.         }
  859.     }
  860. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement