7.9 KiB
PRD – CSV→OutputImageFormat Parser (Erweiterung zum „PRD – Java‑native Bildkonvertierung“)
Ziel: Robustes Parsen einer komma‑separierten Zeichenkette (CSV) in eine ordnungserhaltende, doppeltfreie List<OutputImageFormat> – ohne externe Abhängigkeiten. Unterstützt Synonyme (z. B. jpg→JPEG, tif→TIFF), führende Punkte (.jpg), MIME‑Typen (image/jpeg) und ImageIO‑Formatnamen (jpeg). Fehlerhafte Tokens führen wahlweise zu Exception oder werden „lenient“ ignoriert.
Dieses PRD ergänzt das bestehende Dokument „PRD – Java‑native Bildkonvertierung (Spring Boot, OSS)“ und liefert die Spezifikation für den CSV‑Parser, der z. B. REST‑Parameter
allowed(CSV) inList<OutputImageFormat>transformiert.
1. Scope
In Scope
- Utility‑Klasse zum Parsen einer CSV‑Liste in
List<OutputImageFormat>. - Case‑insensitive, Whitespace‑tolerant, führende
.entfernen. - Synonym‑Mapping für verbreitete Varianten (jpg/jpeg, tif/tiff, jp2/j2k/jpeg2000, webp, mime‑typen etc.).
- Dedup bei gleichzeitigem Erhalt der Eingabereihenfolge.
- Zwei Modi:
- Strict: Unbekannte Tokens →
IllegalArgumentException. - Lenient: Unbekannte Tokens werden ignoriert.
- Strict: Unbekannte Tokens →
- Öffentliche Helper‑API für Single‑Token‑Lookup.
Out of Scope
- Validierung gegen tatsächlich installierte Writer/Reader (das übernimmt die Konvertierungsschicht).
- Automatisches Nachladen von externen Plugins.
2. Nicht‑funktionale Anforderungen
- Zero‑Deps: reine JDK‑Funktionen, keine Tika/Guava etc.
- Performance: O(n) über Anzahl Tokens; Lookup über vorkompilierte
Map. - Threadsafety: statische, unveränderliche Lookup‑Map (unmodifiable); Parser‑Methoden sind reentrant und thread‑safe.
- Internationalisierung: nicht relevant (Tokens sind technische Kürzel/MIME‑Typen).
3. Domänenmodell / Abhängigkeiten
OutputImageFormat(Enum) aus der Haupt‑PRD: liefertextension,mimeType,imageIoFormatName,name().- Synonyme werden aus dem Enum abgeleitet und um gebräuchliche Aliasse ergänzt:
JPEG⇄jpg/jpeg/image/jpegTIFF⇄tif/tiff/image/tiffPNG⇄png/image/pngBMP⇄bmp/image/bmpGIF⇄gif/image/gifWEBP⇄webp/image/webpJP2⇄jp2/j2k/jpeg2000/image/jp2
Hinweis: Falls einzelne Enum‑Werte (WEBP/JP2) im Projekt deaktiviert werden, bleibt das Mapping konsistent – Keys auf nicht existierende Enum‑Werte 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);
/** Einzel‑Token‑Lookup (case‑insensitive, . entfernt, Synonyme & MIME) */
public static Optional<OutputImageFormat> lookup(String token);
}
Verhalten
nulloder leere/blank CSV → leere Liste.- Trennung:
,(Komma). Whitespace wird getrimmt, führende.entfernt (z. B..jpg). - Reihenfolge: Erstes Vorkommen gewinnt; weitere Duplikate werden ignoriert (LinkedHashSet‑Semantik).
- Strict: Tokens, die keinem Format zugeordnet werden können, sammeln und am Ende
IllegalArgumentExceptionmit Auflistung werfen. - Lenient: Unbekannte Tokens stillschweigend überspringen.
5. Implementierungsdetails
- Statische
Map<String, OutputImageFormat> LOOKUPmit allen erlaubten Schlüsseln (kleingeschrieben):enum.name()→png,jpeg, … (kleinbuchstabig)extension()→png,jpg,tiff, …imageIoFormatName()→ typ.png,jpeg,tiff…mimeType()→image/png,image/jpeg, …- Zusatz‑Synonyme je nach Enumwert (siehe §3).
normalize(String):trim(),toLowerCase(Locale.ROOT), führenden Punkt entfernen.
- Deduplizierung:
LinkedHashSet<OutputImageFormat>→ anschließend inList.copyOf(...).
Beispiel (Strict)
"PNG, JPEG, .tif, image/webp, png, jpg" → [PNG, JPEG, TIFF, WEBP]
6. Teststrategie (JUnit 5)
Testklasse: ImageFormatParsersTest
A. Positivfälle
- Grundlegend:
"PNG,JPEG"→[PNG, JPEG](Reihenfolge, Größe=2). - Synonyme:
"jpg,JPEG"→[JPEG];"tif,TIFF"→[TIFF]. - MIME‑Tokens:
"image/jpeg, image/tiff"→[JPEG, TIFF]. - ImageIO‑Formatname:
"jpeg,tiff"→[JPEG, TIFF]. - Führender Punkt:
".jpg, .tif"→[JPEG, TIFF]. - Whitespace & Case:
" PnG , jPeG "→[PNG, JPEG]. - Dedup & Reihenfolge:
"PNG, JPEG, png, jpeg"→[PNG, JPEG];"jpeg,png,jpeg"→[JPEG, PNG]. - JP2‑Aliasse (falls Enum enthält):
"j2k, jpeg2000, JP2"→[JP2].
B. Strict‑Fehlerfälle
9. Unbekanntes Token: "foo,PNG" → IllegalArgumentException mit Hinweis auf foo.
10. Nur Unbekannt: "foo,bar" → IllegalArgumentException (Liste aller unbekannten Tokens).
C. Lenient‑Variante
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 Synonym‑Mengen. - AssertJ/JUnit Assertions für Reihenfolge & Inhalt.
7. Integration in die bestehende Lösung
- REST‑Controller:
allowed(CSV) →ImageFormatParsers.parseCsv(...);preferred(String) →lookup(...)+ Fehler, wennpreferred ∉ allowed. ImageFormatNegotiatorService: Die geparsteallowedTargets‑Liste undpreferredTargetdirekt übergeben.- Konfiguration: Optionale Default‑Liste in
application.yml(z. B.images.allowed-default: "PNG,JPEG") – beim Booten parsen.
8. Akzeptanzkriterien (DoD)
parseCsv(strict) wirft bei unbekannten Tokens eineIllegalArgumentExceptionmit Token‑Liste.parseCsvLenientignoriert unbekannte Tokens und gibt bekannte in Eingabereihenfolge (dedupliziert) zurück.- Unterstützt Synonyme inkl. führender
.und MIME‑Strings. - Testabdeckung ≥ 90 % für
ImageFormatParsers(Branches: strict/lenient, Synonyme, Kantenfälle). - Thread‑safety gegeben (LOOKUP unveränderlich, Methoden reentrant).
9. Aufgabenliste (für Codex)
- Klasse
ImageFormatParsersanlegen,LOOKUPausOutputImageFormatableiten und Synonyme ergänzen. - Methoden
parseCsv,parseCsvLenient,lookupimplementieren. - Testklasse
ImageFormatParsersTestmit den o. g. Fällen (JUnit 5, AssertJ optional) implementieren. - Beispiel‑Snippet in README/PRD (Verwendung aus REST‑Controller) ergänzen.
10. Beispiel‑Snippets
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");
}
Lenient‑Konfiguration
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
- Token‑Drift: Neue Enum‑Werte → Lookup beim Start aus Enum ableiten, Zusatz‑Synonyme zentral.
- Fehlkonfiguration:
preferred ∉ allowed→ Validierung in REST/Service‑Schicht. - Uneinheitliche Schreibweisen: Abgefangen durch Normalisierung (trim, lowercase, Strip leading
.).
— Ende —