image-converter/prd/OutputImageFormat-Parser.md
2025-10-26 09:44:48 +01:00

7.9 KiB
Raw Blame History

PRD CSV→OutputImageFormat Parser (Erweiterung zum „PRD Javanative Bildkonvertierung“)

Ziel: Robustes Parsen einer kommaseparierten Zeichenkette (CSV) in eine ordnungserhaltende, doppeltfreie List<OutputImageFormat> ohne externe Abhängigkeiten. Unterstützt Synonyme (z.B. jpgJPEG, tifTIFF), führende Punkte (.jpg), MIMETypen (image/jpeg) und ImageIOFormatnamen (jpeg). Fehlerhafte Tokens führen wahlweise zu Exception oder werden „lenient“ ignoriert.

Dieses PRD ergänzt das bestehende Dokument „PRD Javanative Bildkonvertierung (Spring Boot, OSS)“ und liefert die Spezifikation für den CSVParser, der z.B. RESTParameter allowed (CSV) in List<OutputImageFormat> transformiert.


1. Scope

In Scope

  • UtilityKlasse zum Parsen einer CSVListe in List<OutputImageFormat>.
  • Caseinsensitive, Whitespacetolerant, führende . entfernen.
  • SynonymMapping für verbreitete Varianten (jpg/jpeg, tif/tiff, jp2/j2k/jpeg2000, webp, mimetypen etc.).
  • Dedup bei gleichzeitigem Erhalt der Eingabereihenfolge.
  • Zwei Modi:
    • Strict: Unbekannte Tokens → IllegalArgumentException.
    • Lenient: Unbekannte Tokens werden ignoriert.
  • Öffentliche HelperAPI für SingleTokenLookup.

Out of Scope

  • Validierung gegen tatsächlich installierte Writer/Reader (das übernimmt die Konvertierungsschicht).
  • Automatisches Nachladen von externen Plugins.

2. Nichtfunktionale Anforderungen

  • ZeroDeps: reine JDKFunktionen, keine Tika/Guava etc.
  • Performance: O(n) über Anzahl Tokens; Lookup über vorkompilierte Map.
  • Threadsafety: statische, unveränderliche LookupMap (unmodifiable); ParserMethoden sind reentrant und threadsafe.
  • Internationalisierung: nicht relevant (Tokens sind technische Kürzel/MIMETypen).

3. Domänenmodell / Abhängigkeiten

  • OutputImageFormat (Enum) aus der HauptPRD: liefert extension, mimeType, imageIoFormatName, name().
  • Synonyme werden aus dem Enum abgeleitet und um gebräuchliche Aliasse ergänzt:
    • JPEGjpg / jpeg / image/jpeg
    • TIFFtif / tiff / image/tiff
    • PNGpng / image/png
    • BMPbmp / image/bmp
    • GIFgif / image/gif
    • WEBPwebp / image/webp
    • JP2jp2 / j2k / jpeg2000 / image/jp2

Hinweis: Falls einzelne EnumWerte (WEBP/JP2) im Projekt deaktiviert werden, bleibt das Mapping konsistent Keys auf nicht existierende EnumWerte entfallen.


4. Öffentliche API

public final class ImageFormatParsers {
  /** Strict: unbekannte Tokens -> IllegalArgumentException */
  public static List<OutputImageFormat> parseCsv(String csv);

  /** Lenient: unbekannte Tokens werden ignoriert */
  public static List<OutputImageFormat> parseCsvLenient(String csv);

  /** EinzelTokenLookup (caseinsensitive, . entfernt, Synonyme & MIME) */
  public static Optional<OutputImageFormat> lookup(String token);
}

Verhalten

  • null oder leere/blank CSV → leere Liste.
  • Trennung: , (Komma). Whitespace wird getrimmt, führende . entfernt (z.B. .jpg).
  • Reihenfolge: Erstes Vorkommen gewinnt; weitere Duplikate werden ignoriert (LinkedHashSetSemantik).
  • Strict: Tokens, die keinem Format zugeordnet werden können, sammeln und am Ende IllegalArgumentException mit Auflistung werfen.
  • Lenient: Unbekannte Tokens stillschweigend überspringen.

5. Implementierungsdetails

  • Statische Map<String, OutputImageFormat> LOOKUP mit allen erlaubten Schlüsseln (kleingeschrieben):
    • enum.name()png, jpeg, … (kleinbuchstabig)
    • extension()png, jpg, tiff, …
    • imageIoFormatName() → typ. png, jpeg, tiff
    • mimeType()image/png, image/jpeg, …
    • ZusatzSynonyme je nach Enumwert (siehe §3).
  • normalize(String):
    • trim(), toLowerCase(Locale.ROOT), führenden Punkt entfernen.
  • Deduplizierung: LinkedHashSet<OutputImageFormat> → anschließend in List.copyOf(...).

Beispiel (Strict)

"PNG, JPEG, .tif, image/webp, png, jpg" → [PNG, JPEG, TIFF, WEBP]

6. Teststrategie (JUnit 5)

Testklasse: ImageFormatParsersTest

A. Positivfälle

  1. Grundlegend: "PNG,JPEG"[PNG, JPEG] (Reihenfolge, Größe=2).
  2. Synonyme: "jpg,JPEG"[JPEG]; "tif,TIFF"[TIFF].
  3. MIMETokens: "image/jpeg, image/tiff"[JPEG, TIFF].
  4. ImageIOFormatname: "jpeg,tiff"[JPEG, TIFF].
  5. Führender Punkt: ".jpg, .tif"[JPEG, TIFF].
  6. Whitespace & Case: " PnG , jPeG "[PNG, JPEG].
  7. Dedup & Reihenfolge: "PNG, JPEG, png, jpeg"[PNG, JPEG]; "jpeg,png,jpeg"[JPEG, PNG].
  8. JP2Aliasse (falls Enum enthält): "j2k, jpeg2000, JP2"[JP2].

B. StrictFehlerfälle 9. Unbekanntes Token: "foo,PNG"IllegalArgumentException mit Hinweis auf foo. 10. Nur Unbekannt: "foo,bar"IllegalArgumentException (Liste aller unbekannten Tokens).

C. LenientVariante 11. "foo, PNG, bar"[PNG] (keine Exception).

D. Kantenfälle 12. Leer/Null: null[], " "[]. 13. Doppelte Kommas: "," bzw. "PNG,,JPEG"[PNG, JPEG].

Hilfen

  • Parametrisierte Tests (@ParameterizedTest) für SynonymMengen.
  • AssertJ/JUnit Assertions für Reihenfolge & Inhalt.

7. Integration in die bestehende Lösung

  • RESTController: allowed (CSV) → ImageFormatParsers.parseCsv(...); preferred (String) → lookup(...) + Fehler, wenn preferred ∉ allowed.
  • ImageFormatNegotiatorService: Die geparste allowedTargetsListe und preferredTarget direkt übergeben.
  • Konfiguration: Optionale DefaultListe in application.yml (z.B. images.allowed-default: "PNG,JPEG") beim Booten parsen.

8. Akzeptanzkriterien (DoD)

  • parseCsv (strict) wirft bei unbekannten Tokens eine IllegalArgumentException mit TokenListe.
  • parseCsvLenient ignoriert unbekannte Tokens und gibt bekannte in Eingabereihenfolge (dedupliziert) zurück.
  • Unterstützt Synonyme inkl. führender . und MIMEStrings.
  • Testabdeckung ≥ 90% für ImageFormatParsers (Branches: strict/lenient, Synonyme, Kantenfälle).
  • Threadsafety gegeben (LOOKUP unveränderlich, Methoden reentrant).

9. Aufgabenliste (für Codex)

  1. Klasse ImageFormatParsers anlegen, LOOKUP aus OutputImageFormat ableiten und Synonyme ergänzen.
  2. Methoden parseCsv, parseCsvLenient, lookup implementieren.
  3. Testklasse ImageFormatParsersTest mit den o.g. Fällen (JUnit 5, AssertJ optional) implementieren.
  4. BeispielSnippet in README/PRD (Verwendung aus RESTController) ergänzen.

10. BeispielSnippets

Verwendung (REST)

List<OutputImageFormat> allowed = ImageFormatParsers.parseCsv(params.getAllowed());
OutputImageFormat preferred = ImageFormatParsers.lookup(params.getPreferred())
    .orElseThrow(() -> new IllegalArgumentException("Unsupported preferred format"));
if (!allowed.contains(preferred)) {
  throw new IllegalArgumentException("Preferred must be included in allowed");
}

LenientKonfiguration

List<OutputImageFormat> defaults = ImageFormatParsers.parseCsvLenient(env.getProperty("images.allowed-default"));

11. Paket & Dateistruktur (Vorschlag)

src/main/java/com/example/images/
  OutputImageFormat.java
  ImageFormatParsers.java
  ImageConversionService.java
  ImageFormatNegotiatorService.java

src/test/java/com/example/images/
  ImageFormatParsersTest.java

12. Risiken & Gegenmaßnahmen

  • TokenDrift: Neue EnumWerte → Lookup beim Start aus Enum ableiten, ZusatzSynonyme zentral.
  • Fehlkonfiguration: preferred ∉ allowed → Validierung in REST/ServiceSchicht.
  • Uneinheitliche Schreibweisen: Abgefangen durch Normalisierung (trim, lowercase, Strip leading .).

— Ende —