Files
mdg-filius/tools/java-oracle/src/DocuRun.java
T
julian.kiedaischandClaude Opus 5.5 56cdb4926e feat: F4.8a the documentation items in model and file, measured against Java's
New oracle DocuRun on Java's JDocuElement. Italic is kept (the model knew
only bold), an item keeps its file's size and font size (Swift's 32 x 24
and 8-72 changed Java's files), an item without a colour is Java's light
grey. Java's new items, colour lists, font sizes and styles, the drag's
smallest sizes and Java's stacking; document commands for the editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:42:21 +00:00

392 lines
17 KiB
Java

// FILIUS' documentation mode measured on its own elements (JDocuElement on a
// GUIDocumentationPanel, as the sidebar's drag makes them): a new text and a
// new rectangle (size, colour, font), their menus (font size, style, colour,
// the colour lists), the font and colour chosen there, the smallest size a
// drag on an edge leaves (a text is dragged while it has the focus, in its
// enlarged frame), how an element is painted in the documentation mode and
// in the others (fill, border, transparency), the order in which the panel
// stacks texts and rectangles, and a project saved with them and loaded
// again (SzenarioVerwaltung.speichern, laden). The reference for the
// editor's documentation mode (packages/doc/test/documentation.test.ts).
//
// Usage: java -cp filius.jar:lib/*:classes DocuRun <out.json> <saved.fls>
import filius.gui.netzwerksicht.GUIDocuItem;
import filius.gui.netzwerksicht.GUIDocumentationPanel;
import filius.gui.netzwerksicht.JDocuColorSelector;
import filius.gui.netzwerksicht.JDocuElement;
import filius.rahmenprogramm.SzenarioVerwaltung;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;
public class DocuRun {
static final Map<String, Object> output = new LinkedHashMap<>();
static GUIDocumentationPanel panel;
static void onEdt(Runnable action) throws Exception {
SwingUtilities.invokeAndWait(action);
Thread.sleep(200);
}
static List<Object> color(Color color) {
List<Object> rgba = new ArrayList<>();
rgba.add(color.getRed());
rgba.add(color.getGreen());
rgba.add(color.getBlue());
rgba.add(color.getAlpha());
return rgba;
}
static Map<String, Object> font(Font font) {
Map<String, Object> shown = new LinkedHashMap<>();
if (font == null) return null;
shown.put("name", font.getName());
shown.put("style", font.getStyle());
shown.put("size", font.getSize());
return shown;
}
static Map<String, Object> element(JDocuElement element) {
Map<String, Object> shown = new LinkedHashMap<>();
shown.put("bounds", List.of(element.getX(), element.getY(), element.getWidth(), element.getHeight()));
shown.put("color", color(element.getColor()));
shown.put("font", font(element.getFont()));
shown.put("text", element.getText());
return shown;
}
static Map<String, Object> item(GUIDocuItem item) {
Map<String, Object> shown = new LinkedHashMap<>();
shown.put("type", item.getType());
shown.put("text", item.getText());
shown.put("bounds", List.of(item.getX(), item.getY(), item.getWidth(), item.getHeight()));
shown.put("color", color(item.getColor()));
shown.put("font", font(item.getFont()));
return shown;
}
static JPopupMenu shownMenu() {
for (Window window : Window.getWindows()) {
JPopupMenu found = find(window, JPopupMenu.class);
if (found != null) return found;
}
return null;
}
static <T> T find(Component component, Class<T> type) {
if (type.isInstance(component) && component.isShowing()) return type.cast(component);
if (component instanceof Container) {
for (Component child : ((Container) component).getComponents()) {
T found = find(child, type);
if (found != null) return found;
}
}
return null;
}
/** The element's menu (showPopupMenu, private): each entry with its sub-entries or colours. */
static JPopupMenu openMenu(JDocuElement element) throws Exception {
Method show = JDocuElement.class.getDeclaredMethod("showPopupMenu", int.class, int.class);
show.setAccessible(true);
onEdt(() -> {
try {
show.invoke(element, 5, 5);
} catch (Exception error) {
throw new RuntimeException(error);
}
});
return shownMenu();
}
static List<Object> menu(JPopupMenu menu) {
List<Object> entries = new ArrayList<>();
for (Component component : menu.getComponents()) {
Map<String, Object> entry = new LinkedHashMap<>();
if (component instanceof JDocuColorSelector) {
JDocuColorSelector selector = (JDocuColorSelector) component;
entry.put("colors", selector.getText());
List<Object> colors = new ArrayList<>();
List<Object> chosen = new ArrayList<>();
JPanel samples = (JPanel) selector.getPopupMenu().getComponent(0);
for (Component sample : samples.getComponents()) {
colors.add(color(sample.getBackground()));
chosen.add(((javax.swing.JComponent) sample).getBorder().getBorderInsets(sample).top == 3);
}
entry.put("list", colors);
entry.put("chosen", chosen);
entry.put("rows", ((java.awt.GridLayout) samples.getLayout()).getRows());
} else if (component instanceof JMenu) {
JMenu sub = (JMenu) component;
entry.put("menu", sub.getText());
List<Object> items = new ArrayList<>();
for (MenuElement each : sub.getSubElements()[0].getSubElements()) {
JMenuItem item = (JMenuItem) each;
items.add(item.getText() + (item.isSelected() ? " *" : ""));
}
entry.put("items", items);
} else if (component instanceof JMenuItem) {
entry.put("item", ((JMenuItem) component).getText());
} else {
entry.put("separator", true);
}
entries.add(entry);
}
return entries;
}
static void choose(JPopupMenu menu, String text) throws Exception {
for (Component component : menu.getComponents()) {
if (component instanceof JMenu && !(component instanceof JDocuColorSelector)) {
for (MenuElement each : ((JMenu) component).getSubElements()[0].getSubElements()) {
JMenuItem item = (JMenuItem) each;
if (item.getText().equals(text)) {
onEdt(item::doClick);
return;
}
}
}
}
throw new IllegalStateException("no entry " + text);
}
static void closeMenu(JPopupMenu menu) throws Exception {
onEdt(() -> menu.setVisible(false));
}
static void pickColor(JPopupMenu menu, Color color) throws Exception {
for (Component component : menu.getComponents()) {
if (component instanceof JDocuColorSelector) {
onEdt(() -> ((JDocuColorSelector) component).triggerMenuResponse(color));
return;
}
}
}
static MouseEvent mouse(Component on, int id, int x, int y) {
return new MouseEvent(on, id, System.currentTimeMillis(), id == MouseEvent.MOUSE_DRAGGED ? MouseEvent.BUTTON1_DOWN_MASK : 0, x, y, 1, false, MouseEvent.BUTTON1);
}
/** A drag from a point of the element to another, in its coordinates (the edge decides what changes). */
static void drag(JDocuElement element, int fromX, int fromY, int toX, int toY) throws Exception {
onEdt(() -> {
element.dispatchEvent(mouse(element, MouseEvent.MOUSE_MOVED, fromX, fromY));
element.dispatchEvent(mouse(element, MouseEvent.MOUSE_PRESSED, fromX, fromY));
element.dispatchEvent(mouse(element, MouseEvent.MOUSE_DRAGGED, toX, toY));
element.dispatchEvent(mouse(element, MouseEvent.MOUSE_RELEASED, toX, toY));
});
}
/** The element painted alone: the pixels at the given points, as RGBA. */
static Map<String, Object> pixels(JDocuElement element, int[][] points) throws Exception {
Map<String, Object> shown = new LinkedHashMap<>();
BufferedImage image = new BufferedImage(element.getWidth(), element.getHeight(), BufferedImage.TYPE_INT_ARGB);
onEdt(() -> {
java.awt.Graphics2D graphics = image.createGraphics();
element.paint(graphics);
graphics.dispose();
});
for (int[] point : points) {
int argb = image.getRGB(point[0], point[1]);
shown.put(point[0] + "," + point[1], List.of((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >>> 24) & 255));
}
return shown;
}
public static void main(String[] args) throws Exception {
try {
run(args);
} catch (Throwable error) {
error.printStackTrace();
System.exit(1);
}
System.exit(0);
}
static void run(String[] args) throws Exception {
JFrame frame = new JFrame();
List<JDocuElement> made = new ArrayList<>();
SwingUtilities.invokeAndWait(() -> {
panel = new GUIDocumentationPanel(800, 600);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(panel);
frame.setSize(820, 640);
frame.setVisible(true);
// As the sidebar's drop: made by drag, placed, added to the panel (which gives it the focus).
JDocuElement text = new JDocuElement(true, true);
text.setLocation(100, 100);
JDocuElement rectangle = new JDocuElement(false, true);
rectangle.setLocation(300, 100);
made.add(text);
made.add(rectangle);
});
JDocuElement text = made.get(0);
JDocuElement rectangle = made.get(1);
Map<String, Object> fresh = new LinkedHashMap<>();
fresh.put("text", element(text));
fresh.put("rectangle", element(rectangle));
output.put("new", fresh);
onEdt(() -> panel.add(text));
Thread.sleep(300);
Map<String, Object> focused = new LinkedHashMap<>();
focused.put("text", element(text));
onEdt(panel::removeElementsFocus);
focused.put("textUnfocused", element(text));
output.put("dropped", focused);
// The menus.
JPopupMenu menu = openMenu(text);
Map<String, Object> menus = new LinkedHashMap<>();
menus.put("text", menu(menu));
choose(menu, "Groß");
closeMenu(menu);
List<Object> fonts = new ArrayList<>();
fonts.add(font(text.getFont()));
menu = openMenu(text);
menus.put("textAfterLarge", menu(menu));
choose(menu, "Kursiv");
closeMenu(menu);
fonts.add(font(text.getFont()));
menu = openMenu(text);
choose(menu, "Fett");
closeMenu(menu);
fonts.add(font(text.getFont()));
menu = openMenu(text);
choose(menu, "Normal"); // the first "Normal", the size menu's
closeMenu(menu);
fonts.add(font(text.getFont()));
menu = openMenu(text);
choose(menu, "Sehr Gro\u00df");
closeMenu(menu);
fonts.add(font(text.getFont()));
menu = openMenu(text);
choose(menu, "Normal");
closeMenu(menu);
menu = openMenu(text);
pickColor(menu, new Color(0.31f, 0.51f, 0.74f));
closeMenu(menu);
output.put("fonts", fonts);
output.put("textColor", color(text.getColor()));
onEdt(() -> panel.add(rectangle));
menu = openMenu(rectangle);
menus.put("rectangle", menu(menu));
pickColor(menu, new Color(1f, 0.79f, 0.79f));
closeMenu(menu);
output.put("rectangleColor", color(rectangle.getColor()));
output.put("menus", menus);
// The smallest sizes a drag leaves: a rectangle by its corner; a text, focused, by its corner, then unfocused.
Map<String, Object> smallest = new LinkedHashMap<>();
onEdt(() -> rectangle.setLocalFocus(true));
drag(rectangle, rectangle.getWidth() - 2, rectangle.getHeight() - 2, 3, 3);
smallest.put("rectangle", element(rectangle));
drag(rectangle, 1, 1, 200, 200);
smallest.put("rectangleByItsTopLeft", element(rectangle));
onEdt(() -> text.setLocalFocus(true));
smallest.put("textFocused", element(text));
drag(text, text.getWidth() - 2, text.getHeight() - 2, 3, 3);
smallest.put("textFocusedSmallest", element(text));
onEdt(panel::removeElementsFocus);
smallest.put("textSmallest", element(text));
// A drag on an unfocused text does nothing (Java moves and sizes a text only in its enlarged frame).
drag(text, 10, 10, 60, 60);
smallest.put("textDraggedUnfocused", element(text));
output.put("smallest", smallest);
// Painted: a rectangle and a text, in the documentation mode (enabled) unfocused and focused, and in the others.
Map<String, Object> painted = new LinkedHashMap<>();
JDocuElement plain = new JDocuElement(false);
JDocuElement words = new JDocuElement(true);
onEdt(() -> {
plain.setBounds(400, 300, 100, 50);
plain.setColor(new Color(1f, 0.79f, 0.79f));
words.setBounds(400, 400, 100, 50);
words.setText("Text");
panel.add(plain);
panel.add(words);
});
int[][] points = { { 50, 25 }, { 1, 25 }, { 2, 25 }, { 0, 25 } };
painted.put("rectangleEnabled", pixels(plain, points));
onEdt(() -> plain.setLocalFocus(true));
painted.put("rectangleFocused", pixels(plain, new int[][] { { 50, 25 }, { 3, 25 }, { 1, 25 } }));
onEdt(() -> plain.setEnabled(false));
painted.put("rectangleDisabled", pixels(plain, points));
painted.put("textEnabled", pixels(words, new int[][] { { 0, 25 }, { 1, 25 }, { 50, 0 }, { 99, 25 } }));
onEdt(() -> words.setEnabled(false));
painted.put("textDisabled", pixels(words, new int[][] { { 0, 25 }, { 1, 25 }, { 50, 0 } }));
output.put("painted", painted);
// The stacking: texts first, then rectangles, each in the list's order; the first component is on top.
List<GUIDocuItem> items = new ArrayList<>();
JDocuElement[] elements = { new JDocuElement(true), new JDocuElement(false), new JDocuElement(true), new JDocuElement(false) };
String[] names = { "text 1", "rectangle 1", "text 2", "rectangle 2" };
onEdt(() -> {
for (int index = 0; index < elements.length; index++) {
elements[index].setBounds(50 + 10 * index, 50 + 10 * index, 100, 50);
elements[index].setText(names[index]);
elements[index].setName(names[index]);
items.add(GUIDocuItem.createDocuItem(elements[index]));
}
panel.updateViewport(items, true);
});
List<String> stacking = new ArrayList<>();
for (Component component : panel.getComponents()) stacking.add(component.getName());
output.put("stacking", stacking);
output.put("topAt", panel.getComponentAt(85, 85).getName());
// A project with documentation, saved and loaded again.
List<GUIDocuItem> saved = new ArrayList<>();
JDocuElement italic = new JDocuElement(true);
JDocuElement bold = new JDocuElement(true);
JDocuElement pink = new JDocuElement(false);
JDocuElement grey = new JDocuElement(false);
onEdt(() -> {
italic.setBounds(40, 40, 160, 60);
italic.setText("Kursiv und mittel\nzweite Zeile");
italic.setFont(italic.getFont().deriveFont(Font.ITALIC, 18f));
italic.setColor(new Color(0.31f, 0.51f, 0.74f));
bold.setBounds(40, 140, 120, 50);
bold.setText("Fett");
bold.setFont(bold.getFont().deriveFont(Font.BOLD, 24f));
pink.setBounds(20, 20, 300, 200);
pink.setColor(new Color(1f, 0.79f, 0.79f));
grey.setBounds(400, 20, 100, 50);
for (JDocuElement each : new JDocuElement[] { italic, bold, pink, grey }) saved.add(GUIDocuItem.createDocuItem(each));
});
List<Object> savedItems = new ArrayList<>();
for (GUIDocuItem each : saved) savedItems.add(item(each));
output.put("saved", savedItems);
if (!SzenarioVerwaltung.getInstance().speichern(args[1], new ArrayList<>(), new ArrayList<>(), saved)) throw new IllegalStateException("not saved");
List<GUIDocuItem> loaded = new ArrayList<>();
SzenarioVerwaltung.getInstance().laden(args[1], new ArrayList<>(), new ArrayList<>(), loaded);
List<Object> loadedItems = new ArrayList<>();
for (GUIDocuItem each : loaded) loadedItems.add(item(each));
output.put("loaded", loadedItems);
try (FileOutputStream out = new FileOutputStream(args[0])) {
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
}
System.out.println("documentation measured");
}
}