|
@@ -6,25 +6,29 @@
|
|
|
*/
|
|
*/
|
|
|
package net.ranides.assira.reflection.util;
|
|
package net.ranides.assira.reflection.util;
|
|
|
|
|
|
|
|
|
|
+import net.ranides.assira.io.PathUtils;
|
|
|
|
|
+import net.ranides.assira.system.RuntimeUtils;
|
|
|
|
|
+import net.ranides.assira.text.Charsets;
|
|
|
|
|
+import net.ranides.assira.text.IOStrings;
|
|
|
|
|
+import net.ranides.assira.trace.ExceptionUtils;
|
|
|
|
|
+
|
|
|
import java.io.File;
|
|
import java.io.File;
|
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
import java.io.InputStream;
|
|
|
import java.lang.reflect.AccessibleObject;
|
|
import java.lang.reflect.AccessibleObject;
|
|
|
|
|
+import java.lang.reflect.Field;
|
|
|
|
|
+import java.lang.reflect.Modifier;
|
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Path;
|
|
|
import java.security.CodeSource;
|
|
import java.security.CodeSource;
|
|
|
-import net.ranides.assira.io.PathUtils;
|
|
|
|
|
-import net.ranides.assira.system.RuntimeUtils;
|
|
|
|
|
-import net.ranides.assira.text.Charsets;
|
|
|
|
|
-import net.ranides.assira.text.IOStrings;
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
*
|
|
*
|
|
|
* @author ranides
|
|
* @author ranides
|
|
|
*/
|
|
*/
|
|
|
public final class ReflectUtils {
|
|
public final class ReflectUtils {
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
private ReflectUtils() { /* utility class */ }
|
|
private ReflectUtils() { /* utility class */ }
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Próbuje wymusić prawa dostępu do podanego obiektu uzyskanego za pomocą refleksji
|
|
* Próbuje wymusić prawa dostępu do podanego obiektu uzyskanego za pomocą refleksji
|
|
|
* (np zyskać uprawnienia do wywołania prywatnej metody, albo odczytu prywatnego pola).
|
|
* (np zyskać uprawnienia do wywołania prywatnej metody, albo odczytu prywatnego pola).
|
|
@@ -33,48 +37,69 @@ public final class ReflectUtils {
|
|
|
* @return
|
|
* @return
|
|
|
*/
|
|
*/
|
|
|
public static <T extends AccessibleObject> T access(T object) {
|
|
public static <T extends AccessibleObject> T access(T object) {
|
|
|
- if( object.isAccessible() ) {
|
|
|
|
|
|
|
+ if( object.isAccessible() ) {
|
|
|
return object;
|
|
return object;
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
RuntimeUtils.doPrivileged(() -> object.setAccessible(true));
|
|
RuntimeUtils.doPrivileged(() -> object.setAccessible(true));
|
|
|
- } catch(SecurityException $0) { // NOPMD
|
|
|
|
|
- // ignore
|
|
|
|
|
|
|
+ } catch(SecurityException cause) {
|
|
|
|
|
+ throw ExceptionUtils.rethrow(cause);
|
|
|
}
|
|
}
|
|
|
return object;
|
|
return object;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public static Field rewritable(Field field) {
|
|
|
|
|
+ if(0 == (field.getModifiers() & ~Modifier.FINAL)) {
|
|
|
|
|
+ return field;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ RuntimeUtils.doPrivileged(() -> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ field.setAccessible(true);
|
|
|
|
|
+ Field modifiers = field.getClass().getDeclaredField("modifiers");
|
|
|
|
|
+ modifiers.setAccessible(true);
|
|
|
|
|
+ modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
|
|
|
|
+ } catch (ReflectiveOperationException cause) {
|
|
|
|
|
+ throw ExceptionUtils.rethrow(cause);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch(SecurityException cause) {
|
|
|
|
|
+ throw ExceptionUtils.rethrow(cause);
|
|
|
|
|
+ }
|
|
|
|
|
+ return field;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * Zwraca zawartość pliku źródłowego, w którym zapisana jest podana klasa.
|
|
|
|
|
|
|
+ * Zwraca zawartość pliku źródłowego, w którym zapisana jest podana klasa.
|
|
|
* Oczywiście, to nie działa idealnie dla klas, które nie mają pliku źródłowego
|
|
* Oczywiście, to nie działa idealnie dla klas, które nie mają pliku źródłowego
|
|
|
- * w sensie stricte. Dla klas zagnieżdżonych albo anonimowych zwracana
|
|
|
|
|
- * jest cała zawartość, w którym je zapisano, a nie tylko fragment.
|
|
|
|
|
|
|
+ * w sensie stricte. Dla klas zagnieżdżonych albo anonimowych zwracana
|
|
|
|
|
+ * jest cała zawartość, w którym je zapisano, a nie tylko fragment.
|
|
|
* <p>
|
|
* <p>
|
|
|
* Kod źródłowy musi być dostępny dla ClassLoadera powiązanego z bieżącym wątkiem.
|
|
* Kod źródłowy musi być dostępny dla ClassLoadera powiązanego z bieżącym wątkiem.
|
|
|
* </p>
|
|
* </p>
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param classname
|
|
* @param classname
|
|
|
- * @return
|
|
|
|
|
|
|
+ * @return
|
|
|
*/
|
|
*/
|
|
|
public static String getSource(String classname) throws IOException {
|
|
public static String getSource(String classname) throws IOException {
|
|
|
return readSourceCode(ClassUtils.loader(), classname);
|
|
return readSourceCode(ClassUtils.loader(), classname);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * Zwraca zawartość pliku źródłowego, w którym zapisana jest podana klasa.
|
|
|
|
|
|
|
+ * Zwraca zawartość pliku źródłowego, w którym zapisana jest podana klasa.
|
|
|
* Oczywiście, to nie działa idealnie dla klas, które nie mają pliku źródłowego
|
|
* Oczywiście, to nie działa idealnie dla klas, które nie mają pliku źródłowego
|
|
|
- * w sensie stricte. Dla klas zagnieżdżonych albo anonimowych zwracana
|
|
|
|
|
- * jest cała zawartość, w którym je zapisano, a nie tylko fragment.
|
|
|
|
|
|
|
+ * w sensie stricte. Dla klas zagnieżdżonych albo anonimowych zwracana
|
|
|
|
|
+ * jest cała zawartość, w którym je zapisano, a nie tylko fragment.
|
|
|
* <p>
|
|
* <p>
|
|
|
* Kod źródłowy musi być dostępny dla classloadera, który załadował klasę.
|
|
* Kod źródłowy musi być dostępny dla classloadera, który załadował klasę.
|
|
|
* </p>
|
|
* </p>
|
|
|
* @param clazz
|
|
* @param clazz
|
|
|
- * @return
|
|
|
|
|
|
|
+ * @return
|
|
|
*/
|
|
*/
|
|
|
public static String getSource(Class<?> clazz) throws IOException {
|
|
public static String getSource(Class<?> clazz) throws IOException {
|
|
|
return readSourceCode(clazz.getClassLoader(), clazz.getName());
|
|
return readSourceCode(clazz.getClassLoader(), clazz.getName());
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
private static String readSourceCode(ClassLoader loader, String classname) throws IOException {
|
|
private static String readSourceCode(ClassLoader loader, String classname) throws IOException {
|
|
|
String name = classname.replace('.', '/').replaceAll("\\$.+", "") + ".java";
|
|
String name = classname.replace('.', '/').replaceAll("\\$.+", "") + ".java";
|
|
|
InputStream istream = loader.getResourceAsStream(name);
|
|
InputStream istream = loader.getResourceAsStream(name);
|
|
@@ -82,7 +107,7 @@ public final class ReflectUtils {
|
|
|
return IOStrings.read(istream, Charsets.UTF8);
|
|
return IOStrings.read(istream, Charsets.UTF8);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- File file;
|
|
|
|
|
|
|
+ File file;
|
|
|
if((file = new File(name)).exists()) {
|
|
if((file = new File(name)).exists()) {
|
|
|
return IOStrings.read(file, Charsets.UTF8);
|
|
return IOStrings.read(file, Charsets.UTF8);
|
|
|
}
|
|
}
|
|
@@ -94,7 +119,7 @@ public final class ReflectUtils {
|
|
|
}
|
|
}
|
|
|
throw new IOException("unknown location of file: " + name);
|
|
throw new IOException("unknown location of file: " + name);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Zwraca lokalizację, w której został wyszukany odpowiedni plik .class z
|
|
* Zwraca lokalizację, w której został wyszukany odpowiedni plik .class z
|
|
|
* definicją załadowanej klasy. Lokalizacja nie wskazuje na konkretny
|
|
* definicją załadowanej klasy. Lokalizacja nie wskazuje na konkretny
|
|
@@ -109,7 +134,7 @@ public final class ReflectUtils {
|
|
|
}
|
|
}
|
|
|
return PathUtils.fromURL(cs.getLocation());
|
|
return PathUtils.fromURL(cs.getLocation());
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Zwraca ścieżkę do pliku .class, z którego została załadowana klasa. Aby
|
|
* Zwraca ścieżkę do pliku .class, z którego została załadowana klasa. Aby
|
|
|
* uzyskać lokalizację, w której rozpoczęto wyszukiwanie pliku .class, należy
|
|
* uzyskać lokalizację, w której rozpoczęto wyszukiwanie pliku .class, należy
|