diff --git a/src/main/java/pw/yumc/YumCore/callback/CallBack.java b/src/main/java/pw/yumc/YumCore/callback/CallBack.java new file mode 100644 index 0000000..f58e82f --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/callback/CallBack.java @@ -0,0 +1,57 @@ +package pw.yumc.YumCore.callback; + +/** + * 回调类 + * Created by 蒋天蓓 on 2016/6/16 0016. + */ +public abstract class CallBack { + /** + * 多个参数回调 + */ + public static abstract class Any { + public abstract void run(Object... param); + } + + /** + * 无参数回调 + */ + public static abstract class None { + public abstract void run(); + } + + /** + * 一个参数回调 + * + * @param + * 参数 + */ + public static abstract class One { + public abstract void run(T param); + } + + /** + * 三个参数回调 + * + * @param + * 参数 + * @param + * 参数 + * @param + * 参数 + */ + public static abstract class Three { + public abstract void run(B param1, A param2, T param3); + } + + /** + * 二个参数回调 + * + * @param + * 参数 + * @param + * 参数 + */ + public static abstract class Two { + public abstract void run(T param1, M param2); + } +} diff --git a/src/main/java/pw/yumc/YumCore/callback/CallBackReturn.java b/src/main/java/pw/yumc/YumCore/callback/CallBackReturn.java new file mode 100644 index 0000000..a301030 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/callback/CallBackReturn.java @@ -0,0 +1,29 @@ +package pw.yumc.YumCore.callback; + +/** + * 带参数回调类 + * Created by 蒋天蓓 on 2016/6/16 0016. + */ +public abstract class CallBackReturn { + /** + * 无参数回调 + * + * @param + * 返回值 + */ + public static abstract class None { + public abstract OUT run(); + } + + /** + * 一个参数回调 + * + * @param + * 参数 + * @param + * 返回值 + */ + public static abstract class One { + public abstract OUT run(IN param); + } +} diff --git a/src/main/java/pw/yumc/YumCore/kit/ExKit.java b/src/main/java/pw/yumc/YumCore/kit/ExKit.java new file mode 100644 index 0000000..2b9e209 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/kit/ExKit.java @@ -0,0 +1,34 @@ +package pw.yumc.YumCore.kit; + +/** + * 异常处理工具 + * + * @since 2016年3月30日 上午10:59:09 + * @author 喵♂呜 + */ +public class ExKit { + /** + * 任意抛出异常 + * + * @param exception + * 异常 + */ + public static void throwException(final Throwable exception) { + ExKit. throwExceptionT(exception); + } + + /** + * 抛出异常 + * + * @param + * 异常 + * @param exception + * 异常 + * @throws T + * 异常 + */ + @SuppressWarnings("unchecked") + private static void throwExceptionT(final Throwable exception) throws T { + throw (T) exception; + } +} diff --git a/src/main/java/pw/yumc/YumCore/kit/FileKit.java b/src/main/java/pw/yumc/YumCore/kit/FileKit.java new file mode 100644 index 0000000..75df22d --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/kit/FileKit.java @@ -0,0 +1,42 @@ +package pw.yumc.YumCore.kit; + +import java.io.File; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +/** + * @author 蒋天蓓 + * + */ +public class FileKit { + /** + * 递归删除目录下的所有文件及子目录下所有文件 + * + * @param sender + * 执行者 + * @param dir + * 将要删除的文件目录 + * @return 是否删除成功. + */ + public static boolean deleteDir(final CommandSender sender, final File dir) { + if (dir.isDirectory()) { + final String[] children = dir.list(); + // 递归删除目录中的子目录下 + for (final String element : children) { + final File file = new File(dir, element); + if (!deleteDir(file)) { + sender.sendMessage("§c删除: §e" + file.getAbsolutePath() + " §c时 发生错误!"); + } else { + sender.sendMessage("§c删除: §e" + file.getAbsolutePath() + " §a成功!"); + } + } + } + // 目录此时为空,可以删除 + return dir.delete(); + } + + public static boolean deleteDir(final File dir) { + return deleteDir(Bukkit.getConsoleSender(), dir); + } +} diff --git a/src/main/java/pw/yumc/YumCore/kit/LogKit.java b/src/main/java/pw/yumc/YumCore/kit/LogKit.java new file mode 100644 index 0000000..f35504d --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/kit/LogKit.java @@ -0,0 +1,110 @@ +package pw.yumc.YumCore.kit; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +import pw.yumc.YumCore.bukkit.Log; +import pw.yumc.YumCore.bukkit.P; + +public class LogKit implements Runnable { + private final static Plugin plugin = P.instance; + private final static File dataFolder = plugin.getDataFolder(); + + private PrintStream ps; + private final List logs = new ArrayList<>(5); + + public LogKit(final File log) { + try { + if (!log.exists()) { + log.createNewFile(); + } + final FileOutputStream fos = new FileOutputStream(log, true); + this.ps = new PrintStream(fos, true, "UTF-8"); + Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this, 0, 100); + } catch (final FileNotFoundException e) { + Log.debug(e); + Log.w("日志文件未找到 %s !", e.getMessage()); + } catch (final IOException e) { + Log.debug(e); + Log.w("无法创建日志文件 %s !", e.getMessage()); + } + + } + + public LogKit(final String name) { + this(new File(dataFolder, name)); + } + + /** + * 关闭日志并保存 + */ + public void close() { + this.ps.close(); + } + + /** + * 添加日志 + * + * @param s + * 日志 + */ + public void log(final String s) { + synchronized (logs) { + logs.add(new Date().toLocaleString() + s); + } + } + + /** + * 添加日志 + * + * @param s + * 日志 + */ + public void logConsole(final String s) { + Log.info(s); + log(s); + } + + /** + * 添加日志 + * + * @param s + * 日志 + */ + public void logSender(final CommandSender sender, final String s) { + sender.sendMessage(s); + log(ChatColor.stripColor(s)); + } + + /** + * 添加日志 + * + * @param s + * 日志 + */ + public void logSender(final String s) { + logSender(Bukkit.getConsoleSender(), s); + } + + @Override + public void run() { + synchronized (logs) { + for (final String s : logs) { + ps.println(s); + } + ps.flush(); + logs.clear(); + } + } +} \ No newline at end of file diff --git a/src/main/java/pw/yumc/YumCore/plugin/protocollib/PacketKit.java b/src/main/java/pw/yumc/YumCore/plugin/protocollib/PacketKit.java new file mode 100644 index 0000000..c2905b7 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/plugin/protocollib/PacketKit.java @@ -0,0 +1,61 @@ +package pw.yumc.YumCore.plugin.protocollib; + +import java.lang.reflect.InvocationTargetException; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; + +import pw.yumc.YumCore.kit.PKit; + +/** + * ProtocolLib发包工具 + * + * @since 2016年7月7日 上午10:03:00 + * @author 喵♂呜 + */ +public class PacketKit { + public static boolean ENABLE = false;; + private static ProtocolManager manager; + + static { + if (!Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) { + PKit.disable("未找到 ProtocolLib 插件 部分功能不可用..."); + } else { + ENABLE = true; + manager = ProtocolLibrary.getProtocolManager(); + } + } + + /** + * 给玩家发送心跳包 + * + * @param player + * 玩家 + * @throws InvocationTargetException + */ + public static void keep_live(final Player player) throws InvocationTargetException { + if (ENABLE) { + send(player, manager.createPacket(PacketType.Play.Client.KEEP_ALIVE)); + } + } + + /** + * 发包 + * + * @param player + * 玩家 + * @param packet + * 数据包 + * @throws InvocationTargetException + */ + public static void send(final Player player, final PacketContainer packet) throws InvocationTargetException { + if (ENABLE) { + manager.sendServerPacket(player, packet); + } + } +} diff --git a/src/main/java/pw/yumc/YumCore/reflect/Reflect.java b/src/main/java/pw/yumc/YumCore/reflect/Reflect.java new file mode 100644 index 0000000..1a67ed5 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/reflect/Reflect.java @@ -0,0 +1,761 @@ +/** + * Copyright (c) 2011-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOR" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package pw.yumc.YumCore.reflect; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A wrapper for an {@link Object} or {@link Class} upon which reflective calls can be made. + *

+ * An example of using Reflect is + * + *

+ * // Static import all reflection methods to decrease verbosity
+ * import static org.joor.Reflect.*;
+ *
+ * // Wrap an Object / Class / class name with the on() method:
+ * on("java.lang.String")
+ * // Invoke constructors using the create() method:
+ * .create("Hello World")
+ * // Invoke methods using the call() method:
+ * .call("toString")
+ * // Retrieve the wrapped object
+ *
+ * @author Lukas Eder
+ */
+public class Reflect {
+
+    // ---------------------------------------------------------------------
+    // Static API used as entrance points to the fluent API
+    // ---------------------------------------------------------------------
+
+    /**
+     * The wrapped object
+     */
+    private final Object object;
+
+    /**
+     * A flag indicating whether the wrapped object is a {@link Class} (for accessing static fields and methods), or any
+     * other type of {@link Object} (for accessing instance fields and methods).
+     */
+    private final boolean isClass;
+
+    private Reflect(final Class type) {
+        this.object = type;
+        this.isClass = true;
+    }
+
+    private Reflect(final Object object) {
+        this.object = object;
+        this.isClass = false;
+    }
+
+    // ---------------------------------------------------------------------
+    // Members
+    // ---------------------------------------------------------------------
+
+    /**
+     * Conveniently render an {@link AccessibleObject} accessible
+     *
+     * @param 
+     *            对象类型
+     * @param accessible
+     *            The object to render accessible
+     * @return The argument object rendered accessible
+     */
+    public static  T accessible(final T accessible) {
+        if (accessible == null) {
+            return null;
+        }
+
+        if (!accessible.isAccessible()) {
+            accessible.setAccessible(true);
+        }
+
+        return accessible;
+    }
+
+    /**
+     *
+     * 获得公共字段
+     *
+     * @param clazz
+     *            类名
+     * @param name
+     *            字段名
+     * @return 字段{@link Field}
+     * @throws NoSuchFieldException
+     */
+    public static Field getDeclaredField(Class clazz, final String name) throws NoSuchFieldException {
+        Field field = null;
+        while (clazz != Object.class) {
+            try {
+                field = clazz.getDeclaredField(name);
+                if (field != null) {
+                    break;
+                }
+            } catch (final Exception e) {
+                clazz = clazz.getSuperclass();
+            }
+        }
+        if (field == null) {
+            throw new NoSuchFieldException("name is not found");
+        }
+        return field;
+    }
+
+    // ---------------------------------------------------------------------
+    // Constructors
+    // ---------------------------------------------------------------------
+
+    /**
+     * Wrap a class.
+     * 

+ * Use this when you want to access static fields and methods on a {@link Class} object, or as a basis for + * constructing objects of that class using {@link #create(Object...)} + * + * @param clazz + * The class to be wrapped + * @return A wrapped class object, to be used for further reflection. + */ + public static Reflect on(final Class clazz) { + return new Reflect(clazz); + } + + /** + * Wrap an object. + *

+ * Use this when you want to access instance fields and methods on any {@link Object} + * + * @param object + * The object to be wrapped + * @return A wrapped object, to be used for further reflection. + */ + public static Reflect on(final Object object) { + return new Reflect(object); + } + + // --------------------------------------------------------------------- + // Fluent Reflection API + // --------------------------------------------------------------------- + + /** + * Wrap a class name. + *

+ * This is the same as calling on(Class.forName(name)) + * + * @param name + * A fully qualified class name + * @return A wrapped class object, to be used for further reflection. + * @throws ReflectException + * If any reflection exception occurred. + * @see #on(Class) + */ + public static Reflect on(final String name) throws ReflectException { + return on(forName(name)); + } + + /** + * Get a wrapper type for a primitive type, or the argument type itself, if it is not a primitive type. + */ + public static Class wrapper(final Class type) { + if (type == null) { + return null; + } else if (type.isPrimitive()) { + if (boolean.class == type) { + return Boolean.class; + } else if (int.class == type) { + return Integer.class; + } else if (long.class == type) { + return Long.class; + } else if (short.class == type) { + return Short.class; + } else if (byte.class == type) { + return Byte.class; + } else if (double.class == type) { + return Double.class; + } else if (float.class == type) { + return Float.class; + } else if (char.class == type) { + return Character.class; + } else if (void.class == type) { + return Void.class; + } + } + + return type; + } + + /** + * Load a class + * + * @see Class#forName(String) + */ + private static Class forName(final String name) throws ReflectException { + try { + return Class.forName(name); + } catch (final Exception e) { + throw new ReflectException(e); + } + } + + /** + * Wrap an object created from a constructor + */ + private static Reflect on(final Constructor constructor, final Object... args) throws ReflectException { + try { + return on(accessible(constructor).newInstance(args)); + } catch (final Exception e) { + throw new ReflectException(e); + } + } + + /** + * Wrap an object returned from a method + */ + private static Reflect on(final Method method, final Object object, final Object... args) throws ReflectException { + try { + accessible(method); + + if (method.getReturnType() == void.class) { + method.invoke(object, args); + return on(object); + } + return on(method.invoke(object, args)); + } catch (final Exception e) { + throw new ReflectException(e); + } + } + + /** + * Get the POJO property name of an getter/setter + */ + private static String property(final String string) { + final int length = string.length(); + + if (length == 0) { + return ""; + } else if (length == 1) { + return string.toLowerCase(); + } else { + return string.substring(0, 1).toLowerCase() + string.substring(1); + } + } + + /** + * Get an array of types for an array of objects + * + * @see Object#getClass() + */ + private static Class[] types(final Object... values) { + if (values == null) { + return new Class[0]; + } + + final Class[] result = new Class[values.length]; + + for (int i = 0; i < values.length; i++) { + final Object value = values[i]; + result[i] = value == null ? Object.class : value.getClass(); + } + + return result; + } + + /** + * Unwrap an object + */ + private static Object unwrap(final Object object) { + if (object instanceof Reflect) { + return ((Reflect) object).get(); + } + + return object; + } + + /** + * Create a proxy for the wrapped object allowing to typesafely invoke methods on it using a custom interface + * + * @param proxyType + * The interface type that is implemented by the proxy + * @return A proxy for the wrapped object + */ + @SuppressWarnings("unchecked") + public

P as(final Class

proxyType) { + final boolean isMap = (object instanceof Map); + final InvocationHandler handler = new InvocationHandler() { + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { + final String name = method.getName(); + + // Actual method name matches always come first + try { + return on(object).call(name, args).get(); + } + + // [#14] Simulate POJO behaviour on wrapped map objects + catch (final ReflectException e) { + if (isMap) { + final Map map = (Map) object; + final int length = (args == null ? 0 : args.length); + + if (length == 0 && name.startsWith("get")) { + return map.get(property(name.substring(3))); + } else if (length == 0 && name.startsWith("is")) { + return map.get(property(name.substring(2))); + } else if (length == 1 && name.startsWith("set")) { + map.put(property(name.substring(3)), args[0]); + return null; + } + } + + throw e; + } + } + }; + + return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[] { proxyType }, handler); + } + + /** + * Call a method by its name. + *

+ * This is a convenience method for calling call(name, new Object[0]) + * + * @param name + * The method name + * @return The wrapped method result or the same wrapped object if the method returns void, to be used + * for further reflection. + * @throws ReflectException + * If any reflection exception occurred. + * @see #call(String, Object...) + */ + public Reflect call(final String name) throws ReflectException { + return call(name, new Object[0]); + } + + /** + * Call a method by its name. + *

+ * This is roughly equivalent to {@link Method#invoke(Object, Object...)}. If the wrapped object is a {@link Class}, + * then this will invoke a static method. If the wrapped object is any other {@link Object}, then this will invoke + * an instance method. + *

+ * Just like {@link Method#invoke(Object, Object...)}, this will try to wrap primitive types or unwrap primitive + * type wrappers if applicable. If several methods are applicable, by that rule, the first one encountered is + * called. i.e. when calling + * + *

+     *  on(...).call("method", 1, 1);
+     * 
+ * + * The first of the following methods will be called: + * + *
+     * 
+     *  public void method(int param1, Integer param2);
+     *  public void method(Integer param1, int param2);
+     *  public void method(Number param1, Number param2);
+     *  public void method(Number param1, Object param2);
+     *  public void method(int param1, Object param2);
+     * 
+     * 
+ * + *

+ * The best matching method is searched for with the following strategy: + *

    + *
  1. public method with exact signature match in class hierarchy
  2. + *
  3. non-public method with exact signature match on declaring class
  4. + *
  5. public method with similar signature in class hierarchy
  6. + *
  7. non-public method with similar signature on declaring class
  8. + *
+ * + * @param name + * The method name + * @param args + * The method arguments + * @return The wrapped method result or the same wrapped object if the method returns void, to be used + * for further reflection. + * @throws ReflectException + * If any reflection exception occurred. + */ + public Reflect call(final String name, final Object... args) throws ReflectException { + final Class[] types = types(args); + + // Try invoking the "canonical" method, i.e. the one with exact + // matching argument types + try { + final Method method = exactMethod(name, types); + return on(method, object, args); + } + + // If there is no exact match, try to find a method that has a "similar" + // signature if primitive argument types are converted to their wrappers + catch (final NoSuchMethodException e) { + try { + final Method method = similarMethod(name, types); + return on(method, object, args); + } catch (final NoSuchMethodException e1) { + throw new ReflectException(e1); + } + } + } + + /** + * Call a constructor. + *

+ * This is a convenience method for calling create(new Object[0]) + * + * @return The wrapped new object, to be used for further reflection. + * @throws ReflectException + * If any reflection exception occurred. + * @see #create(Object...) + */ + public Reflect create() throws ReflectException { + return create(new Object[0]); + } + + /** + * Call a constructor. + *

+ * This is roughly equivalent to {@link Constructor#newInstance(Object...)}. If the wrapped object is a + * {@link Class}, then this will create a new object of that class. If the wrapped object is any other + * {@link Object}, then this will create a new object of the same type. + *

+ * Just like {@link Constructor#newInstance(Object...)}, this will try to wrap primitive types or unwrap primitive + * type wrappers if applicable. If several constructors are applicable, by that rule, the first one encountered is + * called. i.e. when calling + * + *

+     *  
+     *  on(C.class).create(1, 1);
+     *  
+     * 
+ * + * The first of the following constructors will be applied: + * + *
+     * 
+     *  public C(int param1, Integer param2);
+     *  public C(Integer param1, int param2);
+     *  public C(Number param1, Number param2);
+     *  public C(Number param1, Object param2);
+     *  public C(int param1, Object param2);
+     * 
+     * 
+ * + * @param args + * The constructor arguments + * @return The wrapped new object, to be used for further reflection. + * @throws ReflectException + * If any reflection exception occurred. + */ + public Reflect create(final Object... args) throws ReflectException { + final Class[] types = types(args); + + // Try invoking the "canonical" constructor, i.e. the one with exact + // matching argument types + try { + final Constructor constructor = type().getDeclaredConstructor(types); + return on(constructor, args); + } + + // If there is no exact match, try to find one that has a "similar" + // signature if primitive argument types are converted to their wrappers + catch (final NoSuchMethodException e) { + for (final Constructor constructor : type().getConstructors()) { + if (match(constructor.getParameterTypes(), types)) { + return on(constructor, args); + } + } + + throw new ReflectException(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(final Object obj) { + if (obj instanceof Reflect) { + return object.equals(((Reflect) obj).get()); + } + + return false; + } + + /** + * Get a wrapped field. + *

+ * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped object is a {@link Class}, then this will + * wrap a static member field. If the wrapped object is any other {@link Object}, then this wrap an instance member + * field. + * + * @param name + * The field name + * @return The wrapped field + * @throws ReflectException + * If any reflection exception occurred. + */ + public Reflect field(final String name) throws ReflectException { + try { + + // Try getting a public field + final Field field = type().getField(name); + return on(field.get(object)); + } catch (final Exception e1) { + + // Try again, getting a non-public field + try { + return on(accessible(getDeclaredField(type(), name)).get(object)); + } catch (final Exception e2) { + throw new ReflectException(e2); + } + } + } + + // --------------------------------------------------------------------- + // Object API + // --------------------------------------------------------------------- + + /** + * Get a Map containing field names and wrapped values for the fields' values. + *

+ * If the wrapped object is a {@link Class}, then this will return static fields. If the wrapped object is any other + * {@link Object}, then this will return instance fields. + *

+ * These two calls are equivalent + * + *

+     * 
+     *  on(object).field("myField");
+     *  on(object).fields().get("myField");
+     * 
+     * 
+ * + * @return A map containing field names and wrapped values. + */ + public Map fields() { + final Map result = new LinkedHashMap(); + + for (final Field field : type().getFields()) { + if (!isClass ^ Modifier.isStatic(field.getModifiers())) { + final String name = field.getName(); + result.put(name, field(name)); + } + } + return result; + } + + /** + * Get the wrapped object + * + * @param + * A convenience generic parameter for automatic unsafe casting + */ + @SuppressWarnings("unchecked") + public T get() { + return (T) object; + } + + /** + * Get a field value. + *

+ * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped object is a {@link Class}, then this will + * get a value from a static member field. If the wrapped object is any other {@link Object}, then this will get a + * value from an instance member field. + *

+ * If you want to "navigate" to a wrapped version of the field, use {@link #field(String)} instead. + * + * @param name + * The field name + * @return The field value + * @throws ReflectException + * If any reflection exception occurred. + * @see #field(String) + */ + public T get(final String name) throws ReflectException { + return field(name). get(); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return object.hashCode(); + } + + // --------------------------------------------------------------------- + // Utility methods + // --------------------------------------------------------------------- + + /** + * Set a field value. + *

+ * This is roughly equivalent to {@link Field#set(Object, Object)}. If the wrapped object is a {@link Class}, then + * this will set a value to a static member field. If the wrapped object is any other {@link Object}, then this will + * set a value to an instance member field. + * + * @param name + * The field name + * @param value + * The new field value + * @return The same wrapped object, to be used for further reflection. + * @throws ReflectException + * If any reflection exception occurred. + */ + public Reflect set(final String name, final Object value) throws ReflectException { + try { + + // Try setting a public field + final Field field = type().getField(name); + field.set(object, unwrap(value)); + return this; + } catch (final Exception e1) { + + // Try again, setting a non-public field + try { + accessible(type().getDeclaredField(name)).set(object, unwrap(value)); + return this; + } catch (final Exception e2) { + throw new ReflectException(e2); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return object.toString(); + } + + /** + * Get the type of the wrapped object. + * + * @see Object#getClass() + */ + public Class type() { + if (isClass) { + return (Class) object; + } + return object.getClass(); + } + + /** + * Searches a method with the exact same signature as desired. + *

+ * If a public method is found in the class hierarchy, this method is returned. Otherwise a private method with the + * exact same signature is returned. If no exact match could be found, we let the {@code NoSuchMethodException} pass + * through. + */ + private Method exactMethod(final String name, final Class[] types) throws NoSuchMethodException { + final Class type = type(); + + // first priority: find a public method with exact signature match in class hierarchy + try { + return type.getMethod(name, types); + } + + // second priority: find a private method with exact signature match on declaring class + catch (final NoSuchMethodException e) { + return type.getDeclaredMethod(name, types); + } + } + + /** + * Determines if a method has a "similar" signature, especially if wrapping primitive argument types would result in + * an exactly matching signature. + */ + private boolean isSimilarSignature(final Method possiblyMatchingMethod, final String desiredMethodName, final Class[] desiredParamTypes) { + return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes); + } + + /** + * Check whether two arrays of types match, converting primitive types to their corresponding wrappers. + */ + private boolean match(final Class[] declaredTypes, final Class[] actualTypes) { + if (declaredTypes.length == actualTypes.length) { + for (int i = 0; i < actualTypes.length; i++) { + if (!wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) { + return false; + } + } + return true; + } + return false; + } + + /** + * Searches a method with a similar signature as desired using + * {@link #isSimilarSignature(java.lang.reflect.Method, String, Class[])}. + *

+ * First public methods are searched in the class hierarchy, then private methods on the declaring class. If a + * method could be found, it is returned, otherwise a {@code NoSuchMethodException} is thrown. + */ + private Method similarMethod(final String name, final Class[] types) throws NoSuchMethodException { + final Class type = type(); + + // first priority: find a public method with a "similar" signature in class hierarchy + // similar interpreted in when primitive argument types are converted to their wrappers + for (final Method method : type.getMethods()) { + if (isSimilarSignature(method, name, types)) { + return method; + } + } + + // second priority: find a non-public method with a "similar" signature on declaring class + for (final Method method : type.getDeclaredMethods()) { + if (isSimilarSignature(method, name, types)) { + return method; + } + } + + throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + "."); + } +} diff --git a/src/main/java/pw/yumc/YumCore/reflect/ReflectException.java b/src/main/java/pw/yumc/YumCore/reflect/ReflectException.java new file mode 100644 index 0000000..4f6dbc9 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/reflect/ReflectException.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2011-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOR" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package pw.yumc.YumCore.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** + * A unchecked wrapper for any of Java's checked reflection exceptions: + *

+ * These exceptions are + *

    + *
  • {@link ClassNotFoundException}
  • + *
  • {@link IllegalAccessException}
  • + *
  • {@link IllegalArgumentException}
  • + *
  • {@link InstantiationException}
  • + *
  • {@link InvocationTargetException}
  • + *
  • {@link NoSuchMethodException}
  • + *
  • {@link NoSuchFieldException}
  • + *
  • {@link SecurityException}
  • + *
+ * + * @author Lukas Eder + */ +public class ReflectException extends RuntimeException { + + /** + * Generated UID + */ + private static final long serialVersionUID = -6213149635297151442L; + + public ReflectException() { + super(); + } + + public ReflectException(final String message) { + super(message); + } + + public ReflectException(final String message, final Throwable cause) { + super(message, cause); + } + + public ReflectException(final Throwable cause) { + super(cause); + } +}