test(java-oracle): measure the getter defaults XMLEncoder refers to

XMLEncoder shares equal Integer objects, so a node's x coordinate can be
written as a reference to the result of JSidebarButton.getIconTextGap().
GetterDefaults constructs the classes like XMLDecoder does and records
what the getters return (packages/fls/src/javaDefaults.json).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 20:13:28 +00:00
co-authored by Claude Opus 5.5
parent 0c843dc3de
commit fc9d865820
4 changed files with 107 additions and 1 deletions
+17
View File
@@ -0,0 +1,17 @@
{
"DHCPServer#port": 67,
"DNSServer#port": 53,
"Gateway#maxLayerOfOperation": 2,
"JSidebarButton#debugGraphicsOptions": 0,
"JSidebarButton#displayedMnemonicIndex": -1,
"JSidebarButton#enabled": true,
"JSidebarButton#iconTextGap": 4,
"JSidebarButton#verticalTextPosition": 3,
"Notebook#maxLayerOfOperation": 4,
"RIPServer#port": 520,
"Rechner#maxLayerOfOperation": 4,
"ServerBaustein#port": 55555,
"ThreadGroup#maxPriority": 10,
"Vermittlungsrechner#maxLayerOfOperation": 2,
"WebServer#port": 80
}
+12
View File
@@ -22,3 +22,15 @@ virtuellen Bildschirm statt „headless".
* Bisher ausgelesen: Geräte (Klasse, Name, Beschriftung, Position,
Schnittstellen), Kabel (Enden als Gerät/Schnittstelle), Doku-Elemente.
Systemsoftware, Dateisysteme und Anwendungen folgen schrittweise.
## Getter-Standardwerte
XMLEncoder teilt gleiche `Integer`/`Boolean`-Objekte. Ein Wert, der einmal als
Ergebnis eines Getters geschrieben wurde (`<void id="Integer0"
property="iconTextGap"/>`), taucht später nur noch als Verweis auf — etwa als
x-Koordinate eines Geräts. Daran scheitert der Swift-Leser (Plan §13, M2).
`GetterDefaults` erzeugt die betroffenen Klassen wie XMLDecoder (ohne
Argumente) und ruft den Getter auf; das Ergebnis steht in
`packages/fls/src/javaDefaults.json`. Gemessen, nicht geraten: Filius' Knopf
setzt `verticalTextPosition` auf 3 (BOTTOM), nicht auf Swings 0 (CENTER).
+23 -1
View File
@@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then
fi
classpath="$upstream/target/filius.jar:$upstream/target/lib/*"
mkdir -p "$here/.build"
javac -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java"
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java"
# FILIUS writes its settings below $HOME; keep them out of the real one.
mkdir -p "$here/.build/home"
# Removed first: a crashed run must not leave the previous result standing.
@@ -29,3 +29,25 @@ rm -f "$root/corpus/corpus.java.json"
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" FlsOracle \
"$root/corpus" "$root/corpus/corpus.java.json" 2>&1 | grep -v 'logback\|^$' || true
test -s "$root/corpus/corpus.java.json"
# Getter defaults for the XMLEncoder references (packages/fls/src/javaDefaults.json).
defaults="$root/packages/fls/src/javaDefaults.json"
rm -f "$defaults"
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" GetterDefaults "$defaults" \
filius.gui.netzwerksicht.JSidebarButton#iconTextGap \
filius.gui.netzwerksicht.JSidebarButton#verticalTextPosition \
filius.gui.netzwerksicht.JSidebarButton#displayedMnemonicIndex \
filius.gui.netzwerksicht.JSidebarButton#debugGraphicsOptions \
filius.gui.netzwerksicht.JSidebarButton#enabled \
java.lang.ThreadGroup#maxPriority \
filius.hardware.knoten.Rechner#maxLayerOfOperation \
filius.hardware.knoten.Notebook#maxLayerOfOperation \
filius.hardware.knoten.Vermittlungsrechner#maxLayerOfOperation \
filius.hardware.knoten.Gateway#maxLayerOfOperation \
filius.software.clientserver.ServerBaustein#port \
filius.software.dhcp.DHCPServer#port \
filius.software.dns.DNSServer#port \
filius.software.rip.RIPServer#port \
filius.software.www.WebServer#port \
2>&1 | grep -v 'logback\|^$' || true
test -s "$defaults"
+55
View File
@@ -0,0 +1,55 @@
// Measures the value a JavaBean getter returns on a freshly constructed object.
// XMLEncoder shares equal Integer and Boolean objects: an int written once as
// the result of a getter (`<void id="Integer0" property="iconTextGap"/>`) is
// later referenced by id — e.g. as a node's x coordinate. Decoding such a file
// needs these defaults; they are measured here, not guessed.
//
// Usage: java -cp filius.jar:lib/*:classes GetterDefaults <output json> <fqcn#property>...
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
public class GetterDefaults {
public static void main(String[] args) throws Exception {
Map<String, Object> result = new TreeMap<>();
for (int index = 1; index < args.length; index++) {
String key = args[index];
String className = key.substring(0, key.indexOf('#'));
String property = key.substring(key.indexOf('#') + 1);
String simpleName = className.substring(className.lastIndexOf('.') + 1);
try {
Object value = className.equals("java.lang.ThreadGroup")
// A Thread's uncaughtExceptionHandler is its group; XMLDecoder runs
// in the main thread, so that is the group asked.
? read(Thread.currentThread().getThreadGroup(), property)
: read(Class.forName(className).getDeclaredConstructor().newInstance(), property);
result.put(simpleName + "#" + property, value);
} catch (Throwable error) {
Throwable cause = error.getCause() != null ? error.getCause() : error;
result.put(simpleName + "#" + property, Map.of("error", cause.toString()));
}
}
try (FileOutputStream out = new FileOutputStream(args[0])) {
out.write((FlsOracle.Json.write(result, "") + "\n").getBytes(StandardCharsets.UTF_8));
}
System.out.println("getter defaults: " + result.size());
System.exit(0);
}
static Object read(Object target, String property) throws Exception {
Method getter = null;
for (PropertyDescriptor descriptor : Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors()) {
if (descriptor.getName().equals(property)) getter = descriptor.getReadMethod();
}
if (getter == null) getter = target.getClass().getMethod(property);
Object value = getter.invoke(target);
if (value == null || value instanceof Number || value instanceof Boolean || value instanceof String) return value;
return Map.of("class", value.getClass().getName());
}
}