diff --git a/CHANGES.md b/CHANGES.md index 15488f8..6921258 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -53,7 +53,7 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand | `src/topologyWriter.ts` | Java `SzenarioVerwaltung.netzwerkSpeichern`, Konstruktoren von `NetzwerkInterface`, `Port`, `GUIKabelItem` und der Systemsoftware | neu nach Java: `konfiguration.xml` aus Javas Sicht — Geräte, Schnittstellen (vom Konstruktor angelegte über Getter, weitere per `add`), Ports über Getter-Verweise, Kabel samt Zeichen-Enden (`kabelpanel`), Doku-Elemente; Systemsoftware über ihre Setter: SSID, Lernzeit, RIP, manuelle Routen, DHCP-Client und -Server, statische NAT, Modem, Router-Firewall (über das `konfig`-Plug-in des Webservers, wie XMLEncoder sie erreicht); eigene Anwendungen eines Projekts, wie die Datei sie hatte; Dateisysteme (an die Wurzel des Konstruktors angehängt wie bei XMLEncoder, beim Router hinter dessen Vorlagen); installierte Programme nach den Dateien (Webserver, Tauschbörse und E-Mail-Server legen ihre Ordner nur an, wo sie fehlen; der DNS-Server liest `/dns/hosts` beim Installieren), je unter ihrem Schlüssel mit der Systemsoftware wie `installApp`, samt Einstellungen und persönlicher Firewall; E-Mail: Domain und Bean-Konten des Servers samt Postfächern, Ordner und Adressbuch des Clients (sein Konto steht in `/konten.txt`, einer Datei); Javas Fehler nicht übernommen: ein Empfänger, den eine ältere Datei als Zeichenkette hält, wird als der `AddressEntry` geschrieben, den Javas `AddressEntry(String)` daraus macht (Java legt die Zeichenkette ungeprüft in die typisierte Liste, sein E-Mail-Fenster scheitert daran), ein `null`-Empfänger fällt weg; Kopf „Filius version: 2.14.0 (mdg-filius)“ | | `test/topologyWriter.test.ts`, `test/written.java.json` | — | neu: alle 23 Korpus-Dateien gelesen und neu geschrieben, von Java geladen — Topologie, Systemsoftware, Dateisysteme und Programme samt E-Mail wie beim Original (bis auf die benannte Abweichung bei Empfängern als Zeichenkette, nur in `man-in-the-middle.fls`) (Java gegen Java); dazu eine künstliche Topologie mit dem, was der Korpus nicht hat (u. a. eingeschaltete Router-Firewall mit Regeln jedes Protokolls, alle Firewall-Schalter abseits der Vorgabe, Modem mit eigenem Port und Ziel, verschachtelter Dateibaum mit umbenannter Wurzel, namenlosem Ordner, Datei ohne Typ und Inhalt mit CR/CRLF und Zeichen jenseits der BMP, Heimrouter mit eigener Wurzel, jedes Filius-Programm mit Einstellungen abseits der Vorgabe samt persönlicher Firewall, von Java angelegte Ordner `/peer2peer` und `/mailserver`, E-Mail-Server und -Client mit Konto, Postfach, drei Ordnern, Adressbuch und Empfängern aller Formen) | | `src/exportProject.ts` | Gegenstück zu `importProject.ts`; `saveProjectArchive` schreibt eine ganze `.fls` samt der übrigen Archiveinträge (Gegenstück: `openProjectArchive` in `importProject.ts`, das sie in `archiveEntries` des Modells legt); Java `JHostKonfiguration.updateWifiConnection`, `InternetKnotenBetriebssystem.getStandardGateway`/`setStandardGateway` | neu: Editor-Modell → Javas Sicht für den Schreiber, Schritt X1 Topologie — Geräte, Schnittstellen (MAC wie in der Simulation: aus der Datei oder aus der Port-ID abgeleitet), Kabel samt Funk (Karte drahtlos und Switch, wie Java verbindet), Gateway und DNS an allen Schnittstellen wie Javas eigene Setter, Doku-Elemente; X2 Systemsoftware — SSIDs, Lernzeit, Routen, RIP, DHCP-Client und -Server, Portweiterleitung, Firewall (Namen des Kerns als Javas Zahlen), Modems aus den Paaren des Modells (Port aus dem Paarnamen, der erste eines Paars dient, Ziel `localhost`); X3 Dateien von Rechner und Notebook — Typ aus der Inhaltsart (Text nach Endung, sonst `text`; Bild und Binärdaten nach Medientyp), Bilder und Binärdaten in Base64 wie Filius' `Base64.encodeBytes` (Umbruch nach 76 Zeichen, an 19 Längen mit Java gemessen); X4a Programme — je Programm die Filius-Klasse unter ihrem Namen als Schlüssel, eingeschaltete Server, Ports, virtuelle Hosts, DNS-Rekursion, Ziel des Clients (leer als `null` wie Javas Konstruktor), Teilnehmerzahl, persönliche Firewall; X4b E-Mail — Beans des Servers (Domain, Konten, Postfächer) und des Clients (drei Ordner, Adressbuch), `/konten.txt` und `/mailserver/konten.txt` aus den Konten des Modells neu geschrieben im Format von Filius 2.14 (`EmailKonto.toString`, `listeBenutzerkontenZuString`), ohne Javas Fehler (leere Felder statt "null"); ein Zeichen, das ein Feld im Format nicht tragen kann, wird dort zum Leerzeichen, mit Warnung | -| `test/exportProject.test.ts`, `test/exported.java.json` | — | neu: alle 23 Korpus-Dateien importiert, exportiert, geschrieben und von Java geladen — Topologie wie beim Original bis auf benannte Abweichungen (abgeleiteter MAC und abgeleitete Switch-SSID statt gewürfelter, Modem-Rollen nach Reihenfolge im Paar, Doku in Zeichenreihenfolge mit Text, Schrift und Mindestgröße des Modells, Dateien in der Ordnung und mit dem Typ des Modells, keine eigenen Dateien von Routern, Empfänger als Zeichenkette gelesen, Nachrichten des Gesendet-Ordners versendet, Konto-Dateien neu geschrieben wie Filius 2.14); die übrigen Archiveinträge (eigene Anwendungen unter `projekt/anwendungen`) kommen byte-gleich zurück; dazu ein künstliches Modell, das Java ebenfalls lädt | +| `test/exportProject.test.ts`, `test/exported.java.json` | — | neu: alle 23 Korpus-Dateien importiert, exportiert, geschrieben und von Java geladen — Topologie wie beim Original bis auf benannte Abweichungen (abgeleiteter MAC und abgeleitete Switch-SSID statt gewürfelter, Modem-Rollen nach Reihenfolge im Paar, Doku in Zeichenreihenfolge mit Text, Schrift und Mindestgröße des Modells, Dateien in der Ordnung und mit dem Typ des Modells, keine eigenen Dateien von Routern, Empfänger als Zeichenkette gelesen, Nachrichten des Gesendet-Ordners versendet, Konto-Dateien neu geschrieben wie Filius 2.14); die übrigen Archiveinträge (eigene Anwendungen unter `projekt/anwendungen`) kommen byte-gleich zurück; eigene Anwendungen auf Rechnern gehen als erfasste Bean durch das Modell (`ownApplicationsByNodeId`) und werden zurückgeschrieben; dazu ein künstliches Modell, das Java ebenfalls lädt | | `src/importEmail.ts` | `Persistence/TopologyProjectStore.swift` (`registerJavaEmailConfigurations`, `assignedJavaEmailMessages`) | übertragen, Eingabe ist Javas Sicht: Server-Konten aus `/mailserver/konten.txt`, ohne Datei aus der Bean-Liste (Java verlöre sie); Client-Konto aus `/konten.txt`, ohne Datei aus der älteren `kontoListe` (Filius 2.14 liest sie nicht mehr); Konten und Nachrichten bleiben, wo Swift verwirft, was seine Prüfung nicht besteht; Vor- und Nachname getrennt; Empfangszeit nur aus der Datei (Swift nummeriert); gegen Swifts Modell geprüft | | `test/importEmail.test.ts`, `test/email-*.swift.json` | `State/TopologyEmail.swift` (`decodeJavaServer`, `decodeJavaClient`) als Vergleich | neu: dieselben 43 Kontodateien mit Swifts Leser; wie Swift bis auf benannte Fälle, in denen Swift prüft und die ganze Datei verwirft, wo Java liest | | `src/javaFileSystem.ts`, `src/javaString.ts` | Java `Dateisystem.holeDatei`, `String.trim`/`split`/`equalsIgnoreCase` | neu nach Java; die Groß-/Kleinschreibung gegenüber ASCII am JDK über die ganze BMP gemessen | diff --git a/packages/fls/src/exportProject.ts b/packages/fls/src/exportProject.ts index 43c2b36..2efc34b 100644 --- a/packages/fls/src/exportProject.ts +++ b/packages/fls/src/exportProject.ts @@ -30,7 +30,8 @@ // model keeps it), the echo server's port, the generic client's target, the // peer count, the personal firewall. A DNS server's records are in // /dns/hosts, one of the files, and it forwards to the host's DNS server as -// Java's does. +// Java's does. A project's own applications are written as their beans, +// as the file had them; their classes go with the archive's other entries. // // E-mail: the server's domain and accounts with their mailboxes, the client's // folders and address book, as Java's beans. The account files are written @@ -114,7 +115,7 @@ import { import { writeFlsArchive } from './archive.js'; import { EMAIL_CLIENT_FILE, EMAIL_SERVER_FILE, type JavaAddressEntry, type JavaContact, type JavaEmail } from './javaEmail.js'; import { PROGRAM_OF_CLASS } from './importProject.js'; -import type { FlsApplicationClass, FlsDhcpServer, FlsFileTree, FlsFirewall } from './systemSoftware.js'; +import type { FlsApplicationClass, FlsDhcpServer, FlsFileTree, FlsFirewall, FlsOwnApplication } from './systemSoftware.js'; import type { FlsDocumentationItem, FlsInterface, FlsLink, FlsNodeClass } from './topology.js'; import { writeTopologyXml, type ApplicationToWrite, type NodeToWrite, type SystemToWrite, type TopologyToWrite } from './topologyWriter.js'; @@ -145,6 +146,8 @@ export interface ProjectToExport { readonly documentationItems: readonly DocumentationItem[]; /** The archive's other entries (the project's own applications), written unchanged. */ readonly archiveEntries?: ReadonlyMap; + /** A project's own applications on a PC or notebook, written as their beans. */ + readonly ownApplicationsByNodeId?: ReadonlyMap; } export interface ExportedTopology { @@ -286,7 +289,13 @@ function exportSystem(node: TopologyNode, project: ProjectToExport, servingModem const server = project.installedPrograms.get(id)?.has('emailServer') === true ? project.emailServerConfigurations.get(id) : undefined; if (client?.account != null) putFile(root, EMAIL_CLIENT_FILE, 'text/txt', clientAccountFile(client, warn)); if (server !== undefined) putFile(root, EMAIL_SERVER_FILE, 'txt', serverAccountsFile(server, warn)); - return { ...system, ssid: ssid === '' ? null : ssid, fileSystem: root, applications: exportPrograms(id, project) }; + return { + ...system, + ssid: ssid === '' ? null : ssid, + fileSystem: root, + applications: exportPrograms(id, project), + ownApplications: [...(project.ownApplicationsByNodeId?.get(id) ?? [])], + }; } case 'router': return { diff --git a/packages/fls/src/importProject.ts b/packages/fls/src/importProject.ts index 17302c2..14773a6 100644 --- a/packages/fls/src/importProject.ts +++ b/packages/fls/src/importProject.ts @@ -101,7 +101,7 @@ import { readFlsArchive } from './archive.js'; import { dnsRecordsOf, javaHostsFileContent, readDnsHosts } from './dnsHosts.js'; import { decodeJavaBeans } from './javabeans.js'; import { emailClientConfiguration, emailServerConfiguration } from './importEmail.js'; -import type { FlsApplicationClass, FlsFileTree, FlsFirewall } from './systemSoftware.js'; +import type { FlsApplicationClass, FlsFileTree, FlsFirewall, FlsOwnApplication } from './systemSoftware.js'; import { readTopology, type FlsDocumentationItem, type FlsInterface, type FlsNode, type FlsNodeClass, type FlsTopology } from './topology.js'; export interface ImportedProject { @@ -140,6 +140,12 @@ export interface ImportedProject { * they cannot run here, but must not be lost. */ archiveEntries: ReadonlyMap; + /** + * A project's own applications on a PC or notebook, as their beans + * (beanCapture.ts): they cannot run here, but are written again as the + * file had them. Their classes are among the archive's entries. + */ + ownApplicationsByNodeId: Map; /** The topology's warnings, then this step's. */ warnings: string[]; } @@ -188,6 +194,7 @@ export function importProject( echoServerConfigurations: new Map(), simpleClientConfigurations: new Map(), }; + const ownApplicationsByNodeId = new Map(); const nodes = topology.nodes.map((source) => { const node = importNode(source, newId, warnings); registerConfigurations(source, node, deviceConfigurations, interfaceConfigurations); @@ -196,6 +203,8 @@ export function importProject( if (node.kind === 'pc' || node.kind === 'notebook') { system.virtualFileSystems.set(node.id, importFileSystem(source.system.fileSystem, node.displayName, warnings)); registerPrograms(source, node, system, warnings); + const own = source.system.ownApplications ?? []; + if (own.length > 0) ownApplicationsByNodeId.set(node.id, own); } return node; }); @@ -249,6 +258,7 @@ export function importProject( ...system, documentationItems: inDeterministicRenderOrder(documentationItems), archiveEntries, + ownApplicationsByNodeId, warnings, }; } diff --git a/packages/fls/test/exportProject.test.ts b/packages/fls/test/exportProject.test.ts index 694b5dd..33bbfee 100644 --- a/packages/fls/test/exportProject.test.ts +++ b/packages/fls/test/exportProject.test.ts @@ -39,7 +39,6 @@ import { type ProjectToExport, } from '../src/index.js'; import { writeTopologyXml } from '../src/topologyWriter.js'; -import { withoutOwnApplications } from './ownApplications.js'; const here = dirname(fileURLToPath(import.meta.url)); const corpusRoot = join(here, '../../../corpus'); @@ -350,8 +349,7 @@ describe('the editor model exported, as Java loads it', () => { return nic.mac === derived ? derived : undefined; }), ); - // The model does not install own applications yet (B2). - const source = withoutOwnApplications(original[name]!); + const source = original[name]!; const nodeIds = project.graph.nodes.map((node) => node.id); const pairs = nodeIds.map((id) => project.remoteLinkConfigurations.get(id)?.pairIdentifier); // The account files of each host the export writes anew, by path, as the file has them. diff --git a/packages/fls/test/exported.java.json b/packages/fls/test/exported.java.json index f001322..f069d3c 100644 --- a/packages/fls/test/exported.java.json +++ b/packages/fls/test/exported.java.json @@ -8100,7 +8100,15 @@ "directory": "root", "children": [] }, - "applications": [], + "applications": [ + { + "key": "filius.software.clientserver.Accumulator", + "class": "filius.software.clientserver.Accumulator", + "onItsSystem": true, + "aktiv": true, + "port": 55555 + } + ], "dhcpClient": false, "dhcpServer": { "aktiv": false, @@ -8309,7 +8317,7 @@ } ], "documentation": [], - "sha256": "955edc9322d1de6bae59ce05221b94ab18983c46193db034cf37fd18f0146a48" + "sha256": "acca8f9fe5b317e12472b5e975d0463d367b90896e14a07df46e24fc71feef38" }, "java-examples/dhcp_test_scenario.fls": { "success": true, @@ -13346,6 +13354,13 @@ "children": [] }, "applications": [ + { + "key": "filius.software.clientserver.Broadcast", + "class": "filius.software.clientserver.Broadcast", + "onItsSystem": true, + "aktiv": true, + "port": 55555 + }, { "key": "filius.software.clientserver.ServerBaustein", "class": "filius.software.clientserver.ServerBaustein", @@ -13585,7 +13600,7 @@ } ], "documentation": [], - "sha256": "b860d1bdf773a92b90f63ff6c7c76e9d0ee2c9b342e6042c3e39f61acf6697e3" + "sha256": "4f1a87256f67f1d09a25db7eb2f964663b4728b43592731952a0a30e562cf702" }, "java-examples/nat.fls": { "success": true, diff --git a/packages/fls/test/ownApplications.ts b/packages/fls/test/ownApplications.ts index 16b3eff..9324eb5 100644 --- a/packages/fls/test/ownApplications.ts +++ b/packages/fls/test/ownApplications.ts @@ -11,7 +11,7 @@ export const OWN_APPLICATION_FILES = ['java-examples/anwendung_komplett.fls', 'j type View = { nodes?: { system: Record }[] }; /** Java's view without the applications FILIUS does not ship. */ -export function withoutOwnApplications(view: T): T { +function withoutOwnApplications(view: T): T { const { nodes } = view as View; if (nodes === undefined) return view; return {