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(); } }