diff --git a/CHANGES.md b/CHANGES.md
index 622febd..1e64422 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -30,8 +30,12 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| --- | --- | --- |
| `src/zip.ts` | `Persistence/TopologyFLSArchive.swift` (Fehler, Grenzen, Pfadregeln, ZIP-Leser/-Schreiber, CRC-32) | übertragen; DEFLATE über `fflate`, mit Apples Abschneide-Verhalten nachgebildet; Pfadfaltung (NFC, Breite, volles Case-Folding) an 41 Paaren gegen das Swift-Orakel gemessen |
| `src/archive.ts` | ebenda, Archiv-Teil von Import und Export | übertragen, ohne die XML-Ebene |
-| `src/javabeans.ts`, `src/javaDefaults.json` | — | neu: XMLEncoder-Leser nach den Regeln von `java.beans.XMLDecoder` (Swift liest elementweise und verliert geteilte Werte); Getter-Standardwerte mit `tools/java-oracle` gemessen |
+| `src/javabeans.ts`, `src/javaDefaults.json` | — | neu: XMLEncoder-Leser nach den Regeln von `java.beans.XMLDecoder` (Swift liest elementweise und verliert geteilte Werte); Getter-Standardwerte und statische Konstanten mit `tools/java-oracle` gemessen |
+| `src/javaIp.ts` | Java `IP.inetAton`/`ipCheck` | neu nach Java; an 46 Fällen mit Java gemessen |
+| `src/topology.ts` | Java `SzenarioVerwaltung.netzwerkLaden`, Konstruktoren und Setter der Geräte; Swift `Persistence/TopologyProjectStore.swift` (Parser) als Vergleich | neu nach Java statt Übertragung: Geräteart aus der Klasse statt aus der Beschriftung, Geräte ohne Koordinaten bei 0,0 statt verworfen, Werte durch Javas Setter |
| `test/javabeans.test.ts` | — | neu: je XMLDecoder-Regel ein Test; alle 23 Korpus-Dateien, Klasse und Position jedes Geräts wie Java |
+| `test/topology.test.ts` | — | neu: alle 23 Korpus-Dateien Feld für Feld wie Java (Geräte, Schnittstellen, Kabel, Doku-Elemente), dazu die Regeln, die der Korpus nicht berührt |
+| `test/javaIp.test.ts`, `test/ip-check.*` | — | neu: `IP.ipCheck`-Fälle, Ergebnis aus Java |
| `test/zip.test.ts` | `TopologyProjectPersistenceTests.swift` (Archiv-Ablehnungstests, `makeMinimalZIP`, `makePayloadZIP`) | übertragen, ergänzt um DEFLATE-Grenzfälle |
| `test/corpus.test.ts` | — | neu: 23 Korpus-Dateien, gegengeprüft mit `fflate.unzipSync` |
| `test/pathFolding.test.ts`, `test/path-folding.*.json` | — | neu: welche Pfade dieselbe Datei meinen, Ergebnis aus dem Swift-Kern |
diff --git a/corpus/corpus.java.json b/corpus/corpus.java.json
index 5d1457e..ce82209 100644
--- a/corpus/corpus.java.json
+++ b/corpus/corpus.java.json
@@ -12,6 +12,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.12",
@@ -93,6 +95,8 @@
240,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "160.10.20.10",
@@ -113,6 +117,8 @@
68,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "8.8.8.8",
@@ -133,6 +139,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -558,6 +566,8 @@
149,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.12",
@@ -578,6 +588,8 @@
98,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.10",
@@ -598,6 +610,8 @@
90,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.11",
@@ -618,6 +632,8 @@
120,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.13",
@@ -675,6 +691,8 @@
138,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.14",
@@ -695,6 +713,8 @@
177,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.15",
@@ -888,6 +908,8 @@
134,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.2.10",
@@ -908,6 +930,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.1.10",
@@ -928,6 +952,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.1.11",
@@ -1048,6 +1074,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.10.20.11",
@@ -1105,6 +1133,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.10.20.12",
@@ -1343,6 +1373,8 @@
111,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "8.8.8.8",
@@ -1363,6 +1395,8 @@
246,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "120.130.140.80",
@@ -1383,6 +1417,8 @@
233,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "130.100.200.25",
@@ -1403,6 +1439,8 @@
264,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "140.100.200.25",
@@ -1423,6 +1461,8 @@
185,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.0.0.1",
@@ -1470,6 +1510,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.1.11",
@@ -1490,6 +1532,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.13",
@@ -1510,6 +1554,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.11",
@@ -1530,6 +1576,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.14",
@@ -1550,6 +1598,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.12",
@@ -1661,6 +1711,8 @@
189,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "43.0.0.2",
@@ -2339,6 +2391,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.10.20.11",
@@ -2396,6 +2450,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.10.20.12",
@@ -2634,6 +2690,8 @@
111,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "8.8.8.8",
@@ -2654,6 +2712,8 @@
236,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "120.130.140.80",
@@ -2674,6 +2734,8 @@
223,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "130.100.200.25",
@@ -2694,6 +2756,8 @@
254,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "140.100.200.25",
@@ -2714,6 +2778,8 @@
207,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.0.0.1",
@@ -2761,6 +2827,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.1.11",
@@ -2781,6 +2849,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.11",
@@ -2801,6 +2871,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.14",
@@ -2821,6 +2893,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.13",
@@ -2841,6 +2915,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.50.50.12",
@@ -2952,6 +3028,8 @@
184,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "43.0.0.2",
@@ -3588,6 +3666,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.12",
@@ -3669,6 +3749,8 @@
240,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "160.10.20.10",
@@ -3689,6 +3771,8 @@
68,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "8.8.8.8",
@@ -3709,6 +3793,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -4097,6 +4183,8 @@
87,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -4117,6 +4205,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.1",
@@ -4174,6 +4264,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.2",
@@ -4241,6 +4333,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -4261,6 +4355,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.101",
@@ -4281,6 +4377,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -4338,6 +4436,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -4385,6 +4485,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -4515,6 +4617,8 @@
149,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.12",
@@ -4535,6 +4639,8 @@
98,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.10",
@@ -4555,6 +4661,8 @@
90,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.11",
@@ -4575,6 +4683,8 @@
120,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.13",
@@ -4632,6 +4742,8 @@
138,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.14",
@@ -4652,6 +4764,8 @@
177,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.15",
@@ -4771,6 +4885,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -4791,6 +4907,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.12",
@@ -4811,6 +4929,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.13",
@@ -4831,6 +4951,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.14",
@@ -4851,6 +4973,8 @@
67,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -4908,6 +5032,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.59.98",
@@ -4928,6 +5054,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.149.102",
@@ -4948,6 +5076,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.43.216",
@@ -4968,6 +5098,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.19.11",
@@ -5213,11 +5345,13 @@
104,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.231",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "1A:77:96:CF:32:33",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5233,11 +5367,13 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.183",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "5F:A4:08:F1:FC:88",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5253,11 +5389,13 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.184",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "9D:8C:F6:C9:98:AE",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5273,11 +5411,13 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.181",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "A8:13:4D:62:BF:61",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5293,11 +5433,13 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.182",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "FD:92:36:FB:99:6D",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5350,11 +5492,13 @@
101,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.230",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "9D:26:F9:23:B2:F9",
"gateway": "",
"dns": ""
}
@@ -5370,11 +5514,13 @@
172,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.232",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "37:DF:DC:1A:CF:E0",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5390,11 +5536,13 @@
183,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.233",
"mask": "255.255.255.128",
- "mac": null,
+ "mac": "7A:10:61:61:15:A3",
"gateway": "",
"dns": "141.99.50.230"
}
@@ -5721,6 +5869,8 @@
134,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.2.10",
@@ -5741,6 +5891,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.1.10",
@@ -5761,6 +5913,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.1.11",
@@ -5781,6 +5935,8 @@
112,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.3.10",
@@ -5801,6 +5957,8 @@
131,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.10",
@@ -5821,6 +5979,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.4.10",
@@ -5878,6 +6038,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.5.11",
@@ -6075,6 +6237,8 @@
54,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.183",
@@ -6095,6 +6259,8 @@
287,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.184",
@@ -6115,6 +6281,8 @@
48,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.181",
@@ -6172,6 +6340,8 @@
201,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.230",
@@ -6192,6 +6362,8 @@
271,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.232",
@@ -6212,6 +6384,8 @@
282,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.50.233",
@@ -6378,6 +6552,8 @@
112,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -6398,6 +6574,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -6480,6 +6658,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.1",
@@ -6500,6 +6680,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -6520,6 +6702,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.20",
@@ -6651,6 +6835,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -6671,6 +6857,8 @@
92,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "10.0.0.100",
@@ -6937,6 +7125,8 @@
101,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.2.10",
@@ -6957,6 +7147,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -6977,6 +7169,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.101",
@@ -6997,6 +7191,8 @@
213,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.4.10",
@@ -7017,6 +7213,8 @@
198,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -7074,6 +7272,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -7590,6 +7790,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -7610,6 +7812,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -7694,6 +7898,8 @@
100,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.2.10",
@@ -7964,6 +8170,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -7984,6 +8192,8 @@
108,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.12",
@@ -8068,6 +8278,8 @@
301,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "42.0.0.2",
@@ -8122,6 +8334,8 @@
236,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "120.130.140.10",
@@ -8142,6 +8356,8 @@
111,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "8.8.8.8",
@@ -8516,6 +8732,8 @@
116,
19
],
+ "useIpAsName": true,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -8536,6 +8754,8 @@
90,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.10",
@@ -8701,6 +8921,8 @@
134,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.2.10",
@@ -8721,6 +8943,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.1.10",
@@ -8741,6 +8965,8 @@
123,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "141.99.1.11",
@@ -8861,6 +9087,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.11",
@@ -8881,6 +9109,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.12",
@@ -8901,6 +9131,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.13",
@@ -8921,6 +9153,8 @@
91,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.14",
@@ -8941,6 +9175,8 @@
67,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "192.168.0.100",
@@ -8998,6 +9234,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.59.98",
@@ -9018,6 +9256,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.149.102",
@@ -9038,6 +9278,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.43.216",
@@ -9058,6 +9300,8 @@
102,
19
],
+ "useIpAsName": false,
+ "useMacAsName": false,
"ports": [
{
"ip": "169.254.19.11",
diff --git a/packages/fls/src/index.ts b/packages/fls/src/index.ts
index 5adcc9b..8e53cd7 100644
--- a/packages/fls/src/index.ts
+++ b/packages/fls/src/index.ts
@@ -1,4 +1,6 @@
// Public surface of the .fls codec.
export * from './archive.js';
+export * from './javaIp.js';
export * from './javabeans.js';
+export * from './topology.js';
export * from './zip.js';
diff --git a/packages/fls/src/javaDefaults.json b/packages/fls/src/javaDefaults.json
index dba9c77..c181f20 100644
--- a/packages/fls/src/javaDefaults.json
+++ b/packages/fls/src/javaDefaults.json
@@ -11,6 +11,7 @@
"RIPServer#port": 520,
"Rechner#maxLayerOfOperation": 4,
"ServerBaustein#port": 55555,
+ "SwingConstants.CENTER": 0,
"ThreadGroup#maxPriority": 10,
"Vermittlungsrechner#maxLayerOfOperation": 2,
"WebServer#port": 80
diff --git a/packages/fls/src/javaIp.ts b/packages/fls/src/javaIp.ts
new file mode 100644
index 0000000..94e03b5
--- /dev/null
+++ b/packages/fls/src/javaIp.ts
@@ -0,0 +1,65 @@
+// What Java FILIUS stores when a file sets an interface's IP address or mask:
+// NetzwerkInterface.setIp(ip) keeps IP.ipCheck(ip), which is null for text
+// that is not an address and the canonical dotted quad otherwise.
+// Java counterpart: filius.software.vermittlungsschicht.IP.inetAton/ipCheck.
+//
+// Measured against Java (test/ip-check.cases.txt, tools/java-oracle), since
+// the rules are wider than they look:
+// - StringTokenizer(".") skips empty tokens: "10..0.0.1" is 10.0.0.1;
+// - Integer.parseInt takes a sign and leading zeros: "+010" is 10, "-0" is 0;
+// - and any Unicode decimal digit in the BMP: Arabic-Indic "١٠" is 10 —
+// parseInt reads UTF-16 units, so "𝟏" (a surrogate pair) is no digit.
+
+/**
+ * `Integer.parseInt(text)`: an optional sign, then one or more decimal digits
+ * of any script; undefined where Java throws NumberFormatException.
+ */
+export function javaParseInt(text: string): number | undefined {
+ const characters = text.split('');
+ let index = 0;
+ let negative = false;
+ if (characters[0] === '-' || characters[0] === '+') {
+ negative = characters[0] === '-';
+ index = 1;
+ }
+ if (index >= characters.length) return undefined;
+ let value = 0;
+ for (; index < characters.length; index++) {
+ const digit = decimalDigitValue(characters[index]!);
+ if (digit === undefined) return undefined;
+ value = value * 10 + digit;
+ // Past the int range Java throws; the octet check would reject it anyway.
+ if (value > 2 ** 31) return undefined;
+ }
+ if (!negative && value > 2 ** 31 - 1) return undefined;
+ return negative ? -value : value;
+}
+
+/**
+ * `Character.digit(c, 10)` for one UTF-16 unit: Unicode lays out every
+ * script's digits as a run of ten from zero to nine, so the value is the
+ * distance from the start of the run, modulo ten (in case two runs touch).
+ */
+function decimalDigitValue(unit: string): number | undefined {
+ if (!/^\p{Nd}$/u.test(unit)) return undefined;
+ let code = unit.charCodeAt(0);
+ let offset = 0;
+ while (/^\p{Nd}$/u.test(String.fromCharCode(code - 1))) {
+ code -= 1;
+ offset += 1;
+ }
+ return offset % 10;
+}
+
+/** `IP.ipCheck(text)`: four octets 0…255 as `a.b.c.d`, or null. */
+export function javaIpCheck(text: string): string | null {
+ const tokens = text.split('.').filter((token) => token !== '');
+ if (tokens.length !== 4) return null;
+ const octets: number[] = [];
+ for (const token of tokens) {
+ const octet = javaParseInt(token);
+ if (octet === undefined || octet < 0 || octet > 255) return null;
+ octets.push(octet);
+ }
+ return octets.join('.');
+}
diff --git a/packages/fls/src/javabeans.ts b/packages/fls/src/javabeans.ts
index 4e604d9..f8304d9 100644
--- a/packages/fls/src/javabeans.ts
+++ b/packages/fls/src/javabeans.ts
@@ -9,11 +9,12 @@
// cannot follow values XMLEncoder shares by reference (plan §13, M2):
// - `id`/`idref` resolve anywhere, including the results of getters;
// - a getter on a property nobody set answers the value Java would, from
-// measured defaults (javaDefaults.json), or an explicit "unresolved".
+// measured defaults (javaDefaults.json), or an explicit "unresolved";
+// - static constants (`SwingConstants.CENTER`) are their measured values.
//
// XMLDecoder's rules, as far as XMLEncoder uses them:
// new C(args), then the statements
-// the static field C.F
+// the static field C.F (measured value if known)
// the static factory C.m(args)
// the value stored under id X
// target.getP() (a value)
@@ -46,8 +47,12 @@ export interface JavaObject {
readonly properties: Map;
/** Method calls in order (`add`, `put`, …), with their arguments. */
readonly calls: { method: string; args: JavaValue[] }[];
- /** `set(i, v)` on the object. */
- readonly indexed: Map;
+ /**
+ * Elements the object already held before the file's statements — a list
+ * its constructor filled, reached by `` on a getter's
+ * result. `add`/`set` go to `calls`, in order.
+ */
+ readonly presetElements: Map;
/** True when the object was not in the file but returned by a getter. */
readonly fromGetter: boolean;
}
@@ -97,14 +102,40 @@ export function simpleClassName(className: string): string {
return className.slice(className.lastIndexOf('.') + 1);
}
-/** The elements of a list: `add` calls, then `set(i, v)` replacing by index. */
-export function listItems(value: JavaValue | undefined): JavaValue[] {
+/**
+ * The elements of a list: the ones it held before the file's statements,
+ * then `add`, `add(i, v)` and `set(i, v)` replayed in order.
+ *
+ * `constructed` is how many elements the list's constructor made — a Java
+ * `Switch` starts with 24 ports, a `Gateway` with two interfaces. The file
+ * only mentions those it changed; the rest come back as untouched objects.
+ */
+export function listItems(value: JavaValue | undefined, constructed = 0): JavaValue[] {
if (typeof value === 'object' && value !== null && (value as JavaArray).kind === 'array') {
return [...(value as JavaArray).items];
}
if (!isJavaObject(value)) return [];
- const items = value.calls.filter((call) => call.method === 'add').map((call) => call.args.at(-1) ?? null);
- for (const [index, item] of value.indexed) items[index] = item;
+ const items = replayList(value, constructed);
+ return Array.from(items, (item) => item ?? newObject({ className: 'unknown', fromGetter: true }));
+}
+
+/** Like listItems, with `undefined` for a constructed element nobody touched. */
+function replayList(value: JavaObject, constructed: number): (JavaValue | undefined)[] {
+ const presetLength = Math.max(constructed, ...Array.from(value.presetElements.keys(), (index) => index + 1));
+ const items: (JavaValue | undefined)[] = Array.from({ length: presetLength }, (_, index) =>
+ value.presetElements.get(index),
+ );
+ for (const { method, args } of value.calls) {
+ if (method === 'add' && args.length === 1) {
+ items.push(args[0]!);
+ } else if (method === 'add' && args.length === 2 && typeof args[0] === 'number') {
+ items.splice(args[0], 0, args[1]!);
+ } else if (method === 'set' && args.length === 2 && typeof args[0] === 'number') {
+ items[args[0]] = args[1]!;
+ } else if (method === 'clear' && args.length === 0) {
+ items.length = 0;
+ }
+ }
return items;
}
@@ -194,7 +225,7 @@ function newObject(fields: Partial & { className: string }): JavaObj
factoryMethod: fields.factoryMethod,
properties: new Map(),
calls: [],
- indexed: new Map(),
+ presetElements: new Map(),
fromGetter: fields.fromGetter ?? false,
};
}
@@ -297,6 +328,12 @@ class Decoder {
}
const className = attributes.class;
if (className === undefined) throw new JavaBeansError('