├── .gitignore
├── .github
├── FUNDING.yml
└── workflows
│ └── maven.yml
├── renovate.json
├── src
├── main
│ └── java
│ │ └── org
│ │ └── inventivetalent
│ │ └── reflection
│ │ ├── resolver
│ │ ├── wrapper
│ │ │ ├── WrapperAbstract.java
│ │ │ ├── ClassWrapper.java
│ │ │ ├── ConstructorWrapper.java
│ │ │ ├── FieldWrapper.java
│ │ │ └── MethodWrapper.java
│ │ ├── minecraft
│ │ │ ├── OBCClassResolver.java
│ │ │ └── NMSClassResolver.java
│ │ ├── ClassResolver.java
│ │ ├── MemberResolver.java
│ │ ├── ResolverAbstract.java
│ │ ├── ResolverQuery.java
│ │ ├── ConstructorResolver.java
│ │ ├── MethodResolver.java
│ │ └── FieldResolver.java
│ │ ├── annotation
│ │ ├── Class.java
│ │ ├── Field.java
│ │ ├── Method.java
│ │ └── ReflectionAnnotations.java
│ │ ├── accessor
│ │ └── FieldAccessor.java
│ │ ├── minecraft
│ │ ├── MinecraftVersion.java
│ │ ├── Minecraft.java
│ │ └── DataWatcher.java
│ │ └── util
│ │ └── AccessUtil.java
└── test
│ └── java
│ └── org
│ └── inventivetalent
│ └── reflectionhelper
│ └── test
│ ├── ResolverTest.java
│ └── Test.java
├── README.md
├── settings.xml
├── LICENSE
├── .travis.yml
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | target
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: InventivetalentDev
2 | patreon: inventivetalent
3 | custom: ["https://www.paypal.me/inventivetalent", "https://donation.inventivetalent.org"]
4 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "maven": {
6 | "enabled": true
7 | },
8 | "ignoreUnstable": false,
9 | "hostRules": [{
10 | "hostType": "maven",
11 | "endpoint": "https://repo.inventivetalent.org/content/groups/public/"
12 | }]
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Java CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: Set up JDK 1.9
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.9
16 | - name: Build with Maven
17 | run: mvn package --file pom.xml
18 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/wrapper/WrapperAbstract.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver.wrapper;
2 |
3 | public abstract class WrapperAbstract {
4 |
5 | /**
6 | * Check whether the wrapped object exists (i.e. is not null)
7 | *
8 | * @return true if the wrapped object exists
9 | */
10 | public abstract boolean exists();
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReflectionHelper
2 |
3 | [](https://jitpack.io/#org.inventivetalent/reflectionhelper)
4 | [](https://travis-ci.org/InventivetalentDev/ReflectionHelper)
5 |
6 | API for accessing various classes and their members using reflection.
7 |
8 | See the [SpigotMC page](https://r.spiget.org/19241) for usage details
9 |
--------------------------------------------------------------------------------
/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | sonatype-nexus-releases
5 | ${env.CI_DEPLOY_USERNAME}
6 | ${env.CI_DEPLOY_PASSWORD}
7 |
8 |
9 | sonatype-nexus-snapshots
10 | ${env.CI_DEPLOY_USERNAME}
11 | ${env.CI_DEPLOY_PASSWORD}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/minecraft/OBCClassResolver.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver.minecraft;
2 |
3 | import org.inventivetalent.reflection.minecraft.Minecraft;
4 | import org.inventivetalent.reflection.resolver.ClassResolver;
5 |
6 | /**
7 | * {@link ClassResolver} for org.bukkit.craftbukkit.* classes
8 | */
9 | public class OBCClassResolver extends ClassResolver {
10 |
11 | @Override
12 | public Class resolve(String... names) throws ClassNotFoundException {
13 | for (int i = 0; i < names.length; i++) {
14 | if (!names[i].startsWith("org.bukkit")) {
15 | names[i] = Minecraft.getOBCPackage() + "." + names[i];
16 | }
17 | }
18 | return super.resolve(names);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/minecraft/NMSClassResolver.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver.minecraft;
2 |
3 | import org.inventivetalent.reflection.minecraft.Minecraft;
4 | import org.inventivetalent.reflection.minecraft.MinecraftVersion;
5 | import org.inventivetalent.reflection.resolver.ClassResolver;
6 |
7 | /**
8 | * {@link ClassResolver} for net.minecraft.server.* classes
9 | */
10 | public class NMSClassResolver extends ClassResolver {
11 |
12 | @Override
13 | public Class resolve(String... names) throws ClassNotFoundException {
14 | for (int i = 0; i < names.length; i++) {
15 | if (names[i].startsWith("net.minecraft"))
16 | continue;
17 |
18 | if (names[i].contains(".") && MinecraftVersion.VERSION.hasNMSVersionPrefix()) {
19 | /* use class name only */
20 | String[] path = names[i].split("\\.");
21 | names[i] = Minecraft.getNMSPackage() + "." + path[path.length - 1];
22 | continue;
23 | }
24 |
25 | /* use the whole name */
26 | names[i] = Minecraft.getNMSPackage() + "." + names[i];
27 | }
28 | return super.resolve(names);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Haylee Schäfer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/annotation/Class.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.annotation;
2 |
3 | import org.inventivetalent.reflection.minecraft.Minecraft;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | /**
11 | * Resolves the annotated {@link org.inventivetalent.reflection.resolver.wrapper.ClassWrapper} or {@link java.lang.Class} field to the first matching class name.
12 | */
13 | @Target(ElementType.FIELD)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface Class {
16 |
17 | /**
18 | * Name of the class. Use {nms}.MyClass for NMS classes, or {obc}.MyClass for OBC classes. Use > or < as a name prefix in combination with {@link #versions()} to specify versions newer- or older-than.
19 | *
20 | * @return the class name
21 | */
22 | String[] value();
23 |
24 | /**
25 | * Specific versions for the names.
26 | *
27 | * @return Array of versions for the class names
28 | */
29 | Minecraft.Version[] versions() default {};
30 |
31 | /**
32 | * Whether to ignore any reflection exceptions thrown. Defaults to true
33 | *
34 | * @return whether to ignore exceptions
35 | */
36 | boolean ignoreExceptions() default true;
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/ClassResolver.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver;
2 |
3 | import org.inventivetalent.reflection.resolver.wrapper.ClassWrapper;
4 | import org.inventivetalent.reflection.util.AccessUtil;
5 |
6 | /**
7 | * Default {@link ClassResolver}
8 | */
9 | public class ClassResolver extends ResolverAbstract {
10 |
11 | public ClassWrapper resolveWrapper(String... names) {
12 | return new ClassWrapper<>(resolveSilent(names));
13 | }
14 |
15 | public Class resolveSilent(String... names) {
16 | try {
17 | return resolve(names);
18 | } catch (Exception e) {
19 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
20 | }
21 | return null;
22 | }
23 |
24 | public Class resolve(String... names) throws ClassNotFoundException {
25 | ResolverQuery.Builder builder = ResolverQuery.builder();
26 | for (String name : names)
27 | builder.with(name);
28 | try {
29 | return super.resolve(builder.build());
30 | } catch (ReflectiveOperationException e) {
31 | throw (ClassNotFoundException) e;
32 | }
33 | }
34 |
35 | @Override
36 | protected Class resolveObject(ResolverQuery query) throws ReflectiveOperationException {
37 | return Class.forName(query.getName());
38 | }
39 |
40 | @Override
41 | protected ClassNotFoundException notFoundException(String joinedNames) {
42 | return new ClassNotFoundException("Could not resolve class for " + joinedNames);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/annotation/Field.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.annotation;
2 |
3 | import org.inventivetalent.reflection.minecraft.Minecraft;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | /**
11 | * Resolves the annotated {@link org.inventivetalent.reflection.resolver.wrapper.FieldWrapper} or {@link java.lang.reflect.Field} field to the first matching field name.
12 | */
13 | @Target(ElementType.FIELD)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface Field {
16 |
17 | /**
18 | * Name of the class to load this field from
19 | *
20 | * @return name of the class
21 | */
22 | String className();
23 |
24 | /**
25 | * Possible names of the field. Use > or < as a name prefix in combination with {@link #versions()} to specify versions newer- or older-than.
26 | *
27 | * @return names of the field
28 | */
29 | String[] value();
30 |
31 | /**
32 | * Specific versions for the names.
33 | *
34 | * @return Array of versions for the class names
35 | */
36 | Minecraft.Version[] versions() default {};
37 |
38 | /**
39 | * Whether to ignore any reflection exceptions thrown. Defaults to true
40 | *
41 | * @return whether to ignore exceptions
42 | */
43 | boolean ignoreExceptions() default true;
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/annotation/Method.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.annotation;
2 |
3 | import org.inventivetalent.reflection.minecraft.Minecraft;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | /**
11 | * Resolves the annotated {@link org.inventivetalent.reflection.resolver.wrapper.MethodWrapper} or {@link java.lang.reflect.Method} field to the first matching method name.
12 | */
13 | @Target(ElementType.FIELD)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface Method {
16 |
17 | /**
18 | * Name of the class to load this method from
19 | *
20 | * @return name of the class
21 | */
22 | String className();
23 |
24 | /**
25 | * Possible names of the method. Use > or < as a name prefix in combination with {@link #versions()} to specify versions newer- or older-than.
26 | *
27 | * @return method names
28 | */
29 | String[] value();
30 |
31 | /**
32 | * Specific versions for the names.
33 | *
34 | * @return Array of versions for the class names
35 | */
36 | Minecraft.Version[] versions() default {};
37 |
38 | /**
39 | * Whether to ignore any reflection exceptions thrown. Defaults to true
40 | *
41 | * @return whether to ignore exceptions
42 | */
43 | boolean ignoreExceptions() default true;
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/wrapper/ClassWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver.wrapper;
2 |
3 | import org.inventivetalent.reflection.util.AccessUtil;
4 |
5 | public class ClassWrapper extends WrapperAbstract {
6 |
7 | private final Class clazz;
8 |
9 | public ClassWrapper(Class clazz) {
10 | this.clazz = clazz;
11 | }
12 |
13 | @Override
14 | public boolean exists() {
15 | return this.clazz != null;
16 | }
17 |
18 | public Class getClazz() {
19 | return clazz;
20 | }
21 |
22 | public String getName() {
23 | return this.clazz.getName();
24 | }
25 |
26 | public R newInstance() {
27 | try {
28 | return this.clazz.newInstance();
29 | } catch (Exception e) {
30 | throw new RuntimeException(e);
31 | }
32 | }
33 |
34 | public R newInstanceSilent() {
35 | try {
36 | return this.clazz.newInstance();
37 | } catch (Exception e) {
38 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
39 | }
40 | return null;
41 | }
42 |
43 | @Override
44 | public boolean equals(Object object) {
45 | if (this == object) { return true; }
46 | if (object == null || getClass() != object.getClass()) { return false; }
47 |
48 | ClassWrapper> that = (ClassWrapper>) object;
49 |
50 | return clazz != null ? clazz.equals(that.clazz) : that.clazz == null;
51 |
52 | }
53 |
54 | @Override
55 | public int hashCode() {
56 | return clazz != null ? clazz.hashCode() : 0;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/wrapper/ConstructorWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver.wrapper;
2 |
3 | import org.inventivetalent.reflection.util.AccessUtil;
4 |
5 | import java.lang.reflect.Constructor;
6 |
7 | public class ConstructorWrapper extends WrapperAbstract {
8 |
9 | private final Constructor constructor;
10 |
11 | public ConstructorWrapper(Constructor constructor) {
12 | this.constructor = constructor;
13 | }
14 |
15 | @Override
16 | public boolean exists() {
17 | return this.constructor != null;
18 | }
19 |
20 | public R newInstance(Object... args) {
21 | try {
22 | return this.constructor.newInstance(args);
23 | } catch (Exception e) {
24 | throw new RuntimeException(e);
25 | }
26 | }
27 |
28 | public R newInstanceSilent(Object... args) {
29 | try {
30 | return this.constructor.newInstance(args);
31 | } catch (Exception e) {
32 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
33 | }
34 | return null;
35 | }
36 |
37 | public Class>[] getParameterTypes() {
38 | return this.constructor.getParameterTypes();
39 | }
40 |
41 | public Constructor getConstructor() {
42 | return constructor;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object object) {
47 | if (this == object) { return true; }
48 | if (object == null || getClass() != object.getClass()) { return false; }
49 |
50 | ConstructorWrapper> that = (ConstructorWrapper>) object;
51 |
52 | return constructor != null ? constructor.equals(that.constructor) : that.constructor == null;
53 |
54 | }
55 |
56 | @Override
57 | public int hashCode() {
58 | return constructor != null ? constructor.hashCode() : 0;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/MemberResolver.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver;
2 |
3 | import org.inventivetalent.reflection.resolver.wrapper.WrapperAbstract;
4 |
5 | import java.lang.reflect.Member;
6 |
7 | /**
8 | * abstract class to resolve members
9 | *
10 | * @param member type
11 | * @see ConstructorResolver
12 | * @see FieldResolver
13 | * @see MethodResolver
14 | */
15 | public abstract class MemberResolver extends ResolverAbstract {
16 |
17 | protected Class> clazz;
18 |
19 | public MemberResolver(Class> clazz) {
20 | if (clazz == null) { throw new IllegalArgumentException("class cannot be null"); }
21 | this.clazz = clazz;
22 | }
23 |
24 | public MemberResolver(String className) throws ClassNotFoundException {
25 | this(new ClassResolver().resolve(className));
26 | }
27 |
28 | /**
29 | * Resolve a member by its index
30 | *
31 | * @param index index
32 | * @return the member
33 | * @throws IndexOutOfBoundsException if the specified index is out of the available member bounds
34 | * @throws ReflectiveOperationException if the object could not be set accessible
35 | */
36 | public abstract T resolveIndex(int index) throws IndexOutOfBoundsException, ReflectiveOperationException;
37 |
38 | /**
39 | * Resolve member by its index (without exceptions)
40 | *
41 | * @param index index
42 | * @return the member or null
43 | */
44 | public abstract T resolveIndexSilent(int index);
45 |
46 | /**
47 | * Resolce member wrapper by its index
48 | *
49 | * @param index index
50 | * @return the wrapped member
51 | */
52 | public abstract WrapperAbstract resolveIndexWrapper(int index);
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/wrapper/FieldWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver.wrapper;
2 |
3 | import org.inventivetalent.reflection.util.AccessUtil;
4 |
5 | import java.lang.reflect.Field;
6 |
7 | public class FieldWrapper extends WrapperAbstract {
8 |
9 | private final Field field;
10 |
11 | public FieldWrapper(Field field) {
12 | this.field = field;
13 | }
14 |
15 | @Override
16 | public boolean exists() {
17 | return this.field != null;
18 | }
19 |
20 | public String getName() {
21 | return this.field.getName();
22 | }
23 |
24 | public R get(Object object) {
25 | try {
26 | return (R) this.field.get(object);
27 | } catch (Exception e) {
28 | throw new RuntimeException(e);
29 | }
30 | }
31 |
32 | public R getSilent(Object object) {
33 | try {
34 | return (R) this.field.get(object);
35 | } catch (Exception e) {
36 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
37 | }
38 | return null;
39 | }
40 |
41 | public void set(Object object, R value) {
42 | try {
43 | this.field.set(object, value);
44 | } catch (Exception e) {
45 | throw new RuntimeException(e);
46 | }
47 | }
48 |
49 | public void setSilent(Object object, R value) {
50 | try {
51 | this.field.set(object, value);
52 | } catch (Exception e) {
53 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
54 | }
55 | }
56 |
57 | public Field getField() {
58 | return field;
59 | }
60 |
61 | @Override
62 | public boolean equals(Object object) {
63 | if (this == object) { return true; }
64 | if (object == null || getClass() != object.getClass()) { return false; }
65 |
66 | FieldWrapper> that = (FieldWrapper>) object;
67 |
68 | if (field != null ? !field.equals(that.field) : that.field != null) { return false; }
69 |
70 | return true;
71 | }
72 |
73 | @Override
74 | public int hashCode() {
75 | return field != null ? field.hashCode() : 0;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - openjdk8
4 |
5 | script: "mvn deploy --settings settings.xml"
6 | env:
7 | global:
8 | - secure: "hKUxyzr5yj9u7JTOpN8r09P/8X9AJMiVvNvgERq+KIG7ngm3hsxODg/CQiB64ch3Rx3molO6g7qUBUTFa4/OiFqmCePxEF+ZpjGER+9tjpWA4Ot/EKWP+ERAuMg/2M7sPhjzfbV1UJfhEvRGNhThTdiThgXeTAG9DIpUAuaYTCfevFRJuFVaunqGN/f0nKNLEr95DkkYycgni2wye1sRRvcOM+shv/VM3YxwCUlUGEEuMlxaN0vvw13n/keu4Jr2qvfyw7750mqu2IqqTLeVpEZgZwb1bM8U2DcVKv6527fOCWQf1+c/3aPHXQnJly1TeS69k4WadefrVZbsA4y5NwX+vE8Ju+0zBSACWQTGGewZAzZIxIrh7m3VXMouvAdQckt8o4vea7Gq61coOXbNuY6rEdHGofEmrWZccjV0Hn8dpVDxTk7jAWxnO+TxOekdLI3mviqyi/Bfn/obqrGvreUOYoFhKyP9ZR2WWO2To0hyhDDyZxHdq1OPjMqeeFYqG40lDq7LvIhFKeT1zt+vR/X+oV04YuZ7/AyhFP0mwjSFeITf8rh7RmPaJrQxE/g3/aXTAEqGtwOUhsIfnAOAI0kiw1qQlNou+iGWwfXVjBX91dsARWXwD6AzMh9FQaQjh0wwj0Ue6H5+K0d9lK0UmWGVZU0O+sKLxulO4D3r2DM="
9 | - secure: "GH3FtcvragmFD2wbXc9nOCHT4qp8P7aQ8KmSpRbAJ1LR+OkEjvOk1RZwkGjEzQ4AJGWhFHFX3WmFR/9si0ystdWEBI1pubL/L7o79q5l8H60Az90lrz8hmruGeZaVs5klFQBE8IWQ9BvR3n8OGCOIZCqDIshJIB1+3QX7X7vIK95M91ZPps4N5gRpwwU69pB1N16MKJ1BhzZQIWTvA+rB+x5lAlomjWJEwCGuT2d1ZkBjTex/sGEab816LbfOp4/6DvoOdO45FiqCebaSwO6j7Ejg7WV19Q2XLFN6GaFyESHW0hrpoAg96jWyJrKjEP9N0+l7MgwvweZ/cME8BVyA7mstLeqpJgLMlNTKoknKwOv0NNeUn4a64NwCzjkVziokncbCBiJLdw5r0usJqW7rbo9fmQI710asItJN56rqH+elsa1vDpzqSpEqBB3T5AW4VBvsMu5BFhEmC+9W5iMiUhqP/CMFW2+IJeGh4Shb2BNeUm2uelHTiQTogv4/7fK1UUM6opYz2OtFakZO288isg5r+jTrZYFAVVhq4tczyDDhF5fTw9lcpZmreVh/z0u0TemDXM44Iw3wsg8Hmyh+d3C9gDjknpprX3UAN2b2R82+cA43dW3x4rs5m/aHQ4rHuK4oMH0T57gSDtgxp01YjMPQ57Fus2Z8FKVnfhZ8KI="
10 | deploy:
11 | provider: releases
12 | api_key:
13 | secure: "jA5sN5LpJJUUeTvSdsbAJqrvQvqo05dUeRQDEBgCYA8qDa4aEV0YOOEloscw6nNHSt1cU942oI50Xxp8Ykvfxk8FtRFj4COR3bjtLu2COF4jo/tVa7xcyQhh9dPe2srZkmb59CY5KdjwhMcnlk8B8F0p5ir3NWyoFtwEHeLbfRSCMhHqkwosmSM+V11k3+xG8RcZnTlfTqt/I1GnSoL7vUfLUt16IU5CSioomlW3SMJ0wCpb62iTyTQO7WWfRWa3Yl8kYp+nNXS+2zT1821eLAVH7LugZOs22y0OMu+Mka2uwWa5ylbEttqsJwe2ZTJP5fSDN20uyOcJFNLf67bD87BOEz73kl5E/dwJwvJe8XQtveSZ+LP+Iw1viOia8gYUnnI+l9JRGKNECurbY9QC2/5UjCzULw8NRyNjfLU1C+5htT+BNM+O+Oa6PWqvTONDJgR8dGl476vVJL8JLwfBtjrvMXAC3lqEAi4Y0j9eGOYWihSekfyWnPPiL+oyRUMqqhHLrvC+iKrEWpK+9p0hN9nQ6ugbHyi8o4+F+6iqlmoS/mH9P0hekEUJ5WybqcvncGFvKfMFmXVY3vjg2QoDwqNG4Z/K8phbDHGhcTyoFjpMTEnfJTuAEQTxPSslHvkdw7VjKSjyJ8yn0+Yt2SpsDMsINUtyrAsnui2tWbuS54w="
14 | file_glob: true
15 | file:
16 | - "target/ReflectionHelper_v*.jar"
17 | skip_cleanup: true
18 | on:
19 | tags: true
20 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/ResolverAbstract.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver;
2 |
3 | import org.inventivetalent.reflection.util.AccessUtil;
4 |
5 | import java.util.Arrays;
6 | import java.util.Map;
7 | import java.util.concurrent.ConcurrentHashMap;
8 |
9 | /**
10 | * Abstract resolver class
11 | *
12 | * @param resolved type
13 | * @see ClassResolver
14 | * @see ConstructorResolver
15 | * @see FieldResolver
16 | * @see MethodResolver
17 | */
18 | public abstract class ResolverAbstract {
19 |
20 | protected final Map resolvedObjects = new ConcurrentHashMap();
21 |
22 | /**
23 | * Same as {@link #resolve(ResolverQuery...)} but throws no exceptions
24 | *
25 | * @param queries Array of possible queries
26 | * @return the resolved object if it was found, null otherwise
27 | */
28 | protected T resolveSilent(ResolverQuery... queries) {
29 | try {
30 | return resolve(queries);
31 | } catch (Exception e) {
32 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
33 | }
34 | return null;
35 | }
36 |
37 | /**
38 | * Attempts to resolve an array of possible queries to an object
39 | *
40 | * @param queries Array of possible queries
41 | * @return the resolved object (if it was found)
42 | * @throws ReflectiveOperationException if none of the possibilities could be resolved
43 | * @throws IllegalArgumentException if the given possibilities are empty
44 | */
45 | protected T resolve(ResolverQuery... queries) throws ReflectiveOperationException {
46 | if (queries == null || queries.length <= 0) { throw new IllegalArgumentException("Given possibilities are empty"); }
47 | for (ResolverQuery query : queries) {
48 | //Object is already resolved, return it directly
49 | if (resolvedObjects.containsKey(query)) { return resolvedObjects.get(query); }
50 |
51 | //Object is not yet resolved, try to find it
52 | try {
53 | T resolved = resolveObject(query);
54 | //Store if it was found
55 | resolvedObjects.put(query, resolved);
56 | return resolved;
57 | } catch (ReflectiveOperationException e) {
58 | //Not found, ignore the exception
59 | if (AccessUtil.VERBOSE)
60 | e.printStackTrace();
61 | }
62 | }
63 |
64 | //Couldn't find any of the possibilities
65 | throw notFoundException(Arrays.asList(queries).toString());
66 | }
67 |
68 | protected abstract T resolveObject(ResolverQuery query) throws ReflectiveOperationException;
69 |
70 | protected ReflectiveOperationException notFoundException(String joinedNames) {
71 | return new ReflectiveOperationException("Objects could not be resolved: " + joinedNames);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/ResolverQuery.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | /**
8 | * Container class for resolver-queries Used by {@link MethodResolver}
9 | *
10 | * @see org.inventivetalent.reflection.resolver.ResolverQuery.Builder
11 | */
12 | public class ResolverQuery {
13 |
14 | private String name;
15 | private Class>[] types;
16 |
17 | public ResolverQuery(String name, Class>... types) {
18 | this.name = name;
19 | this.types = types;
20 | }
21 |
22 | public ResolverQuery(String name) {
23 | this.name = name;
24 | this.types = new Class[0];
25 | }
26 |
27 | public ResolverQuery(Class>... types) {
28 | this.types = types;
29 | }
30 |
31 | public String getName() {
32 | return name;
33 | }
34 |
35 | public Class>[] getTypes() {
36 | return types;
37 | }
38 |
39 | @Override
40 | public boolean equals(Object o) {
41 | if (this == o) { return true; }
42 | if (o == null || getClass() != o.getClass()) { return false; }
43 |
44 | ResolverQuery that = (ResolverQuery) o;
45 |
46 | if (name != null ? !name.equals(that.name) : that.name != null) { return false; }
47 | // Probably incorrect - comparing Object[] arrays with Arrays.equals
48 | return Arrays.equals(types, that.types);
49 |
50 | }
51 |
52 | @Override
53 | public int hashCode() {
54 | int result = name != null ? name.hashCode() : 0;
55 | result = 31 * result + (types != null ? Arrays.hashCode(types) : 0);
56 | return result;
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return "ResolverQuery{" +
62 | "name='" + name + '\'' +
63 | ", types=" + Arrays.toString(types) +
64 | '}';
65 | }
66 |
67 | public static Builder builder() {
68 | return new Builder();
69 | }
70 |
71 | /**
72 | * Builder class for {@link ResolverQuery} Access using {@link ResolverQuery#builder()}
73 | */
74 | public static class Builder {
75 |
76 | private List queryList = new ArrayList();
77 |
78 | private Builder() {
79 | }
80 |
81 | public Builder with(String name, Class>[] types) {
82 | queryList.add(new ResolverQuery(name, types));
83 | return this;
84 | }
85 |
86 | public Builder with(String name) {
87 | queryList.add(new ResolverQuery(name));
88 | return this;
89 | }
90 |
91 | public Builder with(Class>[] types) {
92 | queryList.add(new ResolverQuery(types));
93 | return this;
94 | }
95 |
96 | public ResolverQuery[] build() {
97 | return queryList.toArray(new ResolverQuery[queryList.size()]);
98 | }
99 |
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/test/java/org/inventivetalent/reflectionhelper/test/ResolverTest.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflectionhelper.test;
2 |
3 | import org.inventivetalent.reflection.resolver.ClassResolver;
4 | import org.inventivetalent.reflection.resolver.ConstructorResolver;
5 | import org.inventivetalent.reflection.resolver.FieldResolver;
6 | import org.inventivetalent.reflection.resolver.MethodResolver;
7 | import org.inventivetalent.reflection.resolver.wrapper.ClassWrapper;
8 | import org.junit.Test;
9 |
10 | import java.lang.reflect.Constructor;
11 | import java.lang.reflect.Field;
12 | import java.lang.reflect.InvocationTargetException;
13 | import java.lang.reflect.Method;
14 |
15 | import static org.junit.Assert.assertEquals;
16 | import static org.junit.Assert.assertNotNull;
17 |
18 | public class ResolverTest {
19 |
20 | @Test
21 | public void basic() throws ReflectiveOperationException {
22 | ClassResolver classResolver = new ClassResolver();
23 | Class clazz = classResolver.resolve("org.inventivetalent.reflection.resolver.wrapper.ClassWrapper");
24 | assertNotNull(clazz);
25 | assertEquals(ClassWrapper.class, clazz);
26 |
27 | FieldResolver fieldResolver = new FieldResolver(clazz);
28 | Field field = fieldResolver.resolve("clazz");
29 | assertNotNull(field);
30 |
31 | ConstructorResolver constructorResolver = new ConstructorResolver(clazz);
32 | Constructor constructor = constructorResolver.resolveFirstConstructor();
33 | assertNotNull(constructor);
34 |
35 | MethodResolver methodResolver = new MethodResolver(clazz);
36 | Method method = methodResolver.resolve("newInstance");
37 | assertNotNull(method);
38 | }
39 |
40 | @Test
41 | public void shouldResolveSuperField() throws NoSuchFieldException, NoSuchMethodException {
42 | FieldResolver fieldResolver = new FieldResolver(SubClass.class);
43 | Field field = fieldResolver.resolve("a");
44 | assertNotNull(field);
45 |
46 | MethodResolver methodResolver = new MethodResolver(SubClass.class);
47 | Method method = methodResolver.resolve("a");
48 | assertNotNull(method);
49 | }
50 |
51 | @Test
52 | public void shouldIgnoreSuperclassIfFoundInSubclass() throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
53 | BaseClass baseClass = new BaseClass();
54 | SubClass subClass = new SubClass();
55 |
56 | FieldResolver fieldResolver = new FieldResolver(SubClass.class);
57 | Field field = fieldResolver.resolve("b");
58 |
59 | MethodResolver methodResolver = new MethodResolver(SubClass.class);
60 | Method method = methodResolver.resolve("a");
61 |
62 | assertEquals("sub-b", field.get(subClass));
63 | assertEquals(2, method.invoke(subClass));
64 | }
65 |
66 | class BaseClass {
67 | private String a = "base";
68 | private String b = "base-b";
69 |
70 | private int a() {
71 | return 1;
72 | }
73 | }
74 |
75 | class SubClass extends BaseClass {
76 | private String b = "sub-b";
77 |
78 | private int a() {
79 | return 2;
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/ConstructorResolver.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver;
2 |
3 | import org.inventivetalent.reflection.resolver.wrapper.ConstructorWrapper;
4 | import org.inventivetalent.reflection.util.AccessUtil;
5 |
6 | import java.lang.reflect.Constructor;
7 |
8 | /**
9 | * Resolver for constructors
10 | */
11 | public class ConstructorResolver extends MemberResolver {
12 |
13 | public ConstructorResolver(Class> clazz) {
14 | super(clazz);
15 | }
16 |
17 | public ConstructorResolver(String className) throws ClassNotFoundException {
18 | super(className);
19 | }
20 |
21 | @Override
22 | public Constructor resolveIndex(int index) throws IndexOutOfBoundsException, ReflectiveOperationException {
23 | return AccessUtil.setAccessible(this.clazz.getDeclaredConstructors()[index]);
24 | }
25 |
26 | @Override
27 | public Constructor resolveIndexSilent(int index) {
28 | try {
29 | return resolveIndex(index);
30 | } catch (IndexOutOfBoundsException | ReflectiveOperationException ignored) {
31 | }
32 | return null;
33 | }
34 |
35 | @Override
36 | public ConstructorWrapper resolveIndexWrapper(int index) {
37 | return new ConstructorWrapper<>(resolveIndexSilent(index));
38 | }
39 |
40 | public ConstructorWrapper resolveWrapper(Class>[]... types) {
41 | return new ConstructorWrapper<>(resolveSilent(types));
42 | }
43 |
44 | public Constructor resolveSilent(Class>[]... types) {
45 | try {
46 | return resolve(types);
47 | } catch (Exception e) {
48 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
49 |
50 | }
51 | return null;
52 | }
53 |
54 | public Constructor resolve(Class>[]... types) throws NoSuchMethodException {
55 | ResolverQuery.Builder builder = ResolverQuery.builder();
56 | for (Class>[] type : types)
57 | builder.with(type);
58 | try {
59 | return super.resolve(builder.build());
60 | } catch (ReflectiveOperationException e) {
61 | throw (NoSuchMethodException) e;
62 | }
63 | }
64 |
65 | @Override
66 | protected Constructor resolveObject(ResolverQuery query) throws ReflectiveOperationException {
67 | return AccessUtil.setAccessible(this.clazz.getDeclaredConstructor(query.getTypes()));
68 | }
69 |
70 | public Constructor resolveFirstConstructor() throws ReflectiveOperationException {
71 | for (Constructor constructor : this.clazz.getDeclaredConstructors()) {
72 | return AccessUtil.setAccessible(constructor);
73 | }
74 | return null;
75 | }
76 |
77 | public Constructor resolveFirstConstructorSilent() {
78 | try {
79 | return resolveFirstConstructor();
80 | } catch (Exception e) {
81 | }
82 | return null;
83 | }
84 |
85 | public Constructor resolveLastConstructor() throws ReflectiveOperationException {
86 | Constructor constructor = null;
87 | for (Constructor constructor1 : this.clazz.getDeclaredConstructors()) {
88 | constructor = constructor1;
89 | }
90 | if (constructor != null) { return AccessUtil.setAccessible(constructor); }
91 | return null;
92 | }
93 |
94 | public Constructor resolveLastConstructorSilent() {
95 | try {
96 | return resolveLastConstructor();
97 | } catch (Exception e) {
98 | }
99 | return null;
100 | }
101 |
102 | @Override
103 | protected NoSuchMethodException notFoundException(String joinedNames) {
104 | return new NoSuchMethodException("Could not resolve constructor for " + joinedNames + " in class " + this.clazz);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | 4.0.0
7 |
8 | org.inventivetalent
9 | reflectionhelper
10 | 1.18.13-SNAPSHOT
11 |
12 |
13 | ReflectionHelper_v${project.version}
14 | src/main/java
15 |
16 |
17 | src/main/java
18 |
19 | **/*.java
20 |
21 |
22 |
23 | src/main/resources
24 | true
25 |
26 | plugin.yml
27 | config.yml
28 |
29 |
30 |
31 |
32 |
33 | org.apache.maven.plugins
34 | maven-compiler-plugin
35 | 3.8.1
36 |
37 | 11
38 | 11
39 |
40 |
41 |
42 | org.apache.maven.plugins
43 | maven-shade-plugin
44 | 2.4.3
45 |
46 |
47 | package
48 |
49 | shade
50 |
51 |
52 |
53 |
54 | org.inventivetalent:reflectionhelper**
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | inventive-repo
66 | https://repo.inventivetalent.org/repository/public/
67 |
68 |
69 | jitpack.io
70 | https://jitpack.io
71 |
72 |
73 | md_5-repo
74 | http://repo.md-5.net/content/repositories/public/
75 |
76 |
77 | spigot-repo
78 | https://hub.spigotmc.org/nexus/content/groups/public/
79 |
80 |
81 |
82 |
83 | org.spigotmc
84 | spigot-api
85 | 1.9-R0.1-SNAPSHOT
86 | provided
87 |
88 |
89 |
90 | junit
91 | junit
92 | 4.12
93 |
94 |
95 |
96 |
97 |
98 | sonatype-nexus-releases
99 | https://repo.inventivetalent.org/repository/maven-releases/
100 |
101 |
102 | sonatype-nexus-snapshots
103 | https://repo.inventivetalent.org/repository/maven-snapshots/
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/resolver/MethodResolver.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.resolver;
2 |
3 | import org.inventivetalent.reflection.resolver.wrapper.MethodWrapper;
4 | import org.inventivetalent.reflection.util.AccessUtil;
5 |
6 | import java.lang.reflect.Method;
7 |
8 | /**
9 | * Resolver for methods
10 | */
11 | public class MethodResolver extends MemberResolver {
12 |
13 | public MethodResolver(Class> clazz) {
14 | super(clazz);
15 | }
16 |
17 | public MethodResolver(String className) throws ClassNotFoundException {
18 | super(className);
19 | }
20 |
21 | public Method resolveSignature(String... signatures)throws ReflectiveOperationException {
22 | for (Method method : clazz.getDeclaredMethods()) {
23 | String methodSignature = MethodWrapper.getMethodSignature(method);
24 | for (String s : signatures) {
25 | if (s.equals(methodSignature)) {
26 | return AccessUtil.setAccessible(method);
27 | }
28 | }
29 | }
30 | return null;
31 | }
32 |
33 | public Method resolveSignatureSilent(String... signatures) {
34 | try {
35 | return resolveSignature(signatures);
36 | } catch (ReflectiveOperationException ignored) {
37 | }
38 | return null;
39 | }
40 |
41 | public MethodWrapper resolveSignatureWrapper(String... signatures) {
42 | return new MethodWrapper(resolveSignatureSilent(signatures));
43 | }
44 |
45 | @Override
46 | public Method resolveIndex(int index) throws IndexOutOfBoundsException, ReflectiveOperationException {
47 | return AccessUtil.setAccessible(this.clazz.getDeclaredMethods()[index]);
48 | }
49 |
50 | @Override
51 | public Method resolveIndexSilent(int index) {
52 | try {
53 | return resolveIndex(index);
54 | } catch (IndexOutOfBoundsException | ReflectiveOperationException ignored) {
55 | }
56 | return null;
57 | }
58 |
59 | @Override
60 | public MethodWrapper resolveIndexWrapper(int index) {
61 | return new MethodWrapper<>(resolveIndexSilent(index));
62 | }
63 |
64 | public MethodWrapper resolveWrapper(String... names) {
65 | return new MethodWrapper<>(resolveSilent(names));
66 | }
67 |
68 | public MethodWrapper resolveWrapper(ResolverQuery... queries) {
69 | return new MethodWrapper<>(resolveSilent(queries));
70 | }
71 |
72 | public Method resolveSilent(String... names) {
73 | try {
74 | return resolve(names);
75 | } catch (Exception e) {
76 | if (AccessUtil.VERBOSE) { e.printStackTrace(); }
77 | }
78 | return null;
79 | }
80 |
81 | @Override
82 | public Method resolveSilent(ResolverQuery... queries) {
83 | return super.resolveSilent(queries);
84 | }
85 |
86 | public Method resolve(String... names) throws NoSuchMethodException {
87 | ResolverQuery.Builder builder = ResolverQuery.builder();
88 | for (String name : names) {
89 | builder.with(name);
90 | }
91 | return resolve(builder.build());
92 | }
93 |
94 | @Override
95 | public Method resolve(ResolverQuery... queries) throws NoSuchMethodException {
96 | try {
97 | return super.resolve(queries);
98 | } catch (ReflectiveOperationException e) {
99 | throw (NoSuchMethodException) e;
100 | }
101 | }
102 |
103 | @Override
104 | protected Method resolveObject(ResolverQuery query) throws ReflectiveOperationException {
105 | Class> currentClass = this.clazz;
106 | while (currentClass != null) {
107 | for (Method method : currentClass.getDeclaredMethods()) {
108 | if (method.getName().equals(query.getName()) && (query.getTypes().length == 0 || ClassListEqual(query.getTypes(), method.getParameterTypes()))) {
109 | return AccessUtil.setAccessible(method);
110 | }
111 | }
112 | currentClass = currentClass.getSuperclass();
113 | }
114 | throw new NoSuchMethodException();
115 | }
116 |
117 | @Override
118 | protected NoSuchMethodException notFoundException(String joinedNames) {
119 | return new NoSuchMethodException("Could not resolve method for " + joinedNames + " in class " + this.clazz);
120 | }
121 |
122 | static boolean ClassListEqual(Class>[] l1, Class>[] l2) {
123 | boolean equal = true;
124 | if (l1.length != l2.length) { return false; }
125 | for (int i = 0; i < l1.length; i++) {
126 | if (l1[i] != l2[i]) {
127 | equal = false;
128 | break;
129 | }
130 | }
131 | return equal;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/test/java/org/inventivetalent/reflectionhelper/test/Test.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflectionhelper.test;
2 |
3 | import org.inventivetalent.reflection.minecraft.Minecraft;
4 | import org.inventivetalent.reflection.resolver.wrapper.MethodWrapper;
5 |
6 | import java.lang.reflect.Method;
7 |
8 | import static org.junit.Assert.*;
9 |
10 | public class Test {
11 |
12 | public double primitiveDummyMethod(String aString, double returnValue) {
13 | return returnValue;
14 | }
15 |
16 | public Method genericDummyMethod(Thread aThread, Exception aException) {
17 | return null;
18 | }
19 |
20 | public void voidDummyMethod(Character aCharacter, Class aClass, String aString) {
21 | }
22 |
23 | public String[] complexDummyMethod(Boolean[][] booleans, Object[] objects) {
24 | return null;
25 | }
26 |
27 | public int wildcardMethod1(String string) {
28 | return 0;
29 | }
30 |
31 | public double wildcardMethod2(String string) {
32 | return 0;
33 | }
34 |
35 | public double wildcardMethod3(boolean b) {
36 | return 0;
37 | }
38 |
39 | public int wildCardMethod4(String string) {
40 | return 0;
41 | }
42 |
43 | @org.junit.Test
44 | public void primitiveSignatureTest() throws ReflectiveOperationException {
45 | String signature = MethodWrapper.getMethodSignature(Test.class.getMethod("primitiveDummyMethod", String.class, double.class));
46 | assertEquals("double primitiveDummyMethod(String,double)", signature);
47 | }
48 |
49 | @org.junit.Test
50 | public void genericSignatureTest() throws ReflectiveOperationException {
51 | String signature = MethodWrapper.getMethodSignature(Test.class.getMethod("genericDummyMethod", Thread.class, Exception.class));
52 | assertEquals("Method genericDummyMethod(Thread,Exception)", signature);
53 | }
54 |
55 | @org.junit.Test
56 | public void voidSignatureTest() throws ReflectiveOperationException {
57 | String signature = MethodWrapper.getMethodSignature(Test.class.getMethod("voidDummyMethod", Character.class, Class.class, String.class));
58 | assertEquals("void voidDummyMethod(Character,Class,String)", signature);
59 | }
60 |
61 | @org.junit.Test
62 | public void fullNameSignatureTest() throws ReflectiveOperationException {
63 | String signature = MethodWrapper.getMethodSignature(Test.class.getMethod("genericDummyMethod", Thread.class, Exception.class), true);
64 | assertEquals("java.lang.reflect.Method genericDummyMethod(java.lang.Thread,java.lang.Exception)", signature);
65 | }
66 |
67 | @org.junit.Test
68 | public void complexSignatureTest() throws ReflectiveOperationException {
69 | String signature = MethodWrapper.getMethodSignature(Test.class.getMethod("complexDummyMethod", Boolean[][].class, Object[].class), true);
70 | assertEquals("[Ljava.lang.String; complexDummyMethod([[Ljava.lang.Boolean;,[Ljava.lang.Object;)", signature);
71 | }
72 |
73 | @org.junit.Test
74 | public void signatureObjectTest() throws ReflectiveOperationException {
75 | MethodWrapper.MethodSignature signature = MethodWrapper.MethodSignature.of(Test.class.getMethod("genericDummyMethod", Thread.class, Exception.class), false);
76 | assertEquals("Method", signature.getReturnType());
77 | assertEquals("genericDummyMethod", signature.getName());
78 | assertArrayEquals(new String[] {
79 | "Thread",
80 | "Exception"
81 | }, signature.getParameterTypes());
82 | }
83 |
84 | @org.junit.Test
85 | public void signatureFromStringTest() {
86 | MethodWrapper.MethodSignature signature = MethodWrapper.MethodSignature.fromString("java.lang.reflect.Method genericDummyMethod(java.lang.Thread,java.lang.Exception)");
87 | assertEquals("java.lang.reflect.Method", signature.getReturnType());
88 | assertEquals("genericDummyMethod", signature.getName());
89 | assertEquals("java.lang.Thread", signature.getParameterType(0));
90 | assertEquals("java.lang.Exception", signature.getParameterType(1));
91 | }
92 |
93 | @org.junit.Test
94 | public void wildcardTest() throws ReflectiveOperationException {
95 | MethodWrapper.MethodSignature wildcardSignature = MethodWrapper.MethodSignature.fromString("* wildcardMethod*(String)");
96 | MethodWrapper.MethodSignature testSignature1 = MethodWrapper.MethodSignature.of(Test.class.getMethod("wildcardMethod1", String.class), false);
97 | MethodWrapper.MethodSignature testSignature2 = MethodWrapper.MethodSignature.of(Test.class.getMethod("wildcardMethod2", String.class), false);
98 | MethodWrapper.MethodSignature testSignature3 = MethodWrapper.MethodSignature.of(Test.class.getMethod("wildcardMethod3", boolean.class), false);
99 | MethodWrapper.MethodSignature testSignature4 = MethodWrapper.MethodSignature.of(Test.class.getMethod("wildCardMethod4", String.class), false);
100 |
101 | assertTrue(wildcardSignature.matches(testSignature1));
102 | assertTrue(wildcardSignature.matches(testSignature2));
103 | assertFalse(wildcardSignature.matches(testSignature3));
104 | assertFalse(wildcardSignature.matches(testSignature4));
105 | }
106 |
107 | @org.junit.Test
108 | public void versionTest() {
109 | assertEquals("net.minecraft.server.v1_16_R3", Minecraft.Version.v1_16_R3.minecraft().getNmsPackage());
110 | assertEquals("net.minecraft", Minecraft.Version.v1_17_R1.minecraft().getNmsPackage());
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/reflection/accessor/FieldAccessor.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.reflection.accessor;
2 |
3 | import org.inventivetalent.reflection.util.AccessUtil;
4 | import sun.misc.Unsafe;
5 |
6 | import java.lang.reflect.Field;
7 | import java.lang.reflect.Modifier;
8 | import java.security.AccessController;
9 | import java.security.PrivilegedAction;
10 |
11 | public class FieldAccessor {
12 |
13 | private final Field field;
14 |
15 | public FieldAccessor(Field field) {
16 | this.field = field;
17 | if (field == null) return;
18 | try {
19 | field.setAccessible(true);
20 | } catch (Exception e) {
21 | if (AccessUtil.VERBOSE) {
22 | e.printStackTrace();
23 | }
24 | }
25 | }
26 |
27 | public boolean hasField() {
28 | return field != null;
29 | }
30 |
31 | public boolean isStatic() {
32 | return Modifier.isStatic(field.getModifiers());
33 | }
34 |
35 | public boolean isPublic() {
36 | return Modifier.isPublic(field.getModifiers());
37 | }
38 |
39 | public boolean isFinal() {
40 | return Modifier.isFinal(field.getModifiers());
41 | }
42 |
43 | public T get(Object obj) {
44 | try {
45 | //noinspection unchecked
46 | return (T) field.get(obj);
47 | } catch (Exception e) {
48 | throw new RuntimeException(e);
49 | }
50 | }
51 |
52 | public void set(Object obj, T value) {
53 | setField(obj, value, field);
54 | }
55 |
56 | /*
57 | * https://github.com/powermock/powermock/blob/42c72daf9d8b04129178d1d3f1fb4e485d3c13dc/powermock-reflect/src/main/java/org/powermock/reflect/internal/WhiteboxImpl.java#L2298-L2403
58 | */
59 |
60 | private static void setField(Object object, Object value, Field foundField) {
61 | boolean isStatic = (foundField.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
62 | if (isStatic) {
63 | setStaticFieldUsingUnsafe(foundField, value);
64 | } else {
65 | setFieldUsingUnsafe(foundField, object, value);
66 | }
67 | }
68 |
69 | private static void setStaticFieldUsingUnsafe(final Field field, final Object newValue) {
70 | try {
71 | field.setAccessible(true);
72 | int fieldModifiersMask = field.getModifiers();
73 | boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL;
74 | if (isFinalModifierPresent) {
75 | AccessController.doPrivileged((PrivilegedAction