├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── test │ └── java │ │ └── org │ │ └── common │ │ └── reflector │ │ ├── ReflectionUtilsTest.java │ │ ├── data │ │ ├── ClassAnnotated.java │ │ ├── Person.java │ │ ├── ExtendedAnnotatedClass.java │ │ ├── MethodAnnotatedClass.java │ │ ├── CustomTestClassForType.java │ │ ├── annotation │ │ │ ├── ClassAnnotation.java │ │ │ ├── ClassAnnotation1.java │ │ │ ├── ParameterAnnotation.java │ │ │ ├── CustomAnnotationForTest.java │ │ │ └── CustomMethodAnnotation.java │ │ ├── CustomTestInvokeClass.java │ │ ├── SimpleAnnotatedEntry.java │ │ └── SimpleEntryClass.java │ │ ├── utils │ │ ├── ObjectUtilsTest.java │ │ ├── FieldsExtraUtilsTest.java │ │ ├── MiscellaneousUtilsTest.java │ │ ├── SecurityUtilsTest.java │ │ ├── PackageUtilsTest.java │ │ ├── ConstructorUtilsTest.java │ │ ├── InvokeUtilsTest.java │ │ ├── MethodEnhancementsUtilsTest.java │ │ ├── GeneralUtilsTest.java │ │ ├── ConstructorUtilsGetConstructorsAndDeclaredTest.java │ │ ├── FieldUtilsAdditionalTest.java │ │ ├── AnnotationUtilsTest.java │ │ ├── FieldUtilsTest.java │ │ └── MethodUtilsTest.java │ │ ├── util │ │ └── TestConstant.java │ │ └── ReflectionUtilsLegacyTest.java └── main │ └── java │ └── org │ └── reflector │ ├── exception │ ├── MethodInvokeException.java │ ├── InstanceInvocationException.java │ └── FieldAccessException.java │ ├── util │ └── ReflectionConstant.java │ ├── SecurityUtils.java │ ├── MiscellaneousUtils.java │ ├── MethodEnhancementsUtils.java │ ├── GeneralUtils.java │ ├── ObjectUtils.java │ ├── FieldsExtraUtils.java │ ├── ConstructorUtils.java │ ├── PackageUtils.java │ ├── InvokeUtils.java │ ├── AnnotationUtils.java │ ├── ClassBasicUtils.java │ ├── MethodUtils.java │ ├── FieldUtils.java │ └── ReflectionUtilsLegacy.java ├── .travis.yml ├── .gitignore ├── LICENSE.md ├── .github └── workflows │ └── gradle.yml └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'reflectors' 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxkm/reflector/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/ReflectionUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector; 2 | 3 | public class ReflectionUtilsTest { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/ClassAnnotated.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | import org.common.reflector.data.annotation.ClassAnnotation; 4 | 5 | @ClassAnnotation 6 | public class ClassAnnotated { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/Person.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | public class Person { 4 | private String id; 5 | 6 | public String getId() { 7 | return id; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/ExtendedAnnotatedClass.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | import org.common.reflector.data.annotation.ClassAnnotation; 4 | 5 | @ClassAnnotation 6 | public class ExtendedAnnotatedClass { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/exception/MethodInvokeException.java: -------------------------------------------------------------------------------- 1 | package org.reflector.exception; 2 | 3 | public class MethodInvokeException extends RuntimeException { 4 | public MethodInvokeException(String cause) { 5 | super(cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | 4 | language: java 5 | 6 | jdk: 7 | - oraclejdk8 8 | 9 | env: 10 | global: 11 | - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 12 | - PATH=/usr/lib/jvm/java-8-openjdk-amd64/bin:$PATH 13 | 14 | script: 15 | - gradle build -------------------------------------------------------------------------------- /src/main/java/org/reflector/exception/InstanceInvocationException.java: -------------------------------------------------------------------------------- 1 | package org.reflector.exception; 2 | 3 | public class InstanceInvocationException extends RuntimeException { 4 | public InstanceInvocationException(String cause) { 5 | super(cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/MethodAnnotatedClass.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | import org.common.reflector.data.annotation.CustomMethodAnnotation; 4 | 5 | public class MethodAnnotatedClass { 6 | 7 | @CustomMethodAnnotation 8 | public void annotatedMethod() { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/CustomTestClassForType.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | public class CustomTestClassForType { 4 | private String stringField; 5 | private Object objectField; 6 | public int notPrivateField; 7 | private float floatField; 8 | public int oneConstant = 1; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/exception/FieldAccessException.java: -------------------------------------------------------------------------------- 1 | package org.reflector.exception; 2 | 3 | public class FieldAccessException extends RuntimeException { 4 | public FieldAccessException(String cause) { 5 | super(cause); 6 | } 7 | public FieldAccessException(String cause, ReflectiveOperationException e) { 8 | super(cause, e); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/annotation/ClassAnnotation.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface ClassAnnotation { 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/annotation/ClassAnnotation1.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface ClassAnnotation1 { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/util/ReflectionConstant.java: -------------------------------------------------------------------------------- 1 | package org.reflector.util; 2 | 3 | 4 | final public class ReflectionConstant { 5 | private ReflectionConstant() { 6 | } 7 | 8 | public static String CLASS = ".class"; 9 | public static String DOT = "."; 10 | public static char DOT_SYMBOL = '.'; 11 | public static char SLASH = '/'; 12 | public static int CLASS_NAME_CONSTANT = 6; 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/annotation/ParameterAnnotation.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.PARAMETER}) 10 | public @interface ParameterAnnotation { 11 | } -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/annotation/CustomAnnotationForTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.FIELD) 10 | public @interface CustomAnnotationForTest { 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/annotation/CustomMethodAnnotation.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.FIELD}) 10 | public @interface CustomMethodAnnotation { 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | ### Gradle ### 5 | .idea/gradle.xml 6 | .gradle 7 | gradle/ 8 | gradlew.bat 9 | build 10 | 11 | # Log file 12 | *.log 13 | 14 | # Eclipse 15 | .classpath 16 | .project 17 | .settings/ 18 | bin/ 19 | 20 | # BlueJ files 21 | *.ctxt 22 | 23 | # Mobile Tools for Java (J2ME) 24 | .mtj.tmp/ 25 | 26 | # Package Files # 27 | *.jar 28 | *.war 29 | *.nar 30 | *.ear 31 | *.zip 32 | *.tar.gz 33 | *.rar 34 | 35 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 36 | hs_err_pid* 37 | 38 | # IntelliJ IDEA 39 | .idea/ 40 | *.iml 41 | *.iws 42 | *.ipr -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/CustomTestInvokeClass.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | public class CustomTestInvokeClass { 4 | private String key; 5 | private String value; 6 | 7 | public CustomTestInvokeClass() { 8 | } 9 | 10 | public CustomTestInvokeClass(String value) { 11 | this.value = value; 12 | } 13 | 14 | public String getKey() { 15 | return key; 16 | } 17 | 18 | public void setKey(String key) { 19 | this.key = key; 20 | } 21 | 22 | public String getValue() { 23 | return value; 24 | } 25 | 26 | public void setValue(String name) { 27 | this.value = name; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/ObjectUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.SimpleEntryClass; 4 | import org.junit.jupiter.api.Test; 5 | import org.reflector.ObjectUtils; 6 | import org.reflector.ReflectionUtilsLegacy; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | public class ObjectUtilsTest { 11 | 12 | @Test 13 | public void testCopy_NullObject() { 14 | assertEquals(ReflectionUtilsLegacy.copy(null), null); 15 | } 16 | 17 | @Test 18 | public void copyObjectTest() { 19 | SimpleEntryClass simpleEntryClass = new SimpleEntryClass("K", "V"); 20 | SimpleEntryClass simpleEntryClassCopy = (SimpleEntryClass) ObjectUtils.copy(simpleEntryClass); 21 | assertEquals(simpleEntryClass, simpleEntryClassCopy); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/SimpleAnnotatedEntry.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | 4 | import org.common.reflector.data.annotation.CustomAnnotationForTest; 5 | 6 | public class SimpleAnnotatedEntry { 7 | @CustomAnnotationForTest 8 | private String key; 9 | @CustomAnnotationForTest 10 | private String value; 11 | private String info; 12 | 13 | public String getKey() { 14 | return key; 15 | } 16 | 17 | public void setKey(String key) { 18 | this.key = key; 19 | } 20 | 21 | public String getValue() { 22 | return value; 23 | } 24 | 25 | public void setValue(String value) { 26 | this.value = value; 27 | } 28 | 29 | public String getInfo() { 30 | return info; 31 | } 32 | 33 | public void setInfo(String info) { 34 | this.info = info; 35 | } 36 | 37 | private void doSomething() { 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/data/SimpleEntryClass.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.data; 2 | 3 | import java.util.Objects; 4 | 5 | public class SimpleEntryClass { 6 | private String key; 7 | private String value; 8 | 9 | public SimpleEntryClass() { 10 | } 11 | 12 | public SimpleEntryClass(String key, String value) { 13 | this.key = key; 14 | this.value = value; 15 | } 16 | 17 | private SimpleEntryClass(String key, String value, Object obj) { 18 | this.key = key; 19 | this.value = value; 20 | } 21 | 22 | public SimpleEntryClass(String value) { 23 | this.value = value; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) return true; 29 | if (!(o instanceof SimpleEntryClass)) return false; 30 | SimpleEntryClass that = (SimpleEntryClass) o; 31 | return key.equals(that.key) && Objects.equals(value, that.value); 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | return Objects.hash(key, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/FieldsExtraUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.CustomTestClassForType; 4 | import org.common.reflector.util.TestConstant; 5 | import org.junit.jupiter.api.Test; 6 | import org.reflector.FieldsExtraUtils; 7 | 8 | import java.lang.reflect.Field; 9 | import java.util.List; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertAll; 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | public class FieldsExtraUtilsTest { 15 | @Test 16 | void getAllPrivateFieldsTest() { 17 | 18 | List fields = FieldsExtraUtils.getAllPrivateFields(CustomTestClassForType.class); 19 | 20 | assertAll("privateFields", 21 | () -> assertEquals(fields.get(0).getName(), TestConstant.STRING_FIELD), 22 | () -> assertEquals(fields.get(1).getName(), TestConstant.OBJECT_FIELD), 23 | () -> assertEquals(fields.get(2).getName(), TestConstant.FLOAT_FIELD), 24 | () -> assertEquals(fields.size(), 3) 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] Oleksandr Klymenko 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/reflector/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Method; 5 | 6 | public final class SecurityUtils { 7 | 8 | private SecurityUtils() {} 9 | 10 | /** 11 | * Sets a method to be accessible. 12 | * 13 | * @param method the method to be set accessible 14 | * @throws NullPointerException if the method is null 15 | */ 16 | public static void setMethodAccessible(Method method) { 17 | if (method == null) { 18 | throw new NullPointerException("Method cannot be null"); 19 | } 20 | method.setAccessible(true); 21 | } 22 | 23 | /** 24 | * Sets a constructor to be accessible. 25 | * 26 | * @param constructor the constructor to be set accessible 27 | * @throws NullPointerException if the constructor is null 28 | */ 29 | public static void setConstructorAccessible(Constructor constructor) { 30 | if (constructor == null) { 31 | throw new NullPointerException("Constructor cannot be null"); 32 | } 33 | constructor.setAccessible(true); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/MiscellaneousUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reflector.MiscellaneousUtils; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | import java.lang.reflect.InvocationTargetException; 9 | 10 | public class MiscellaneousUtilsTest { 11 | 12 | private static class SampleClass { 13 | public SampleClass() {} 14 | } 15 | 16 | @Test 17 | public void testNewInstance() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { 18 | SampleClass instance = MiscellaneousUtils.newInstance(SampleClass.class); 19 | assertNotNull(instance); 20 | } 21 | 22 | @Test 23 | public void testNewInstance_nullClass() { 24 | assertThrows(NullPointerException.class, () -> { 25 | MiscellaneousUtils.newInstance(null); 26 | }); 27 | } 28 | 29 | @Test 30 | public void testGetArrayComponentType() { 31 | Class arrayClass = int[].class; 32 | Class componentType = MiscellaneousUtils.getArrayComponentType(arrayClass); 33 | assertEquals(int.class, componentType); 34 | } 35 | 36 | @Test 37 | public void testGetArrayComponentType_nullArrayClass() { 38 | assertThrows(NullPointerException.class, () -> { 39 | MiscellaneousUtils.getArrayComponentType(null); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/SecurityUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reflector.SecurityUtils; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Method; 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | public class SecurityUtilsTest { 11 | 12 | private static class SampleClass { 13 | private SampleClass() {} 14 | private void privateMethod() {} 15 | } 16 | 17 | @Test 18 | public void testSetMethodAccessible() throws NoSuchMethodException { 19 | Method method = SampleClass.class.getDeclaredMethod("privateMethod"); 20 | assertFalse(method.isAccessible()); 21 | SecurityUtils.setMethodAccessible(method); 22 | assertTrue(method.isAccessible()); 23 | } 24 | 25 | @Test 26 | public void testSetMethodAccessible_nullMethod() { 27 | assertThrows(NullPointerException.class, () -> { 28 | SecurityUtils.setMethodAccessible(null); 29 | }); 30 | } 31 | 32 | @Test 33 | public void testSetConstructorAccessible() throws NoSuchMethodException { 34 | Constructor constructor = SampleClass.class.getDeclaredConstructor(); 35 | assertFalse(constructor.isAccessible()); 36 | SecurityUtils.setConstructorAccessible(constructor); 37 | assertTrue(constructor.isAccessible()); 38 | } 39 | 40 | @Test 41 | public void testSetConstructorAccessible_nullConstructor() { 42 | assertThrows(NullPointerException.class, () -> { 43 | SecurityUtils.setConstructorAccessible(null); 44 | }); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/PackageUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.annotation.ClassAnnotation; 4 | import org.common.reflector.data.annotation.CustomMethodAnnotation; 5 | import org.common.reflector.util.TestConstant; 6 | import org.junit.jupiter.api.Test; 7 | import org.reflector.PackageUtils; 8 | 9 | import java.io.IOException; 10 | import java.net.URISyntaxException; 11 | import java.util.List; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | 16 | public class PackageUtilsTest { 17 | @Test 18 | public void findAllClassesByPackageTest() throws IOException, URISyntaxException, ClassNotFoundException { 19 | List> classesByPackage = PackageUtils.getClassesByPackage(TestConstant.REFLECTOR_PACKAGE); 20 | assertTrue(classesByPackage.size() >= 7); 21 | } 22 | 23 | @Test 24 | public void getAnnotatedClassesTest() throws IOException, URISyntaxException, ClassNotFoundException { 25 | List> classes = PackageUtils.getAllAnnotatedClassesByPackage(TestConstant.REFLECTOR_DATA_PACKAGE, ClassAnnotation.class); 26 | int expectedAnnotationClassesQuantity = 2; 27 | assertEquals(expectedAnnotationClassesQuantity, classes.size()); 28 | } 29 | 30 | @Test 31 | public void getNotAnnotatedClassesTest() throws IOException, URISyntaxException, ClassNotFoundException { 32 | List> classes = PackageUtils.getAllAnnotatedClassesByPackage(TestConstant.REFLECTOR_DATA_PACKAGE, CustomMethodAnnotation.class); 33 | int expectedAnnotationClassesQuantity = 0; 34 | assertEquals(expectedAnnotationClassesQuantity, classes.size()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/MiscellaneousUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | 6 | public final class MiscellaneousUtils { 7 | 8 | private MiscellaneousUtils() {} 9 | 10 | /** 11 | * Creates a new instance of a class using its no-argument constructor. 12 | * 13 | * @param clazz the class of which to create an instance 14 | * @return a new instance of the specified class 15 | * @throws NullPointerException if the class is null 16 | * @throws InstantiationException if the class represents an abstract class, an interface, an array class, a primitive type, or void; 17 | * or if the class has no nullable constructor 18 | * @throws IllegalAccessException if the class or its nullable constructor is not accessible 19 | * @throws InvocationTargetException if the nullable constructor throws an exception 20 | */ 21 | public static T newInstance(Class clazz) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { 22 | if (clazz == null) { 23 | throw new NullPointerException("Class cannot be null"); 24 | } 25 | Constructor constructor = clazz.getDeclaredConstructor(); 26 | constructor.setAccessible(true); 27 | return constructor.newInstance(); 28 | } 29 | 30 | /** 31 | * Retrieves the component type of an array class. 32 | * 33 | * @param arrayClass the array class 34 | * @return the component type of the array class 35 | * @throws NullPointerException if the array class is null 36 | */ 37 | public static Class getArrayComponentType(Class arrayClass) { 38 | if (arrayClass == null) { 39 | throw new NullPointerException("Array class cannot be null"); 40 | } 41 | return arrayClass.getComponentType(); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/ConstructorUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.SimpleEntryClass; 4 | import org.junit.jupiter.api.Test; 5 | import org.reflector.ConstructorUtils; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Modifier; 9 | import java.lang.reflect.Parameter; 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | public class ConstructorUtilsTest { 13 | 14 | private static class SampleClass { 15 | public SampleClass(String param1, int param2) {} 16 | } 17 | 18 | @Test 19 | public void testGetConstructorParameters() throws NoSuchMethodException { 20 | Constructor constructor = SampleClass.class.getConstructor(String.class, int.class); 21 | Parameter[] parameters = ConstructorUtils.getConstructorParameters(constructor); 22 | assertEquals(2, parameters.length); 23 | assertEquals("arg0", parameters[0].getName()); 24 | assertEquals(int.class, parameters[1].getType()); 25 | } 26 | 27 | @Test 28 | public void testGetConstructorParameters_nullConstructor() { 29 | assertThrows(NullPointerException.class, () -> { 30 | ConstructorUtils.getConstructorParameters(null); 31 | }); 32 | } 33 | 34 | @Test 35 | public void testGetConstructorModifiers() throws NoSuchMethodException { 36 | Constructor constructor = SampleClass.class.getConstructor(String.class, int.class); 37 | int modifiers = ConstructorUtils.getConstructorModifiers(constructor); 38 | assertTrue(Modifier.isPublic(modifiers)); 39 | } 40 | 41 | @Test 42 | public void testGetConstructorModifiers_nullConstructor() { 43 | assertThrows(NullPointerException.class, () -> { 44 | ConstructorUtils.getConstructorModifiers(null); 45 | }); 46 | } 47 | 48 | @Test 49 | public void getConstructors() { 50 | Constructor[] constructors = ConstructorUtils.getConstructors(SimpleEntryClass.class); 51 | assertEquals(constructors.length, 3); 52 | } 53 | 54 | @Test 55 | public void getDeclaredConstructors() { 56 | Constructor[] constructors = ConstructorUtils.getDeclaredConstructors(SimpleEntryClass.class); 57 | assertEquals(constructors.length, 4); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/util/TestConstant.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.util; 2 | 3 | public final class TestConstant { 4 | private TestConstant() {} 5 | 6 | public static final String NOT_PRIVATE_FIELD = "notPrivateField"; 7 | public static final String ANNOTATED_METHOD_ANNOTATED_CLASS = "annotatedMethod"; 8 | public static final String GET_CLASS_METHOD_METHOD_ANNOTATED_CLASS = "getClass"; 9 | public static final String WAIT_METHOD_ANNOTATED_CLASS = "wait"; 10 | public static final String HASH_CODE_METHOD_ANNOTATED_CLASS = "hashCode"; 11 | public static final String EQUALS_METHOD_ANNOTATED_CLASS = "equals"; 12 | public static final String NOTIFY_ALL_METHOD_ANNOTATED_CLASS = "notifyAll"; 13 | public static final String TO_STRING_METHOD_ANNOTATED_CLASS = "toString"; 14 | public static final String NOTIFY_METHOD_ANNOTATED_CLASS = "notify"; 15 | public static final String SIMPLE_CLASS_SIMPLE_VALUE = "SimpleClassSimpleValue"; 16 | public static final String CUSTOM_TEST_INVOKE_CLASS = "CustomTestInvokeClass"; 17 | public static final String REFLECTOR_DATA_PACKAGE = "org.common.reflector.data"; 18 | public static final String REFLECTOR_PACKAGE = "org.common.reflector"; 19 | public static final String ANNOTATED_METHOD_NAME = "annotatedMethod"; 20 | public static final String DO_SOMETHING_METHOD_NAME = "doSomething"; 21 | public static final String ENTRY_KEY = "entryKey"; 22 | public static final String ENTRY_VALUE = "entryValue"; 23 | public static final String ENTRY_INFO = "entryInfo"; 24 | public static final String KEY = "key"; 25 | public static final String VALUE = "value"; 26 | public static final String SIMPLE_VALUE = "SimpleValue"; 27 | public static final String SET_VALUE = "setValue"; 28 | public static final String GET_VALUE = "getValue"; 29 | public static final String CUSTOM_TEST_INVOKE_CLASS_PACKAGE = "org.common.reflector.data.CustomTestInvokeClass"; 30 | public static final String STRING_FIELD = "stringField"; 31 | public static final String OBJECT_FIELD = "objectField"; 32 | public static final String FLOAT_FIELD = "floatField"; 33 | public static final String JAVA_LANG_OBJECT = "java.lang.Object"; 34 | public static final String ORG_COMMON_REFLECTOR_DATA = "org.common.reflector.data"; 35 | public static final String SOME_VALUE = "SomeValue"; 36 | public static final String ONE_CONSTANT = "oneConstant"; 37 | public static final String METHOD_NAME_GET_ID = "getId"; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/MethodEnhancementsUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.Method; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public final class MethodEnhancementsUtils { 10 | 11 | private MethodEnhancementsUtils() {} 12 | 13 | /** 14 | * Retrieves methods annotated with a specific annotation. 15 | * 16 | * @param clazz the class from which to retrieve methods 17 | * @param annotationClass the Class object corresponding to the annotation type 18 | * @return a list of methods annotated with the specified annotation 19 | * @throws NullPointerException if the clazz or annotationClass is null 20 | */ 21 | public static List getAnnotatedMethods(final Class clazz, Class annotationClass) { 22 | if (clazz == null || annotationClass == null) { 23 | throw new NullPointerException("Class and annotation class cannot be null"); 24 | } 25 | 26 | List annotatedMethods = new ArrayList<>(); 27 | for (Method method : clazz.getDeclaredMethods()) { 28 | if (method.isAnnotationPresent(annotationClass)) { 29 | annotatedMethods.add(method); 30 | } 31 | } 32 | return annotatedMethods; 33 | } 34 | 35 | /** 36 | * Retrieves constructors annotated with a specific annotation. 37 | * 38 | * @param clazz the class from which to retrieve constructors 39 | * @param annotationClass the Class object corresponding to the annotation type 40 | * @return a list of constructors annotated with the specified annotation 41 | * @throws NullPointerException if the clazz or annotationClass is null 42 | */ 43 | public static List> getAnnotatedConstructors(final Class clazz, Class annotationClass) { 44 | if (clazz == null || annotationClass == null) { 45 | throw new NullPointerException("Class and annotation class cannot be null"); 46 | } 47 | 48 | List> annotatedConstructors = new ArrayList<>(); 49 | for (Constructor constructor : clazz.getDeclaredConstructors()) { 50 | if (constructor.isAnnotationPresent(annotationClass)) { 51 | annotatedConstructors.add(constructor); 52 | } 53 | } 54 | return annotatedConstructors; 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | 8 | name: Java CI with Gradle 9 | 10 | on: 11 | push: 12 | branches: [ "master" ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: read 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up JDK 17 26 | uses: actions/setup-java@v4 27 | with: 28 | java-version: '17' 29 | distribution: 'temurin' 30 | 31 | # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. 32 | # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md 33 | - name: Setup Gradle 34 | uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 35 | 36 | - name: Make gradlew executable 37 | run: chmod +x ./gradlew 38 | 39 | - name: Build with Gradle Wrapper 40 | run: ./gradlew build 41 | 42 | # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). 43 | # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. 44 | # 45 | # - name: Setup Gradle 46 | # uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 47 | # with: 48 | # gradle-version: '8.5' 49 | # 50 | # - name: Build with Gradle 8.5 51 | # run: gradle build 52 | 53 | 54 | dependency-submission: 55 | 56 | runs-on: ubuntu-latest 57 | permissions: 58 | contents: write 59 | 60 | steps: 61 | - uses: actions/checkout@v4 62 | - name: Set up JDK 17 63 | uses: actions/setup-java@v4 64 | with: 65 | java-version: '17' 66 | distribution: 'temurin' 67 | 68 | # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. 69 | # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md 70 | - name: Generate and submit dependency graph 71 | uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 72 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/InvokeUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.CustomTestInvokeClass; 4 | import org.common.reflector.util.TestConstant; 5 | import org.junit.jupiter.api.Test; 6 | import org.reflector.InvokeUtils; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertAll; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 11 | 12 | public class InvokeUtilsTest { 13 | @Test 14 | void invokeClassMethodTest() { 15 | CustomTestInvokeClass instance = (CustomTestInvokeClass) InvokeUtils.invokeInstance( 16 | TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE); 17 | Object ret1 = InvokeUtils.invokeMethod(instance, TestConstant.SET_VALUE, 18 | new Class[]{String.class}, new String[]{TestConstant.SIMPLE_VALUE}); 19 | String ret2 = (String) InvokeUtils.invokeMethod(instance, TestConstant.GET_VALUE, null, null); 20 | 21 | assertAll("invokedMethodValues", 22 | () -> assertEquals(ret1, null), 23 | () -> assertEquals(ret2, TestConstant.SIMPLE_VALUE) 24 | ); 25 | } 26 | 27 | @Test 28 | void invokeClassInstanceTest() { 29 | CustomTestInvokeClass instance = (CustomTestInvokeClass) InvokeUtils.invokeInstance( 30 | TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE); 31 | assertAll("classInstance", 32 | () -> assertNotEquals(instance, null) 33 | ); 34 | 35 | } 36 | 37 | @Test 38 | void invokeSingleClassMethodTest() { 39 | CustomTestInvokeClass instance = (CustomTestInvokeClass) InvokeUtils.invokeInstance(TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE); 40 | Object ret1 = InvokeUtils.invokeSingleMethod(instance, TestConstant.SET_VALUE, String.class, TestConstant.SIMPLE_VALUE); 41 | String ret2 = (String) InvokeUtils.invokeMethod(instance, TestConstant.GET_VALUE, null, null); 42 | 43 | assertAll("singleClassMethodValue", 44 | () -> assertEquals(ret1, null), 45 | () -> assertEquals(ret2, TestConstant.SIMPLE_VALUE) 46 | ); 47 | } 48 | 49 | @Test 50 | void invokeClassInstanceWithParametersTest() { 51 | Object[] obj = {TestConstant.SOME_VALUE}; 52 | CustomTestInvokeClass instance = (CustomTestInvokeClass) InvokeUtils.invokeInstance( 53 | TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE, obj); 54 | assertAll("classMultipleParametersInstance", 55 | () -> assertNotEquals(instance, null), 56 | () -> assertEquals(instance.getValue(), obj[0]) 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/GeneralUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | public final class GeneralUtils { 4 | 5 | private GeneralUtils() {} 6 | 7 | /** 8 | * Checks if a given class is an interface. 9 | * 10 | * @param clazz the class to check 11 | * @return true if the class is an interface, false otherwise 12 | */ 13 | public static boolean isInterface(Class clazz) { 14 | if (clazz == null) { 15 | throw new NullPointerException("Class cannot be null"); 16 | } 17 | return clazz.isInterface(); 18 | } 19 | 20 | /** 21 | * Checks if a given class is an array. 22 | * 23 | * @param clazz the class to check 24 | * @return true if the class is an array, false otherwise 25 | */ 26 | public static boolean isArray(Class clazz) { 27 | if (clazz == null) { 28 | throw new NullPointerException("Class cannot be null"); 29 | } 30 | return clazz.isArray(); 31 | } 32 | 33 | /** 34 | * Checks if a given class is an enum. 35 | * 36 | * @param clazz the class to check 37 | * @return true if the class is an enum, false otherwise 38 | */ 39 | public static boolean isEnum(Class clazz) { 40 | if (clazz == null) { 41 | throw new NullPointerException("Class cannot be null"); 42 | } 43 | return clazz.isEnum(); 44 | } 45 | 46 | /** 47 | * Checks if a given class is an annotation. 48 | * 49 | * @param clazz the class to check 50 | * @return true if the class is an annotation, false otherwise 51 | */ 52 | public static boolean isAnnotation(Class clazz) { 53 | if (clazz == null) { 54 | throw new NullPointerException("Class cannot be null"); 55 | } 56 | return clazz.isAnnotation(); 57 | } 58 | 59 | /** 60 | * Checks if a given class is anonymous. 61 | * 62 | * @param clazz the class to check 63 | * @return true if the class is anonymous, false otherwise 64 | */ 65 | public static boolean isAnonymousClass(Class clazz) { 66 | if (clazz == null) { 67 | throw new NullPointerException("Class cannot be null"); 68 | } 69 | return clazz.isAnonymousClass(); 70 | } 71 | 72 | /** 73 | * Retrieves the inner classes declared within a class. 74 | * 75 | * @param clazz the class to check 76 | * @return an array of inner classes declared within the class 77 | */ 78 | public static Class[] getInnerClasses(Class clazz) { 79 | if (clazz == null) { 80 | throw new NullPointerException("Class cannot be null"); 81 | } 82 | return clazz.getDeclaredClasses(); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/ObjectUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Modifier; 8 | 9 | public final class ObjectUtils { 10 | private static final Logger LOGGER = LoggerFactory.getLogger(ObjectUtils.class); 11 | 12 | private ObjectUtils() { 13 | } 14 | 15 | /** 16 | * Checks if the type of a field is a primitive type or a wrapper class. 17 | * 18 | * @param field the field to check 19 | * @return true if the type of the field is a primitive type or a wrapper class, false otherwise 20 | */ 21 | public static boolean isFieldPrimitiveType(final Field field) { 22 | return field.getType().isPrimitive() || 23 | field.getType() == String.class || 24 | field.getType() == Integer.class || 25 | field.getType() == Long.class || 26 | field.getType() == Boolean.class || 27 | field.getType() == Byte.class || 28 | field.getType() == Character.class || 29 | field.getType() == Short.class || 30 | field.getType() == Float.class || 31 | field.getType() == Double.class; 32 | } 33 | 34 | /** 35 | * Creates a deep copy of the given object. 36 | * 37 | * @param object the object to be copied 38 | * @return the deep copy of the object 39 | * @throws IllegalStateException if copying fails 40 | */ 41 | public static Object copy(final Object object) { 42 | Object copyObj = null; 43 | try { 44 | try { 45 | copyObj = object.getClass().newInstance(); 46 | } catch (Exception ex) { 47 | LOGGER.error("Error copy for object{{}}", object); 48 | return null; 49 | } 50 | for (Field field : object.getClass().getDeclaredFields()) { 51 | field.setAccessible(true); 52 | if (field.get(object) == null || Modifier.isFinal(field.getModifiers())) { 53 | continue; 54 | } 55 | if (isFieldPrimitiveType(field)) { 56 | field.set(copyObj, field.get(object)); 57 | } else { 58 | Object childObj = field.get(object); 59 | field.set(copyObj, (childObj == object) ? copyObj : copy(field.get(object))); 60 | } 61 | } 62 | } catch (Exception e) { 63 | LOGGER.error("Error during copy object", e); 64 | throw new IllegalStateException("Failed to get declared methods for Class [" + object.getClass().getName() + "] from ClassLoader [" + object.getClass().getClassLoader() + "]", e); 65 | } 66 | return copyObj; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/MethodEnhancementsUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reflector.MethodEnhancementsUtils; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.reflect.Constructor; 11 | import java.lang.reflect.Method; 12 | import java.util.List; 13 | 14 | public class MethodEnhancementsUtilsTest { 15 | 16 | @Retention(RetentionPolicy.RUNTIME) 17 | private @interface TestAnnotation {} 18 | 19 | private static class SampleClass { 20 | @TestAnnotation 21 | public SampleClass() {} 22 | 23 | @TestAnnotation 24 | public void annotatedMethod() {} 25 | 26 | public void nonAnnotatedMethod() {} 27 | } 28 | 29 | @Test 30 | public void testGetAnnotatedMethods() { 31 | List methods = MethodEnhancementsUtils.getAnnotatedMethods(SampleClass.class, TestAnnotation.class); 32 | assertEquals(1, methods.size()); 33 | assertEquals("annotatedMethod", methods.get(0).getName()); 34 | } 35 | 36 | @Test 37 | public void testGetAnnotatedMethods_noAnnotations() { 38 | List methods = MethodEnhancementsUtils.getAnnotatedMethods(String.class, TestAnnotation.class); 39 | assertEquals(0, methods.size()); 40 | } 41 | 42 | @Test 43 | public void testGetAnnotatedMethods_nullClass() { 44 | assertThrows(NullPointerException.class, () -> { 45 | MethodEnhancementsUtils.getAnnotatedMethods(null, TestAnnotation.class); 46 | }); 47 | } 48 | 49 | @Test 50 | public void testGetAnnotatedMethods_nullAnnotationClass() { 51 | assertThrows(NullPointerException.class, () -> { 52 | MethodEnhancementsUtils.getAnnotatedMethods(SampleClass.class, null); 53 | }); 54 | } 55 | 56 | @Test 57 | public void testGetAnnotatedConstructors() { 58 | List> constructors = MethodEnhancementsUtils.getAnnotatedConstructors(SampleClass.class, TestAnnotation.class); 59 | assertEquals(1, constructors.size()); 60 | } 61 | 62 | @Test 63 | public void testGetAnnotatedConstructors_noAnnotations() { 64 | List> constructors = MethodEnhancementsUtils.getAnnotatedConstructors(String.class, TestAnnotation.class); 65 | assertEquals(0, constructors.size()); 66 | } 67 | 68 | @Test 69 | public void testGetAnnotatedConstructors_nullClass() { 70 | assertThrows(NullPointerException.class, () -> { 71 | MethodEnhancementsUtils.getAnnotatedConstructors(null, TestAnnotation.class); 72 | }); 73 | } 74 | 75 | @Test 76 | public void testGetAnnotatedConstructors_nullAnnotationClass() { 77 | assertThrows(NullPointerException.class, () -> { 78 | MethodEnhancementsUtils.getAnnotatedConstructors(SampleClass.class, null); 79 | }); 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/FieldsExtraUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Field; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public final class FieldsExtraUtils { 11 | 12 | private FieldsExtraUtils() {} 13 | 14 | /** 15 | * Retrieves all private fields of a given class, including fields declared in its superclasses. 16 | * 17 | * @param clazz the class from which to retrieve private fields 18 | * @return a list of all private fields of the specified class 19 | * @throws NullPointerException if the clazz is null 20 | */ 21 | public static List getAllPrivateFields(final Class clazz) { 22 | return FieldUtils.getAllPrivateFields(clazz); 23 | } 24 | 25 | /** 26 | * Retrieves all private fields of a given class, including fields declared in its superclasses, 27 | * and returns them as a map with field names as keys. 28 | * 29 | * @param clazz the class from which to retrieve private fields 30 | * @return a map of all private fields of the specified class with field names as keys 31 | * @throws NullPointerException if the clazz is null 32 | */ 33 | public static Map getAllPrivateFieldsMap(final Class clazz) { 34 | List allFields = getAllPrivateFields(clazz); 35 | return getFieldsMap(allFields); 36 | } 37 | 38 | /** 39 | * Helper method to convert a list of fields to a map with field names as keys. 40 | * 41 | * @param fields the list of fields to convert to a map 42 | * @return a map with field names as keys and Field objects as values 43 | */ 44 | public static Map getFieldsMap(final List fields) { 45 | Map map = new HashMap<>(); 46 | for (Field field : fields) { 47 | map.put(field.getName(), field); 48 | } 49 | return map; 50 | } 51 | 52 | /** 53 | * Retrieves all fields annotated with a specific annotation from a given class, 54 | * including fields declared in its superclasses. 55 | * 56 | * @param type the class from which to retrieve annotated fields 57 | * @param annotation the annotation class to search for 58 | * @return a list of fields annotated with the specified annotation 59 | * @throws NullPointerException if type or annotation is null 60 | */ 61 | public static List getAllAnnotatedFields(final Class type, final Class annotation) { 62 | if (type == null || annotation == null) { 63 | throw new NullPointerException("Class type and annotation cannot be null"); 64 | } 65 | 66 | List annotatedFields = new ArrayList<>(); 67 | for (Field field : FieldUtils.getAllFields(type)) { 68 | if (field.isAnnotationPresent(annotation)) { 69 | field.setAccessible(true); 70 | annotatedFields.add(field); 71 | } 72 | } 73 | return annotatedFields; 74 | } 75 | } -------------------------------------------------------------------------------- /src/main/java/org/reflector/ConstructorUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Parameter; 8 | 9 | public final class ConstructorUtils { 10 | private static final Logger LOGGER = LoggerFactory.getLogger(ConstructorUtils.class); 11 | 12 | private ConstructorUtils() { 13 | } 14 | 15 | /** 16 | * Retrieves the parameters of a constructor. 17 | * 18 | * @param constructor the constructor from which to retrieve parameters 19 | * @return an array of parameters of the constructor 20 | * @throws NullPointerException if the constructor is null 21 | */ 22 | public static Parameter[] getConstructorParameters(Constructor constructor) { 23 | if (constructor == null) { 24 | throw new NullPointerException("Constructor cannot be null"); 25 | } 26 | return constructor.getParameters(); 27 | } 28 | 29 | /** 30 | * Retrieves the modifiers of a constructor. 31 | * 32 | * @param constructor the constructor from which to retrieve modifiers 33 | * @return an integer representing the modifiers of the constructor 34 | * @throws NullPointerException if the constructor is null 35 | */ 36 | public static int getConstructorModifiers(Constructor constructor) { 37 | if (constructor == null) { 38 | throw new NullPointerException("Constructor cannot be null"); 39 | } 40 | return constructor.getModifiers(); 41 | } 42 | 43 | /** 44 | * Retrieves all public constructors of the specified class. 45 | * 46 | * @param clazz the class from which to retrieve constructors 47 | * @return an array of public constructors of the specified class 48 | * @throws NullPointerException if the class is null 49 | */ 50 | public static Constructor[] getConstructors(final Class clazz) { 51 | if (clazz == null) { 52 | throw new NullPointerException("Class type cannot be null"); 53 | } 54 | 55 | try { 56 | return clazz.getConstructors(); 57 | } catch (Exception e) { 58 | LOGGER.error("Error retrieving constructors for class '{}'", clazz.getName(), e); 59 | throw e; 60 | } 61 | } 62 | 63 | /** 64 | * Retrieves all declared constructors of the specified class, including public, protected, default (package), and private constructors. 65 | * 66 | * @param clazz the class from which to retrieve declared constructors 67 | * @return an array of declared constructors of the specified class 68 | * @throws NullPointerException if the class is null 69 | */ 70 | public static Constructor[] getDeclaredConstructors(final Class clazz) { 71 | if (clazz == null) { 72 | throw new NullPointerException("Class type cannot be null"); 73 | } 74 | 75 | try { 76 | return clazz.getDeclaredConstructors(); 77 | } catch (Exception e) { 78 | LOGGER.error("Error retrieving declared constructors for class '{}'", clazz.getName(), e); 79 | throw e; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/GeneralUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reflector.GeneralUtils; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | 9 | public class GeneralUtilsTest { 10 | 11 | private static interface SampleInterface { 12 | } 13 | 14 | private static class SampleClass { 15 | private class InnerClass { 16 | } 17 | } 18 | 19 | private enum SampleEnum {ONE, TWO, THREE} 20 | 21 | @interface SampleAnnotation { 22 | } 23 | 24 | @Test 25 | public void testIsInterface() { 26 | assertTrue(GeneralUtils.isInterface(SampleInterface.class)); 27 | assertFalse(GeneralUtils.isInterface(SampleClass.class)); 28 | } 29 | 30 | @Test 31 | public void testIsArray() { 32 | assertTrue(GeneralUtils.isArray(int[].class)); 33 | assertFalse(GeneralUtils.isArray(int.class)); 34 | } 35 | 36 | @Test 37 | public void testIsEnum() { 38 | assertTrue(GeneralUtils.isEnum(SampleEnum.class)); 39 | assertFalse(GeneralUtils.isEnum(SampleClass.class)); 40 | } 41 | 42 | @Test 43 | public void testIsAnnotation() { 44 | assertTrue(GeneralUtils.isAnnotation(SampleAnnotation.class)); 45 | assertFalse(GeneralUtils.isAnnotation(SampleClass.class)); 46 | } 47 | 48 | @Test 49 | public void testIsAnonymousClass() { 50 | SampleClass sampleClass = new SampleClass() { 51 | }; 52 | assertTrue(GeneralUtils.isAnonymousClass(sampleClass.getClass())); 53 | assertFalse(GeneralUtils.isAnonymousClass(SampleClass.class)); 54 | } 55 | 56 | @Test 57 | public void testGetInnerClasses() { 58 | Class[] innerClasses = GeneralUtils.getInnerClasses(SampleClass.class); 59 | assertEquals(1, innerClasses.length); 60 | assertEquals("org.common.reflector.utils.GeneralUtilsTest$SampleClass$InnerClass", innerClasses[0].getName()); 61 | } 62 | 63 | @Test 64 | public void testIsInterface_nullClass() { 65 | assertThrows(NullPointerException.class, () -> { 66 | GeneralUtils.isInterface(null); 67 | }); 68 | } 69 | 70 | @Test 71 | public void testIsArray_nullClass() { 72 | assertThrows(NullPointerException.class, () -> { 73 | GeneralUtils.isArray(null); 74 | }); 75 | } 76 | 77 | @Test 78 | public void testIsEnum_nullClass() { 79 | assertThrows(NullPointerException.class, () -> { 80 | GeneralUtils.isEnum(null); 81 | }); 82 | } 83 | 84 | @Test 85 | public void testIsAnnotation_nullClass() { 86 | assertThrows(NullPointerException.class, () -> { 87 | GeneralUtils.isAnnotation(null); 88 | }); 89 | } 90 | 91 | @Test 92 | public void testIsAnonymousClass_nullClass() { 93 | assertThrows(NullPointerException.class, () -> { 94 | GeneralUtils.isAnonymousClass(null); 95 | }); 96 | } 97 | 98 | @Test 99 | public void testGetInnerClasses_nullClass() { 100 | assertThrows(NullPointerException.class, () -> { 101 | GeneralUtils.getInnerClasses(null); 102 | }); 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/ConstructorUtilsGetConstructorsAndDeclaredTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reflector.ConstructorUtils; 5 | 6 | import java.lang.reflect.Constructor; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertThrows; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | public class ConstructorUtilsGetConstructorsAndDeclaredTest { 13 | private static class TestClass { 14 | public TestClass() {} 15 | 16 | public TestClass(int x) {} 17 | 18 | private TestClass(String s) {} 19 | } 20 | 21 | @Test 22 | public void testGetConstructors_PublicConstructors() { 23 | Constructor[] constructors = ConstructorUtils.getConstructors(TestClass.class); 24 | 25 | assertEquals(2, constructors.length); 26 | 27 | assertTrue(constructors[0].getParameterCount() == 0 || constructors[0].getParameterCount() == 1); 28 | assertTrue(constructors[1].getParameterCount() == 0 || constructors[1].getParameterCount() == 1); 29 | } 30 | 31 | @Test 32 | public void testGetConstructors_NoPublicConstructors() { 33 | class PrivateConstructorClass { 34 | private PrivateConstructorClass() {} 35 | } 36 | 37 | Constructor[] constructors = ConstructorUtils.getConstructors(PrivateConstructorClass.class); 38 | 39 | assertEquals(0, constructors.length); 40 | } 41 | 42 | @Test 43 | public void testGetConstructors_ObjectClass() { 44 | Constructor[] constructors = ConstructorUtils.getConstructors(Object.class); 45 | 46 | assertEquals(1, constructors.length); 47 | assertEquals(0, constructors[0].getParameterCount()); 48 | } 49 | 50 | @Test 51 | public void testGetConstructors_NullClass() { 52 | assertThrows(NullPointerException.class, () -> { 53 | ConstructorUtils.getConstructors(null); 54 | }); 55 | } 56 | 57 | @Test 58 | public void testGetDeclaredConstructors() { 59 | Constructor[] constructors = ConstructorUtils.getDeclaredConstructors(TestClass.class); 60 | 61 | assertEquals(3, constructors.length); 62 | 63 | assertTrue(constructors[0].getParameterCount() == 0 || constructors[0].getParameterCount() == 1 || constructors[0].getParameterCount() == 2); 64 | assertTrue(constructors[1].getParameterCount() == 0 || constructors[1].getParameterCount() == 1 || constructors[1].getParameterCount() == 2); 65 | assertTrue(constructors[2].getParameterCount() == 0 || constructors[2].getParameterCount() == 1 || constructors[2].getParameterCount() == 2); 66 | } 67 | 68 | @Test 69 | public void testGetDeclaredConstructors_NoDeclaredConstructors() { 70 | class NoConstructorClass {} 71 | 72 | Constructor[] constructors = ConstructorUtils.getDeclaredConstructors(NoConstructorClass.class); 73 | 74 | assertEquals(1, constructors.length); // Implicit default constructor 75 | } 76 | 77 | @Test 78 | public void testGetDeclaredConstructors_ObjectClass() { 79 | Constructor[] constructors = ConstructorUtils.getDeclaredConstructors(Object.class); 80 | 81 | assertEquals(1, constructors.length); 82 | assertEquals(0, constructors[0].getParameterCount()); 83 | } 84 | 85 | @Test 86 | public void testGetDeclaredConstructors_NullClass() { 87 | assertThrows(NullPointerException.class, () -> { 88 | ConstructorUtils.getDeclaredConstructors(null); 89 | }); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/PackageUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import org.reflector.util.ReflectionConstant; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.net.URL; 10 | import java.util.ArrayList; 11 | import java.util.Enumeration; 12 | import java.util.List; 13 | 14 | public final class PackageUtils { 15 | 16 | private static final ClassLoader CLASSLOADER; 17 | 18 | static { 19 | final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); 20 | CLASSLOADER = (threadClassLoader != null) ? threadClassLoader : PackageUtils.class.getClassLoader(); 21 | } 22 | 23 | private PackageUtils() {} 24 | 25 | /** 26 | * Retrieves all classes within a package. 27 | * 28 | * @param packageName the name of the package 29 | * @return a list of classes within the specified package 30 | * @throws ClassNotFoundException if a class cannot be found 31 | * @throws IOException if an I/O error occurs 32 | * @throws URISyntaxException if a URI syntax error occurs 33 | */ 34 | public static List> getClassesByPackage(final String packageName) throws ClassNotFoundException, IOException, URISyntaxException { 35 | // Convert package name to directory path 36 | String path = packageName.replace(ReflectionConstant.DOT_SYMBOL, ReflectionConstant.SLASH); 37 | // Get resources within the package 38 | Enumeration resources = CLASSLOADER.getResources(path); 39 | List directories = new ArrayList<>(); 40 | // Store directories containing resources 41 | while (resources.hasMoreElements()) { 42 | directories.add(new File(new URI(resources.nextElement().toString()).getPath())); 43 | } 44 | // Store classes found in directories 45 | List> classes = new ArrayList<>(); 46 | for (File directory : directories) { 47 | classes.addAll(getClassesByDirectoryAndPackage(directory, packageName)); 48 | } 49 | return classes; 50 | } 51 | 52 | /** 53 | * Retrieves all classes within a directory and its subdirectories. 54 | * 55 | * @param directory the directory to search for classes 56 | * @param packageName the name of the package 57 | * @return a list of classes within the specified directory and package 58 | * @throws ClassNotFoundException if a class cannot be found 59 | */ 60 | public static List> getClassesByDirectoryAndPackage(final File directory, final String packageName) throws ClassNotFoundException { 61 | List> classes = new ArrayList<>(); 62 | if (!directory.exists()) { 63 | return classes; 64 | } 65 | File[] files = directory.listFiles(); 66 | if (files != null) { 67 | for (File file : files) { 68 | if (file.isDirectory()) { 69 | classes.addAll(getClassesByDirectoryAndPackage(file, packageName + ReflectionConstant.DOT + file.getName())); 70 | } else if (file.getName().endsWith(ReflectionConstant.CLASS)) { 71 | classes.add(Class.forName(packageName + ReflectionConstant.DOT + file.getName().substring(0, file.getName().length() - ReflectionConstant.CLASS_NAME_CONSTANT))); 72 | } 73 | } 74 | } 75 | return classes; 76 | } 77 | 78 | /** 79 | * Retrieves all classes within a package that are annotated with a specific annotation. 80 | * 81 | * @param packageName the name of the package 82 | * @param annotation the annotation to filter classes by 83 | * @return a list of classes within the specified package that are annotated with the specified annotation 84 | * @throws IOException if an I/O error occurs 85 | * @throws URISyntaxException if a URI syntax error occurs 86 | * @throws ClassNotFoundException if a class cannot be found 87 | */ 88 | public static List> getAllAnnotatedClassesByPackage(final String packageName, final Class annotation) throws IOException, URISyntaxException, ClassNotFoundException { 89 | List> classesByPackage = getClassesByPackage(packageName); 90 | List> classes = new ArrayList<>(); 91 | for (Class aClass : classesByPackage) { 92 | if (aClass.isAnnotationPresent(annotation)) { 93 | classes.add(aClass); 94 | } 95 | } 96 | return classes; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/InvokeUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import org.reflector.exception.InstanceInvocationException; 4 | import org.reflector.exception.MethodInvokeException; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.lang.reflect.Constructor; 9 | import java.lang.reflect.Method; 10 | 11 | public final class InvokeUtils { 12 | private static final Logger LOGGER = LoggerFactory.getLogger(InvokeUtils.class); 13 | 14 | private InvokeUtils() { 15 | } 16 | 17 | /** 18 | * Invokes a method on an object. 19 | * 20 | * @param objectToInvokeOn the object to invoke the method on 21 | * @param methodName the name of the method to invoke 22 | * @param parameterTypes the parameter types of the method 23 | * @param args the arguments to pass to the method 24 | * @return the result of the method invocation 25 | * @throws MethodInvokeException if an error occurs during method invocation 26 | */ 27 | public static Object invokeMethod(final Object objectToInvokeOn, final String methodName, final Class[] parameterTypes, final Object[] args) { 28 | try { 29 | Method method = objectToInvokeOn.getClass().getDeclaredMethod(methodName, parameterTypes); 30 | return method.invoke(objectToInvokeOn, args); 31 | } catch (Exception e) { 32 | LOGGER.error("Could not invoke method", e); 33 | } 34 | throw new MethodInvokeException("Error during method invoke has been happened"); 35 | } 36 | 37 | /** 38 | * Invokes a single-parameter method on an object. 39 | * 40 | * @param objectToInvokeOn the object to invoke the method on 41 | * @param methodName the name of the method to invoke 42 | * @param parameterType the type of the parameter of the method 43 | * @param parameter the parameter value to pass to the method 44 | * @return the result of the method invocation 45 | * @throws MethodInvokeException if an error occurs during method invocation 46 | */ 47 | public static Object invokeSingleMethod(final Object objectToInvokeOn, final String methodName, final Class parameterType, final Object parameter) { 48 | try { 49 | final Class clazz = objectToInvokeOn.getClass(); 50 | final Method method = clazz.getMethod(methodName, parameterType); 51 | return method.invoke(objectToInvokeOn, parameter); 52 | } catch (Exception e) { 53 | LOGGER.error("Could not invoke {{}} method ", methodName, e); 54 | } 55 | throw new MethodInvokeException("Error during method invoke has been happened"); 56 | } 57 | 58 | /** 59 | * Instantiates a class without constructor arguments. 60 | * 61 | * @param className the name of the class to instantiate 62 | * @return the new instance of the class 63 | * @throws InstanceInvocationException if an error occurs during instance invocation 64 | */ 65 | public static Object invokeInstance(final String className) throws InstanceInvocationException { 66 | try { 67 | return Class.forName(className).newInstance(); 68 | } catch (Exception e) { 69 | LOGGER.error("Could not instantiate class object ", e); 70 | } 71 | throw new InstanceInvocationException("Error during instance invoke has been happened"); 72 | } 73 | 74 | /** 75 | * Instantiates a class with constructor arguments. 76 | * 77 | * @param classFullName the fully qualified name of the class to instantiate 78 | * @param args the arguments to pass to the constructor 79 | * @return the new instance of the class 80 | * @throws InstanceInvocationException if an error occurs during instance invocation 81 | */ 82 | public static Object invokeInstance(final String classFullName, final Object... args) throws InstanceInvocationException { 83 | try { 84 | final Class clazz = Class.forName(classFullName); 85 | final Class[] ctorTypes = getArrayValuesTypesByArgs(args); 86 | final Constructor ctor = getAccessibleConstructor(ctorTypes, clazz); 87 | return ctor.newInstance(args); 88 | } catch (Exception e) { 89 | LOGGER.error("Could not instantiate class {{}} object ", classFullName, e); 90 | } 91 | throw new InstanceInvocationException("Error during instance invoke has been happened"); 92 | } 93 | 94 | /** 95 | * Instantiates a class with constructor arguments. 96 | * 97 | * @param clazz the class to instantiate 98 | * @param args the arguments to pass to the constructor 99 | * @param the type of the class to instantiate 100 | * @return the new instance of the class 101 | * @throws InstanceInvocationException if an error occurs during instance invocation 102 | */ 103 | public static T invokeInstance(final Class clazz, final Object... args) throws InstanceInvocationException { 104 | try { 105 | final Class[] ctorTypes = getArrayValuesTypesByArgs(args); 106 | final Constructor ctor = getAccessibleConstructor(ctorTypes, clazz); 107 | return ctor.newInstance(args); 108 | } catch (Exception e) { 109 | LOGGER.error("Could not instantiate class {{}} object ", clazz, e); 110 | } 111 | throw new InstanceInvocationException("Error during instance invoke has been happened"); 112 | } 113 | 114 | /** 115 | * Gets the types of the arguments. 116 | * 117 | * @param args the arguments 118 | * @return an array of argument types 119 | */ 120 | public static Class[] getArrayValuesTypesByArgs(final Object[] args) { 121 | final Class[] ctorTypes = new Class[args.length]; 122 | for (int i = 0; i < args.length; i++) { 123 | ctorTypes[i] = args[i].getClass(); 124 | } 125 | return ctorTypes; 126 | } 127 | 128 | /** 129 | * Gets a constructor with accessible flag set. 130 | * 131 | * @param contTypes the types of the constructor parameters 132 | * @param clazz the class 133 | * @param the type of the class 134 | * @return the constructor 135 | * @throws NoSuchMethodException if the constructor is not found 136 | */ 137 | public static Constructor getAccessibleConstructor(final Class[] contTypes, final Class clazz) throws NoSuchMethodException { 138 | final Constructor ctor = clazz.getConstructor(contTypes); 139 | ctor.setAccessible(true); 140 | return ctor; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/AnnotationUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.AnnotatedElement; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | import java.util.Map; 8 | import java.util.Objects; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public final class AnnotationUtils { 13 | 14 | private AnnotationUtils() {} 15 | 16 | /** 17 | * Retrieves all annotations present on the given class. 18 | * 19 | * @param clazz the class whose annotations are to be retrieved 20 | * @return an array of annotations present on the given class 21 | * @throws NullPointerException if the provided class is null 22 | */ 23 | public static Annotation[] getClassAnnotations(final Class clazz) { 24 | if (clazz == null) { 25 | throw new NullPointerException("Class must not be null"); 26 | } 27 | 28 | return clazz.getAnnotations(); 29 | } 30 | 31 | /** 32 | * Retrieves annotations by type from a class or element. 33 | * 34 | * @param the type of the annotation to query for and return if present 35 | * @param element the element from which to get the annotations 36 | * @param annotationClass the Class object corresponding to the annotation type 37 | * @return an array of all annotations of the specified annotation type if present on this element, else an empty array 38 | * @throws NullPointerException if the element or annotationClass is null 39 | */ 40 | public static T[] getAnnotationsByType(AnnotatedElement element, Class annotationClass) { 41 | if (element == null || annotationClass == null) { 42 | throw new NullPointerException("Element and annotation class cannot be null"); 43 | } 44 | return element.getAnnotationsByType(annotationClass); 45 | } 46 | 47 | /** 48 | * Gets annotations declared directly on a class, method, or field. 49 | * 50 | * @param element the element from which to get the annotations 51 | * @return an array of annotations directly declared on the element 52 | * @throws NullPointerException if the element is null 53 | */ 54 | public static Annotation[] getDeclaredAnnotations(AnnotatedElement element) { 55 | if (element == null) { 56 | throw new NullPointerException("Element cannot be null"); 57 | } 58 | return element.getDeclaredAnnotations(); 59 | } 60 | 61 | /** 62 | * Retrieves the annotations declared on a method. 63 | * 64 | * @param method the method to retrieve annotations from 65 | * @return an array of annotations declared on the method 66 | * @throws NullPointerException if the method is null 67 | */ 68 | public static Annotation[] getMethodDeclaredAnnotations(final Method method) { 69 | if (method == null) { 70 | throw new NullPointerException("Method must not be null"); 71 | } 72 | return method.getDeclaredAnnotations(); 73 | } 74 | 75 | /** 76 | * Retrieves a map of methods to their declared annotations for the given array of methods. 77 | * 78 | * @param methods the array of methods whose declared annotations are to be retrieved 79 | * @return a map where the keys are the methods and the values are arrays of their declared annotations 80 | * @throws NullPointerException if the methods array is null 81 | */ 82 | public static Map getMethodsDeclaredAnnotations(final Method[] methods) { 83 | if (methods == null) { 84 | throw new NullPointerException("Methods array must not be null"); 85 | } 86 | return Stream.of(methods).filter(Objects::nonNull).collect(Collectors.toMap(method -> method, Method::getDeclaredAnnotations)); 87 | } 88 | 89 | /** 90 | * Checks if a specific annotation is present on the given class. 91 | * 92 | * @param clazz the class to check for the presence of the annotation 93 | * @param annotationClass the annotation class to look for 94 | * @param the type of the annotation 95 | * @return true if the specified annotation is present on the class, false otherwise 96 | * @throws IllegalArgumentException if the provided class or annotation class is null 97 | */ 98 | public static boolean isAnnotationOnClassPresent(final Class clazz, final Class annotationClass) { 99 | if (clazz == null) { 100 | throw new IllegalArgumentException("Class must not be null"); 101 | } 102 | if (annotationClass == null) { 103 | throw new IllegalArgumentException("Annotation class must not be null"); 104 | } 105 | 106 | return clazz.isAnnotationPresent(annotationClass); 107 | } 108 | 109 | /** 110 | * Checks if any parameter of the given method is annotated with the specified annotation class. 111 | * 112 | * @param method the method whose parameters are to be checked 113 | * @param clazz the annotation class to look for on the method parameters 114 | * @param the type of the annotation 115 | * @return true if any parameter of the method is annotated with the specified annotation, false otherwise 116 | * @throws NullPointerException if the provided method or annotation class is null 117 | */ 118 | public static boolean isMethodParameterAnnotated(final Method method, final Class clazz) { 119 | if (method == null) { 120 | throw new NullPointerException("Method must not be null"); 121 | } 122 | if (clazz == null) { 123 | throw new NullPointerException("Annotation class must not be null"); 124 | } 125 | 126 | Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 127 | for (Annotation[] annotations : parameterAnnotations) { 128 | for (Annotation annotation : annotations) { 129 | if (clazz.isInstance(annotation)) { 130 | return true; 131 | } 132 | } 133 | } 134 | return false; 135 | } 136 | 137 | /** 138 | * Gets all annotations present on a given field. 139 | * 140 | * @param field the field whose annotations are to be retrieved 141 | * @return an array of annotations present on the field 142 | * @throws NullPointerException if the provided method or annotation class is null 143 | */ 144 | public static Annotation[] getFieldAnnotations(final Field field) { 145 | if (field == null) { 146 | throw new NullPointerException("Field must not be null"); 147 | } 148 | return field.getAnnotations(); 149 | } 150 | 151 | 152 | /** 153 | * Checks if the given method is annotated with the specified annotation class. 154 | * 155 | * @param method the method to check for the annotation 156 | * @param clazz the annotation class to look for on the method 157 | * @param the type of the annotation 158 | * @return true if the method is annotated with the specified annotation, false otherwise 159 | * @throws NullPointerException if the provided method or annotation class is null 160 | */ 161 | public static boolean isMethodAnnotated(final Method method, final Class clazz) { 162 | if (method == null) { 163 | throw new NullPointerException("Method must not be null"); 164 | } 165 | if (clazz == null) { 166 | throw new NullPointerException("Annotation class must not be null"); 167 | } 168 | 169 | return method.getAnnotation(clazz) != null; 170 | } 171 | } 172 | 173 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/ClassBasicUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | public final class ClassBasicUtils { 7 | 8 | private ClassBasicUtils() {} 9 | 10 | /** 11 | * Gets the full name (including the package name) of the class of the given object. 12 | * 13 | * @param obj the object whose class full name is to be retrieved 14 | * @return the full name of the class of the object 15 | * @throws NullPointerException if the input object is null 16 | */ 17 | public static String getClassFullName(final Object obj) { 18 | if (obj == null) { 19 | throw new NullPointerException("Object must not be null"); 20 | } 21 | return obj.getClass().getName(); 22 | } 23 | 24 | /** 25 | * Gets the canonical name of the class of the given object. 26 | * 27 | * @param obj the object whose class canonical name is to be retrieved 28 | * @return the canonical name of the class of the object 29 | * @throws NullPointerException if the input object is null 30 | */ 31 | public static String getClassCanonicalName(final Object obj) { 32 | if (obj == null) { 33 | throw new NullPointerException("Object must not be null"); 34 | } 35 | return obj.getClass().getCanonicalName(); 36 | } 37 | 38 | /** 39 | * Gets the simple name of the class of the given object. 40 | * 41 | * @param obj the object whose class simple name is to be retrieved 42 | * @return the simple name of the class of the object 43 | * @throws NullPointerException if the input object is null 44 | */ 45 | public static String getClassSimpleName(final Object obj) { 46 | if (obj == null) { 47 | throw new NullPointerException("Object must not be null"); 48 | } 49 | return obj.getClass().getSimpleName(); 50 | } 51 | 52 | /** 53 | * Gets the package name of the class of the given object. 54 | * 55 | * @param obj the object whose class package name is to be retrieved 56 | * @return the package name of the class of the object, or null if the class has no package 57 | * @throws NullPointerException if the input object is null 58 | */ 59 | public static String getPackage(final Object obj) { 60 | if (obj == null) { 61 | throw new NullPointerException("Object must not be null"); 62 | } 63 | Package pkg = obj.getClass().getPackage(); 64 | return (pkg != null) ? pkg.getName() : null; 65 | } 66 | 67 | /** 68 | * Gets the full name (including the package name) of the given class. 69 | * 70 | * @param clazz the class whose full name is to be retrieved 71 | * @return the full name of the class, or an empty string if the class is null 72 | */ 73 | public static String getClassFullNameByClass(final Class clazz) { 74 | if (clazz == null) { 75 | return ""; 76 | } 77 | return clazz.getName(); 78 | } 79 | 80 | /** 81 | * Gets the canonical name of the given class. 82 | * 83 | * @param clazz the class whose canonical name is to be retrieved 84 | * @return the canonical name of the class, or null if the class is null 85 | */ 86 | public static String getClassCanonicalNameByClass(final Class clazz) { 87 | if (clazz == null) { 88 | return null; 89 | } 90 | return clazz.getCanonicalName(); 91 | } 92 | 93 | /** 94 | * Gets the simple name of the given class. 95 | * 96 | * @param clazz the class whose simple name is to be retrieved 97 | * @return the simple name of the class 98 | * @throws NullPointerException if the input class is null 99 | */ 100 | public static String getClassSimpleNameByClass(final Class clazz) { 101 | if (clazz == null) { 102 | throw new NullPointerException("Class must not be null"); 103 | } 104 | return clazz.getSimpleName(); 105 | } 106 | 107 | /** 108 | * Gets the package name of the given class. 109 | * 110 | * @param clazz the class whose package name is to be retrieved 111 | * @return the package name of the class, or null if the class has no package 112 | * @throws NullPointerException if the input class is null 113 | */ 114 | public static String getPackageByClass(final Class clazz) { 115 | if (clazz == null) { 116 | throw new NullPointerException("Class must not be null"); 117 | } 118 | 119 | Package pkg = clazz.getPackage(); 120 | return (pkg != null) ? pkg.getName() : null; 121 | } 122 | 123 | /** 124 | * Gets the name of the superclass of the given object's class. 125 | * 126 | * @param obj the object whose superclass name is to be retrieved 127 | * @return the name of the superclass, or null if the class has no superclass 128 | * @throws NullPointerException if the input object is null 129 | */ 130 | public static String getSuperClassNameForObject(final Object obj) { 131 | if (obj == null) { 132 | throw new NullPointerException("Object must not be null"); 133 | } 134 | 135 | Class superClass = obj.getClass().getSuperclass(); 136 | if (superClass == null) { 137 | return null; 138 | } 139 | 140 | return superClass.getName(); 141 | } 142 | 143 | /** 144 | * Retrieves the name of the superclass of the given class. 145 | * 146 | *

147 | * This method returns the name of the superclass of the provided class. If the provided class is null, 148 | * it throws an NullPointerException. If the provided class does not have a superclass (i.e., it is an interface, 149 | * a primitive type, an array class, or the class is java.lang.Object), the method returns null. 150 | *

151 | * 152 | * @param clazz the class for which the superclass name is to be retrieved 153 | * @return the name of the superclass of the provided class, or null if the class has no superclass 154 | * @throws NullPointerException if the provided class is null 155 | */ 156 | public static String getSuperClassNameByClass(final Class clazz) { 157 | if (clazz == null) { 158 | throw new NullPointerException("Class must not be null"); 159 | } 160 | 161 | Class superClass = clazz.getSuperclass(); 162 | if (superClass == null) { 163 | return null; 164 | } 165 | 166 | return superClass.getName(); 167 | } 168 | 169 | /** 170 | * Retrieves the enclosing class of the given class if it is an inner class. 171 | * 172 | * @param clazz the class whose enclosing class is to be retrieved 173 | * @return the enclosing class if the given class is an inner class, null otherwise 174 | * @throws NullPointerException if the provided class is null 175 | */ 176 | public static Class getEnclosingClass(final Class clazz) { 177 | if (clazz == null) { 178 | throw new NullPointerException("Class must not be null"); 179 | } 180 | 181 | return clazz.getEnclosingClass(); 182 | } 183 | 184 | /** 185 | * Retrieves the superclass of the given object's class. 186 | * 187 | * @param obj the object whose class's superclass is to be retrieved 188 | * @return the superclass of the given object's class 189 | * @throws NullPointerException if the provided object is null 190 | */ 191 | public static Class getSuperClass(final Object obj) { 192 | if (obj == null) { 193 | throw new NullPointerException("Object must not be null"); 194 | } 195 | return obj.getClass().getSuperclass(); 196 | } 197 | 198 | /** 199 | * Retrieves the interfaces implemented by the given class. 200 | * 201 | * @param clazz the class whose implemented interfaces are to be retrieved 202 | * @return a list of classes representing the interfaces implemented by the given class 203 | * @throws NullPointerException if the provided class is null 204 | */ 205 | public static List> getInterfaces(final Class clazz) { 206 | if (clazz == null) { 207 | throw new NullPointerException("Class must not be null"); 208 | } 209 | 210 | return Arrays.asList(clazz.getInterfaces()); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/MethodUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.Modifier; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | import java.util.function.IntPredicate; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | public final class MethodUtils { 14 | 15 | private MethodUtils() {} 16 | 17 | /** 18 | * Retrieves the parameter types of the given method. 19 | * 20 | * @param method the method whose parameter types are to be retrieved 21 | * @return an array of Classes representing the parameter types of the method 22 | * @throws IllegalArgumentException if the provided method is null 23 | */ 24 | public static Class[] getParameterTypes(final Method method) { 25 | if (method == null) { 26 | throw new IllegalArgumentException("Method must not be null"); 27 | } 28 | 29 | return method.getParameterTypes(); 30 | } 31 | 32 | /** 33 | * Gets the return type of the given method. 34 | * 35 | * @param method the method whose return type is to be retrieved 36 | * @return the Class representing the return type of the method 37 | * @throws IllegalArgumentException if the provided method is null 38 | */ 39 | public static Class getReturnType(final Method method) { 40 | if (method == null) { 41 | throw new IllegalArgumentException("Method must not be null"); 42 | } 43 | 44 | return method.getReturnType(); 45 | } 46 | 47 | /** 48 | * Gets the types of exceptions thrown by the given method. 49 | * 50 | * @param method the method whose exception types are to be retrieved 51 | * @return an array of Classes representing the exception types thrown by the method 52 | * @throws IllegalArgumentException if the provided method is null 53 | */ 54 | public static Class[] getExceptionTypes(final Method method) { 55 | if (method == null) { 56 | throw new IllegalArgumentException("Method must not be null"); 57 | } 58 | 59 | return method.getExceptionTypes(); 60 | } 61 | 62 | /** 63 | * Retrieves the modifiers of the given method. 64 | * 65 | * @param method the method whose modifiers are to be retrieved 66 | * @return an int representing the modifiers of the method 67 | * @throws IllegalArgumentException if the provided method is null 68 | */ 69 | public static int getMethodModifiers(final Method method) { 70 | if (method == null) { 71 | throw new IllegalArgumentException("Method must not be null"); 72 | } 73 | return method.getModifiers(); 74 | } 75 | 76 | /** 77 | * Checks if the given method takes a variable number of arguments. 78 | * 79 | * @param method the method to be checked 80 | * @return true if the method takes a variable number of arguments, false otherwise 81 | * @throws IllegalArgumentException if the provided method is null 82 | */ 83 | public static boolean isMethodVarArgs(final Method method) { 84 | if (method == null) { 85 | throw new IllegalArgumentException("Method must not be null"); 86 | } 87 | return method.isVarArgs(); 88 | } 89 | 90 | /** 91 | * Gets the default value of the given method's annotation element. 92 | * 93 | * @param method the method whose annotation element's default value is to be retrieved 94 | * @return the default value of the annotation element, or null if none 95 | * @throws IllegalArgumentException if the provided method is null 96 | */ 97 | public static Object getDefaultValue(final Method method) { 98 | if (method == null) { 99 | throw new IllegalArgumentException("Method must not be null"); 100 | } 101 | return method.getDefaultValue(); 102 | } 103 | 104 | /** 105 | * Retrieves all private methods of a class. 106 | * 107 | * @param clazz the class from which to retrieve methods 108 | * @return a list of private methods of the specified class 109 | * @throws NullPointerException if the clazz is null 110 | */ 111 | public static List getAllPrivateMethods(final Class clazz) { 112 | return getAllMethodsWithModifiers(clazz, Collections.singletonList(Modifier::isPrivate)); 113 | } 114 | 115 | /** 116 | * Retrieves all public and protected methods of a class. 117 | * 118 | * @param clazz the class from which to retrieve methods 119 | * @return a list of public and protected methods of the specified class 120 | * @throws NullPointerException if the clazz is null 121 | */ 122 | public static List getAllPublicProtectedMethods(final Class clazz) { 123 | return getAllMethodsWithModifiers(clazz, Arrays.asList(Modifier::isPublic, Modifier::isProtected)); 124 | } 125 | 126 | /** 127 | * Retrieves all public methods of a class. 128 | * 129 | * @param clazz the class from which to retrieve methods 130 | * @return a list of public methods of the specified class 131 | * @throws NullPointerException if the clazz is null 132 | */ 133 | public static List getAllPublicMethods(final Class clazz) { 134 | return getAllMethodsWithModifiers(clazz, Collections.singletonList(Modifier::isPublic)); 135 | } 136 | 137 | /** 138 | * Retrieves all methods of a class that match the given modifiers. 139 | * 140 | * @param clazz the class from which to retrieve methods 141 | * @param modifiers the list of predicates to match the method modifiers 142 | * @return a list of methods that match the given modifiers 143 | * @throws NullPointerException if the clazz or modifiers are null 144 | */ 145 | public static List getAllMethodsWithModifiers(final Class clazz, final List modifiers) { 146 | if (clazz == null) { 147 | throw new NullPointerException("Class cannot be null"); 148 | } 149 | if (modifiers == null) { 150 | throw new NullPointerException("Modifiers cannot be null"); 151 | } 152 | 153 | List methods = new ArrayList<>(); 154 | for (Method method : clazz.getDeclaredMethods()) { 155 | boolean matches = modifiers.stream().anyMatch(modifier -> modifier.test(method.getModifiers())); 156 | if (matches) { 157 | methods.add(method); 158 | } 159 | } 160 | return methods; 161 | } 162 | 163 | /** 164 | * Retrieves all default methods from the interfaces implemented by the specified class. 165 | * 166 | * @param clazz the class whose interfaces' default methods are to be retrieved. 167 | * @return a list of default methods from the interfaces implemented by the specified class. 168 | * @throws IllegalArgumentException if the class parameter is null. 169 | */ 170 | public static List getDefaultMethodsOfInterfaces(final Class clazz) { 171 | if (clazz == null) { 172 | throw new IllegalArgumentException("Class parameter cannot be null"); 173 | } 174 | 175 | return Stream.of(clazz.getInterfaces()) 176 | .flatMap(ifc -> Stream.of(ifc.getMethods())) 177 | .filter(Method::isDefault) 178 | .collect(Collectors.toList()); 179 | } 180 | 181 | /** 182 | * Retrieves all declared methods of the specified class, including default methods from its interfaces. 183 | * 184 | * @param clazz the class whose declared methods and default interface methods are to be retrieved. 185 | * @return an array of {@link Method} objects reflecting all declared methods of the class, 186 | * including default methods from its interfaces. 187 | * @throws IllegalArgumentException if the class parameter is null. 188 | * @throws IllegalStateException if an error occurs while retrieving the methods. 189 | */ 190 | public static Method[] getDeclaredMethods(final Class clazz) { 191 | if (clazz == null) { 192 | throw new IllegalArgumentException("Class parameter cannot be null"); 193 | } 194 | 195 | try { 196 | Method[] declaredMethods = clazz.getDeclaredMethods(); 197 | List defaultMethods = getDefaultMethodsOfInterfaces(clazz); 198 | 199 | Method[] result = new Method[declaredMethods.length + defaultMethods.size()]; 200 | System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); 201 | 202 | int index = declaredMethods.length; 203 | for (Method defaultMethod : defaultMethods) { 204 | result[index++] = defaultMethod; 205 | } 206 | 207 | return result; 208 | } catch (Throwable ex) { 209 | throw new IllegalStateException("Failed to get declared methods for Class [" + clazz.getName() + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); 210 | } 211 | } 212 | 213 | /** 214 | * Retrieves all declared methods of the specified class, including default methods from its interfaces, 215 | * and returns them as a list. 216 | * 217 | * @param clazz the class whose declared methods and default interface methods are to be retrieved. 218 | * @return a list of {@link Method} objects reflecting all declared methods of the class, 219 | * including default methods from its interfaces. 220 | * @throws IllegalArgumentException if the class parameter is null. 221 | * @throws IllegalStateException if an error occurs while retrieving the methods. 222 | */ 223 | public static List getDeclaredMethodsList(final Class clazz) { 224 | if (clazz == null) { 225 | throw new IllegalArgumentException("Class parameter cannot be null"); 226 | } 227 | 228 | try { 229 | List methods = new ArrayList<>(Arrays.asList(clazz.getDeclaredMethods())); 230 | methods.addAll(getDefaultMethodsOfInterfaces(clazz)); 231 | return methods; 232 | } catch (Throwable ex) { 233 | throw new IllegalStateException("Failed to get declared methods for Class [" + clazz.getName() + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); 234 | } 235 | } 236 | 237 | /** 238 | * Finds a method by name in the specified class or its superclasses and interfaces. 239 | * 240 | * @param clazz the class in which to search for the method. 241 | * @param name the name of the method to search for. 242 | * @return the {@link Method} object if a method with the specified name is found, or null if not found. 243 | * @throws IllegalArgumentException if the class or method name parameter is null. 244 | */ 245 | public static Method findMethodByName(final Class clazz, final String name) { 246 | if (clazz == null || name == null) { 247 | throw new IllegalArgumentException("Class and method name parameters cannot be null"); 248 | } 249 | 250 | Class classSearchType = clazz; 251 | while (classSearchType != null) { 252 | Method[] methods = (classSearchType.isInterface() ? classSearchType.getMethods() : getDeclaredMethods(classSearchType)); 253 | for (Method method : methods) { 254 | if (name.equals(method.getName())) { 255 | return method; 256 | } 257 | } 258 | classSearchType = classSearchType.getSuperclass(); 259 | } 260 | return null; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/FieldUtils.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import org.reflector.exception.FieldAccessException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.Field; 9 | import java.lang.reflect.Modifier; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Collection; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public final class FieldUtils { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(FieldUtils.class); 19 | 20 | private FieldUtils() { 21 | } 22 | 23 | /** 24 | * Retrieves the type of a specified field in the given class. 25 | * 26 | * @param clazz the class from which the field type is to be retrieved 27 | * @param fieldName the name of the field whose type is to be retrieved 28 | * @return the type of the specified field 29 | * @throws NoSuchFieldException if the specified field does not exist 30 | * @throws NullPointerException if the clazz or fieldName is null 31 | */ 32 | public static Class getFieldType(Class clazz, String fieldName) throws NoSuchFieldException { 33 | if (clazz == null || fieldName == null) { 34 | throw new NullPointerException("Class and field name cannot be null"); 35 | } 36 | Field field = clazz.getDeclaredField(fieldName); 37 | return field.getType(); 38 | } 39 | 40 | /** 41 | * Retrieves the modifiers of a specified field in the given class. 42 | * 43 | * @param clazz the class from which the field modifiers are to be retrieved 44 | * @param fieldName the name of the field whose modifiers are to be retrieved 45 | * @return the modifiers of the specified field 46 | * @throws NoSuchFieldException if the specified field does not exist 47 | * @throws NullPointerException if the clazz or fieldName is null 48 | */ 49 | public static int getFieldModifiers(Class clazz, String fieldName) throws NoSuchFieldException { 50 | if (clazz == null || fieldName == null) { 51 | throw new NullPointerException("Class and field name cannot be null"); 52 | } 53 | Field field = clazz.getDeclaredField(fieldName); 54 | return field.getModifiers(); 55 | } 56 | 57 | /** 58 | * Checks if a specified field in the given class is final. 59 | * 60 | * @param clazz the class from which the field is to be checked 61 | * @param fieldName the name of the field to be checked 62 | * @return true if the specified field is final, false otherwise 63 | * @throws NoSuchFieldException if the specified field does not exist 64 | * @throws NullPointerException if the clazz or fieldName is null 65 | */ 66 | public static boolean isFieldFinal(Class clazz, String fieldName) throws NoSuchFieldException { 67 | if (clazz == null || fieldName == null) { 68 | throw new NullPointerException("Class and field name cannot be null"); 69 | } 70 | Field field = clazz.getDeclaredField(fieldName); 71 | return Modifier.isFinal(field.getModifiers()); 72 | } 73 | 74 | /** 75 | * Checks if a specified field in the given class is static. 76 | * 77 | * @param clazz the class from which the field is to be checked 78 | * @param fieldName the name of the field to be checked 79 | * @return true if the specified field is static, false otherwise 80 | * @throws NoSuchFieldException if the specified field does not exist 81 | * @throws NullPointerException if the clazz or fieldName is null 82 | */ 83 | public static boolean isFieldStatic(Class clazz, String fieldName) throws NoSuchFieldException { 84 | if (clazz == null || fieldName == null) { 85 | throw new NullPointerException("Class and field name cannot be null"); 86 | } 87 | Field field = clazz.getDeclaredField(fieldName); 88 | return Modifier.isStatic(field.getModifiers()); 89 | } 90 | 91 | /** 92 | * Sets a specified field in the given class to be accessible. 93 | * 94 | * @param clazz the class containing the field 95 | * @param fieldName the name of the field to be set accessible 96 | * @throws NoSuchFieldException if the specified field does not exist 97 | * @throws NullPointerException if the clazz or fieldName is null 98 | */ 99 | public static void setFieldAccessible(Class clazz, String fieldName) throws NoSuchFieldException { 100 | if (clazz == null || fieldName == null) { 101 | throw new NullPointerException("Class and field name cannot be null"); 102 | } 103 | Field field = clazz.getDeclaredField(fieldName); 104 | field.setAccessible(true); 105 | } 106 | 107 | /** 108 | * Checks if a field is annotated with a specific annotation. 109 | * 110 | * @param field the field to check 111 | * @param annotationClass the annotation class to look for 112 | * @param the type of the annotation 113 | * @return true if the field is annotated with the specified annotation, false otherwise 114 | * @throws NullPointerException if the field or annotationClass is null 115 | */ 116 | public static boolean isFieldAnnotated(final Field field, final Class annotationClass) { 117 | if (field == null) { 118 | throw new NullPointerException("Field cannot be null"); 119 | } 120 | if (annotationClass == null) { 121 | throw new NullPointerException("Annotation class cannot be null"); 122 | } 123 | 124 | return field.isAnnotationPresent(annotationClass); 125 | } 126 | 127 | /** 128 | * Checks if a field is exactly annotated with a specific annotation. 129 | * 130 | * @param field the field to check 131 | * @param annotationClass the annotation class to look for 132 | * @param the type of the annotation 133 | * @return true if the field is exactly annotated with the specified annotation, false otherwise 134 | * @throws NullPointerException if the field or annotationClass is null 135 | */ 136 | public static boolean isFieldExactAnnotated(final Field field, final Class annotationClass) { 137 | if (field == null) { 138 | throw new NullPointerException("Field cannot be null"); 139 | } 140 | if (annotationClass == null) { 141 | throw new NullPointerException("Annotation class cannot be null"); 142 | } 143 | 144 | T annotation = field.getAnnotation(annotationClass); 145 | return annotation != null; 146 | } 147 | 148 | /** 149 | * Retrieves all fields of a given class, including fields declared in its superclasses. 150 | * 151 | * @param type the class from which to retrieve fields 152 | * @return a list of all fields of the specified class 153 | * @throws NullPointerException if the type is null 154 | */ 155 | public static List getAllFields(final Class type) { 156 | if (type == null) { 157 | throw new NullPointerException("Class type cannot be null"); 158 | } 159 | 160 | List fields = new ArrayList<>(); 161 | Class currentType = type; 162 | 163 | while (currentType != null) { 164 | fields.addAll(Arrays.asList(currentType.getDeclaredFields())); 165 | currentType = currentType.getSuperclass(); 166 | } 167 | 168 | return fields; 169 | } 170 | 171 | /** 172 | * Retrieves all private fields of a given class, including fields declared in its superclasses. 173 | * 174 | * @param clazz the class from which to retrieve private fields 175 | * @return a list of all private fields of the specified class 176 | * @throws NullPointerException if the clazz is null 177 | */ 178 | public static List getAllPrivateFields(final Class clazz) { 179 | if (clazz == null) { 180 | throw new NullPointerException("Class type cannot be null"); 181 | } 182 | 183 | List fields = new ArrayList<>(); 184 | Class currentClass = clazz; 185 | 186 | while (currentClass != null) { 187 | Field[] classFields = currentClass.getDeclaredFields(); 188 | for (Field field : classFields) { 189 | if (Modifier.isPrivate(field.getModifiers())) { 190 | fields.add(field); 191 | } 192 | } 193 | currentClass = currentClass.getSuperclass(); 194 | } 195 | 196 | return fields; 197 | } 198 | 199 | /** 200 | * Retrieves all fields of a given class, including fields declared in its superclasses, 201 | * and returns them as a map with field names as keys. 202 | * 203 | * @param clazz the class from which to retrieve fields 204 | * @return a map of all fields of the specified class with field names as keys 205 | * @throws NullPointerException if the clazz is null 206 | */ 207 | public static Map getAllFieldsMap(final Class clazz) { 208 | List allFields = getAllFields(clazz); 209 | Map fieldsMap = new HashMap<>(); 210 | for (Field field : allFields) { 211 | fieldsMap.put(field.getName(), field); 212 | } 213 | return fieldsMap; 214 | } 215 | 216 | /** 217 | * Reads the value of a field from an object. 218 | * 219 | * @param object the object from which to read the field 220 | * @param fieldName the name of the field to read 221 | * @return the value of the field in the object 222 | * @throws FieldAccessException if the field cannot be accessed 223 | */ 224 | public static Object readField(final Object object, final String fieldName) { 225 | try { 226 | Field field = object.getClass().getDeclaredField(fieldName); 227 | field.setAccessible(true); 228 | return field.get(object); 229 | } catch (NoSuchFieldException | IllegalAccessException e) { 230 | LOGGER.error("Error reading field '{}'", fieldName, e); 231 | throw new FieldAccessException("Requested field is not accessible", e); 232 | } 233 | } 234 | 235 | /** 236 | * Clears the values of unselected fields of the given object. 237 | * 238 | *

For each field of the object's class, if the field name is not present in the specified 239 | * collection of selected fields, the field value is set to null. 240 | * 241 | * @param object the object whose fields are to be cleared 242 | * @param selectedFields a collection containing the names of the fields to keep 243 | * @throws IllegalArgumentException if the object is null 244 | */ 245 | public static void clearUnselectedFields(final Object object, final Collection selectedFields) { 246 | if (object == null) { 247 | throw new IllegalArgumentException("Object cannot be null"); 248 | } 249 | 250 | if (selectedFields != null && !selectedFields.isEmpty()) { 251 | Class clazz = object.getClass(); 252 | for (Field field : FieldUtils.getAllFields(clazz)) { 253 | if (!selectedFields.contains(field.getName())) { 254 | try { 255 | field.setAccessible(true); 256 | field.set(object, null); 257 | } catch (Exception e) { 258 | LOGGER.error("Failed to clear field '{}'. Error: {}", field.getName(), e.getMessage()); 259 | } 260 | } 261 | } 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/FieldUtilsAdditionalTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.CustomTestClassForType; 4 | import org.common.reflector.data.SimpleAnnotatedEntry; 5 | import org.common.reflector.data.annotation.CustomAnnotationForTest; 6 | import org.common.reflector.util.TestConstant; 7 | import org.junit.jupiter.api.Test; 8 | import org.reflector.FieldUtils; 9 | import org.reflector.FieldsExtraUtils; 10 | import org.reflector.exception.FieldAccessException; 11 | 12 | 13 | import java.lang.annotation.Retention; 14 | import java.lang.annotation.RetentionPolicy; 15 | import java.lang.reflect.Field; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | public class FieldUtilsAdditionalTest { 22 | 23 | private static class SuperClass { 24 | private int superClassPrivateField; 25 | public String superClassPublicField; 26 | } 27 | 28 | private static class SubClass extends SuperClass { 29 | private double subClassPrivateField; 30 | protected boolean subClassProtectedField; 31 | } 32 | 33 | @Test 34 | public void testGetAllFields() { 35 | List fields = FieldUtils.getAllFields(SubClass.class); 36 | 37 | assertEquals(4, fields.size()); 38 | 39 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPrivateField"))); 40 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPublicField"))); 41 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("subClassPrivateField"))); 42 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("subClassProtectedField"))); 43 | } 44 | 45 | @Test 46 | public void testGetAllFields_SuperClassOnly() { 47 | List fields = FieldUtils.getAllFields(SuperClass.class); 48 | 49 | assertEquals(2, fields.size()); 50 | 51 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPrivateField"))); 52 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPublicField"))); 53 | } 54 | 55 | @Test 56 | public void testGetAllFields_nullClass() { 57 | assertThrows(NullPointerException.class, () -> { 58 | FieldUtils.getAllFields(null); 59 | }); 60 | } 61 | 62 | @Test 63 | public void testGetAllFieldsMap() { 64 | Map fieldsMap = FieldUtils.getAllFieldsMap(SubClass.class); 65 | 66 | assertEquals(4, fieldsMap.size()); 67 | 68 | assertTrue(fieldsMap.containsKey("superClassPrivateField")); 69 | assertTrue(fieldsMap.containsKey("superClassPublicField")); 70 | assertTrue(fieldsMap.containsKey("subClassPrivateField")); 71 | assertTrue(fieldsMap.containsKey("subClassProtectedField")); 72 | } 73 | 74 | @Test 75 | public void testGetAllFieldsMap_SuperClassOnly() { 76 | Map fieldsMap = FieldUtils.getAllFieldsMap(SuperClass.class); 77 | 78 | assertEquals(2, fieldsMap.size()); 79 | 80 | assertTrue(fieldsMap.containsKey("superClassPrivateField")); 81 | assertTrue(fieldsMap.containsKey("superClassPublicField")); 82 | } 83 | 84 | @Test 85 | public void testGetAllFieldsMap_nullClass() { 86 | assertThrows(NullPointerException.class, () -> { 87 | FieldUtils.getAllFieldsMap(null); 88 | }); 89 | } 90 | 91 | @Test 92 | public void testGetAllPrivateFields() { 93 | List fields = FieldUtils.getAllPrivateFields(SubClass.class); 94 | 95 | assertEquals(2, fields.size()); 96 | 97 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPrivateField"))); 98 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("subClassPrivateField"))); 99 | } 100 | 101 | @Test 102 | public void testGetAllPrivateFields_SuperClassOnly() { 103 | List fields = FieldUtils.getAllPrivateFields(SuperClass.class); 104 | 105 | assertEquals(1, fields.size()); 106 | 107 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPrivateField"))); 108 | } 109 | 110 | @Test 111 | public void testGetAllPrivateFields_noPrivateFields() { 112 | class NoPrivateFieldsClass { 113 | public int publicField; 114 | protected int protectedField; 115 | } 116 | 117 | List fields = FieldUtils.getAllPrivateFields(NoPrivateFieldsClass.class); 118 | 119 | assertTrue(fields.isEmpty()); 120 | } 121 | 122 | @Test 123 | public void testGetAllPrivateFields_nullClass() { 124 | assertThrows(NullPointerException.class, () -> { 125 | FieldUtils.getAllPrivateFields(null); 126 | }); 127 | } 128 | 129 | @Test 130 | public void testGetAllPrivateFieldsMap() { 131 | Map fieldsMap = FieldsExtraUtils.getAllPrivateFieldsMap(SubClass.class); 132 | 133 | assertEquals(2, fieldsMap.size()); 134 | 135 | assertTrue(fieldsMap.containsKey("superClassPrivateField")); 136 | assertTrue(fieldsMap.containsKey("subClassPrivateField")); 137 | } 138 | 139 | @Test 140 | public void testGetAllPrivateFieldsMap_SuperClassOnly() { 141 | Map fieldsMap = FieldsExtraUtils.getAllPrivateFieldsMap(SuperClass.class); 142 | 143 | assertEquals(1, fieldsMap.size()); 144 | 145 | assertTrue(fieldsMap.containsKey("superClassPrivateField")); 146 | } 147 | 148 | @Test 149 | public void testGetAllPrivateFieldsMap_noPrivateFields() { 150 | class NoPrivateFieldsClass { 151 | public int publicField; 152 | protected int protectedField; 153 | } 154 | 155 | Map fieldsMap = FieldsExtraUtils.getAllPrivateFieldsMap(NoPrivateFieldsClass.class); 156 | 157 | assertTrue(fieldsMap.isEmpty()); 158 | } 159 | 160 | @Test 161 | public void testGetAllPrivateFieldsMap_nullClass() { 162 | assertThrows(NullPointerException.class, () -> { 163 | FieldsExtraUtils.getAllPrivateFieldsMap(null); 164 | }); 165 | } 166 | 167 | // test getAllAnnotatedFields 168 | 169 | @Retention(RetentionPolicy.RUNTIME) 170 | @interface TestAnnotation {} 171 | 172 | private static class TestClass { 173 | @TestAnnotation 174 | private int annotatedField1; 175 | 176 | private String nonAnnotatedField; 177 | 178 | @TestAnnotation 179 | public double annotatedField2; 180 | } 181 | 182 | @Test 183 | public void testGetAllAnnotatedFields() { 184 | List annotatedFields = FieldsExtraUtils.getAllAnnotatedFields(TestClass.class, TestAnnotation.class); 185 | 186 | assertEquals(2, annotatedFields.size()); 187 | assertTrue(annotatedFields.stream().anyMatch(field -> field.getName().equals("annotatedField1"))); 188 | assertTrue(annotatedFields.stream().anyMatch(field -> field.getName().equals("annotatedField2"))); 189 | } 190 | 191 | @Test 192 | public void testGetAllAnnotatedFields_noFields() { 193 | List annotatedFields = FieldsExtraUtils.getAllAnnotatedFields(TestClass.class, Override.class); 194 | assertTrue(annotatedFields.isEmpty()); 195 | } 196 | 197 | @Test 198 | public void testGetAllAnnotatedFields_nullClass() { 199 | assertThrows(NullPointerException.class, () -> { 200 | FieldsExtraUtils.getAllAnnotatedFields(null, TestAnnotation.class); 201 | }); 202 | } 203 | 204 | @Test 205 | public void testGetAllAnnotatedFields_nullAnnotation() { 206 | assertThrows(NullPointerException.class, () -> { 207 | FieldsExtraUtils.getAllAnnotatedFields(TestClass.class, null); 208 | }); 209 | } 210 | 211 | // read field 212 | 213 | private static class TestClass1 { 214 | private int privateField = 42; 215 | public String publicField = "Hello"; 216 | } 217 | 218 | @Test 219 | public void testReadField_PrivateField() { 220 | TestClass1 obj = new TestClass1(); 221 | Object value = FieldUtils.readField(obj, "privateField"); 222 | assertEquals(42, value); 223 | } 224 | 225 | @Test 226 | public void testReadField_PublicField() { 227 | TestClass1 obj = new TestClass1(); 228 | Object value = FieldUtils.readField(obj, "publicField"); 229 | assertEquals("Hello", value); 230 | } 231 | 232 | @Test 233 | public void testReadField_NonExistingField() { 234 | TestClass1 obj = new TestClass1(); 235 | assertThrows(FieldAccessException.class, () -> { 236 | FieldUtils.readField(obj, "nonExistingField"); 237 | }); 238 | } 239 | 240 | @Test 241 | public void testReadField_NullObject() { 242 | assertThrows(NullPointerException.class, () -> { 243 | FieldUtils.readField(null, "fieldName"); 244 | }); 245 | } 246 | 247 | @Test 248 | public void testReadField_NullFieldName() { 249 | TestClass1 obj = new TestClass1(); 250 | assertThrows(NullPointerException.class, () -> { 251 | FieldUtils.readField(obj, null); 252 | }); 253 | } 254 | 255 | @Test 256 | void readSingleField() { 257 | CustomTestClassForType customClass = new CustomTestClassForType(); 258 | 259 | Object oneConstant = FieldUtils.readField(customClass, TestConstant.ONE_CONSTANT); 260 | System.out.println(oneConstant); 261 | 262 | assertAll("readSingleField", 263 | () -> assertEquals(oneConstant, 1), 264 | () -> assertEquals(oneConstant.getClass(), Integer.class) 265 | ); 266 | } 267 | 268 | @Test 269 | public void getAllFieldsMap() { 270 | Map fields = FieldUtils.getAllFieldsMap(CustomTestClassForType.class); 271 | 272 | assertAll("allFields", 273 | () -> assertEquals(fields.get(TestConstant.STRING_FIELD).getName(), TestConstant.STRING_FIELD), 274 | () -> assertEquals(fields.get(TestConstant.OBJECT_FIELD).getName(), TestConstant.OBJECT_FIELD), 275 | () -> assertEquals(fields.get(TestConstant.FLOAT_FIELD).getName(), TestConstant.FLOAT_FIELD), 276 | () -> assertEquals(fields.get(TestConstant.NOT_PRIVATE_FIELD).getName(), TestConstant.NOT_PRIVATE_FIELD), 277 | () -> assertEquals(fields.get(TestConstant.ONE_CONSTANT).getName(), TestConstant.ONE_CONSTANT), 278 | () -> assertEquals(fields.size(), 5) 279 | ); 280 | } 281 | 282 | @Test 283 | void getAllAnnotatedFieldsTest() { 284 | List fields = FieldsExtraUtils.getAllAnnotatedFields(SimpleAnnotatedEntry.class, CustomAnnotationForTest.class); 285 | assertAll("classMultipleParametersInstance", 286 | () -> assertEquals(fields.size(), 2), 287 | () -> assertEquals(fields.get(0).getName(), TestConstant.KEY), 288 | () -> assertEquals(fields.get(1).getName(), TestConstant.VALUE) 289 | ); 290 | } 291 | 292 | @Test 293 | public void getAllPrivateFieldsMap() { 294 | Map fields = FieldsExtraUtils.getAllPrivateFieldsMap(CustomTestClassForType.class); 295 | int fieldsCounter = 3; 296 | assertAll("privateFields", 297 | () -> assertEquals(fields.get(TestConstant.STRING_FIELD).getName(), TestConstant.STRING_FIELD), 298 | () -> assertEquals(fields.get(TestConstant.OBJECT_FIELD).getName(), TestConstant.OBJECT_FIELD), 299 | () -> assertEquals(fields.get(TestConstant.FLOAT_FIELD).getName(), TestConstant.FLOAT_FIELD), 300 | () -> assertEquals(fields.size(), fieldsCounter) 301 | ); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/ReflectionUtilsLegacyTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector; 2 | 3 | import org.common.reflector.data.CustomTestClassForType; 4 | import org.common.reflector.data.CustomTestInvokeClass; 5 | import org.common.reflector.data.MethodAnnotatedClass; 6 | import org.common.reflector.data.Person; 7 | import org.common.reflector.data.SimpleAnnotatedEntry; 8 | import org.common.reflector.data.SimpleEntryClass; 9 | import org.common.reflector.data.annotation.ClassAnnotation; 10 | import org.common.reflector.data.annotation.CustomAnnotationForTest; 11 | import org.common.reflector.data.annotation.CustomMethodAnnotation; 12 | import org.common.reflector.util.TestConstant; 13 | import org.junit.jupiter.api.Test; 14 | import org.reflector.ReflectionUtilsLegacy; 15 | 16 | import java.io.IOException; 17 | import java.lang.annotation.Annotation; 18 | import java.lang.reflect.Constructor; 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Method; 21 | import java.net.URISyntaxException; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.stream.Collectors; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertAll; 29 | import static org.junit.jupiter.api.Assertions.assertEquals; 30 | import static org.junit.jupiter.api.Assertions.assertFalse; 31 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 32 | import static org.junit.jupiter.api.Assertions.assertTrue; 33 | 34 | public class ReflectionUtilsLegacyTest { 35 | @Test 36 | public void getAllClassNamesTest() { 37 | Object obj = new CustomTestInvokeClass(TestConstant.SIMPLE_CLASS_SIMPLE_VALUE); 38 | assertAll("classNames", 39 | () -> assertEquals(TestConstant.CUSTOM_TEST_INVOKE_CLASS, ReflectionUtilsLegacy.getClassSimpleName(obj)), 40 | () -> assertEquals(TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE, ReflectionUtilsLegacy.getClassFullName(obj)), 41 | () -> assertEquals(TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE, ReflectionUtilsLegacy.getClassCanonicalName(obj)) 42 | ); 43 | } 44 | 45 | @Test 46 | public void getPackageNameTest() { 47 | Object obj = new CustomTestInvokeClass(TestConstant.SIMPLE_CLASS_SIMPLE_VALUE); 48 | assertEquals(TestConstant.REFLECTOR_DATA_PACKAGE, ReflectionUtilsLegacy.getPackage(obj)); 49 | } 50 | 51 | @Test 52 | public void getSuperClassTest() { 53 | CustomTestInvokeClass obj = new CustomTestInvokeClass(TestConstant.SIMPLE_CLASS_SIMPLE_VALUE); 54 | assertEquals(Object.class, ReflectionUtilsLegacy.getSuperClass(obj)); 55 | } 56 | 57 | @Test 58 | public void getAllClassNamesByClassTest() { 59 | assertAll("classNames", 60 | () -> assertEquals(TestConstant.CUSTOM_TEST_INVOKE_CLASS, ReflectionUtilsLegacy.getClassSimpleNameByClass(CustomTestInvokeClass.class)), 61 | () -> assertEquals(TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE, ReflectionUtilsLegacy.getClassFullNameByClass(CustomTestInvokeClass.class)), 62 | () -> assertEquals(TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE, ReflectionUtilsLegacy.getClassCanonicalNameByClass(CustomTestInvokeClass.class)) 63 | ); 64 | } 65 | 66 | @Test 67 | public void getSuperClassFoClassTest() { 68 | assertEquals(Object.class, ReflectionUtilsLegacy.getSuperClass(CustomTestInvokeClass.class)); 69 | } 70 | 71 | @Test 72 | void getAllPrivateFieldsTest() { 73 | 74 | List fields = ReflectionUtilsLegacy.getAllPrivateFields(CustomTestClassForType.class); 75 | 76 | assertAll("privateFields", 77 | () -> assertEquals(fields.get(0).getName(), TestConstant.STRING_FIELD), 78 | () -> assertEquals(fields.get(1).getName(), TestConstant.OBJECT_FIELD), 79 | () -> assertEquals(fields.get(2).getName(), TestConstant.FLOAT_FIELD), 80 | () -> assertEquals(fields.size(), 3) 81 | ); 82 | } 83 | 84 | @Test 85 | void invokeClassMethodTest() { 86 | CustomTestInvokeClass instance = (CustomTestInvokeClass) ReflectionUtilsLegacy.invokeInstance( 87 | TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE); 88 | Object ret1 = ReflectionUtilsLegacy.invokeMethod(instance, TestConstant.SET_VALUE, 89 | new Class[]{String.class}, new String[]{TestConstant.SIMPLE_VALUE}); 90 | String ret2 = (String) ReflectionUtilsLegacy.invokeMethod(instance, TestConstant.GET_VALUE, null, null); 91 | 92 | assertAll("invokedMethodValues", 93 | () -> assertEquals(ret1, null), 94 | () -> assertEquals(ret2, TestConstant.SIMPLE_VALUE) 95 | ); 96 | } 97 | 98 | @Test 99 | void invokeClassInstanceTest() { 100 | CustomTestInvokeClass instance = (CustomTestInvokeClass) ReflectionUtilsLegacy.invokeInstance( 101 | TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE); 102 | assertAll("classInstance", 103 | () -> assertNotEquals(instance, null) 104 | ); 105 | 106 | } 107 | 108 | @Test 109 | void readSingleField() { 110 | CustomTestClassForType customClass = new CustomTestClassForType(); 111 | 112 | Object oneConstant = ReflectionUtilsLegacy.readField(customClass, TestConstant.ONE_CONSTANT); 113 | System.out.println(oneConstant); 114 | 115 | assertAll("readSingleField", 116 | () -> assertEquals(oneConstant, 1), 117 | () -> assertEquals(oneConstant.getClass(), Integer.class) 118 | ); 119 | } 120 | 121 | @Test 122 | void invokeSingleClassMethodTest() { 123 | CustomTestInvokeClass instance = (CustomTestInvokeClass) ReflectionUtilsLegacy.invokeInstance(TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE); 124 | Object ret1 = ReflectionUtilsLegacy.invokeSingleMethod(instance, TestConstant.SET_VALUE, String.class, TestConstant.SIMPLE_VALUE); 125 | String ret2 = (String) ReflectionUtilsLegacy.invokeMethod(instance, TestConstant.GET_VALUE, null, null); 126 | 127 | assertAll("singleClassMethodValue", 128 | () -> assertEquals(ret1, null), 129 | () -> assertEquals(ret2, TestConstant.SIMPLE_VALUE) 130 | ); 131 | } 132 | 133 | @Test 134 | void invokeClassInstanceWithParametersTest() { 135 | Object[] obj = {TestConstant.SOME_VALUE}; 136 | CustomTestInvokeClass instance = (CustomTestInvokeClass) ReflectionUtilsLegacy.invokeInstance( 137 | TestConstant.CUSTOM_TEST_INVOKE_CLASS_PACKAGE, obj); 138 | assertAll("classMultipleParametersInstance", 139 | () -> assertNotEquals(instance, null), 140 | () -> assertEquals(instance.getValue(), obj[0]) 141 | ); 142 | } 143 | 144 | @Test 145 | void getAllAnnotatedFieldsTest() { 146 | List fields = ReflectionUtilsLegacy.getAllAnnotatedFields(SimpleAnnotatedEntry.class, CustomAnnotationForTest.class); 147 | assertAll("classMultipleParametersInstance", 148 | () -> assertEquals(fields.size(), 2), 149 | () -> assertEquals(fields.get(0).getName(), TestConstant.KEY), 150 | () -> assertEquals(fields.get(1).getName(), TestConstant.VALUE) 151 | ); 152 | } 153 | 154 | @Test 155 | void clearUnselectedFieldsTest() { 156 | SimpleAnnotatedEntry entry = new SimpleAnnotatedEntry(); 157 | entry.setKey(TestConstant.ENTRY_KEY); 158 | entry.setValue(TestConstant.ENTRY_VALUE); 159 | entry.setInfo(TestConstant.ENTRY_INFO); 160 | 161 | List valuesList = new ArrayList<>(); 162 | valuesList.add(TestConstant.KEY); 163 | valuesList.add(TestConstant.VALUE); 164 | 165 | ReflectionUtilsLegacy.clearUnselectedFields(entry, valuesList); 166 | 167 | assertAll("classMultipleParametersInstance", 168 | () -> assertEquals(entry.getInfo(), null), 169 | () -> assertNotEquals(entry.getKey(), null), 170 | () -> assertNotEquals(entry.getValue(), null) 171 | ); 172 | } 173 | 174 | @Test 175 | public void getAllPublicMethodsTest() { 176 | List allPublicProtectedMethods = ReflectionUtilsLegacy.getAllPublicProtectedMethods(SimpleAnnotatedEntry.class); 177 | assertAll("publicProtectedMethods", 178 | () -> assertEquals(allPublicProtectedMethods.size(), 17) 179 | ); 180 | } 181 | 182 | @Test 183 | public void getAllPrivateMethodsTest() { 184 | List allPublicProtectedMethods = ReflectionUtilsLegacy.getAllPrivateMethods(SimpleAnnotatedEntry.class); 185 | assertEquals(allPublicProtectedMethods.get(0).getName(), TestConstant.DO_SOMETHING_METHOD_NAME); 186 | } 187 | 188 | @Test 189 | public void getAllFieldsMap() { 190 | Map fields = ReflectionUtilsLegacy.getAllFieldsMap(CustomTestClassForType.class); 191 | 192 | assertAll("allFields", 193 | () -> assertEquals(fields.get(TestConstant.STRING_FIELD).getName(), TestConstant.STRING_FIELD), 194 | () -> assertEquals(fields.get(TestConstant.OBJECT_FIELD).getName(), TestConstant.OBJECT_FIELD), 195 | () -> assertEquals(fields.get(TestConstant.FLOAT_FIELD).getName(), TestConstant.FLOAT_FIELD), 196 | () -> assertEquals(fields.get(TestConstant.NOT_PRIVATE_FIELD).getName(), TestConstant.NOT_PRIVATE_FIELD), 197 | () -> assertEquals(fields.get(TestConstant.ONE_CONSTANT).getName(), TestConstant.ONE_CONSTANT), 198 | () -> assertEquals(fields.size(), 5) 199 | ); 200 | } 201 | 202 | @Test 203 | public void getAllPrivateFieldsMap() { 204 | Map fields = ReflectionUtilsLegacy.getAllPrivateFieldsMap(CustomTestClassForType.class); 205 | int fieldsCounter = 3; 206 | assertAll("privateFields", 207 | () -> assertEquals(fields.get(TestConstant.STRING_FIELD).getName(), TestConstant.STRING_FIELD), 208 | () -> assertEquals(fields.get(TestConstant.OBJECT_FIELD).getName(), TestConstant.OBJECT_FIELD), 209 | () -> assertEquals(fields.get(TestConstant.FLOAT_FIELD).getName(), TestConstant.FLOAT_FIELD), 210 | () -> assertEquals(fields.size(), fieldsCounter) 211 | ); 212 | } 213 | 214 | @Test 215 | public void getConstructors() { 216 | Constructor[] constructors = ReflectionUtilsLegacy.getConstructors(SimpleEntryClass.class); 217 | assertEquals(constructors.length, 3); 218 | } 219 | 220 | @Test 221 | public void getDeclaredConstructors() { 222 | Constructor[] constructors = ReflectionUtilsLegacy.getDeclaredConstructors(SimpleEntryClass.class); 223 | assertEquals(constructors.length, 4); 224 | } 225 | 226 | @Test 227 | public void copyObjectTest() { 228 | SimpleEntryClass simpleEntryClass = new SimpleEntryClass("K", "V"); 229 | SimpleEntryClass simpleEntryClassCopy = (SimpleEntryClass) ReflectionUtilsLegacy.copy(simpleEntryClass); 230 | assertEquals(simpleEntryClass, simpleEntryClassCopy); 231 | } 232 | 233 | @Test 234 | public void testCopy_NullObject() { 235 | assertEquals(ReflectionUtilsLegacy.copy(null), null); 236 | } 237 | 238 | @Test 239 | public void findAllClassesByPackageTest() throws IOException, URISyntaxException, ClassNotFoundException { 240 | List> classesByPackage = ReflectionUtilsLegacy.getClassesByPackage(TestConstant.REFLECTOR_PACKAGE); 241 | assertTrue(classesByPackage.size() >= 7); 242 | } 243 | 244 | @Test 245 | public void methodAnnotationTest() { 246 | List allPublicProtectedMethods = ReflectionUtilsLegacy.getAllPublicProtectedMethods(MethodAnnotatedClass.class); 247 | Method[] methods = new Method[allPublicProtectedMethods.size()]; 248 | for (int i = 0; i < allPublicProtectedMethods.size(); i++) { 249 | methods[i] = allPublicProtectedMethods.get(i); 250 | } 251 | Map methodDeclaredAnnotations = ReflectionUtilsLegacy.getMethodDeclaredAnnotations(methods); 252 | Annotation actualAnnotation = null; 253 | for (Map.Entry methodEntry : methodDeclaredAnnotations.entrySet()) { 254 | if (methodEntry.getKey().getName().equals(TestConstant.ANNOTATED_METHOD_NAME)) { 255 | actualAnnotation = methodEntry.getValue()[0]; 256 | break; 257 | } 258 | } 259 | assertEquals(actualAnnotation.annotationType(), CustomMethodAnnotation.class); 260 | } 261 | 262 | @Test 263 | public void doesHasAnnotations() { 264 | List allPublicProtectedMethods = ReflectionUtilsLegacy.getAllPublicMethods(MethodAnnotatedClass.class); 265 | List expected = new ArrayList<>(Arrays.asList(TestConstant.GET_CLASS_METHOD_METHOD_ANNOTATED_CLASS, 266 | TestConstant.ANNOTATED_METHOD_ANNOTATED_CLASS, 267 | TestConstant.WAIT_METHOD_ANNOTATED_CLASS, 268 | TestConstant.HASH_CODE_METHOD_ANNOTATED_CLASS, 269 | TestConstant.EQUALS_METHOD_ANNOTATED_CLASS, 270 | TestConstant.NOTIFY_ALL_METHOD_ANNOTATED_CLASS, 271 | TestConstant.TO_STRING_METHOD_ANNOTATED_CLASS, 272 | TestConstant.NOTIFY_METHOD_ANNOTATED_CLASS)); 273 | List actual = allPublicProtectedMethods.stream().map(Method::getName).collect(Collectors.toList()); 274 | assertFalse(expected.size() == actual.size() && expected.containsAll(actual) && actual.containsAll(expected)); 275 | } 276 | 277 | @Test 278 | public void getAnnotatedClassesTest() throws IOException, URISyntaxException, ClassNotFoundException { 279 | List> classes = ReflectionUtilsLegacy.getAllAnnotatedClassesByPackage(TestConstant.REFLECTOR_DATA_PACKAGE, ClassAnnotation.class); 280 | int expectedAnnotationClassesQuantity = 2; 281 | assertEquals(expectedAnnotationClassesQuantity, classes.size()); 282 | } 283 | 284 | @Test 285 | public void getNotAnnotatedClassesTest() throws IOException, URISyntaxException, ClassNotFoundException { 286 | List> classes = ReflectionUtilsLegacy.getAllAnnotatedClassesByPackage(TestConstant.REFLECTOR_DATA_PACKAGE, CustomMethodAnnotation.class); 287 | int expectedAnnotationClassesQuantity = 0; 288 | assertEquals(expectedAnnotationClassesQuantity, classes.size()); 289 | } 290 | 291 | @Test 292 | public void findMethodsTestByName() { 293 | Method method = ReflectionUtilsLegacy.findMethodByName(Person.class, TestConstant.METHOD_NAME_GET_ID); 294 | assertEquals(TestConstant.METHOD_NAME_GET_ID, method.getName()); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/AnnotationUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.MethodAnnotatedClass; 4 | import org.common.reflector.data.annotation.ClassAnnotation; 5 | import org.common.reflector.data.annotation.ClassAnnotation1; 6 | import org.common.reflector.data.annotation.CustomAnnotationForTest; 7 | import org.common.reflector.data.annotation.CustomMethodAnnotation; 8 | import org.common.reflector.data.annotation.ParameterAnnotation; 9 | import org.common.reflector.util.TestConstant; 10 | import org.junit.jupiter.api.BeforeAll; 11 | import org.junit.jupiter.api.Test; 12 | import org.reflector.AnnotationUtils; 13 | import org.reflector.MethodUtils; 14 | import org.reflector.ReflectionUtils; 15 | 16 | import static org.junit.jupiter.api.Assertions.*; 17 | 18 | import java.lang.annotation.Annotation; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.reflect.Field; 22 | import java.lang.reflect.Method; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | public class AnnotationUtilsTest { 27 | 28 | @Retention(RetentionPolicy.RUNTIME) 29 | private @interface TestAnnotation1 {} 30 | 31 | @Retention(RetentionPolicy.RUNTIME) 32 | private @interface TestAnnotation2 {} 33 | 34 | @TestAnnotation1 35 | @TestAnnotation2 36 | private static class SampleClass { 37 | @TestAnnotation1 38 | @TestAnnotation2 39 | private static void sampleMethod() {} 40 | 41 | @TestAnnotation 42 | public void annotatedMethod() {} 43 | 44 | public void nonAnnotatedMethod() {} 45 | } 46 | 47 | @Test 48 | public void testGetAnnotationsByType_class() { 49 | TestAnnotation1[] annotations = AnnotationUtils.getAnnotationsByType(SampleClass.class, TestAnnotation1.class); 50 | assertEquals(1, annotations.length); 51 | } 52 | 53 | @Test 54 | public void testGetAnnotationsByType_method() throws NoSuchMethodException { 55 | TestAnnotation1[] annotations = AnnotationUtils.getAnnotationsByType(SampleClass.class.getDeclaredMethod("sampleMethod"), TestAnnotation1.class); 56 | assertEquals(1, annotations.length); 57 | } 58 | 59 | @Test 60 | public void testGetAnnotationsByType_noAnnotations() { 61 | TestAnnotation1[] annotations = AnnotationUtils.getAnnotationsByType(String.class, TestAnnotation1.class); 62 | assertEquals(0, annotations.length); 63 | } 64 | 65 | @Test 66 | public void testGetAnnotationsByType_nullElement() { 67 | assertThrows(NullPointerException.class, () -> { 68 | AnnotationUtils.getAnnotationsByType(null, TestAnnotation1.class); 69 | }); 70 | } 71 | 72 | @Test 73 | public void testGetAnnotationsByType_nullAnnotationClass() { 74 | assertThrows(NullPointerException.class, () -> { 75 | AnnotationUtils.getAnnotationsByType(SampleClass.class, null); 76 | }); 77 | } 78 | 79 | @Test 80 | public void testGetDeclaredAnnotations_class() { 81 | Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(SampleClass.class); 82 | assertEquals(2, annotations.length); 83 | } 84 | 85 | @Test 86 | public void testGetDeclaredAnnotations_method() throws NoSuchMethodException { 87 | Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(SampleClass.class.getDeclaredMethod("sampleMethod")); 88 | assertEquals(2, annotations.length); 89 | } 90 | 91 | @Test 92 | public void testGetDeclaredAnnotations_noAnnotations() { 93 | Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(String.class); 94 | assertEquals(0, annotations.length); 95 | } 96 | 97 | @Test 98 | public void testGetDeclaredAnnotations_nullElement() { 99 | assertThrows(NullPointerException.class, () -> { 100 | AnnotationUtils.getDeclaredAnnotations(null); 101 | }); 102 | } 103 | 104 | @Test 105 | public void testGetMethodDeclaredAnnotations_withAnnotation() throws NoSuchMethodException { 106 | Method method = SampleClass.class.getDeclaredMethod("annotatedMethod"); 107 | Annotation[] annotations = AnnotationUtils.getMethodDeclaredAnnotations(method); 108 | assertEquals(1, annotations.length); 109 | assertTrue(annotations[0] instanceof TestAnnotation); 110 | } 111 | 112 | @Test 113 | public void testGetMethodDeclaredAnnotations_withoutAnnotation() throws NoSuchMethodException { 114 | Method method = SampleClass.class.getDeclaredMethod("nonAnnotatedMethod"); 115 | Annotation[] annotations = AnnotationUtils.getMethodDeclaredAnnotations(method); 116 | assertEquals(0, annotations.length); 117 | } 118 | 119 | @Test 120 | public void testGetMethodDeclaredAnnotations_nullMethod() { 121 | assertThrows(NullPointerException.class, () -> { 122 | AnnotationUtils.getMethodDeclaredAnnotations(null); 123 | }); 124 | } 125 | 126 | //getClassAnnotations tests 127 | 128 | @ClassAnnotation 129 | @ClassAnnotation1 130 | class SimpleAnnotatedClass { 131 | } 132 | 133 | class SimpleNonAnnotatedClass { 134 | } 135 | 136 | @Test 137 | public void testGetClassAnnotations_AnnotatedClass() { 138 | Annotation[] annotations = AnnotationUtils.getClassAnnotations(SimpleAnnotatedClass.class); 139 | assertNotNull(annotations); 140 | assertEquals(2, annotations.length); 141 | assertTrue(annotations[0] instanceof ClassAnnotation || annotations[0] instanceof ClassAnnotation1); 142 | assertTrue(annotations[1] instanceof ClassAnnotation || annotations[1] instanceof ClassAnnotation1); 143 | } 144 | 145 | @Test 146 | public void testGetClassAnnotations_NonAnnotatedClass() { 147 | Annotation[] annotations = AnnotationUtils.getClassAnnotations(SimpleNonAnnotatedClass.class); 148 | assertNotNull(annotations); 149 | assertEquals(0, annotations.length); 150 | } 151 | 152 | @Test 153 | public void testGetClassAnnotations_NullClass() { 154 | Exception exception = assertThrows(NullPointerException.class, () -> { 155 | AnnotationUtils.getClassAnnotations(null); 156 | }); 157 | String expectedMessage = "Class must not be null"; 158 | String actualMessage = exception.getMessage(); 159 | assertTrue(actualMessage.contains(expectedMessage)); 160 | } 161 | 162 | @ClassAnnotation 163 | class AnnotatedClass { 164 | } 165 | 166 | class NonAnnotatedClass { 167 | } 168 | 169 | @Test 170 | public void testIsAnnotationPresent_AnnotatedClass() { 171 | boolean result = AnnotationUtils.isAnnotationOnClassPresent(AnnotatedClass.class, ClassAnnotation.class); 172 | assertTrue(result); 173 | } 174 | 175 | @Test 176 | public void testIsAnnotationPresent_NonAnnotatedClass() { 177 | boolean result = AnnotationUtils.isAnnotationOnClassPresent(NonAnnotatedClass.class, ClassAnnotation.class); 178 | assertFalse(result); 179 | } 180 | 181 | @Test 182 | public void testIsAnnotationPresent_NullClass() { 183 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 184 | AnnotationUtils.isAnnotationOnClassPresent(null, ClassAnnotation.class); 185 | }); 186 | String expectedMessage = "Class must not be null"; 187 | String actualMessage = exception.getMessage(); 188 | assertTrue(actualMessage.contains(expectedMessage)); 189 | } 190 | 191 | @Test 192 | public void testIsAnnotationPresent_NullAnnotationClass() { 193 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 194 | AnnotationUtils.isAnnotationOnClassPresent(AnnotatedClass.class, null); 195 | }); 196 | String expectedMessage = "Annotation class must not be null"; 197 | String actualMessage = exception.getMessage(); 198 | assertTrue(actualMessage.contains(expectedMessage)); 199 | } 200 | 201 | 202 | // is method parameter annotated 203 | 204 | 205 | class TestClassWithAnnotaedAndNotMethod { 206 | public void annotatedMethod(@ParameterAnnotation String param) { 207 | } 208 | 209 | public void nonAnnotatedMethod(String param) { 210 | } 211 | } 212 | 213 | @Test 214 | public void testIsMethodParameterAnnotated_AnnotatedParameter() throws NoSuchMethodException { 215 | Method method = TestClassWithAnnotaedAndNotMethod.class.getMethod("annotatedMethod", String.class); 216 | boolean result = AnnotationUtils.isMethodParameterAnnotated(method, ParameterAnnotation.class); 217 | assertTrue(result); 218 | } 219 | 220 | @Test 221 | public void testIsMethodParameterAnnotated_NonAnnotatedParameter() throws NoSuchMethodException { 222 | Method method = TestClassWithAnnotaedAndNotMethod.class.getMethod("nonAnnotatedMethod", String.class); 223 | boolean result = AnnotationUtils.isMethodParameterAnnotated(method, ParameterAnnotation.class); 224 | assertFalse(result); 225 | } 226 | 227 | @Test 228 | public void testIsMethodParameterAnnotated_NullMethod() { 229 | Exception exception = assertThrows(NullPointerException.class, () -> { 230 | AnnotationUtils.isMethodParameterAnnotated(null, ParameterAnnotation.class); 231 | }); 232 | String expectedMessage = "Method must not be null"; 233 | String actualMessage = exception.getMessage(); 234 | assertTrue(actualMessage.contains(expectedMessage)); 235 | } 236 | 237 | @Test 238 | public void testIsMethodParameterAnnotated_NullAnnotationClass() throws NoSuchMethodException { 239 | Method method = TestClassWithAnnotaedAndNotMethod.class.getMethod("annotatedMethod", String.class); 240 | Exception exception = assertThrows(NullPointerException.class, () -> { 241 | AnnotationUtils.isMethodParameterAnnotated(method, null); 242 | }); 243 | String expectedMessage = "Annotation class must not be null"; 244 | String actualMessage = exception.getMessage(); 245 | assertTrue(actualMessage.contains(expectedMessage)); 246 | } 247 | 248 | // get field annotations 249 | 250 | private static Field deprecatedField; 251 | private static Field customAnnotatedField; 252 | private static Field nonAnnotatedField; 253 | 254 | @BeforeAll 255 | public static void setUp() throws NoSuchFieldException { 256 | class Example { 257 | @Deprecated 258 | private String deprecatedField; 259 | @CustomAnnotationForTest 260 | private int customAnnotatedField; 261 | private double nonAnnotatedField; 262 | } 263 | 264 | deprecatedField = Example.class.getDeclaredField("deprecatedField"); 265 | customAnnotatedField = Example.class.getDeclaredField("customAnnotatedField"); 266 | nonAnnotatedField = Example.class.getDeclaredField("nonAnnotatedField"); 267 | } 268 | 269 | @Test 270 | public void testGetFieldAnnotations_DeprecatedField() { 271 | Annotation[] annotations = AnnotationUtils.getFieldAnnotations(deprecatedField); 272 | assertNotNull(annotations); 273 | assertEquals(1, annotations.length); 274 | assertTrue(annotations[0] instanceof Deprecated); 275 | } 276 | 277 | @Test 278 | public void testGetFieldAnnotations_SuppressedField() { 279 | Annotation[] annotations = AnnotationUtils.getFieldAnnotations(customAnnotatedField); 280 | assertNotNull(annotations); 281 | assertEquals(1, annotations.length); 282 | assertTrue(annotations[0] instanceof CustomAnnotationForTest); 283 | } 284 | 285 | @Test 286 | public void testGetFieldAnnotations_NonAnnotatedField() { 287 | Annotation[] annotations = AnnotationUtils.getFieldAnnotations(nonAnnotatedField); 288 | assertNotNull(annotations); 289 | assertEquals(0, annotations.length); 290 | } 291 | 292 | @Test 293 | public void testGetFieldAnnotations_NullField() { 294 | Exception exception = assertThrows(NullPointerException.class, () -> { 295 | AnnotationUtils.getFieldAnnotations(null); 296 | }); 297 | 298 | String expectedMessage = "Field must not be null"; 299 | String actualMessage = exception.getMessage(); 300 | assertTrue(actualMessage.contains(expectedMessage)); 301 | } 302 | 303 | // is method annotated 304 | 305 | class TestClass { 306 | @CustomMethodAnnotation 307 | public void annotatedMethod() { 308 | } 309 | 310 | public void nonAnnotatedMethod() { 311 | } 312 | } 313 | 314 | @Test 315 | public void testIsMethodAnnotated_AnnotatedMethod() throws NoSuchMethodException { 316 | Method method = TestClass.class.getMethod("annotatedMethod"); 317 | boolean result = AnnotationUtils.isMethodAnnotated(method, CustomMethodAnnotation.class); 318 | assertTrue(result); 319 | } 320 | 321 | @Test 322 | public void testIsMethodAnnotated_NonAnnotatedMethod() throws NoSuchMethodException { 323 | Method method = TestClass.class.getMethod("nonAnnotatedMethod"); 324 | boolean result = AnnotationUtils.isMethodAnnotated(method, CustomMethodAnnotation.class); 325 | assertFalse(result); 326 | } 327 | 328 | @Test 329 | public void testIsMethodAnnotated_NullMethod() { 330 | Exception exception = assertThrows(NullPointerException.class, () -> { 331 | AnnotationUtils.isMethodAnnotated(null, CustomMethodAnnotation.class); 332 | }); 333 | String expectedMessage = "Method must not be null"; 334 | String actualMessage = exception.getMessage(); 335 | assertTrue(actualMessage.contains(expectedMessage)); 336 | } 337 | 338 | @Test 339 | public void testIsMethodAnnotated_NullAnnotationClass() throws NoSuchMethodException { 340 | Method method = TestClass.class.getMethod("annotatedMethod"); 341 | Exception exception = assertThrows(NullPointerException.class, () -> { 342 | AnnotationUtils.isMethodAnnotated(method, null); 343 | }); 344 | String expectedMessage = "Annotation class must not be null"; 345 | String actualMessage = exception.getMessage(); 346 | assertTrue(actualMessage.contains(expectedMessage)); 347 | } 348 | 349 | @Test 350 | public void methodAnnotationTest() { 351 | List allPublicProtectedMethods = MethodUtils.getAllPublicProtectedMethods(MethodAnnotatedClass.class); 352 | Method[] methods = new Method[allPublicProtectedMethods.size()]; 353 | for (int i = 0; i < allPublicProtectedMethods.size(); i++) { 354 | methods[i] = allPublicProtectedMethods.get(i); 355 | } 356 | Map methodDeclaredAnnotations = AnnotationUtils.getMethodsDeclaredAnnotations(methods); 357 | Annotation actualAnnotation = null; 358 | for (Map.Entry methodEntry : methodDeclaredAnnotations.entrySet()) { 359 | if (methodEntry.getKey().getName().equals(TestConstant.ANNOTATED_METHOD_NAME)) { 360 | actualAnnotation = methodEntry.getValue()[0]; 361 | break; 362 | } 363 | } 364 | assertEquals(actualAnnotation.annotationType(), CustomMethodAnnotation.class); 365 | } 366 | 367 | @Test 368 | public void testGetMethodDeclaredAnnotations_ValidMethods() throws NoSuchMethodException { 369 | Method annotatedMethod = TestClass.class.getMethod("annotatedMethod"); 370 | Method nonAnnotatedMethod = TestClass.class.getMethod("nonAnnotatedMethod"); 371 | Method[] methods = {annotatedMethod, nonAnnotatedMethod}; 372 | 373 | Map result = AnnotationUtils.getMethodsDeclaredAnnotations(methods); 374 | 375 | assertNotNull(result); 376 | assertEquals(2, result.size()); 377 | assertTrue(result.containsKey(annotatedMethod)); 378 | assertTrue(result.containsKey(nonAnnotatedMethod)); 379 | assertEquals(1, result.get(annotatedMethod).length); 380 | assertEquals(CustomMethodAnnotation.class, result.get(annotatedMethod)[0].annotationType()); 381 | assertEquals(0, result.get(nonAnnotatedMethod).length); 382 | } 383 | 384 | @Test 385 | public void testGetMethodDeclaredAnnotations_NullMethodsArray() { 386 | Exception exception = assertThrows(NullPointerException.class, () -> AnnotationUtils.getMethodsDeclaredAnnotations(null)); 387 | String expectedMessage = "Methods array must not be null"; 388 | String actualMessage = exception.getMessage(); 389 | assertTrue(actualMessage.contains(expectedMessage)); 390 | } 391 | 392 | @Test 393 | public void testGetMethodDeclaredAnnotations_NullMethodInArray() throws NoSuchMethodException { 394 | Method annotatedMethod = TestClass.class.getMethod("annotatedMethod"); 395 | Method[] methods = {annotatedMethod, null}; 396 | 397 | Map result = AnnotationUtils.getMethodsDeclaredAnnotations(methods); 398 | 399 | assertNotNull(result); 400 | assertEquals(1, result.size()); 401 | assertTrue(result.containsKey(annotatedMethod)); 402 | assertEquals(1, result.get(annotatedMethod).length); 403 | assertEquals(CustomMethodAnnotation.class, result.get(annotatedMethod)[0].annotationType()); 404 | } 405 | } 406 | 407 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/FieldUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.SimpleAnnotatedEntry; 4 | import org.common.reflector.util.TestConstant; 5 | import org.junit.jupiter.api.Test; 6 | import org.reflector.FieldUtils; 7 | import org.reflector.ReflectionUtils; 8 | 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.reflect.Field; 12 | import java.lang.reflect.Modifier; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import static org.junit.jupiter.api.Assertions.assertAll; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | import static org.junit.jupiter.api.Assertions.assertFalse; 19 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | 24 | public class FieldUtilsTest { 25 | private static class SampleClass1 { 26 | private int intField; 27 | private String stringField; 28 | private double doubleField; 29 | } 30 | 31 | @Test 32 | public void testGetFieldType_intField() throws NoSuchFieldException { 33 | Class fieldType = FieldUtils.getFieldType(SampleClass1.class, "intField"); 34 | assertEquals(int.class, fieldType); 35 | } 36 | 37 | @Test 38 | public void testGetFieldType_stringField() throws NoSuchFieldException { 39 | Class fieldType = FieldUtils.getFieldType(SampleClass1.class, "stringField"); 40 | assertEquals(String.class, fieldType); 41 | } 42 | 43 | @Test 44 | public void testGetFieldType_doubleField() throws NoSuchFieldException { 45 | Class fieldType = FieldUtils.getFieldType(SampleClass1.class, "doubleField"); 46 | assertEquals(double.class, fieldType); 47 | } 48 | 49 | @Test 50 | public void testGetFieldType_nonExistentField() { 51 | assertThrows(NoSuchFieldException.class, () -> { 52 | FieldUtils.getFieldType(SampleClass1.class, "nonExistentField"); 53 | }); 54 | } 55 | 56 | @Test 57 | public void testGetFieldType_nullClass() { 58 | assertThrows(NullPointerException.class, () -> { 59 | FieldUtils.getFieldType(null, "intField"); 60 | }); 61 | } 62 | 63 | @Test 64 | public void testGetFieldType_nullFieldName() { 65 | assertThrows(NullPointerException.class, () -> { 66 | FieldUtils.getFieldType(SampleClass1.class, null); 67 | }); 68 | } 69 | 70 | 71 | private static class SampleClass2 { 72 | public int publicField; 73 | private String privateField; 74 | protected double protectedField; 75 | static int staticField; 76 | final int finalField = 0; 77 | volatile int volatileField; 78 | transient int transientField; 79 | } 80 | 81 | @Test 82 | public void testGetFieldModifiers_publicField() throws NoSuchFieldException { 83 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "publicField"); 84 | assertTrue(Modifier.isPublic(modifiers)); 85 | } 86 | 87 | @Test 88 | public void testGetFieldModifiers_privateField() throws NoSuchFieldException { 89 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "privateField"); 90 | assertTrue(Modifier.isPrivate(modifiers)); 91 | } 92 | 93 | @Test 94 | public void testGetFieldModifiers_protectedField() throws NoSuchFieldException { 95 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "protectedField"); 96 | assertTrue(Modifier.isProtected(modifiers)); 97 | } 98 | 99 | @Test 100 | public void testGetFieldModifiers_staticField() throws NoSuchFieldException { 101 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "staticField"); 102 | assertTrue(Modifier.isStatic(modifiers)); 103 | } 104 | 105 | @Test 106 | public void testGetFieldModifiers_finalField() throws NoSuchFieldException { 107 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "finalField"); 108 | assertTrue(Modifier.isFinal(modifiers)); 109 | } 110 | 111 | @Test 112 | public void testGetFieldModifiers_volatileField() throws NoSuchFieldException { 113 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "volatileField"); 114 | assertTrue(Modifier.isVolatile(modifiers)); 115 | } 116 | 117 | @Test 118 | public void testGetFieldModifiers_transientField() throws NoSuchFieldException { 119 | int modifiers = FieldUtils.getFieldModifiers(SampleClass2.class, "transientField"); 120 | assertTrue(Modifier.isTransient(modifiers)); 121 | } 122 | 123 | @Test 124 | public void testGetFieldModifiers_nonExistentField() { 125 | assertThrows(NoSuchFieldException.class, () -> { 126 | FieldUtils.getFieldModifiers(SampleClass2.class, "nonExistentField"); 127 | }); 128 | } 129 | 130 | @Test 131 | public void testGetFieldModifiers_nullClass() { 132 | assertThrows(NullPointerException.class, () -> { 133 | FieldUtils.getFieldModifiers(null, "publicField"); 134 | }); 135 | } 136 | 137 | @Test 138 | public void testGetFieldModifiers_nullFieldName() { 139 | assertThrows(NullPointerException.class, () -> { 140 | FieldUtils.getFieldModifiers(SampleClass2.class, null); 141 | }); 142 | } 143 | 144 | private static class SampleClass3 { 145 | public int publicField; 146 | private String privateField; 147 | protected double protectedField; 148 | static int staticField; 149 | final int finalField = 0; 150 | volatile int volatileField; 151 | transient int transientField; 152 | } 153 | 154 | @Test 155 | public void testIsFieldFinal_finalField() throws NoSuchFieldException { 156 | boolean isFinal = FieldUtils.isFieldFinal(SampleClass3.class, "finalField"); 157 | assertTrue(isFinal); 158 | } 159 | 160 | @Test 161 | public void testIsFieldFinal_nonFinalField() throws NoSuchFieldException { 162 | boolean isFinal = FieldUtils.isFieldFinal(SampleClass3.class, "publicField"); 163 | assertFalse(isFinal); 164 | } 165 | 166 | @Test 167 | public void testIsFieldFinal_staticField() throws NoSuchFieldException { 168 | boolean isFinal = FieldUtils.isFieldFinal(SampleClass3.class, "staticField"); 169 | assertFalse(isFinal); 170 | } 171 | 172 | @Test 173 | public void testIsFieldFinal_volatileField() throws NoSuchFieldException { 174 | boolean isFinal = FieldUtils.isFieldFinal(SampleClass3.class, "volatileField"); 175 | assertFalse(isFinal); 176 | } 177 | 178 | @Test 179 | public void testIsFieldFinal_transientField() throws NoSuchFieldException { 180 | boolean isFinal = FieldUtils.isFieldFinal(SampleClass3.class, "transientField"); 181 | assertFalse(isFinal); 182 | } 183 | 184 | @Test 185 | public void testIsFieldFinal_nonExistentField() { 186 | assertThrows(NoSuchFieldException.class, () -> { 187 | FieldUtils.isFieldFinal(SampleClass3.class, "nonExistentField"); 188 | }); 189 | } 190 | 191 | @Test 192 | public void testIsFieldFinal_nullClass() { 193 | assertThrows(NullPointerException.class, () -> { 194 | FieldUtils.isFieldFinal(null, "finalField"); 195 | }); 196 | } 197 | 198 | @Test 199 | public void testIsFieldFinal_nullFieldName() { 200 | assertThrows(NullPointerException.class, () -> { 201 | FieldUtils.isFieldFinal(SampleClass3.class, null); 202 | }); 203 | } 204 | 205 | private static class SampleClass4 { 206 | public int publicField; 207 | private String privateField; 208 | protected double protectedField; 209 | static int staticField; 210 | final int finalField = 0; 211 | volatile int volatileField; 212 | transient int transientField; 213 | } 214 | 215 | @Test 216 | public void testIsFieldStatic_staticField() throws NoSuchFieldException { 217 | boolean isStatic = FieldUtils.isFieldStatic(SampleClass4.class, "staticField"); 218 | assertTrue(isStatic); 219 | } 220 | 221 | @Test 222 | public void testIsFieldStatic_nonStaticField() throws NoSuchFieldException { 223 | boolean isStatic = FieldUtils.isFieldStatic(SampleClass4.class, "publicField"); 224 | assertFalse(isStatic); 225 | } 226 | 227 | @Test 228 | public void testIsFieldStatic_finalField() throws NoSuchFieldException { 229 | boolean isStatic = FieldUtils.isFieldStatic(SampleClass4.class, "finalField"); 230 | assertFalse(isStatic); 231 | } 232 | 233 | @Test 234 | public void testIsFieldStatic_volatileField() throws NoSuchFieldException { 235 | boolean isStatic = FieldUtils.isFieldStatic(SampleClass4.class, "volatileField"); 236 | assertFalse(isStatic); 237 | } 238 | 239 | @Test 240 | public void testIsFieldStatic_transientField() throws NoSuchFieldException { 241 | boolean isStatic = FieldUtils.isFieldStatic(SampleClass4.class, "transientField"); 242 | assertFalse(isStatic); 243 | } 244 | 245 | @Test 246 | public void testIsFieldStatic_nonExistentField() { 247 | assertThrows(NoSuchFieldException.class, () -> { 248 | FieldUtils.isFieldStatic(SampleClass4.class, "nonExistentField"); 249 | }); 250 | } 251 | 252 | @Test 253 | public void testIsFieldStatic_nullClass() { 254 | assertThrows(NullPointerException.class, () -> { 255 | FieldUtils.isFieldStatic(null, "staticField"); 256 | }); 257 | } 258 | 259 | @Test 260 | public void testIsFieldStatic_nullFieldName() { 261 | assertThrows(NullPointerException.class, () -> { 262 | FieldUtils.isFieldStatic(SampleClass4.class, null); 263 | }); 264 | } 265 | 266 | 267 | private static class SampleClass5 { 268 | private int privateField = 42; 269 | } 270 | 271 | @Test 272 | public void testSetFieldAccessible() throws NoSuchFieldException, IllegalAccessException { 273 | SampleClass5 instance = new SampleClass5(); 274 | FieldUtils.setFieldAccessible(SampleClass5.class, "privateField"); 275 | Field field = SampleClass5.class.getDeclaredField("privateField"); 276 | assertEquals(false, field.isAccessible()); 277 | } 278 | 279 | @Test 280 | public void testSetFieldAccessible_nonExistentField() { 281 | assertThrows(NoSuchFieldException.class, () -> { 282 | FieldUtils.setFieldAccessible(SampleClass5.class, "nonExistentField"); 283 | }); 284 | } 285 | 286 | @Test 287 | public void testSetFieldAccessible_nullClass() { 288 | assertThrows(NullPointerException.class, () -> { 289 | FieldUtils.setFieldAccessible(null, "privateField"); 290 | }); 291 | } 292 | 293 | @Test 294 | public void testSetFieldAccessible_nullFieldName() { 295 | assertThrows(NullPointerException.class, () -> { 296 | FieldUtils.setFieldAccessible(SampleClass5.class, null); 297 | }); 298 | } 299 | 300 | // is field annotated 301 | 302 | 303 | @Retention(RetentionPolicy.RUNTIME) 304 | private @interface TestAnnotation {} 305 | 306 | private static class SampleClass6 { 307 | @TestAnnotation 308 | private String annotatedField; 309 | private String nonAnnotatedField; 310 | } 311 | 312 | @Test 313 | public void testIsFieldAnnotated_withAnnotation() throws NoSuchFieldException { 314 | Field field = SampleClass6.class.getDeclaredField("annotatedField"); 315 | assertTrue(FieldUtils.isFieldAnnotated(field, TestAnnotation.class)); 316 | } 317 | 318 | @Test 319 | public void testIsFieldAnnotated_withoutAnnotation() throws NoSuchFieldException { 320 | Field field = SampleClass6.class.getDeclaredField("nonAnnotatedField"); 321 | assertFalse(FieldUtils.isFieldAnnotated(field, TestAnnotation.class)); 322 | } 323 | 324 | @Test 325 | public void testIsFieldAnnotated_nullField() { 326 | assertThrows(NullPointerException.class, () -> { 327 | FieldUtils.isFieldAnnotated(null, TestAnnotation.class); 328 | }); 329 | } 330 | 331 | @Test 332 | public void testIsFieldAnnotated_nullAnnotationClass() throws NoSuchFieldException { 333 | Field field = SampleClass6.class.getDeclaredField("annotatedField"); 334 | assertThrows(NullPointerException.class, () -> { 335 | FieldUtils.isFieldAnnotated(field, null); 336 | }); 337 | } 338 | 339 | // exact annotated 340 | 341 | 342 | private static class SampleClass7 { 343 | @TestAnnotation 344 | private String annotatedField; 345 | private String nonAnnotatedField; 346 | } 347 | 348 | @Test 349 | public void testIsFieldExactAnnotated_withAnnotation() throws NoSuchFieldException { 350 | Field field = SampleClass7.class.getDeclaredField("annotatedField"); 351 | assertTrue(FieldUtils.isFieldExactAnnotated(field, TestAnnotation.class)); 352 | } 353 | 354 | @Test 355 | public void testIsFieldExactAnnotated_withoutAnnotation() throws NoSuchFieldException { 356 | Field field = SampleClass7.class.getDeclaredField("nonAnnotatedField"); 357 | assertFalse(FieldUtils.isFieldExactAnnotated(field, TestAnnotation.class)); 358 | } 359 | 360 | @Test 361 | public void testIsFieldExactAnnotated_nullField() { 362 | assertThrows(NullPointerException.class, () -> { 363 | FieldUtils.isFieldExactAnnotated(null, TestAnnotation.class); 364 | }); 365 | } 366 | 367 | @Test 368 | public void testIsFieldExactAnnotated_nullAnnotationClass() throws NoSuchFieldException { 369 | Field field = SampleClass7.class.getDeclaredField("annotatedField"); 370 | assertThrows(NullPointerException.class, () -> { 371 | FieldUtils.isFieldExactAnnotated(field, null); 372 | }); 373 | } 374 | 375 | // get all fields 376 | 377 | private static class SuperSimpleClass { 378 | private int superClassField; 379 | public String superClassPublicField; 380 | } 381 | 382 | private static class SubSimpleClass extends SuperSimpleClass { 383 | private double subClassField; 384 | protected boolean subClassProtectedField; 385 | } 386 | 387 | @Test 388 | public void testGetAllFields() { 389 | List fields = FieldUtils.getAllFields(SubSimpleClass.class); 390 | 391 | assertEquals(4, fields.size()); 392 | 393 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassField"))); 394 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPublicField"))); 395 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("subClassField"))); 396 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("subClassProtectedField"))); 397 | } 398 | 399 | @Test 400 | public void testGetAllFields_SuperClassOnly() { 401 | List fields = FieldUtils.getAllFields(SuperSimpleClass.class); 402 | 403 | assertEquals(2, fields.size()); 404 | 405 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassField"))); 406 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPublicField"))); 407 | } 408 | 409 | @Test 410 | public void testGetAllFields_nullClass() { 411 | assertThrows(NullPointerException.class, () -> { 412 | FieldUtils.getAllFields(null); 413 | }); 414 | } 415 | 416 | // get all private fields 417 | 418 | private static class SuperClass { 419 | private int superClassPrivateField; 420 | public String superClassPublicField; 421 | } 422 | 423 | private static class SubClass extends SuperClass { 424 | private double subClassPrivateField; 425 | protected boolean subClassProtectedField; 426 | } 427 | 428 | @Test 429 | public void testGetAllPrivateFields() { 430 | List fields = FieldUtils.getAllPrivateFields(SubClass.class); 431 | 432 | assertEquals(2, fields.size()); 433 | 434 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPrivateField"))); 435 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("subClassPrivateField"))); 436 | } 437 | 438 | @Test 439 | public void testGetAllPrivateFields_SuperClassOnly() { 440 | List fields = FieldUtils.getAllPrivateFields(SuperClass.class); 441 | 442 | assertEquals(1, fields.size()); 443 | 444 | assertTrue(fields.stream().anyMatch(field -> field.getName().equals("superClassPrivateField"))); 445 | } 446 | 447 | @Test 448 | public void testGetAllPrivateFields_noPrivateFields() { 449 | class NoPrivateFieldsClass { 450 | public int publicField; 451 | protected int protectedField; 452 | } 453 | 454 | List fields = FieldUtils.getAllPrivateFields(NoPrivateFieldsClass.class); 455 | 456 | assertTrue(fields.isEmpty()); 457 | } 458 | 459 | @Test 460 | public void testGetAllPrivateFields_nullClass() { 461 | assertThrows(NullPointerException.class, () -> { 462 | FieldUtils.getAllPrivateFields(null); 463 | }); 464 | } 465 | 466 | @Test 467 | void clearUnselectedFieldsTest() { 468 | SimpleAnnotatedEntry entry = new SimpleAnnotatedEntry(); 469 | entry.setKey(TestConstant.ENTRY_KEY); 470 | entry.setValue(TestConstant.ENTRY_VALUE); 471 | entry.setInfo(TestConstant.ENTRY_INFO); 472 | 473 | List valuesList = new ArrayList<>(); 474 | valuesList.add(TestConstant.KEY); 475 | valuesList.add(TestConstant.VALUE); 476 | 477 | FieldUtils.clearUnselectedFields(entry, valuesList); 478 | 479 | assertAll("classMultipleParametersInstance", 480 | () -> assertEquals(entry.getInfo(), null), 481 | () -> assertNotEquals(entry.getKey(), null), 482 | () -> assertNotEquals(entry.getValue(), null) 483 | ); 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /src/main/java/org/reflector/ReflectionUtilsLegacy.java: -------------------------------------------------------------------------------- 1 | package org.reflector; 2 | 3 | import org.reflector.exception.FieldAccessException; 4 | import org.reflector.exception.InstanceInvocationException; 5 | import org.reflector.exception.MethodInvokeException; 6 | import org.reflector.util.ReflectionConstant; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.lang.annotation.Annotation; 13 | import java.lang.reflect.Constructor; 14 | import java.lang.reflect.Field; 15 | import java.lang.reflect.Method; 16 | import java.lang.reflect.Modifier; 17 | import java.net.URI; 18 | import java.net.URISyntaxException; 19 | import java.net.URL; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.Collection; 23 | import java.util.Collections; 24 | import java.util.Enumeration; 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Stack; 29 | import java.util.function.Predicate; 30 | 31 | public class ReflectionUtilsLegacy { 32 | private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtils.class); 33 | private static final ClassLoader CLASSLOADER; 34 | 35 | static { 36 | final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader(); 37 | CLASSLOADER = (threadClassLoader != null) ? threadClassLoader : ReflectionUtils.class.getClassLoader(); 38 | } 39 | 40 | /** 41 | * The private constructor of {@link ReflectionUtils}. 42 | * */ 43 | private ReflectionUtilsLegacy() { 44 | } 45 | 46 | public static String getClassFullName(final Object obj) { 47 | return obj.getClass().getName(); 48 | } 49 | 50 | public static String getClassCanonicalName(final Object obj) { 51 | return obj.getClass().getCanonicalName(); 52 | } 53 | 54 | public static String getClassSimpleName(final Object obj) { 55 | return obj.getClass().getSimpleName(); 56 | } 57 | 58 | public static String getPackage(final Object obj) { 59 | return obj.getClass().getPackage().getName(); 60 | } 61 | 62 | public static String getClassFullNameByClass(final Class clazz) { 63 | return clazz.getName(); 64 | } 65 | 66 | public static String getClassCanonicalNameByClass(final Class clazz) { 67 | return clazz.getCanonicalName(); 68 | } 69 | 70 | public static String getClassSimpleNameByClass(final Class clazz) { 71 | return clazz.getSimpleName(); 72 | } 73 | 74 | public static String getPackageByClass(final Class clazz) { 75 | return clazz.getPackage().getName(); 76 | } 77 | 78 | public static String getSuperClassName(final Object obj) { 79 | return obj.getClass().getSuperclass().getName(); 80 | } 81 | 82 | public static String getSuperClassNameByClass(final Class clazz) { 83 | return clazz.getSuperclass().getName(); 84 | } 85 | 86 | public static Class getSuperClass(final Object obj) { 87 | return obj.getClass().getSuperclass(); 88 | } 89 | 90 | public static void clearUnselectedFields(final Object object, final Collection fields) { 91 | if (fields != null && !fields.isEmpty()) { 92 | for (Field field : getAllFields(object.getClass())) { 93 | if (!fields.contains(field.getName())) { 94 | try { 95 | field.setAccessible(true); 96 | field.set(object, null); 97 | } catch (Exception e) { 98 | LOGGER.error("Could not clear private field. " + e.getMessage()); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | public static Annotation[] getClassAnnotations(final Class clazz) { 106 | return clazz.getAnnotations(); 107 | } 108 | 109 | public static Annotation[] getMethodDeclaredAnnotations(final Method method) { 110 | return method.getDeclaredAnnotations(); 111 | } 112 | 113 | public static Map getMethodDeclaredAnnotations(final Method[] methods) { 114 | Map methodToAnnotations = new HashMap<>(); 115 | for (Method method : methods) { 116 | methodToAnnotations.put(method, getMethodDeclaredAnnotations(method)); 117 | } 118 | return methodToAnnotations; 119 | } 120 | 121 | public static boolean isMethodAnnotated(final Method method, final Class clazz) { 122 | Annotation annotation = method.getAnnotation(clazz); 123 | return clazz.isInstance(annotation); 124 | } 125 | 126 | public static boolean isMethodParameterAnnotated(final Method method, final Class clazz) { 127 | Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 128 | for (Annotation[] annotations : parameterAnnotations) { 129 | for (Annotation annotation : annotations) { 130 | if (clazz.isInstance(annotation)) { 131 | return true; 132 | } 133 | } 134 | } 135 | return false; 136 | } 137 | 138 | public static boolean isFieldAnnotated(final Field field, final Class clazz) { 139 | Annotation[] annotations = field.getDeclaredAnnotations(); 140 | for (Annotation annotation : annotations) { 141 | if (clazz.isInstance(annotation)) { 142 | return true; 143 | } 144 | } 145 | return false; 146 | } 147 | 148 | public static boolean isFieldExactAnnotated(final Field field, final Class type) { 149 | Annotation annotation = field.getAnnotation(type); 150 | return type.isInstance(annotation); 151 | } 152 | 153 | public static List getAllFields(final Class type) { 154 | return getAllFields(new ArrayList<>(), type); 155 | } 156 | 157 | private static List getAllFields(final List fields, final Class type) { 158 | fields.addAll(Arrays.asList(type.getDeclaredFields())); 159 | 160 | if (type.getSuperclass() != null) { 161 | getAllFields(fields, type.getSuperclass()); 162 | } 163 | return fields; 164 | } 165 | 166 | public static List getAllPrivateFields(final Class clazz) { 167 | List fields = new ArrayList<>(); 168 | Field[] classFields = clazz.getDeclaredFields(); 169 | 170 | for (Field field : classFields) { 171 | if (Modifier.isPrivate(field.getModifiers())) { 172 | fields.add(field); 173 | } 174 | } 175 | return fields; 176 | } 177 | 178 | public static Map getAllFieldsMap(final Class clazz) { 179 | List allFields = getAllFields(clazz); 180 | return getFieldsMap(allFields); 181 | } 182 | 183 | public static Map getAllPrivateFieldsMap(final Class clazz) { 184 | List allFields = getAllPrivateFields(clazz); 185 | return getFieldsMap(allFields); 186 | } 187 | 188 | private static Map getFieldsMap(final List fields) { 189 | Map map = new HashMap<>(); 190 | for (Field field : fields) { 191 | map.put(field.getName(), field); 192 | } 193 | return map; 194 | } 195 | 196 | public static List getAllPrivateMethods(final Class clazz) { 197 | return getAllMethodsWithModifiers(clazz, Collections.singletonList(Modifier::isPrivate)); 198 | } 199 | 200 | public static List getAllPublicProtectedMethods(final Class clazz) { 201 | return getAllMethodsWithModifiers(clazz, Arrays.asList(Modifier::isPublic, Modifier::isProtected)); 202 | } 203 | 204 | public static List getAllPublicMethods(final Class clazz) { 205 | return getAllMethodsWithModifiers(clazz, Collections.singletonList(Modifier::isPublic)); 206 | } 207 | 208 | public static List getAllAnnotatedFields(final Class type, final Class annotation) { 209 | List fieldList = new ArrayList<>(); 210 | for (Field allField : getAllFields(type)) { 211 | if (allField.getAnnotation(annotation) != null) { 212 | fieldList.add(allField); 213 | allField.setAccessible(true); 214 | } 215 | } 216 | return fieldList; 217 | } 218 | 219 | public static Object readField(final Object object, final String fieldName) { 220 | try { 221 | Field field = object.getClass().getDeclaredField(fieldName); 222 | field.setAccessible(true); 223 | return field.get(object); 224 | } catch (Exception e) { 225 | LOGGER.error("Could not invoke method", e); 226 | } 227 | throw new FieldAccessException("Requested field is not accessible"); 228 | } 229 | 230 | private static List getAllMethodsWithModifiers(final Class clazz1, final List> predicates) { 231 | List result = new ArrayList<>(); 232 | Stack> classStack = new Stack<>(); 233 | classStack.push(clazz1); 234 | while (!classStack.isEmpty()) { 235 | Class clazz = classStack.pop(); 236 | for (Method method : clazz.getDeclaredMethods()) { 237 | int modifiers = method.getModifiers(); 238 | 239 | List modifiersChecks = new ArrayList<>(); 240 | for (Predicate predicate : predicates) { 241 | if (!predicate.test(modifiers)) { 242 | modifiersChecks.add(false); 243 | } else { 244 | modifiersChecks.add(true); 245 | } 246 | } 247 | boolean allModifiersCheck = false; 248 | for (Boolean modifiersCheck : modifiersChecks) { 249 | if (modifiersCheck) { 250 | allModifiersCheck = true; 251 | break; 252 | } 253 | } 254 | 255 | if (allModifiersCheck) { 256 | result.add(method); 257 | } 258 | } 259 | 260 | if (clazz.getSuperclass() != null) { 261 | classStack.push(clazz.getSuperclass()); 262 | } 263 | } 264 | return result; 265 | } 266 | 267 | public static Object invokeMethod(final Object objectToInvokeOn, final String methodName, final Class[] parameterTypes, final Object[] args) { 268 | try { 269 | Method method = objectToInvokeOn.getClass().getDeclaredMethod(methodName, parameterTypes); 270 | return method.invoke(objectToInvokeOn, args); 271 | } catch (Exception e) { 272 | LOGGER.error("Could not invoke method", e); 273 | } 274 | throw new MethodInvokeException("Error during method invoke has been happened"); 275 | } 276 | 277 | public static Object invokeSingleMethod(final Object objectToInvokeOn, 278 | final String methodName, 279 | final Class parameterType, 280 | final Object parameter) { 281 | try { 282 | final Class clazz = objectToInvokeOn.getClass(); 283 | final Method method = clazz.getMethod(methodName, parameterType); 284 | return method.invoke(objectToInvokeOn, parameter); 285 | } catch (Exception e) { 286 | LOGGER.error("Could not invoke {{}} method ", methodName, e); 287 | } 288 | throw new MethodInvokeException("Error during method invoke has been happened"); 289 | } 290 | 291 | public static Object invokeInstance(final String className) throws InstanceInvocationException { 292 | try { 293 | return Class.forName(className).newInstance(); 294 | } catch (Exception e) { 295 | LOGGER.error("Could not instantiate class object ", e); 296 | } 297 | throw new InstanceInvocationException("Error during instance invoke has been happened"); 298 | } 299 | 300 | public static Object invokeInstance(final String classFullName, final Object... args) throws InstanceInvocationException { 301 | try { 302 | final Class clazz = Class.forName(classFullName); 303 | final Class[] ctorTypes = getArrayValuesTypesByArgs(args); 304 | final Constructor ctor = getAccessibleConstructor(ctorTypes, clazz); 305 | return ctor.newInstance(args); 306 | } catch (Exception e) { 307 | LOGGER.error("Could not instantiate class {{}} object ", classFullName, e); 308 | } 309 | throw new InstanceInvocationException("Error during instance invoke has been happened"); 310 | } 311 | 312 | public static T invokeInstance(final Class clazz, final Object... args) throws InstanceInvocationException { 313 | try { 314 | final Class[] ctorTypes = getArrayValuesTypesByArgs(args); 315 | final Constructor ctor = getAccessibleConstructor(ctorTypes, clazz); 316 | return ctor.newInstance(args); 317 | } catch (Exception e) { 318 | LOGGER.error("Could not instantiate class {{}} object ", clazz, e); 319 | } 320 | throw new InstanceInvocationException("Error during instance invoke has been happened"); 321 | } 322 | 323 | public static Class[] getArrayValuesTypesByArgs(final Object[] args) { 324 | final Class[] ctorTypes = new Class[args.length]; 325 | for (int i = 0; i < args.length; i++) { 326 | ctorTypes[i] = args[i].getClass(); 327 | } 328 | return ctorTypes; 329 | } 330 | 331 | public static Constructor getAccessibleConstructor(final Class[] contTypes, final Class clazz) 332 | throws NoSuchMethodException { 333 | final Constructor ctor = clazz.getConstructor(contTypes); 334 | ctor.setAccessible(true); 335 | return ctor; 336 | } 337 | 338 | public static Constructor[] getConstructors(final Class clazz) { 339 | return clazz.getConstructors(); 340 | } 341 | 342 | public static Constructor[] getDeclaredConstructors(final Class clazz) { 343 | return clazz.getDeclaredConstructors(); 344 | } 345 | 346 | private static boolean isFieldPrimitiveType(final Field field) { 347 | return field.getType().isPrimitive() 348 | || field.getType().equals(String.class) 349 | || field.getType().getSuperclass().equals(Number.class) 350 | || field.getType().equals(Boolean.class); 351 | } 352 | 353 | public static Object copy(final Object object) { 354 | Object copyObj = null; 355 | try { 356 | try { 357 | copyObj = object.getClass().newInstance(); 358 | } catch (Exception ex) { 359 | LOGGER.error("Error copy for object{{}}", object); 360 | return null; 361 | } 362 | for (Field field : object.getClass().getDeclaredFields()) { 363 | field.setAccessible(true); 364 | if (field.get(object) == null || Modifier.isFinal(field.getModifiers())) { 365 | continue; 366 | } 367 | if (isFieldPrimitiveType(field)) { 368 | field.set(copyObj, field.get(object)); 369 | } else { 370 | Object childObj = field.get(object); 371 | field.set(copyObj, (childObj == object) ? copyObj : copy(field.get(object))); 372 | } 373 | } 374 | } catch (Exception e) { 375 | LOGGER.error("Error during copy object", e); 376 | throw new IllegalStateException("Failed to get declared methods for Class [" + object.getClass().getName() + "] from ClassLoader [" + object.getClass().getClassLoader() + "]", e); 377 | } 378 | return copyObj; 379 | } 380 | 381 | public static List> getClassesByPackage(final String packageName) 382 | throws ClassNotFoundException, IOException, URISyntaxException { 383 | String path = packageName.replace(ReflectionConstant.DOT_SYMBOL, ReflectionConstant.SLASH); 384 | Enumeration resources = CLASSLOADER.getResources(path); 385 | List directories = new ArrayList<>(); 386 | while (resources.hasMoreElements()) { 387 | directories.add(new File(new URI(resources.nextElement().toString()).getPath())); 388 | } 389 | List> classes = new ArrayList<>(); 390 | for (File directory : directories) { 391 | classes.addAll(getClassesByDirectoryAndPackage(directory, packageName)); 392 | } 393 | return classes; 394 | } 395 | 396 | public static List> getClassesByDirectoryAndPackage(final File directory, final String packageName) 397 | throws ClassNotFoundException { 398 | List> classes = new ArrayList<>(); 399 | if (!directory.exists()) { 400 | return classes; 401 | } 402 | File[] files = directory.listFiles(); 403 | if (files != null) { 404 | for (File file : files) { 405 | if (file.isDirectory()) { 406 | classes.addAll(getClassesByDirectoryAndPackage(file, packageName + ReflectionConstant.DOT + file.getName())); 407 | } else if (file.getName().endsWith(ReflectionConstant.CLASS)) { 408 | classes.add(Class.forName(packageName + ReflectionConstant.DOT + file.getName().substring(0, file.getName().length() - ReflectionConstant.CLASS_NAME_CONSTANT))); 409 | } 410 | } 411 | } 412 | return classes; 413 | } 414 | 415 | public static Method findMethodByName(final Class clazz, final String name) { 416 | Class classSearchType = clazz; 417 | while (classSearchType != null) { 418 | Method[] methods = (classSearchType.isInterface() ? classSearchType.getMethods() : getDeclaredMethods(classSearchType)); 419 | for (Method method : methods) { 420 | if (name.equals(method.getName())) { 421 | return method; 422 | } 423 | } 424 | classSearchType = classSearchType.getSuperclass(); 425 | } 426 | return null; 427 | } 428 | 429 | public static List> getAllAnnotatedClassesByPackage(final String packageName, final Class annotation) 430 | throws IOException, URISyntaxException, ClassNotFoundException { 431 | List> classesByPackage = getClassesByPackage(packageName); 432 | List> classes = new ArrayList<>(); 433 | for (Class aClass : classesByPackage) { 434 | if (aClass.isAnnotationPresent(annotation)) { 435 | classes.add(aClass); 436 | } 437 | } 438 | return classes; 439 | } 440 | 441 | public static List findDefaultMethodsOnInterfaces(final Class clazz) { 442 | List result = new ArrayList<>(); 443 | for (Class ifc : clazz.getInterfaces()) { 444 | for (Method method : ifc.getMethods()) { 445 | if (method.isDefault()) { 446 | result.add(method); 447 | } 448 | } 449 | } 450 | return result; 451 | } 452 | 453 | public static Method[] getDeclaredMethods(final Class clazz) { 454 | Method[] result; 455 | try { 456 | Method[] declaredMethods = clazz.getDeclaredMethods(); 457 | List defaultMethods = findDefaultMethodsOnInterfaces(clazz); 458 | result = new Method[declaredMethods.length + defaultMethods.size()]; 459 | System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); 460 | int index = declaredMethods.length; 461 | for (Method defaultMethod : defaultMethods) { 462 | result[index] = defaultMethod; 463 | index++; 464 | } 465 | } 466 | catch (Throwable ex) { 467 | throw new IllegalStateException("Failed to get declared methods for Class [" + clazz.getName() + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); 468 | } 469 | 470 | return result; 471 | } 472 | 473 | public static List getDeclaredMethodsList(final Class clazz) { 474 | List methods = new ArrayList<>(); 475 | try { 476 | methods.addAll(Arrays.asList(clazz.getDeclaredMethods())); 477 | methods.addAll(findDefaultMethodsOnInterfaces(clazz)); 478 | } 479 | catch (Throwable ex) { 480 | throw new IllegalStateException("Failed to get declared methods for Class [" + clazz.getName() + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); 481 | } 482 | return methods; 483 | } 484 | 485 | public static Method findMethod(final Class clazz, final String name) { 486 | Class classSearchType = clazz; 487 | while (classSearchType != null) { 488 | Method[] methods = (classSearchType.isInterface() ? classSearchType.getMethods() : getDeclaredMethods(classSearchType)); 489 | for (Method method : methods) { 490 | if (name.equals(method.getName())) { 491 | return method; 492 | } 493 | } 494 | classSearchType = classSearchType.getSuperclass(); 495 | } 496 | return null; 497 | } 498 | } 499 | -------------------------------------------------------------------------------- /src/test/java/org/common/reflector/utils/MethodUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.common.reflector.utils; 2 | 3 | import org.common.reflector.data.MethodAnnotatedClass; 4 | import org.common.reflector.data.Person; 5 | import org.common.reflector.data.SimpleAnnotatedEntry; 6 | import org.common.reflector.util.TestConstant; 7 | import org.junit.jupiter.api.Test; 8 | import org.reflector.MethodUtils; 9 | 10 | import java.lang.annotation.ElementType; 11 | import java.lang.annotation.Retention; 12 | import java.lang.annotation.RetentionPolicy; 13 | import java.lang.annotation.Target; 14 | import java.lang.reflect.Method; 15 | import java.lang.reflect.Modifier; 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | 21 | import static org.junit.jupiter.api.Assertions.assertAll; 22 | import static org.junit.jupiter.api.Assertions.assertEquals; 23 | import static org.junit.jupiter.api.Assertions.assertFalse; 24 | import static org.junit.jupiter.api.Assertions.assertNotNull; 25 | import static org.junit.jupiter.api.Assertions.assertNull; 26 | import static org.junit.jupiter.api.Assertions.assertThrows; 27 | import static org.junit.jupiter.api.Assertions.assertTrue; 28 | 29 | 30 | class TestClassParameter { 31 | public void testMethod(String param1, int param2) throws IllegalArgumentException, NullPointerException { 32 | } 33 | 34 | public int returnMethod() { 35 | return 0; 36 | } 37 | 38 | public void noExceptionMethod() { 39 | } 40 | 41 | public void noVarArgsMethod() { 42 | } 43 | } 44 | 45 | @Retention(RetentionPolicy.RUNTIME) 46 | @Target({ElementType.METHOD, ElementType.FIELD}) 47 | @interface TestAnnotation { 48 | String value() default "default"; 49 | } 50 | 51 | class TestClass1 { 52 | @TestAnnotation 53 | public void testMethod() { 54 | } 55 | 56 | public void varArgsMethod(String... args) { 57 | } 58 | 59 | public void noVarArgsMethod(String arg) { 60 | } 61 | } 62 | 63 | public class MethodUtilsTest { 64 | 65 | @Test 66 | public void testGetParameterTypes_ValidMethod() throws NoSuchMethodException { 67 | Method method = TestClassParameter.class.getMethod("testMethod", String.class, int.class); 68 | Class[] parameterTypes = MethodUtils.getParameterTypes(method); 69 | assertNotNull(parameterTypes); 70 | assertEquals(2, parameterTypes.length); 71 | assertEquals(String.class, parameterTypes[0]); 72 | assertEquals(int.class, parameterTypes[1]); 73 | } 74 | 75 | @Test 76 | public void testGetParameterTypes_NullMethod() { 77 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 78 | MethodUtils.getParameterTypes(null); 79 | }); 80 | String expectedMessage = "Method must not be null"; 81 | String actualMessage = exception.getMessage(); 82 | assertTrue(actualMessage.contains(expectedMessage)); 83 | } 84 | 85 | @Test 86 | public void testGetReturnType_ValidMethod() throws NoSuchMethodException { 87 | Method method = TestClassParameter.class.getMethod("returnMethod"); 88 | Class returnType = MethodUtils.getReturnType(method); 89 | assertNotNull(returnType); 90 | assertEquals(int.class, returnType); 91 | } 92 | 93 | @Test 94 | public void testGetReturnType_NullMethod() { 95 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 96 | MethodUtils.getReturnType(null); 97 | }); 98 | String expectedMessage = "Method must not be null"; 99 | String actualMessage = exception.getMessage(); 100 | assertTrue(actualMessage.contains(expectedMessage)); 101 | } 102 | 103 | @Test 104 | public void testGetExceptionTypes_ValidMethod() throws NoSuchMethodException { 105 | Method method = TestClassParameter.class.getMethod("testMethod", String.class, int.class); 106 | Class[] exceptionTypes = MethodUtils.getExceptionTypes(method); 107 | assertNotNull(exceptionTypes); 108 | assertEquals(2, exceptionTypes.length); 109 | assertEquals(IllegalArgumentException.class, exceptionTypes[0]); 110 | assertEquals(NullPointerException.class, exceptionTypes[1]); 111 | } 112 | 113 | @Test 114 | public void testGetExceptionTypes_NoExceptionMethod() throws NoSuchMethodException { 115 | Method method = TestClassParameter.class.getMethod("noVarArgsMethod"); 116 | Class[] exceptionTypes = MethodUtils.getExceptionTypes(method); 117 | assertNotNull(exceptionTypes); 118 | assertEquals(0, exceptionTypes.length); 119 | } 120 | 121 | @Test 122 | public void testGetExceptionTypes_NullMethod() { 123 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 124 | MethodUtils.getExceptionTypes(null); 125 | }); 126 | String expectedMessage = "Method must not be null"; 127 | String actualMessage = exception.getMessage(); 128 | assertTrue(actualMessage.contains(expectedMessage)); 129 | } 130 | 131 | @Test 132 | public void testGetMethodModifiers_PublicMethod() throws NoSuchMethodException { 133 | Method method = TestClass1.class.getMethod("testMethod"); 134 | int modifiers = MethodUtils.getMethodModifiers(method); 135 | assertTrue(Modifier.isPublic(modifiers)); 136 | } 137 | 138 | @Test 139 | public void testGetMethodModifiers_NullMethod() { 140 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 141 | MethodUtils.getMethodModifiers(null); 142 | }); 143 | String expectedMessage = "Method must not be null"; 144 | String actualMessage = exception.getMessage(); 145 | assertTrue(actualMessage.contains(expectedMessage)); 146 | } 147 | 148 | @Test 149 | public void testIsMethodVarArgs_VarArgsMethod() throws NoSuchMethodException { 150 | Method method = TestClass1.class.getMethod("varArgsMethod", String[].class); 151 | boolean isVarArgs = MethodUtils.isMethodVarArgs(method); 152 | assertTrue(isVarArgs); 153 | } 154 | 155 | @Test 156 | public void testIsMethodVarArgs_NoVarArgsMethod() throws NoSuchMethodException { 157 | Method method = TestClass1.class.getMethod("noVarArgsMethod", String.class); 158 | boolean isVarArgs = MethodUtils.isMethodVarArgs(method); 159 | assertFalse(isVarArgs); 160 | } 161 | 162 | @Test 163 | public void testIsMethodVarArgs_NullMethod() { 164 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 165 | MethodUtils.isMethodVarArgs(null); 166 | }); 167 | String expectedMessage = "Method must not be null"; 168 | String actualMessage = exception.getMessage(); 169 | assertTrue(actualMessage.contains(expectedMessage)); 170 | } 171 | 172 | @Test 173 | public void testGetDefaultValue_AnnotationMethod() throws NoSuchMethodException { 174 | Method method = TestAnnotation.class.getMethod("value"); 175 | Object defaultValue = MethodUtils.getDefaultValue(method); 176 | assertNotNull(defaultValue); 177 | assertEquals("default", defaultValue); 178 | } 179 | 180 | @Test 181 | public void testGetDefaultValue_NoDefaultValue() throws NoSuchMethodException { 182 | Method method = TestClass1.class.getMethod("testMethod"); 183 | Object defaultValue = MethodUtils.getDefaultValue(method); 184 | assertNull(defaultValue); 185 | } 186 | 187 | @Test 188 | public void testGetDefaultValue_NullMethod() { 189 | Exception exception = assertThrows(IllegalArgumentException.class, () -> { 190 | MethodUtils.getDefaultValue(null); 191 | }); 192 | String expectedMessage = "Method must not be null"; 193 | String actualMessage = exception.getMessage(); 194 | assertTrue(actualMessage.contains(expectedMessage)); 195 | } 196 | 197 | private static class SampleClass { 198 | private void privateMethod() {} 199 | protected void protectedMethod() {} 200 | public void publicMethod() {} 201 | public static void publicStaticMethod() {} 202 | } 203 | 204 | @Test 205 | public void testGetAllPrivateMethods() { 206 | List methods = MethodUtils.getAllPrivateMethods(SampleClass.class); 207 | assertEquals(1, methods.size()); 208 | assertEquals("privateMethod", methods.get(0).getName()); 209 | } 210 | 211 | @Test 212 | public void testGetAllPublicProtectedMethods() { 213 | List methods = MethodUtils.getAllPublicProtectedMethods(SampleClass.class); 214 | assertEquals(3, methods.size()); 215 | assertTrue(methods.stream().anyMatch(method -> method.getName().equals("protectedMethod"))); 216 | assertTrue(methods.stream().anyMatch(method -> method.getName().equals("publicMethod"))); 217 | assertTrue(methods.stream().anyMatch(method -> method.getName().equals("publicStaticMethod"))); 218 | } 219 | 220 | @Test 221 | public void testGetAllPublicMethods() { 222 | List methods = MethodUtils.getAllPublicMethods(SampleClass.class); 223 | assertEquals(2, methods.size()); 224 | assertTrue(methods.stream().anyMatch(method -> method.getName().equals("publicMethod"))); 225 | assertTrue(methods.stream().anyMatch(method -> method.getName().equals("publicStaticMethod"))); 226 | } 227 | 228 | @Test 229 | public void testGetAllPrivateMethods_nullClass() { 230 | assertThrows(NullPointerException.class, () -> { 231 | MethodUtils.getAllPrivateMethods(null); 232 | }); 233 | } 234 | 235 | @Test 236 | public void testGetAllPublicProtectedMethods_nullClass() { 237 | assertThrows(NullPointerException.class, () -> { 238 | MethodUtils.getAllPublicProtectedMethods(null); 239 | }); 240 | } 241 | 242 | @Test 243 | public void testGetAllPublicMethods_nullClass() { 244 | assertThrows(NullPointerException.class, () -> { 245 | MethodUtils.getAllPublicMethods(null); 246 | }); 247 | } 248 | 249 | @Test 250 | public void testGetAllMethodsWithModifiers_nullModifiers() { 251 | assertThrows(NullPointerException.class, () -> { 252 | MethodUtils.getAllMethodsWithModifiers(SampleClass.class, null); 253 | }); 254 | } 255 | 256 | @Test 257 | public void getAllPublicMethodsTest() { 258 | List allPublicProtectedMethods = MethodUtils.getAllPublicProtectedMethods(SimpleAnnotatedEntry.class); 259 | assertAll("publicProtectedMethods", 260 | () -> assertEquals(allPublicProtectedMethods.size(), 6) 261 | ); 262 | } 263 | 264 | @Test 265 | public void getAllPrivateMethodsTest() { 266 | List allPublicProtectedMethods = MethodUtils.getAllPrivateMethods(SimpleAnnotatedEntry.class); 267 | assertEquals(TestConstant.DO_SOMETHING_METHOD_NAME, allPublicProtectedMethods.get(0).getName()); 268 | } 269 | 270 | @Test 271 | public void testGetAllMethodsWithModifiers_nullPredicates() { 272 | assertThrows(NullPointerException.class, () -> { 273 | MethodUtils.getAllMethodsWithModifiers(SampleClass.class, null); 274 | }); 275 | } 276 | 277 | @Test 278 | public void testGetAllMethodsWithModifiers_emptyPredicates() { 279 | List methods = MethodUtils.getAllMethodsWithModifiers(SampleClass.class, List.of()); 280 | assertTrue(methods.isEmpty()); 281 | } 282 | 283 | 284 | 285 | @Test 286 | public void doesHasAnnotations() { 287 | List allPublicProtectedMethods = MethodUtils.getAllPublicMethods(MethodAnnotatedClass.class); 288 | List expected = new ArrayList<>(Arrays.asList(TestConstant.GET_CLASS_METHOD_METHOD_ANNOTATED_CLASS, 289 | TestConstant.ANNOTATED_METHOD_ANNOTATED_CLASS, 290 | TestConstant.WAIT_METHOD_ANNOTATED_CLASS, 291 | TestConstant.HASH_CODE_METHOD_ANNOTATED_CLASS, 292 | TestConstant.EQUALS_METHOD_ANNOTATED_CLASS, 293 | TestConstant.NOTIFY_ALL_METHOD_ANNOTATED_CLASS, 294 | TestConstant.TO_STRING_METHOD_ANNOTATED_CLASS, 295 | TestConstant.NOTIFY_METHOD_ANNOTATED_CLASS)); 296 | List actual = allPublicProtectedMethods.stream().map(Method::getName).collect(Collectors.toList()); 297 | assertFalse(expected.size() == actual.size() && expected.containsAll(actual) && actual.containsAll(expected)); 298 | } 299 | 300 | 301 | @Test 302 | public void findMethodsTestByName() { 303 | Method method = MethodUtils.findMethodByName(Person.class, TestConstant.METHOD_NAME_GET_ID); 304 | assertEquals(TestConstant.METHOD_NAME_GET_ID, method.getName()); 305 | } 306 | 307 | //getDefaultMethodsOfInterfaces 308 | 309 | /** 310 | * An interface with a default method for testing. 311 | */ 312 | interface TestInterfaceWithMethod1 { 313 | default void defaultMethod1() { 314 | System.out.println("Default Method 1"); 315 | } 316 | 317 | void nonDefaultMethod1(); 318 | } 319 | 320 | /** 321 | * Another interface with a default method for testing. 322 | */ 323 | interface TestInterfaceWithMethod2 { 324 | default void defaultMethod2() { 325 | System.out.println("Default Method 2"); 326 | } 327 | 328 | void nonDefaultMethod2(); 329 | } 330 | 331 | /** 332 | * A class implementing the test interfaces. 333 | */ 334 | class AggregatedTestClass implements TestInterfaceWithMethod1, TestInterfaceWithMethod2 { 335 | @Override 336 | public void nonDefaultMethod1() {} 337 | 338 | @Override 339 | public void nonDefaultMethod2() {} 340 | } 341 | 342 | /** 343 | * Tests if getDefaultMethodsOfInterfaces retrieves the correct default methods. 344 | */ 345 | @Test 346 | void testGetDefaultMethodsOfInterfaces() throws NoSuchMethodException { 347 | List defaultMethods = MethodUtils.getDefaultMethodsOfInterfaces(AggregatedTestClass.class); 348 | 349 | assertEquals(2, defaultMethods.size()); 350 | 351 | Method defaultMethod1 = TestInterfaceWithMethod1.class.getMethod("defaultMethod1"); 352 | Method defaultMethod2 = TestInterfaceWithMethod2.class.getMethod("defaultMethod2"); 353 | 354 | assertTrue(defaultMethods.contains(defaultMethod1)); 355 | assertTrue(defaultMethods.contains(defaultMethod2)); 356 | } 357 | 358 | /** 359 | * Tests if getDefaultMethodsOfInterfaces returns an empty list when no default methods are present. 360 | */ 361 | @Test 362 | void testGetDefaultMethodsOfInterfaces_noDefaultMethods() { 363 | class NoDefaultMethodClass {} 364 | 365 | List defaultMethods = MethodUtils.getDefaultMethodsOfInterfaces(NoDefaultMethodClass.class); 366 | assertTrue(defaultMethods.isEmpty()); 367 | } 368 | 369 | /** 370 | * Tests if getDefaultMethodsOfInterfaces returns an empty list when the class has no interfaces. 371 | */ 372 | @Test 373 | void testGetDefaultMethodsOfInterfaces_classWithNoInterfaces() { 374 | class NoInterfacesClass {} 375 | 376 | List defaultMethods = MethodUtils.getDefaultMethodsOfInterfaces(NoInterfacesClass.class); 377 | assertTrue(defaultMethods.isEmpty()); 378 | } 379 | 380 | /** 381 | * Tests if getDefaultMethodsOfInterfaces throws an IllegalArgumentException when the class is null. 382 | */ 383 | @Test 384 | void testGetDefaultMethodsOfInterfaces_nullClass() { 385 | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { 386 | MethodUtils.getDefaultMethodsOfInterfaces(null); 387 | }); 388 | 389 | assertEquals("Class parameter cannot be null", exception.getMessage()); 390 | } 391 | 392 | 393 | /** 394 | * An interface with a default method for testing. 395 | */ 396 | interface TestInterfaceWithDefaultAndNotDefaultMethods1 { 397 | default void defaultMethod1() { 398 | System.out.println("Default Method 1"); 399 | } 400 | 401 | void nonDefaultMethod1(); 402 | } 403 | 404 | /** 405 | * Another interface with a default method for testing. 406 | */ 407 | interface TestInterfaceWithDefaultAndNotDefaultMethods2 { 408 | default void defaultMethod2() { 409 | System.out.println("Default Method 2"); 410 | } 411 | 412 | void nonDefaultMethod2(); 413 | } 414 | 415 | /** 416 | * A class implementing the test interfaces. 417 | */ 418 | class AggregatedTestClassForInterfacesWithDefaultMethods implements TestInterfaceWithDefaultAndNotDefaultMethods1, TestInterfaceWithDefaultAndNotDefaultMethods2 { 419 | @Override 420 | public void nonDefaultMethod1() {} 421 | 422 | @Override 423 | public void nonDefaultMethod2() {} 424 | 425 | public void declaredMethod1() {} 426 | 427 | private void declaredMethod2() {} 428 | } 429 | 430 | /** 431 | * Tests if getDeclaredMethodsList retrieves the correct declared methods and default methods. 432 | */ 433 | @Test 434 | void testGetDeclaredMethodsList() throws NoSuchMethodException { 435 | List methods = MethodUtils.getDeclaredMethodsList(AggregatedTestClassForInterfacesWithDefaultMethods.class); 436 | 437 | assertEquals(6, methods.size()); 438 | 439 | Method declaredMethod1 = AggregatedTestClassForInterfacesWithDefaultMethods.class.getDeclaredMethod("declaredMethod1"); 440 | Method declaredMethod2 = AggregatedTestClassForInterfacesWithDefaultMethods.class.getDeclaredMethod("declaredMethod2"); 441 | Method defaultMethod1 = TestInterfaceWithDefaultAndNotDefaultMethods1.class.getMethod("defaultMethod1"); 442 | Method defaultMethod2 = TestInterfaceWithDefaultAndNotDefaultMethods2.class.getMethod("defaultMethod2"); 443 | 444 | assertTrue(methods.contains(declaredMethod1)); 445 | assertTrue(methods.contains(declaredMethod2)); 446 | assertTrue(methods.contains(defaultMethod1)); 447 | assertTrue(methods.contains(defaultMethod2)); 448 | } 449 | 450 | /** 451 | * Tests if getDeclaredMethodsList returns only declared methods when no default methods are present. 452 | */ 453 | @Test 454 | void testGetDeclaredMethodsList_noDefaultMethods() { 455 | class NoDefaultMethodClass { 456 | public void declaredMethod() {} 457 | } 458 | 459 | List methods = MethodUtils.getDeclaredMethodsList(NoDefaultMethodClass.class); 460 | assertEquals(1, methods.size()); 461 | } 462 | 463 | /** 464 | * Tests if getDeclaredMethodsList returns only declared methods when the class has no interfaces. 465 | */ 466 | @Test 467 | void testGetDeclaredMethodsList_classWithNoInterfaces() { 468 | class NoInterfacesClass { 469 | public void declaredMethod() {} 470 | } 471 | 472 | List methods = MethodUtils.getDeclaredMethodsList(NoInterfacesClass.class); 473 | assertEquals(1, methods.size()); 474 | } 475 | 476 | /** 477 | * Tests if getDeclaredMethodsList throws an IllegalArgumentException when the class parameter is null. 478 | */ 479 | @Test 480 | void testGetDeclaredMethodsList_nullClass() { 481 | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { 482 | MethodUtils.getDeclaredMethodsList(null); 483 | }); 484 | 485 | assertEquals("Class parameter cannot be null", exception.getMessage()); 486 | } 487 | 488 | //MethodUtils.findMethodByName 489 | 490 | 491 | interface TestInterface1 { 492 | default void defaultMethod1() { 493 | System.out.println("Default Method 1"); 494 | } 495 | 496 | void nonDefaultMethod1(); 497 | } 498 | 499 | interface TestInterface2 { 500 | default void defaultMethod2() { 501 | System.out.println("Default Method 2"); 502 | } 503 | 504 | void nonDefaultMethod2(); 505 | } 506 | 507 | class TestClass implements TestInterface1, TestInterface2 { 508 | @Override 509 | public void nonDefaultMethod1() {} 510 | 511 | @Override 512 | public void nonDefaultMethod2() {} 513 | 514 | public void declaredMethod1() {} 515 | 516 | private void declaredMethod2() {} 517 | } 518 | 519 | class SubTestClass extends TestClass { 520 | public void subDeclaredMethod() {} 521 | } 522 | 523 | /** 524 | * Tests if findMethodByName retrieves the correct method by name from the class hierarchy. 525 | */ 526 | @Test 527 | void testFindMethodByName() throws NoSuchMethodException { 528 | Method method = MethodUtils.findMethodByName(SubTestClass.class, "subDeclaredMethod"); 529 | assertNotNull(method); 530 | assertEquals("subDeclaredMethod", method.getName()); 531 | 532 | method = MethodUtils.findMethodByName(SubTestClass.class, "declaredMethod1"); 533 | assertNotNull(method); 534 | assertEquals("declaredMethod1", method.getName()); 535 | 536 | method = MethodUtils.findMethodByName(SubTestClass.class, "defaultMethod1"); 537 | assertNotNull(method); 538 | assertEquals("defaultMethod1", method.getName()); 539 | } 540 | 541 | /** 542 | * Tests if findMethodByName returns null when the method name is not found. 543 | */ 544 | @Test 545 | void testFindMethodByName_notFound() { 546 | Method method = MethodUtils.findMethodByName(SubTestClass.class, "nonExistentMethod"); 547 | assertNull(method); 548 | } 549 | 550 | /** 551 | * Tests if findMethodByName throws an IllegalArgumentException when the class parameter is null. 552 | */ 553 | @Test 554 | void testFindMethodByName_nullClass() { 555 | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { 556 | MethodUtils.findMethodByName(null, "someMethod"); 557 | }); 558 | 559 | assertEquals("Class and method name parameters cannot be null", exception.getMessage()); 560 | } 561 | 562 | /** 563 | * Tests if findMethodByName throws an IllegalArgumentException when the method name parameter is null. 564 | */ 565 | @Test 566 | void testFindMethodByName_nullMethodName() { 567 | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { 568 | MethodUtils.findMethodByName(SubTestClass.class, null); 569 | }); 570 | 571 | assertEquals("Class and method name parameters cannot be null", exception.getMessage()); 572 | } 573 | } 574 | --------------------------------------------------------------------------------