Deutsche Drama Numbers validieren

Ja, Rosi hat ein Telefon
Auch ich hab ihre Nummer schon
Unter zwounddreißig, sechzehn, acht
Herrscht Konjunktur die ganze Nacht

Spider Murphy Gang

Die deutsche Bundesnetzagentur stellt eine Reihe Drama Numbers für Medienproduktionen bereit. Diese Telefonnummern sind für die Nutzung in Fernsehen, Radio, Broschüren und anderen Medien gedacht, in denen keine tatsächlich genutzten Telefonnummern auftauchen sollten. Der Spider Murphy Gang mag man die Wahl der Telefonnummer +40 69 32168 verzeihen, denn die bestehenden Drama Numbers passen alle nicht in das Versmaß.

Eine Drama Number in einem Beispiel zu nutzen ist eine freundliche Geste um niemanden Anrufe durch Witzbolde aufzubürden, andererseits sollte niemand eine Drama Number in ein Pflichtfeld der eigenen Web-Anwendung eingeben können. Daher sollte eine Validierung der Telefonnummer auch die deutschen Drama Numbers erkennen können. Die Telephone Bibliothek bietet solch eine Validierung über die @GermanDramaNumber Annotation.

@GermanDramaNumber
InternationalPhoneNumber international;

@GermanDramaNumber
NationalPhoneNumber national;

Bei beiden Telefonnummern wird gegen eine Liste der deutschen Drama Numbers getestet. Bei der InternationalPhoneNumber wird zusätzlich die internationale Vorwahl geprüft. Bei der NationalPhoneNumber existiert keine internationale Vorwahl und der Anwendungsfall muss sicherstellen, dass nur deutsche Telefonnummern verarbeitet werden.

In dieser Form wird aber nur positiv validiert, dass eine Telefonnummer eine Drama Number ist. der übliche Anwendungsfall sieht aber anders aus.

@GermanDestinationCode
@GermanDramaNumber(allowed=false)
InternationalPhoneNumber international;

@GermanDramaNumber(allowed=false)
NationalPhoneNumber national;

In diesem Beispiel sind die Drama Numbers über das Attribut allowed verboten. Damit aber bei der InternationalPhoneNumber nur deutsche Telefonnummern betrachtet werden, wurde noch die Annotation @GermanDestinationCode hinzugefügt.

Die Validatoren für die Drama Numbers verwenden alle die statische Methode GermanDramaNumbers#isValid, die anhand einer InternationalPhoneNumber bestimmt, ob diese zu den Drama Numbers gehört.

public class NationalPhoneNumberGermanDramaValidator extends AbstractGermanDramaValidator<NationalPhoneNumber> {
    @Override
    public boolean isValid(NationalPhoneNumber phoneNumber, ConstraintValidatorContext constraintValidatorContext) {
        if (phoneNumber == null) {
            return true;
        }
        return allowed == GermanDramaNumbers.isValid(InternationalPhoneNumber.of("49", phoneNumber));
    }
}

Der NationalPhoneNumberGermanDramaValidator validiert eine NationalPhoneNumber, indem er eine InternationalPhoneNumber mit der internationalen Vorwahl für Deutschland erzeugt. Dies ist keine teure Operation, weil eine InternationalPhoneNumber nur eine NationalPhoneNumber mit internationaler Vorwahl ist.

Die Methode GermanDramaNumbers#isValid greift auf eine Map zurück, in der für jede Vorwahl eine Liste von PhoneNumberAccessor Instanzen hinterlegt sind. Implementierungen von PhoneNumberAccessor sind die Klassen NationalPhoneNumber, InternationalPhoneNumber und PhoneNumberBlock. Zweck des PhoneNumberAccessor an dieser Stelle ist die Verwendung seiner Methode matches. Bei einer NationalPhoneNumber, InternationalPhoneNumber wird damit die Gleichheit geprüft und bei PhoneNumberBlock, ob die Telefonnummer innerhalb der Rufnummernblocks zu finden ist.

public final class GermanDramaNumbers {

    private static final Map<String, List<PhoneNumberAccessor>> numbers = Map.ofEntries(
            Map.entry("30", List.of(trunk("30", "23125", "000", "999"))),
            Map.entry("69", List.of(trunk("69", "90009", "000", "999"))),
            Map.entry("40", List.of(trunk("40", "66969", "000", "999"))),
            Map.entry("221", List.of(trunk("221", "4710", "000", "999"))),
            Map.entry("89", List.of(trunk("89", "99998", "000", "999"))),
            Map.entry("152", numbers("152", "28817386", "28895456", "54599371")),
            Map.entry("171", List.of(trunk("171", "39200", "00", "99"))),
            Map.entry("176", List.of(trunk("176", "040690", "00", "99"))),
            Map.entry("172", numbers("172", "9925904", "9968532", "9973185", "9973186", "9980752")),
            Map.entry("174", numbers("174", "9091317", "9464308"))
    );

    public static boolean isValid(InternationalPhoneNumber phoneNumber) {
        return Stream.of(numbers.get(phoneNumber.getNationalDestinationCode())).filter(Objects::nonNull).flatMap(List::stream).anyMatch(x -> x.matches(phoneNumber));
    }

    private static PhoneNumberAccessor trunk(String nationalDestinationCode, String subscriberNumber, String blockStart, String blockEnd) {
        return PhoneNumberBlock.of(InternationalPhoneNumber.of("49", "0", nationalDestinationCode, subscriberNumber), blockStart, blockEnd);
    }

    private static List<PhoneNumberAccessor> numbers(String nationalDestinationCode, String... subscriberNumbers) {
        return Stream.of(subscriberNumbers).map(sn -> InternationalPhoneNumber.of("49", "0", nationalDestinationCode, sn))
                .map(PhoneNumberAccessor.class::cast).toList();
    }
}

Mit Hilfe der PhoneNumberBlock Instanzen kann die Map mit ihren 5210 Telefonnummern kompakt repräsentiert werden.

Die Unit Tests der Bibliothek haben auch durch die Drama Numbers gewonnen. Bislang wurde häufig die Telefonnummer +49 521 112233 verwendet, die unvorsichtige Zeitgenossen vielleicht an den falschen Adressaten durchgestellt hätte.

Leave a Comment