Java Class Info
---------------- ClassInfo.java ----------------
import java.lang.reflect.*;
import java.lang.annotation.*;
import java.util.*;
import java.util.function.*;
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.web.*;
import javafx.concurrent.*;
import javafx.collections.*;
import netscape.javascript.JSObject;
public class ClassInfo extends Application {
TabPane tabPane;
@Override
public void start(Stage stage) {
tabPane = new TabPane();
tabPane.setTabMinWidth(50.0);
tabPane.setTabMaxWidth(150.0);
tabPane.getTabs().addListener((ListChangeListener.Change<? extends Tab> c) -> {
while (c.next()) {
if (c.wasAdded()) {
tabPane.getTabs().get(0).setClosable(tabPane.getTabs().size() != 1);
} else if (c.wasRemoved() && tabPane.getTabs().size() == 1) {
tabPane.getTabs().get(0).setClosable(false);
}
}
});
List<String> p = getParameters().getRaw();
String className = (p.isEmpty() ? getClass().getCanonicalName() : p.get(0));
addTab(className);
stage.setScene(new Scene(tabPane, 800, 600));
stage.show();
}
public void addTab(String className) {
for (Tab tab : tabPane.getTabs()) {
if (className.equals(tab.getId())) {
tabPane.getSelectionModel().select(tab);
return;
}
}
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.getLoadWorker().stateProperty().addListener((v, o, n) -> {
if (n == Worker.State.SUCCEEDED) {
JSObject jsobj = (JSObject) webEngine.executeScript("window");
jsobj.setMember("jMember", this);
}
});
StringBuilder sb = new StringBuilder();
sb.append("<html><head>\n");
sb.append(" <script language=\"javascript\">\n");
sb.append(" function f(element) {\n");
sb.append(" jMember.addTab(element.href);\n");
sb.append(" return false;\n");
sb.append(" }\n");
sb.append(" </script>\n");
sb.append(" <style> p { margin-top: 0; margin-bottom: 0.4em; } </style>\n");
sb.append("</head><body>\n ");
Class<?> c = null;
char[] chars = className.toCharArray();
for (int i = chars.length - 1; i >= 0; i--) {
try {
c = Class.forName(String.valueOf(chars));
break;
} catch (Exception e) {}
for (; i >= 0; i--) {
if (chars[i] == '.') {
chars[i] = '$';
break;
}
}
}
if (c == null) {
sb.append("<p>Error loading class <b>").append(className).append("</b></p>");
} else {
final String cName = className = c.getCanonicalName();
String sName = shortName(cName);
Function<Class<?>, String> getType = type -> {
String tcName = type.getCanonicalName();
int i = 0;
char ch = 0;
for (String s = type.getName(); i < s.length(); i++) {
if ((ch = s.charAt(i)) != '[') break;
}
boolean primitive = type.isPrimitive();
if (i > 0) {
tcName = tcName.substring(0, tcName.length() - i * 2);
primitive |= (ch != 'L');
}
String tsName = shortName(tcName);
String taq = type.getCanonicalName().substring(tcName.length());
if (cName.equals(tcName)) return tsName.concat(taq);
if (primitive) return new StringBuilder("<i>").append(tsName).append("</i>").append(taq).toString();
return new StringBuilder("<a href=\"").append(tcName).append("\" title=\"").append(tcName)
.append("\" onclick=\"return f(this);\">").append(tsName).append("</a>").append(taq).toString();
};
Module mdl = c.getModule();
if (mdl != null && mdl.getName() != null && !mdl.getName().isEmpty()) {
sb.append(getAnnotations(mdl.getDeclaredAnnotations()));
sb.append("<p><b>Module</b> ").append(mdl.getName()).append("</p>");
}
Package pcg = c.getPackage();
if (pcg != null && pcg.getName() != null && !pcg.getName().isEmpty()) {
sb.append(getAnnotations(pcg.getDeclaredAnnotations()));
sb.append("<p><b>Package</b> ").append(pcg.getName()).append("</p>");
}
String t = (c.isInterface() ? "Interface" : (c.isEnum() ? "Enum" : "Class"));
sb.append("<p><font size=\"+2\"><b>").append(t).append(' ').append(sName).append("</b></font></p>");
ArrayList<Class<?>> al = new ArrayList<>();
for (Class<?> sc = c; (sc = sc.getSuperclass()) != null; al.add(sc));
sb.append("<p>");
String indent = "";
for (int i = al.size() - 1; i >= 0; i--){
sb.append(indent).append("<a href=\"").append(al.get(i).getCanonicalName()).append("\" onclick=\"return f(this);\">")
.append(al.get(i).getCanonicalName()).append("</a><br>");
indent = indent.concat(" ");
}
sb.append(indent).append(cName).append("</p>");
Class<?>[] interfaces = c.getInterfaces();
if (interfaces.length > 0) {
sb.append("<p><b>Implemented Interfaces:</b><br>");
for (int i = 0; i < interfaces.length; i++) {
if (i > 0) sb.append(", ");
sb.append(getType.apply(interfaces[i]));
}
sb.append("</p>");
}
sb.append(getAnnotations(c.getDeclaredAnnotations()));
sb.append("<p>").append(getModifiers(c.getModifiers())).append(t.toLowerCase()).append(' ').append(sName).append("</p>");
Field[] fields = c.getDeclaredFields();
if (fields.length > 0) {
sb.append("<p><b>Fields:</b><br>");
for (Field field : fields) {
sb.append(getAnnotations(field.getDeclaredAnnotations()));
sb.append(getModifiers(field.getModifiers()));
sb.append(getType.apply(field.getType())).append(' ');
sb.append(field.getName()).append("<br>");
}
sb.append("</p>");
}
for (Class<?> sc : al) {
boolean first = true;
for (Field field : sc.getDeclaredFields()) {
if ((field.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
if (first) sb.append("<p><b>Fields inherited from ").append(getType.apply(sc)).append(":</b> ");
else sb.append(", ");
sb.append(field.getName());
first = false;
}
}
if (!first) sb.append("</p>");
}
for (int n = 0; n < 2; n++) {
Executable[] executables = (n == 0 ? c.getDeclaredConstructors() : c.getDeclaredMethods());
if (executables.length > 0) {
sb.append("<p><b>").append(n == 0 ? "Constructors" : "Methods").append(":</b><br>");
for (Executable executable : executables) {
sb.append(getAnnotations(executable.getDeclaredAnnotations()));
sb.append(getModifiers(executable.getModifiers()));
if (n > 0) sb.append(getType.apply(((Method)executable).getReturnType())).append(' ');
sb.append(shortName(executable.getName())).append('(');
Class<?>[] paramTypes = executable.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0) sb.append(", ");
sb.append(getType.apply(paramTypes[i]));
}
sb.append(")<br>");
}
sb.append("</p>");
}
if (n > 0) {
for (Class<?> sc : al) {
boolean first = true;
for (Method method : sc.getDeclaredMethods()) {
if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
if (first) sb.append("<p><b>Methods inherited from ").append(getType.apply(sc)).append(":</b> ");
else sb.append(", ");
sb.append(method.getName());
first = false;
}
}
if (!first) sb.append("</p>");
}
}
}
}
sb.append("\n</body></html>\n");
//try { java.nio.file.Files.write(java.nio.file.Paths.get("out_"+shortName(className)+".html"), sb.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8)); } catch (Exception e) {}
webEngine.loadContent(sb.toString());
Tab tab = new Tab(shortName(className), webView);
tab.setId(className);
tab.setTooltip(new Tooltip(className));
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
}
String shortName(String name) {
char[] chars = name.toCharArray();
int i = chars.length - 1, end = i;
for (; i >= 0; i--) {
if (chars[i] == '.' || chars[i] == '$') break;
}
return (i < 0 ? name : new String(chars, i + 1, end - i));
}
String getModifiers(int mod) {
String str = Modifier.toString(~Modifier.INTERFACE & mod);
return (str.isEmpty() ? str : str.concat(" "));
}
String getAnnotations(Annotation[] annotations) {
if (annotations.length == 0) return "";
StringBuilder sb = new StringBuilder();
for (Annotation annotation : annotations) {
sb.append(annotation.toString()).append("<br>");
}
return sb.toString();
}
}