diff --git a/src/main/java/de/mediathekview/mlib/Const.java b/src/main/java/de/mediathekview/mlib/Const.java index d32cb16f..825f1efd 100644 --- a/src/main/java/de/mediathekview/mlib/Const.java +++ b/src/main/java/de/mediathekview/mlib/Const.java @@ -39,6 +39,7 @@ public class Const { public static final int ALTER_FILMLISTE_SEKUNDEN_FUER_AUTOUPDATE = 3 * 60 * 60; // beim Start des Programms wir die Liste geladen wenn sie älter ist als .. public static final String TIME_MAX_AGE_FOR_DIFF = "09"; // Uhrzeit ab der die Diffliste alle Änderungen abdeckt, die Filmliste darf also nicht vor xx erstellt worden sein public static final int MAX_BESCHREIBUNG = 400; // max länge der Beschreibung in Zeichen -> mehr gibts aber jetzt nicht mehr! + public static final int MAX_BESCHREIBUNG_MARGIN_LENGTH = 40; public static final String DREISAT = "3Sat"; public static final String ARD = "ARD"; diff --git a/src/main/java/de/mediathekview/mlib/daten/DatenFilm.java b/src/main/java/de/mediathekview/mlib/daten/DatenFilm.java index 8a0fc222..c253365c 100644 --- a/src/main/java/de/mediathekview/mlib/daten/DatenFilm.java +++ b/src/main/java/de/mediathekview/mlib/daten/DatenFilm.java @@ -201,7 +201,7 @@ public static String cleanDescription(String description) { description = description.replace("\\\"", "\""); } if (description.length() > Const.MAX_BESCHREIBUNG) { - return description.substring(0, Const.MAX_BESCHREIBUNG) + "\n....."; + return SentenceShortener.shorten(description, Const.MAX_BESCHREIBUNG, Const.MAX_BESCHREIBUNG_MARGIN_LENGTH); } else { return description; } diff --git a/src/main/java/de/mediathekview/mlib/tool/SentenceShortener.java b/src/main/java/de/mediathekview/mlib/tool/SentenceShortener.java new file mode 100644 index 00000000..1ecef80e --- /dev/null +++ b/src/main/java/de/mediathekview/mlib/tool/SentenceShortener.java @@ -0,0 +1,105 @@ +package de.mediathekview.mlib.tool; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public final class SentenceShortener { + private static final Pattern WHITESPACE_REGEX = Pattern.compile("\\s+"); + private static final Pattern SENTENCE_BOUNDARY_REGEX = Pattern.compile("(?<=[.!?])\\s+"); + private static final String SHORTENING_SUFFIX = "..."; + + private SentenceShortener() { + } + + /** + * Shortens text to a target length while preserving complete sentences. + * + *

Whitespace is normalized before shortening. If the text is already within the target + * length, the normalized text is returned unchanged. If adding the next sentence would exceed + * {@code targetLength + marginLength}, that sentence is omitted and {@code ...} is appended to + * the returned text. If the first sentence alone is already longer than the limit, it is kept + * whole. + * + * @param text the text to shorten; may be {@code null} + * @param targetLength the preferred target length; must be positive + * @param marginLength the allowed soft overflow beyond the target; must not be negative + * @return the shortened text, or {@code null} if the input is {@code null} or blank + * @throws IllegalArgumentException if {@code targetLength <= 0} or {@code marginLength < 0} + */ + public static String shorten(String text, int targetLength, int marginLength) { + if (targetLength <= 0) { + throw new IllegalArgumentException("targetLength must be positive"); + } + if (marginLength < 0) { + throw new IllegalArgumentException("marginLength must not be negative"); + } + + String normalized = normalize(text); + if (normalized == null || normalized.length() <= targetLength) { + return normalized; + } + + int softLimit = targetLength + marginLength; + String[] rawSentences = SENTENCE_BOUNDARY_REGEX.split(normalized); + List sentences = new ArrayList<>(); + for (String rawSentence : rawSentences) { + String sentence = rawSentence.trim(); + if (!sentence.isEmpty()) { + sentences.add(sentence); + } + } + if (sentences.isEmpty()) { + return normalized; + } + + List selectedSentences = new ArrayList<>(); + int currentLength = 0; + for (String sentence : sentences) { + int candidateLength = selectedSentences.isEmpty() + ? sentence.length() + : currentLength + 1 + sentence.length(); + if (candidateLength > softLimit) { + break; + } + selectedSentences.add(sentence); + currentLength = candidateLength; + } + + if (!selectedSentences.isEmpty()) { + String shortened = String.join(" ", selectedSentences); + return selectedSentences.size() < sentences.size() + ? shortened + SHORTENING_SUFFIX + : shortened; + } + return sentences.size() > 1 + ? sentences.get(0) + SHORTENING_SUFFIX + : sentences.get(0); + } + + /** + * Shortens text to a target length without any soft overflow margin. + * + * @param text the text to shorten; may be {@code null} + * @param targetLength the preferred target length; must be positive + * @return the shortened text, or {@code null} if the input is {@code null} or blank + * @throws IllegalArgumentException if {@code targetLength <= 0} + */ + public static String shorten(String text, int targetLength) { + return shorten(text, targetLength, 0); + } + + /** + * Normalizes whitespace and trims a text value. + * + * @param text the text to normalize; may be {@code null} + * @return the normalized text, or {@code null} if the input is {@code null} or blank + */ + public static String normalize(String text) { + if (text == null) { + return null; + } + String normalized = WHITESPACE_REGEX.matcher(text).replaceAll(" ").trim(); + return normalized.isEmpty() ? null : normalized; + } +}