From 2ea486a05d6f37dd06eefdb1722d119386b1e35c Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Wed, 24 Dec 2025 04:18:01 +0100 Subject: [PATCH] Fixed Java 9+ reflection issue --- src/zutil/ClassUtil.java | 48 ++++++++++++++++++++++++++++++--- src/zutil/parser/Templator.java | 11 ++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/zutil/ClassUtil.java b/src/zutil/ClassUtil.java index 72c3cc6..d1f5757 100755 --- a/src/zutil/ClassUtil.java +++ b/src/zutil/ClassUtil.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2020 Ziver Koc + * Copyright (c) 2020-2025 Ziver Koc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,9 +24,7 @@ package zutil; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -248,4 +246,46 @@ public class ClassUtil { return fields; } + + + /** + * Finds a method definition that is public and accessible for a given method. + * This is required for Java 9+ compatibility when reflecting on internal classes. + * + * @param method the method to search for. + * @return a public Method that can be called by reflection without throwing an exception, or the original method will be returned. + */ + public static Method getAccessibleMethod(Method method) { + if (Modifier.isPublic(method.getDeclaringClass().getModifiers())) { + // The method is already accessible so return it directly + return method; + } + + String methodName = method.getName(); + Class[] paramTypes = method.getParameterTypes(); + + // Check implemented interfaces for the method (e.g., finding Map.Entry.getKey()) + for (Class interfaceClass : method.getDeclaringClass().getInterfaces()) { + try { + Method interfaceMethod = interfaceClass.getMethod(methodName, paramTypes); + if (Modifier.isPublic(interfaceMethod.getDeclaringClass().getModifiers())) { + return interfaceMethod; + } + } catch (NoSuchMethodException e) { + // Interface does not have this method, continue loop + } + } + + // Check if superclass has the method + Class superClass = method.getDeclaringClass().getSuperclass(); + if (superClass != null) { + try { + Method superMethod = superClass.getMethod(methodName, paramTypes); + return getAccessibleMethod(superMethod); + } catch (NoSuchMethodException e) {} + } + + // No public version was found, return original + return method; + } } diff --git a/src/zutil/parser/Templator.java b/src/zutil/parser/Templator.java index 9e0ffce..fed3022 100755 --- a/src/zutil/parser/Templator.java +++ b/src/zutil/parser/Templator.java @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2020 Ziver Koc + * Copyright (c) 2020-2025 Ziver Koc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -152,7 +152,7 @@ public class Templator { } /** - * Will parse or re-parse the source template. + * Will parse or reparse the source template. */ private void parseTemplate(String tmpl) { tmplRoot = parseTemplate(new TemplateNode(), tmpl, new MutableInt(), null); @@ -396,11 +396,11 @@ public class Templator { if (attrib.endsWith("()")) { // Is this a function call? if (attrib.length() > 2) { String funcName = attrib.substring(0, attrib.length()-2); - // Using a loop as the direct lookup throws a exception if no field was found + // Using a loop as the direct lookup throws an exception if no field was found // So this is probably a bit faster for (Method m : obj.getClass().getMethods()) { if (m.getParameterTypes().length == 0 && m.getName().equals(funcName)) { - m.setAccessible(true); + m = ClassUtil.getAccessibleMethod(m); return m.invoke(obj); } } @@ -411,11 +411,10 @@ public class Templator { else if (obj instanceof Collection && "length".equals(attrib)) return ((Collection) obj).size(); else { - // Using a loop as the direct lookup throws a exception if no field was found + // Using a loop as the direct lookup throws an exception if no field was found // So this is probably a bit faster for (Field field : ClassUtil.getAllDeclaredFields(obj.getClass())) { // Only look for public fields if (field.getName().equals(attrib)) { - field.setAccessible(true); return field.get(obj); } }