001package arcommon;
002
003import java.io.File;
004import java.lang.reflect.Constructor;
005import java.lang.reflect.Field;
006import java.lang.reflect.InvocationTargetException;
007import java.lang.reflect.Method;
008import java.lang.reflect.Modifier;
009import java.net.URL;
010import java.util.Arrays;
011import java.util.HashMap;
012import java.util.HashSet;
013import java.util.List;
014import java.util.Map;
015
016import sun.reflect.ReflectionFactory;
017
018public class ReflectionUtil {
019    public static Class<?> loadClass(String className) {
020        return loadClass(className, true);
021    }
022    
023    public static Class<?> loadClass(String className, boolean throwOnFail) {
024        try {
025            return Class.forName(className);
026        } catch (ClassNotFoundException e) {
027            if (throwOnFail) {
028                throw new RuntimeException(e);
029            }
030            return null;
031        } catch (NoClassDefFoundError e) {
032            if (throwOnFail) {
033                throw new RuntimeException(e);
034            }
035            return null;
036        }
037    }
038    
039    private static Class<?>[] parametersToClasses(Object[] parameterValues) {
040        if (parameterValues == null) {
041            return new Class[0];
042        }
043        Class<?>[] parameters = new Class[parameterValues.length];
044        for (int i = 0; i < parameters.length; i++) {
045            if (parameterValues[i] == null) {
046                continue;
047            }
048            parameters[i] = parameterValues[i].getClass();
049        }
050        return parameters;
051    }
052    
053    private static Class<?>[] parametersToClassesPrimitiveModified(Class<?>[] parameters) {
054        Class<?>[] result = new Class[parameters.length];
055        Class<?>[] search = new Class[]  {Long.class, Integer.class, Short.class, Character.class, Byte.class, Boolean.class, Double.class, Float.class};
056        Class<?>[] replace = new Class[] {long.class, int.class    , short.class, char.class     , byte.class, boolean.class, double.class, float.class};
057        for (int i = 0; i < parameters.length; i++) {
058            for (int j = 0; j < search.length; j++) {
059                if (parameters[i] == search[j]) {
060                    result[i] = replace[j];
061                    break;
062                }
063            }
064            if (result[i] == null) {
065                result[i] = parameters[i];
066            }
067        }
068        return result;
069    }
070    
071    public static void setStaticFieldValue(Class<?> onClass, String fieldName, Object value) {
072        setFieldValue(null, onClass, fieldName, value);
073    }
074    
075    public static Field findField(Class<?> onClass, String fieldName) {
076        return findField(onClass, fieldName, true);
077    }
078    
079    public static Field findField(Class<?> onClass, String fieldName, boolean throwOnFail) {
080        Field field = null;
081        do {
082            try {
083                field = onClass.getDeclaredField(fieldName);
084            } catch (NoSuchFieldException e) {
085                if (onClass.getSuperclass() != null) {
086                    onClass = onClass.getSuperclass();
087                } else {
088                    if (throwOnFail) {
089                        throw new RuntimeException(e);
090                    }
091                    return null;
092                }
093            }
094        } while (field == null);
095        return field;
096    }
097    
098    public static Field setFieldValue(Object onObject, Class<?> onClass, String fieldName, Object value) {
099        Field field = findField(onClass, fieldName);
100        setFieldValue(onObject, field, value);
101        return field;
102    }
103    
104    public static void setFieldValue(Object onObject, Field field, Object value) {
105        try {
106            field.setAccessible(true);
107            field.set(onObject, value);
108        } catch (IllegalArgumentException e) {
109            throw new RuntimeException(e);
110        } catch (IllegalAccessException e) {
111            throw new RuntimeException(e);
112        } catch (SecurityException e) {
113            throw new RuntimeException(e);
114        }
115    }
116    
117    public static Object getStaticFieldValue(Class<?> onClass, String fieldName) {
118        return getStaticFieldValue(onClass, fieldName, true);
119    }
120    
121    public static Object getStaticFieldValue(Class<?> onClass, String fieldName, boolean throwOnFieldMissing) {
122        return getFieldValue(null, onClass, fieldName, throwOnFieldMissing);
123    }
124    
125    public static Object getFieldValue(Object onObject, String fieldName) {
126        return getFieldValue(onObject, onObject.getClass(), fieldName);
127    }
128    
129    public static Object getFieldValue(Object onObject, Class<?> onClass, String fieldName) {
130        return getFieldValue(onObject, onClass, fieldName, true);
131    }
132    
133    public static Object getFieldValue(Object onObject, Class<?> onClass, String fieldName, boolean throwOnFieldMissing) {
134        Field field = null;
135        if (onObject == null) { // static
136            try {
137                field = onClass.getDeclaredField(fieldName);
138            } catch (SecurityException e) {
139                throw new RuntimeException(e);
140            } catch (NoSuchFieldException e) {
141                if (throwOnFieldMissing) {
142                    throw new RuntimeException(e);
143                }
144                return null;
145            }
146        } else {
147            try {
148                field = onClass.getField(fieldName);
149            } catch (SecurityException e1) {
150                throw new RuntimeException(e1);
151            } catch (NoSuchFieldException e1) {
152                try {
153                    field = onClass.getDeclaredField(fieldName);
154                } catch (SecurityException e) {
155                    throw new RuntimeException(e1);
156                } catch (NoSuchFieldException e) {
157                    if (onClass != Object.class) {
158                        return getFieldValue(onObject, onClass.getSuperclass(), fieldName, throwOnFieldMissing);
159                    }
160                    if (throwOnFieldMissing) {
161                        throw new RuntimeException(e1);
162                    }
163                    return null;
164                }
165            }
166        }
167        
168        try {
169            field.setAccessible(true);
170            return field.get(onObject);
171        } catch (IllegalArgumentException e) {
172            throw new RuntimeException(e);
173        } catch (IllegalAccessException e) {
174            throw new RuntimeException(e);
175        } catch (SecurityException e) {
176            throw new RuntimeException(e);
177        }
178    }
179    
180    public static boolean hasMethod(Class<?> onClass, String methodName, Class<?>[] parameters) {
181        return findMethod(onClass, methodName, parameters, true, false) != null;
182    }
183    
184    public static Method findMethod(Class<?> onClass, String methodName, Class<?>[] parameters) {
185        return findMethod(onClass, methodName, parameters, true, true);
186    }
187
188    public static Method findStaticMethod(Class<?> onClass, String methodName, Class<?>[] parameters) {
189        return findMethod(onClass, methodName, parameters, false, true);
190    }
191    
192    public static Method findMethod(Class<?> onClass, String methodName, Class<?>[] parameters, boolean nonStatic, boolean throwOnMissing) {
193        Exception exception = null;
194        if (nonStatic) {
195            try {
196                Method method = onClass.getMethod(methodName, parameters);
197                return method;
198            } catch (NoSuchMethodException e) {
199                Class<?>[] parametersPrimitive = parametersToClassesPrimitiveModified(parameters);
200                try {
201                    Method method = onClass.getMethod(methodName, parametersPrimitive);
202                    return method;
203                } catch (NoSuchMethodException e2) {
204                    exception = e2;
205                }
206            }
207        }
208        
209        try {
210            Method method = onClass.getDeclaredMethod(methodName, parameters);
211            return method;
212        } catch (NoSuchMethodException e) {
213            Class<?>[] parametersPrimitive = parametersToClassesPrimitiveModified(parameters);
214            try {
215                Method method = onClass.getDeclaredMethod(methodName, parametersPrimitive);
216                return method;
217            } catch (NoSuchMethodException e2) {
218                exception = e2;
219            }
220        }
221
222        Method method = null;
223        Method[] methods = onClass.getMethods();
224        for (Method m : methods) {
225            if (m.getName().equals(methodName)) {
226                if (method == null) {
227                    method = m;
228                } else {
229                    throw new RuntimeException("Method \"" + methodName + "\" is not unique, and the parameters doesn't match one of them.\n"
230                            + "Parameters: " + parameters + "\n"
231                            + "Method1: " + m + "\n"
232                            + "Method2: " + method);
233                }
234            }
235        }
236        
237        if (method != null) {
238            return method;
239        }
240        
241        if (throwOnMissing && exception != null) {
242            throw new RuntimeException(exception);
243        }
244        
245        return method;
246    }
247    
248    public static Object invokeStaticMethod(Class<?> onClass, String methodName, Object ... parameterValues) {
249        Class<?>[] parameters = parametersToClasses(parameterValues);
250        return invokeMethod(onClass, null, methodName, parameters, parameterValues);
251    }
252    
253    public static Object invokeStaticMethod(Class<?> onClass, String methodName, Class<?>[] parameters, Object[] parameterValues) {
254        return invokeMethod(onClass, null, methodName, parameters, parameterValues);
255    }
256    
257    public static Object invokeMethod(Object onObject, String methodName, Object ... parameterValues) {
258        Class<?>[] parameters = parametersToClasses(parameterValues);
259        return invokeMethod(onObject, methodName, parameters, parameterValues);
260    }
261    
262    public static Object invokeMethod(Object onObject, String methodName, Class<?>[] parameters, Object[] parameterValues) {
263        return invokeMethod(onObject.getClass(), onObject, methodName, parameters, parameterValues);
264    }
265    
266    private static Object invokeMethod(Class<?> onClass, Object onObject, String methodName, Class<?>[] parameters, Object[] parameterValues) {
267        Method method = findMethod(onClass, methodName, parameters, onObject != null, true);
268        return invokeMethod(onObject, method, parameterValues);
269    }
270    
271    public static Object invokeMethod(Object onObject, Method method, Object[] parameterValues) {
272        try {
273            method.setAccessible(true);
274            Object returnObject = method.invoke(onObject, parameterValues);
275            return returnObject;
276        } catch (SecurityException e) {
277            throw new RuntimeException(e);
278        } catch (IllegalArgumentException e) {
279            throw new RuntimeException(e);
280        } catch (IllegalAccessException e) {
281            throw new RuntimeException(e);
282        } catch (InvocationTargetException e) {
283            throw new RuntimeException(e);
284        }
285    }
286    
287    public static <T> T createInstanceDefaultConstructor(Class<T> clazz) {
288        return createInstanceUsingConstructor(clazz, new Class<?>[0], new Object[0]);
289    }
290    
291    public static <T> T createInstanceUsingConstructor(Class<T> clazz, Object ... parameterValues) {
292        Class<?>[] parameters = parametersToClasses(parameterValues);
293        return createInstanceUsingConstructor(clazz, parameters, parameterValues);
294    }
295
296    public static <T> T createInstanceUsingConstructor(Class<T> clazz, Class<?>[] parameters, Object[] parameterValues) {
297        try {
298            Constructor<?> constructor = clazz.getConstructor(parameters);
299            Object returnObject = constructor.newInstance(parameterValues);
300            return clazz.cast(returnObject);
301        } catch (SecurityException e) {
302            throw new RuntimeException(e);
303        } catch (IllegalArgumentException e) {
304            throw new RuntimeException(e);
305        } catch (IllegalAccessException e) {
306            throw new RuntimeException(e);
307        } catch (NoSuchMethodException e) {
308            throw new RuntimeException(e);
309        } catch (InvocationTargetException e) {
310            throw new RuntimeException(e);
311        } catch (InstantiationException e) {
312            throw new RuntimeException(e);
313        }
314    }
315    
316    /**
317     * Creates an object using default private constructor.
318     * <br>
319     * Note: Can create an object, even though the class only has a private constructor.
320     */
321    public static <T> T createInstanceDefaultPrivateConstructor(Class<T> clazz) {
322        Constructor<?> constructor;
323        try {
324            constructor = clazz.getDeclaredConstructor();
325            constructor.setAccessible(true);
326            return clazz.cast(constructor.newInstance());
327        } catch (SecurityException e) {
328            throw new RuntimeException(e);
329        } catch (NoSuchMethodException e) {
330            throw new RuntimeException(e);
331        } catch (IllegalArgumentException e) {
332            throw new RuntimeException(e);
333        } catch (InstantiationException e) {
334            throw new RuntimeException(e);
335        } catch (IllegalAccessException e) {
336            throw new RuntimeException(e);
337        } catch (InvocationTargetException e) {
338            throw new RuntimeException(e);
339        }
340    }
341        
342    /**
343     * Create an instance of clazz without calling any constructors.
344     * <br>
345     * Code copied from <a href="http://www.javaspecialists.eu/archive/Issue175.html">http://www.javaspecialists.eu/archive/Issue175.html</a><br>
346     * and modified
347     */
348    public static <T> T createInstanceNoConstructorCall(Class<T> clazz) {
349        return createInstanceUsingParentConstructorOnly(clazz, Object.class);
350    }
351
352    /**
353     * Create an instance of clazz calling only the class parent's constructor(s).
354     */
355    @SuppressWarnings("unchecked")
356    public static <T> T createInstanceUsingParentConstructorOnly(Class<T> clazz, Class<? super T> parent) {
357        return (T)createInstanceUsingParentConstructorOnlyUnsafe(clazz, parent);
358    }
359    
360    private static Map<Class<?>, Map<Constructor<?>, Constructor<?>>> newConstructorForSerializationMap;
361    
362    /**
363     * @see #createInstanceNoConstructorCall(Class) see createInstanceNoConstructorCall(Class) for details
364     */
365    public static Object createInstanceUsingParentConstructorOnlyUnsafe(Class<?> clazz, Class<?> parent) {
366        try {
367            if (newConstructorForSerializationMap == null) {
368                newConstructorForSerializationMap = new HashMap<Class<?>, Map<Constructor<?>, Constructor<?>>>();
369            }
370            
371            Map<Constructor<?>, Constructor<?>> classMap = newConstructorForSerializationMap.get(clazz);
372            if (classMap == null) {
373                classMap = new HashMap<Constructor<?>, Constructor<?>>();
374                newConstructorForSerializationMap.put(clazz, classMap);
375            }
376            
377            ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
378            Constructor<?> declaredConstructor = parent.getDeclaredConstructor();
379            Constructor<?> constructor = classMap.get(declaredConstructor);
380            if (constructor == null) {
381                if (clazz.isArray()) {
382                    throw new RuntimeException("clazz.isArray() => Use Array.newInstance(clazz.getComponentType(), length)");
383                }
384                constructor = reflectionFactory.newConstructorForSerialization(clazz, declaredConstructor); // This call seems to be very show => Result is cached.
385                classMap.put(declaredConstructor, constructor);
386            }
387            
388            return clazz.cast(constructor.newInstance());
389        } catch (RuntimeException e) {
390            throw e;
391        } catch (Exception e) {
392            throw new IllegalStateException("Cannot create object", e);
393        }
394    }
395    
396    public static <T> T createInstanceImplementingInterface(String className, Class<T> interfaceToImplement) {
397        Class<?> clazz = loadClass(className);
398        return createInstanceImplementingInterface(clazz, interfaceToImplement);
399    }
400    
401    @SuppressWarnings("unchecked")
402    public static <T> T createInstanceImplementingInterface(Class<?> clazz, Class<T> interfaceToImplement) {
403        boolean ok = false;
404        Class<?> currentClass = clazz;
405        do {
406            for (int i = 0; i < currentClass.getInterfaces().length; i++) {
407                Class<?> interfaceClass = currentClass.getInterfaces()[i];
408                if (interfaceClass.equals(interfaceToImplement)) {
409                    ok = true;
410                    break;
411                }
412            }
413            currentClass = currentClass.getSuperclass();
414        } while (currentClass != null && !ok);
415        if (!ok) {
416            throw new RuntimeException("The class " + clazz.getName() + " doesn't implement the " + interfaceToImplement.getName() + " interface");
417        }
418        
419        Object instance = createInstanceDefaultConstructor(clazz);
420
421        return (T)instance;
422    }
423    
424    public static boolean isClassEnum(Class<?> clazz) {
425        // See Enum.getDeclaringClass() for a reason for this code
426        if (clazz.isEnum()) {
427            return true;
428        }
429        Class<?> superclass = clazz.getSuperclass();
430        if (superclass == null) {
431            return false;
432        }
433        return superclass.isEnum();
434    }
435    
436    @SuppressWarnings("rawtypes")
437    public static String enumToStringUnsafe(Object object) {
438        return enumToString((Enum)object);
439    }
440    
441    public static String enumToString(Enum<?> object) {
442        Enum<?> o = object;
443        return o.name();
444    }
445    
446    public static Enum<?> stringToEnum(Class<?> clazz, String value) {
447        if (!clazz.isEnum()) { // This occurs for enum constants with constant-specific class bodies.
448            // See Enum.getDeclaringClass() for a reason for this code
449            clazz = clazz.getSuperclass();
450        }
451        @SuppressWarnings({"unchecked", "rawtypes"})
452        Enum result = Enum.valueOf((Class<Enum>)clazz, value);
453        return result;
454    }
455    
456    public static void copyFields(Object fromObject, Object toObject) {
457        if (!fromObject.getClass().equals(toObject.getClass())) {
458            throw new RuntimeException("The classes are not identical: " + fromObject.getClass() + " <-> " + toObject.getClass());
459        }
460        Field[] fromDeclaredFields = fromObject.getClass().getDeclaredFields();
461        Field[] toDeclaredFields = toObject.getClass().getDeclaredFields();
462        Field.setAccessible(fromDeclaredFields, true);
463        Field.setAccessible(toDeclaredFields, true);
464        for (int i = 0; i < fromDeclaredFields.length; i++) {
465            String name = fromDeclaredFields[i].getName();
466            if ((fromDeclaredFields[i].getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
467                continue;
468            }
469            
470            boolean found = false;
471            for (int j = 0; j < toDeclaredFields.length; j++) {
472                if (toDeclaredFields[j].getName().equals(name)) {
473                    Object valueObject;
474                    try {
475                        valueObject = fromDeclaredFields[i].get(fromObject);
476                        toDeclaredFields[j].set(toObject, valueObject);
477                    } catch (IllegalArgumentException e) {
478                        throw new RuntimeException(e);
479                    } catch (IllegalAccessException e) {
480                        throw new RuntimeException(e);
481                    }
482                    found = true;
483                    break;
484                }
485            }
486            if (!found) {
487                throw new RuntimeException("Internal error: Field " + name + " was not found");
488            }
489        }
490    }
491    
492    public static void printClassTree(Object object) {
493        HashSet<Object> printed = new HashSet<Object>();
494        try {
495            Util.printMessageWithSourceLine("printClassTree()...");
496            printClassTree(1, printed, object);
497            Util.printMessageWithSourceLine("..printClassTree()");
498        } catch (Exception e) {
499            throw new RuntimeException(e);
500        }
501    }
502
503    private static final List<Class<?>> SKIP_CLASSES = Arrays.asList(new Class <?>[] {String.class});
504    
505    private static void printClassTree(int level, HashSet<Object> printed, Object object) throws IllegalArgumentException, IllegalAccessException {
506        printed.add(object);
507        Field[] fields = object.getClass().getDeclaredFields();
508        String indent = StringUtil.stringWithNumberOfChar(level * 3, ' ');
509        boolean fieldsFound = false;
510        for (int i = 0; i < fields.length; i++) {
511            Field field = fields[i];
512            int modifiers = field.getModifiers();
513            if ((modifiers & Modifier.STATIC) == Modifier.STATIC) {
514                continue;
515            }
516            fieldsFound = true;
517            String fieldName = field.getName();
518            Class<?> type = field.getType();
519            String className = type.getName();
520            field.setAccessible(true);
521            Object value = field.get(object);
522            boolean arrayPrimitive = false;
523            Object[] objectArrayValue = null;
524            if (type.isArray() && value != null) {
525                Class<?> componentType = type.getComponentType();
526                boolean found = false;
527                if (componentType == int.class) {
528                    int[] arrayValue = (int[])value;
529                    className = "int [" + arrayValue.length + "]";
530                    value = Arrays.toString(arrayValue);
531                    arrayPrimitive = true;
532                    found = true;
533                }
534                if (componentType == char.class) {
535                    char[] arrayValue = (char[])value;
536                    className = "char [" + arrayValue.length + "]";
537                    value = new String(arrayValue);
538                    arrayPrimitive = true;
539                    found = true;
540                }
541                if (!found) {
542                    try {
543                        objectArrayValue = (Object[])value;
544                    } catch (Exception e) {
545                        throw new RuntimeException(e);
546                    }
547                    className = componentType.getName() + " [" + objectArrayValue.length + "]";
548                    value = Arrays.toString(objectArrayValue);
549                }
550            }
551            System.out.print(indent + fieldName + " (" + className + ") = " + value);
552            if (value == null || type.isPrimitive() || SKIP_CLASSES.contains(type) || arrayPrimitive) {
553                System.out.println();
554                continue;
555            }
556            if (printed.contains(value)) {
557                System.out.println(" - Recursively");
558            } else {
559                System.out.println();
560                if (objectArrayValue != null) {
561                    for (int j = 0; j < objectArrayValue.length; j++) {
562                        if (objectArrayValue[j] == null || SKIP_CLASSES.contains(objectArrayValue[j].getClass())) {
563                            continue;
564                        }
565                        System.out.println(indent + "  [" + j + "] " + objectArrayValue[j].getClass().getCanonicalName());
566                        printClassTree(level + 2, printed, objectArrayValue[j]);
567                    }
568                } else {
569                    printClassTree(level + 1, printed, value);
570                }
571            }
572        }
573        
574        if (!fieldsFound) {
575            System.out.println(indent + "- No fields found -");
576        }
577    }
578
579    public static String classToClassNameInJavaSyntax(Class<?> clazz) {
580        return classToClassNameInJavaSyntax(clazz, true);
581    }
582    
583    public static String classToClassNameInJavaSyntax(Class<?> clazz, boolean includePackage) {
584        String result;
585        if (clazz.isArray()) {
586            Class<?> componentType = clazz.getComponentType();
587            result = classToClassNameInJavaSyntax(componentType, includePackage);
588            result += "[]";
589        } else {
590            if (includePackage) {
591                result = clazz.getName();
592            } else {
593                result = clazz.getSimpleName();
594            }
595        }
596        return result;
597    }
598
599    public static Class<?> classNameInJavaSyntaxToClass(String className) {
600        return classNameInJavaSyntaxToClass(className, true);
601    }
602    
603    public static Class<?> classNameInJavaSyntaxToClass(String classNameOriginal, boolean throwOnFail) {
604        String className = classNameOriginal;
605        if (className.endsWith("[]")) {
606            int dimensions = 0;
607            while (className.endsWith("[]")) {
608                className = className.substring(0, className.length() - 2);
609                dimensions++;
610            }
611            String dimensionsString = StringUtil.stringWithNumberOfChar(dimensions, '[');
612            if (className.endsWith(" ")) {
613                className = className.substring(0, className.length() - 1);
614            }
615            
616            if (className.contains(" ")) {
617                throw new RuntimeException("Class name \"" + className + "\" contains space (\"" + classNameOriginal + "\")");
618            }
619            
620            String primitiveName = null;
621            if (className.equals("long")) {
622                primitiveName = "J";
623            } else if (className.equals("int")) {
624                primitiveName = "I";
625            } else if (className.equals("short")) {
626                primitiveName = "S";
627            } else if (className.equals("byte")) {
628                primitiveName = "B";
629            } else if (className.equals("char")) {
630                primitiveName = "C";
631            } else if (className.equals("boolean")) {
632                primitiveName = "Z";
633            } else if (className.equals("double")) {
634                primitiveName = "D";
635            } else if (className.equals("float")) {
636                primitiveName = "F";
637            }
638            
639            String arrayClassName;
640            if (primitiveName != null) {
641                arrayClassName = dimensionsString + primitiveName;
642            } else {
643                arrayClassName = dimensionsString + "L" + className + ";";
644            }
645            
646            return loadClass(arrayClassName, throwOnFail);
647        }
648        
649        if (className.equals("long")) {
650            return long.class;
651        }
652        if (className.equals("int")) {
653            return int.class;
654        }
655        if (className.equals("short")) {
656            return short.class;
657        }
658        if (className.equals("byte")) {
659            return byte.class;
660        }
661        if (className.equals("char")) {
662            return char.class;
663        }
664        if (className.equals("boolean")) {
665            return boolean.class;
666        }
667        if (className.equals("double")) {
668            return double.class;
669        }
670        if (className.equals("float")) {
671            return float.class;
672        }
673        
674        if (className.contains(" ")) {
675            throw new RuntimeException("Class name \"" + className + "\" contains space");
676        }
677
678        return loadClass(className, throwOnFail);
679    }
680    
681    public static String classToEmptyImplementationSourceCode(Class<?> clazz) {
682        StringBuilder stringBuilder = new StringBuilder();
683        Method[] methods = clazz.getMethods();
684        for (int i = 0; i < methods.length; i++) {
685            Method method = methods[i];
686            if ((method.getModifiers() & Modifier.NATIVE) == Modifier.NATIVE) {
687                continue;
688            }
689            if ((method.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
690                continue;
691            }
692
693            // Copied from Method.toString() and modified
694            int mod = method.getModifiers() & Modifier.PUBLIC;
695            if (mod != 0) {
696                stringBuilder.append(Modifier.toString(mod) + " ");
697            }
698            stringBuilder.append(classToClassNameInJavaSyntax(method.getReturnType(), false));
699            stringBuilder.append(" ");
700            stringBuilder.append(method.getName() + "(");
701            Class<?>[] parameters = method.getParameterTypes();
702            for (int j = 0; j < parameters.length; j++) {
703                stringBuilder.append(classToClassNameInJavaSyntax(parameters[j], false));
704                stringBuilder.append(" p" + j);
705                if (j < (parameters.length - 1)) {
706                    stringBuilder.append(", ");
707                }
708            }
709            stringBuilder.append(")");
710            Class<?>[] exceptions = method.getExceptionTypes();
711            if (exceptions.length > 0) {
712                stringBuilder.append(" throws ");
713                for (int k = 0; k < exceptions.length; k++) {
714                    stringBuilder.append(exceptions[k].getSimpleName());
715                    if (k < (exceptions.length - 1)) {
716                        stringBuilder.append(", ");
717                    }
718                }
719            }
720            stringBuilder.append(" {\n");
721//            stringBuilder.append("    // TODO Auto-generated method stub\n");
722            stringBuilder.append("    throw new RuntimeException(\"Not implemented\");\n");
723            stringBuilder.append("}\n");
724            stringBuilder.append("\n");
725        }
726        
727        return stringBuilder.toString();
728    }
729    
730    private static final String JAR_FILE_NAME_PREFIX = "jar:file:";
731    
732    public static boolean loadedFromJarFile(Class<?> clazz) {
733        URL location = clazz.getResource('/' + clazz.getName().replace('.', '/') + ".class");
734        String locationString = location.toString();
735        return locationString.startsWith(JAR_FILE_NAME_PREFIX);
736    }
737    
738    public static String calculateJarFileName(Class<?> clazz) {
739        URL location = clazz.getResource('/' + clazz.getName().replace('.', '/') + ".class");
740        System.out.println("location.getPath(): " + location.getPath());
741        String locationString = location.toString();
742        
743        if (!locationString.startsWith(JAR_FILE_NAME_PREFIX)) {
744            throw new RuntimeException("!locationString.startsWith(JAR_FILE_NAME_PREFIX): " + JAR_FILE_NAME_PREFIX + "    " + locationString);
745        }
746        locationString = locationString.substring(JAR_FILE_NAME_PREFIX.length());
747        int indexOf = locationString.indexOf("!");
748        if (indexOf == -1) {
749            throw new RuntimeException("indexOf == -1: " + locationString);
750        }
751        locationString = locationString.substring(0, indexOf);
752        
753        locationString = locationString.replace('/', File.separatorChar);
754        locationString = locationString.replace("%20", " ");
755        
756        return locationString;
757    }
758
759    // NOTE: This method has not been tested a lot.
760    public static String calculateClassPath(Class<?> clazz) {
761        String classNameModfied = '/' + clazz.getName().replace('.', '/') + ".class";
762        URL location = clazz.getResource(classNameModfied);
763        String path = location.getPath();
764        if (path.endsWith(classNameModfied)) {
765            path = path.substring(0, path.length() - classNameModfied.length() + 1);
766        } else {
767            throw new RuntimeException("!path.endsWith(classNameModfied)  " + path + "  " + classNameModfied);
768        }
769        path = path.replace('/', File.separatorChar);
770        path = path.replace("%20", " ");
771        return path;
772    }
773    
774    /**
775     * Use this method to avoid the following warning.
776     * <br>
777     *  WARNING: An illegal reflective access operation has occurred<br>
778     *  WARNING: Illegal reflective access by &lt;packageName&gt;.&lt;className&gt; (file:&lt;path&gt;) to field &lt;packageName&gt;.&lt;className&gt;.&lt;fieldName&gt;<br>
779     *  WARNING: Please consider reporting this to the maintainers of arcommon.ReflectionUtil<br>
780     *  WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations<br>
781     *  WARNING: All illegal access operations will be denied in a future release<br>
782     */
783    public static void openPackageForModuleAccess(Class<?> classInPackage, Class<?> classInModule) {
784//        openPackageForModuleAccess(classInPackage, classInModule.getModule());
785        if (Util.getJavaVersion() < Util.JAVA_MODULE_SYSTEM_VERSION || Util.getJavaVersion() >= Util.JAVA_MODULE_SYSTEM_STRICT_VERSION) { // Util.getJavaVersion() == 16 => "java.lang.IllegalCallerException: sun.awt.X11 is not open to unnamed module @5a6d67c3"
786            return ;
787        }
788        // java.lang.Module is a java 9 class => Use reflection to make it compile on Java 8.
789        Object module = invokeMethod(classInModule, "getModule");
790/*    }
791
792    public static void openPackageForModuleAccess(Class<?> classInPackage, Module module) {
793        if (module.isNamed()) {
794            return ;
795        }*/
796        Boolean isNamed = (Boolean)invokeMethod(module, "isNamed");
797        if (isNamed.booleanValue()) {
798            return ;
799        }
800        
801        // Code copied from https://stackoverflow.com/questions/46454995/how-to-hide-warning-illegal-reflective-access-in-java-9-without-jvm-argument
802        // and modified
803//        classInPackage.getModule().addOpens(classInPackage.getPackageName(), module);
804        Object classInPackageModule = invokeMethod(classInPackage, "getModule");
805        Object packageName = invokeMethod(classInPackage, "getPackageName");
806        invokeMethod(classInPackageModule, "addOpens", packageName, module);
807    }
808}