feat(fls): Java's view of the e-mail programs
The Java view now reads what FILIUS 2.14 keeps for its e-mail programs: the server's domain, switch and bean accounts with their mailboxes, and the accounts of /mailserver/konten.txt, which Java reads at every simulation start and which so win over the bean list; the client's three folders and address book, and its account from /konten.txt (no bean holds it). javaEmail.ts ports the bean setters and both file readers (stringZuListeBenutzerkonten, EmailKonto(String)); javaString.ts Java's trim, split and StringTokenizer; javaFileSystem.ts the file lookup, now shared with the DNS server, with the four non-ASCII units Java's equalsIgnoreCase also takes for an ASCII letter (measured over the BMP). Checked against Java on the corpus (the oracle dumps the e-mail state) and on 43 account files read by EmailServer.kontenLaden and EmailAnwendung.laden (EmailFileCases). Found: older files keep recipients as strings in the typed lists; a getter copy of the sender is filled and set back; one short server line makes Java throw for the whole file; a message with an empty text is lost on reading; a broken last client line empties the account. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
// How Java FILIUS finds a file by path (Dateisystem.holeDatei): the first
|
||||
// node in preorder whose path equals the path ignoring case, and only if that
|
||||
// node is a file.
|
||||
|
||||
import type { FlsFileTree } from './systemSoftware.js';
|
||||
|
||||
/**
|
||||
* The content of the file at an ASCII path as `Dateisystem.holeDatei` finds
|
||||
* it. Undefined when there is none; a file without content reads as empty
|
||||
* (Java would fail on it).
|
||||
*/
|
||||
export function javaFileContent(root: FlsFileTree | undefined, asciiPath: string): string | undefined {
|
||||
if (root === undefined) return undefined;
|
||||
const found = firstInPreorder(root, '', (path) => javaEqualsIgnoreCase(path, asciiPath));
|
||||
return found?.kind === 'file' ? (found.content ?? '') : undefined;
|
||||
}
|
||||
|
||||
/** Preorder below the root; a node's path is "/" plus each name after the root's. */
|
||||
function firstInPreorder(node: FlsFileTree, path: string, matches: (path: string) => boolean): FlsFileTree | undefined {
|
||||
for (const child of node.children) {
|
||||
const childPath = `${path}/${child.kind === 'other' ? child.className : String(child.name)}`;
|
||||
if (matches(childPath)) return child;
|
||||
const found = firstInPreorder(child, childPath, matches);
|
||||
if (found !== undefined) return found;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Besides ASCII case, String.equalsIgnoreCase takes these BMP units for an
|
||||
// ASCII letter (measured on the JDK over every unit): dotted and dotless I,
|
||||
// the Kelvin sign and the long s.
|
||||
const JAVA_ASCII_CASE_FOLDS = new Map([
|
||||
['\u0130', 'i'],
|
||||
['\u0131', 'i'],
|
||||
['\u212A', 'k'],
|
||||
['\u017F', 's'],
|
||||
]);
|
||||
|
||||
/** `String.equalsIgnoreCase` against an ASCII path, unit by unit. */
|
||||
function javaEqualsIgnoreCase(text: string, asciiPath: string): boolean {
|
||||
if (text.length !== asciiPath.length) return false;
|
||||
for (let index = 0; index < text.length; index++) {
|
||||
if (asciiFold(text[index]!) !== asciiFold(asciiPath[index]!)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function asciiFold(unit: string): string {
|
||||
if (unit >= 'A' && unit <= 'Z') return unit.toLowerCase();
|
||||
return JAVA_ASCII_CASE_FOLDS.get(unit) ?? unit;
|
||||
}
|
||||
Reference in New Issue
Block a user