Rufnummernblöcke in der Telephone Bibliothek

I will not be at the mercy of the telephone!

C. S. Lewis

Nachdem in den letzten Beiträgen zur Bibliothek Telephone, die Bean Validierung und der Jackson Support hinzugefügt wurden, geht es in diesem Beitrag um eine fachliche Ergänzung.

Bisher behandelt die Bibliothek nur nationale und internationale Telefonnummern. In diesem Beitrag kommen Rufnummernblöcke hinzu. Ein Rufnummernblock ist eine Liste von aufeinanderfolgende Rufnummern. Im folgenden Beispiel sind 10 Rufnummern dargestellt, die alle die gemeinsame Basisrufnummer

1122
1122 besitzen und die Durchwahlen
0
0 bis
9
9.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
+49 5223 11220
+49 5223 11221
+49 5223 11222
+49 5223 11223
+49 5223 11224
+49 5223 11225
+49 5223 11226
+49 5223 11227
+49 5223 11228
+49 5223 11229
+49 5223 11220 +49 5223 11221 +49 5223 11222 +49 5223 11223 +49 5223 11224 +49 5223 11225 +49 5223 11226 +49 5223 11227 +49 5223 11228 +49 5223 11229
+49 5223 11220
+49 5223 11221
+49 5223 11222
+49 5223 11223
+49 5223 11224
+49 5223 11225
+49 5223 11226
+49 5223 11227
+49 5223 11228
+49 5223 11229

Um einen solchen Rufnummernblock anzugeben ist die Liste aller Durchwahlen keine besonders kluge Idee, da es auch Rufnummernblöcke mit 100 Durchwahlen geben kann. Eine einfachere Darstellung verwendet die Vorwahl, die Basisrufnummer und die Menge der Durchwahlen in Form eines Intervalls oder eines Musters. Im Folgenden vier Darstellungsvarianten für einstellige Durchwahlen und vier Varianten für zweistellige Durchwahlen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
+49 5223 1122 0-9
+49 5223 1122 [0-9]
+49 5223 1122 X
+49 5223 1122 [X]
+49 5223 1122 00-99
+49 5223 1122 [00-99]
+49 5223 1122 XX
+49 5223 1122 [XX]
+49 5223 1122 0-9 +49 5223 1122 [0-9] +49 5223 1122 X +49 5223 1122 [X] +49 5223 1122 00-99 +49 5223 1122 [00-99] +49 5223 1122 XX +49 5223 1122 [XX]
+49 5223 1122 0-9
+49 5223 1122 [0-9]
+49 5223 1122 X
+49 5223 1122 [X]

+49 5223 1122 00-99
+49 5223 1122 [00-99]
+49 5223 1122 XX
+49 5223 1122 [XX]

Die Darstellung mit eckigen Klammern dient der besseren Lesbarkeit und hat ansonsten keine weitere Bewandtnis.

Wie bei den Klassen

InternationalPhoneNumber
InternationalPhoneNumber und
NationalPhoneNumber
NationalPhoneNumber kann die Darstellung der PhoneNumberBlock Klasse für das Parsen und Formatieren angepasst werden.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
assertEquals("+49 5223 1122 [00-99]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] ZZZ")));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] zzz")));
assertEquals("+49 5223 1122 [00-99]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] BBB")));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] bbb")));
assertEquals("+49 5223 1122 [XX]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] XXX")));
assertEquals("+49 5223 1122 XX", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] xxx")));
assertEquals("+49 5223 1122 [00-99]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] ZZZ"))); assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] zzz"))); assertEquals("+49 5223 1122 [00-99]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] BBB"))); assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] bbb"))); assertEquals("+49 5223 1122 [XX]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] XXX"))); assertEquals("+49 5223 1122 XX", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] xxx")));
assertEquals("+49 5223 1122 [00-99]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] ZZZ")));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] zzz")));
assertEquals("+49 5223 1122 [00-99]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] BBB")));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] bbb")));
assertEquals("+49 5223 1122 [XX]", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] XXX")));
assertEquals("+49 5223 1122 XX", phoneNumberBlock.format(ofPattern("+ccc nnn sss[-eee] xxx")));

Zu den bisherigen Pattern

+
+,
i
i,
I
I,
c
c,
a
a,
n
n,
s
s und
e
e kommen noch die Pattern
b
b,
B
B,
x
x,
X
X,
z
z und
Z
Z hinzu. Die Pattern
b
b und
B
B stellen die Durchwahlen als Intervall ohne bzw. mit eckigen Klammern dar. Die Pattern
x
x und
X
X stellen die Durchwahlen als Muster ohne bzw. mit eckigen Klammern dar. Als letztes stellen die Pattern
z
z und
Z
Z die Durchwahlen als Intervall ohne bzw. mit eckigen Klammern dar. Der Unterschied zu den vorherigen Pattern ist die Möglichkeit Eingaben zu parsen, die entweder Intervall oder Muster sind. Alle anderen Pattern parsen nur das Format, dass sie auch darstellen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(PhoneNumberFormatter.INTERNATIONAL_BLOCK));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.toString());
assertEquals("05223 1122 00-99", phoneNumberBlock.format(PhoneNumberFormatter.NATIONAL_BLOCK));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(PhoneNumberFormatter.INTERNATIONAL_BLOCK)); assertEquals("+49 5223 1122 00-99", phoneNumberBlock.toString()); assertEquals("05223 1122 00-99", phoneNumberBlock.format(PhoneNumberFormatter.NATIONAL_BLOCK));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.format(PhoneNumberFormatter.INTERNATIONAL_BLOCK));
assertEquals("+49 5223 1122 00-99", phoneNumberBlock.toString());
assertEquals("05223 1122 00-99", phoneNumberBlock.format(PhoneNumberFormatter.NATIONAL_BLOCK));

Für die PhoneNumberBlock Klasse existieren zwei vordefinierte Formatter. Der

PhoneNumberFormatter.INTERNATIONAL_BLOCK
PhoneNumberFormatter.INTERNATIONAL_BLOCK stellt die Blöcke mit internationaler Vorwahl dar und
PhoneNumberFormatter.NATIONAL_BLOCK
PhoneNumberFormatter.NATIONAL_BLOCK stellt sie mit nationaler Vorwahl dar.

Zur Verarbeitung der neuen Pattern wird die zentrale Switch-Anweisung um die zusätzlichen Cases erweitert. Der Unterschied zwischen den Anweisungen mit groß- bzw. kleingeschriebenen Buchstaben sind die

LiteralPart
LiteralPart Instanzen für die eckigen Klammern.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case 'b':
active.parts.add(new BlockPart(6, length));
active.parts.add(new LiteralPart('-'));
active.parts.add(new BlockPart(7, length));
break;
case 'B':
active.parts.add(new LiteralPart('['));
active.parts.add(new BlockPart(6, length));
active.parts.add(new LiteralPart('-'));
active.parts.add(new BlockPart(7, length));
active.parts.add(new LiteralPart(']'));
break;
case 'x':
active.parts.add(new BlockPatternPart(length));
break;
case 'X':
active.parts.add(new LiteralPart('['));
active.parts.add(new BlockPatternPart(length));
active.parts.add(new LiteralPart(']'));
break;
case 'z':
active.parts.add(new BlockPatternOrBlockPart(length));
break;
case 'Z':
active.parts.add(new LiteralPart('['));
active.parts.add(new BlockPatternOrBlockPart(length));
active.parts.add(new LiteralPart(']'));
break;
case 'b': active.parts.add(new BlockPart(6, length)); active.parts.add(new LiteralPart('-')); active.parts.add(new BlockPart(7, length)); break; case 'B': active.parts.add(new LiteralPart('[')); active.parts.add(new BlockPart(6, length)); active.parts.add(new LiteralPart('-')); active.parts.add(new BlockPart(7, length)); active.parts.add(new LiteralPart(']')); break; case 'x': active.parts.add(new BlockPatternPart(length)); break; case 'X': active.parts.add(new LiteralPart('[')); active.parts.add(new BlockPatternPart(length)); active.parts.add(new LiteralPart(']')); break; case 'z': active.parts.add(new BlockPatternOrBlockPart(length)); break; case 'Z': active.parts.add(new LiteralPart('[')); active.parts.add(new BlockPatternOrBlockPart(length)); active.parts.add(new LiteralPart(']')); break;
case 'b':
  active.parts.add(new BlockPart(6, length));
  active.parts.add(new LiteralPart('-'));
  active.parts.add(new BlockPart(7, length));
  break;
case 'B':
  active.parts.add(new LiteralPart('['));
  active.parts.add(new BlockPart(6, length));
  active.parts.add(new LiteralPart('-'));
  active.parts.add(new BlockPart(7, length));
  active.parts.add(new LiteralPart(']'));
  break;
case 'x':
  active.parts.add(new BlockPatternPart(length));
  break;
case 'X':
  active.parts.add(new LiteralPart('['));
  active.parts.add(new BlockPatternPart(length));
  active.parts.add(new LiteralPart(']'));
  break;
case 'z':
  active.parts.add(new BlockPatternOrBlockPart(length));
  break;
case 'Z':
  active.parts.add(new LiteralPart('['));
  active.parts.add(new BlockPatternOrBlockPart(length));
  active.parts.add(new LiteralPart(']'));
  break;

Die

BlockPart
BlockPart,
BlockPatternPart
BlockPatternPart und
BlockPatternOrBlockPart
BlockPatternOrBlockPart Instanzen bekommen jeweils die maximal zulässige Länge der Eingaben übergeben. Dies unterscheide diese
PhoneNumberPart
PhoneNumberPart Implementierungen von den bisherigen, bei denen die Länge nicht speziell geprüft wurde.

Der letzte interessante Teil der Implementierung ist die

parseBlock
parseBlock Methode. Sie wertet die einzelnen
PhoneNumberPart
PhoneNumberPart Instanzen aus, aus denen das aktuelle Format aufgebaut ist.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public PhoneNumberBlock parseBlock(String text) {
PhoneNumberFormatterContext context = new PhoneNumberFormatterContext(this);
int pos = 0;
for (PhoneNumberPart part : partList) {
pos = part.parse(context, text, pos);
if (pos < 0) {
throw new IllegalArgumentException("parse exception: " + pos + " " + text);
}
}
NationalPhoneNumber nationalPhoneNumber;
CharSequence[] parts = context.parts;
if (parts[5] != null) {
nationalPhoneNumber = NationalPhoneNumber
.of(Objects.toString(parts[2], "0"), String.valueOf(parts[3]), String.valueOf(parts[4]), String.valueOf(parts[5]));
} else {
nationalPhoneNumber = NationalPhoneNumber.of(Objects.toString(parts[2], "0"), String.valueOf(parts[3]), String.valueOf(parts[4]));
}
InternationalPhoneNumber internationalPhoneNumber = InternationalPhoneNumber
.of(Objects.toString(parts[0], "00"), Objects.toString(parts[1], "49"), nationalPhoneNumber);
String blockStart = Objects.toString(parts[6], null);
String blockEnd = Objects.toString(parts[7], null);
if (parts[8] != null) {
blockStart = blockStart == null ? "0".repeat(parts[8].length()) : blockStart;
blockEnd = blockEnd == null ? "9".repeat(parts[8].length()) : blockEnd;
}
return PhoneNumberBlock.of(internationalPhoneNumber, blockStart, blockEnd);
}
public PhoneNumberBlock parseBlock(String text) { PhoneNumberFormatterContext context = new PhoneNumberFormatterContext(this); int pos = 0; for (PhoneNumberPart part : partList) { pos = part.parse(context, text, pos); if (pos < 0) { throw new IllegalArgumentException("parse exception: " + pos + " " + text); } } NationalPhoneNumber nationalPhoneNumber; CharSequence[] parts = context.parts; if (parts[5] != null) { nationalPhoneNumber = NationalPhoneNumber .of(Objects.toString(parts[2], "0"), String.valueOf(parts[3]), String.valueOf(parts[4]), String.valueOf(parts[5])); } else { nationalPhoneNumber = NationalPhoneNumber.of(Objects.toString(parts[2], "0"), String.valueOf(parts[3]), String.valueOf(parts[4])); } InternationalPhoneNumber internationalPhoneNumber = InternationalPhoneNumber .of(Objects.toString(parts[0], "00"), Objects.toString(parts[1], "49"), nationalPhoneNumber); String blockStart = Objects.toString(parts[6], null); String blockEnd = Objects.toString(parts[7], null); if (parts[8] != null) { blockStart = blockStart == null ? "0".repeat(parts[8].length()) : blockStart; blockEnd = blockEnd == null ? "9".repeat(parts[8].length()) : blockEnd; } return PhoneNumberBlock.of(internationalPhoneNumber, blockStart, blockEnd); }
public PhoneNumberBlock parseBlock(String text) {
    PhoneNumberFormatterContext context = new PhoneNumberFormatterContext(this);
    int pos = 0;
    for (PhoneNumberPart part : partList) {
        pos = part.parse(context, text, pos);
        if (pos < 0) {
            throw new IllegalArgumentException("parse exception: " + pos + " " + text);
        }
    }
    NationalPhoneNumber nationalPhoneNumber;
    CharSequence[] parts = context.parts;
    if (parts[5] != null) {
        nationalPhoneNumber = NationalPhoneNumber
                .of(Objects.toString(parts[2], "0"), String.valueOf(parts[3]), String.valueOf(parts[4]), String.valueOf(parts[5]));
    } else {
        nationalPhoneNumber = NationalPhoneNumber.of(Objects.toString(parts[2], "0"), String.valueOf(parts[3]), String.valueOf(parts[4]));
    }
    InternationalPhoneNumber internationalPhoneNumber = InternationalPhoneNumber
            .of(Objects.toString(parts[0], "00"), Objects.toString(parts[1], "49"), nationalPhoneNumber);
    String blockStart = Objects.toString(parts[6], null);
    String blockEnd = Objects.toString(parts[7], null);
    if (parts[8] != null) {
        blockStart = blockStart == null ? "0".repeat(parts[8].length()) : blockStart;
        blockEnd = blockEnd == null ? "9".repeat(parts[8].length()) : blockEnd;
    }
    return PhoneNumberBlock.of(internationalPhoneNumber, blockStart, blockEnd);
}

Nachdem die Basis-Telefonnummer erstellt wurde, werden am Ende der Methode die Intervallgrenzen des Blocks bestimmt. Sind die Felder

part[6]
part[6] und
part[7]
part[7] ungleich
null
null, dann sind
blockStart
blockStart und
blockEnd
blockEnd gesetzt, ansonsten werden die Intervallgrenzen mit Hilfe von Feld part[8] gesetzt. Dieses Feld enthält ein mögliches Blockpattern und bestimmt mit seiner Länge die
0
0 bzw.
9
9 Sequenzen für
blockStart
blockStart und
blockEnd
blockEnd.

Am Ende wird nun die Factory-Methode für

PhoneNumberBlock
PhoneNumberBlock Methoden aufgerufen und bei validen Parametern ein neuer Rufnummernblock erzeugt.

1 thought on “Rufnummernblöcke in der Telephone Bibliothek”

Leave a Comment