Java tutorial
/* * Copyright (c) 2017 by Oliver Boehm * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * (c)reated 04.09.2017 by oboehm (ob@oasd.de) */ package de.jfachwert.net; import de.jfachwert.Text; import de.jfachwert.pruefung.TelefonnummerValidator; import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.StringUtils; import java.net.URI; import java.util.Optional; import java.util.WeakHashMap; /** * Die Klasse Telefonnummer steht fuer alle Arten von Rufnummern im * Telefon-Netz wie Fesetnetznummer, Faxnummer oder Mobilfunknummer. * Ueblicherweise bestehen Telefonnummern aus * <ul> * <li>Laenderkennzahl (LKz),</li> * <li>Ortsnetzkennzahl (ONKz) bzw. die eigentliche Vorwahl</li> * <li>Teilnehmerrufnummer (RufNr),</li> * <li>Durchwahl (optional).</li> * </ul> * Die Telefonnummer +49 30 12345-67 hat die Laenderkennzahl 49 (Deutschland), * die Vorwahl 030 (Berlin), die Teilnehmerrufnummer 12345 und die Durchwahl * 67. * <p> * Frueher waren Telefon-Netz und Computer-Netzwerke strikt getrennt. * Inwischen wachsen diese beiden Netze immer mehr zusammen und unterscheiden * sich nur noch durch das Netzwerkprotokoll. Deswegen ist diese Klasse * im gleichen Package 'net' wie die EMailAdresse zu finden und nicht in * 'comm' (fuer Kommunikation), wie urspruenglich geplant. * </p> * * @author oboehm * @since 0.5 (04.09.2017) */ public class Telefonnummer extends Text { private static final TelefonnummerValidator DEFAULT_VALIDATOR = new TelefonnummerValidator(); private static final WeakHashMap<String, Telefonnummer> WEAK_CACHE = new WeakHashMap<>(); /** * Legt eine neue Instanz einer Telefonnummer an, sofern die uebergebene * Nummer valide ist. * * @param nummer z.B. "+49 (0)30 12345-67" */ public Telefonnummer(String nummer) { this(nummer, DEFAULT_VALIDATOR); } /** * Eine Telefonnummer lasesst sich auch ueber eine URI kreieren. Der * RFC 3966 schlaegt dabei "tel:" als Schema vor. * * @param uri z.B. "tel:+49-30-1234567" */ public Telefonnummer(URI uri) { this(uri.getSchemeSpecificPart()); } /** * Legt eine Instanz einer Telefonnummer an. Dieser Konstruktor ist * hauptsaechlich fuer abgeleitete Klassen gedacht, die ihre eigene * Validierung mit einbringen wollen oder aus Performance-Gruenden * abschalten wollen. * * @param nummer eine gueltige Telefonnummer, z.B. "+49 30 12345-67" * @param validator SimpleValidator zur Adressen-Validierung */ public Telefonnummer(String nummer, TelefonnummerValidator validator) { super(normalize(validator.verify(nummer))); } /** * Liefert eine Telefonnummer zurueck. * * @param nummer eine gueltige Telefonnummer, z.B. "+49 30 12345-67" * @return Telefonnummer */ public static Telefonnummer of(String nummer) { return WEAK_CACHE.computeIfAbsent(nummer, Telefonnummer::new); } /** * Liefert die Telefonnummer ohne Laenderkennzahl, dafuer mit Vorwahl * inklusive fuehrender Null. * * @return z.B. 0811/32168 */ public Telefonnummer getInlandsnummer() { if (getLaenderkennzahl().isPresent()) { String nummer = this.getCode().substring(3).trim(); if (StringUtils.startsWithAny(nummer, "1", "2", "3", "4", "5", "6", "7", "8", "9")) { nummer = "0" + nummer; } return new Telefonnummer(nummer); } else { return this; } } /** * Die Laenderkennzahl (LKZ) ist die Vorwahl, die man fuer Telefonate ins * Ausland waehlen muss. Fuer Deutschland ist die LKZ "+49*, d.h. wenn * man von Oesterreich nach Deutschland waehlen muss, muss man "0049" * vorwaehlen. * <p> * Da die Laenderkennzahl optional ist, wird sie als {@link Optional} * zurueckgegeben. * </p> * * @return z.B. "+49" */ public Optional<String> getLaenderkennzahl() { String laenderkennzahl = this.getCode().substring(0, 3); if (laenderkennzahl.startsWith("+")) { return Optional.of(laenderkennzahl); } else { return Optional.empty(); } } /** * Liefert die Vorwahl oder auch Ortskennzahl (ONKz). * * @return z.B. "0711" fuer Stuttgart */ public String getVorwahl() { String[] parts = this.getCode().trim().split("[ /-]|(\\(0\\))"); String vorwahl = parts[0]; if (vorwahl.startsWith("+")) { vorwahl = StringUtils.isBlank(parts[1]) ? parts[2] : parts[1]; } vorwahl = RegExUtils.removeAll(vorwahl, "[ \t+-/(\\(\\))]"); if (vorwahl.startsWith("0")) { return vorwahl; } return "0" + vorwahl; } /** * Liefert die Nummer der Ortsvermittlungsstelle, d.h. die Telefonnummer * ohne Vorwahl und Laenderkennzahl. * * @return z.B. "32168" */ public Telefonnummer getRufnummer() { String inlandsnummer = RegExUtils.replaceAll(this.getInlandsnummer().toString(), "[ /]+", " "); return new Telefonnummer(StringUtils.substringAfter(inlandsnummer, " ").replaceAll(" ", "")); } /** * Wenn zwei Telefonnummern gleich sind, muessen sie auch den gleichen * Hashcode liefern. * * @return Hashcode, der nur aus den Ziffern ermittelt wird */ @Override public int hashCode() { return toShortString().hashCode(); } /** * Beim Vergleich zweier Telefonnummern spielen Trennzeichen keine Rolle. * Hier sind nur die Nummern relevant. * * @param obj zu vergleichende Telefonnummer * @return true bei Gleichheit */ @Override public boolean equals(Object obj) { if (!(obj instanceof Telefonnummer)) { return false; } Telefonnummer other = (Telefonnummer) obj; return this.toShortString().equals(other.toShortString()); } /** * Stellt eine Telefonnummer in verkuerzter Schreibweise ohne Leerzeichen * und Trennzeichen dar. * * @return z.B. "+49301234567" */ public String toShortString() { return RegExUtils.removeAll(getCode(), "[ \t+-/]|(\\(0\\))"); } /** * Gibt den String nach DIN 5008 aus. Die Nummern werden dabei * funktionsbezogen durch Leerzeichen und die Durchwahl mit Bindestrich * abgetrennt. * * @return z.B. "+49 30 12345-67" bzw. "030 12345-67" */ public String toDinString() { Optional<String> laenderkennzahl = getLaenderkennzahl(); return laenderkennzahl.map(s -> s + " " + getVorwahl().substring(1) + " " + getRufnummer()) .orElseGet(() -> getVorwahl() + " " + getRufnummer()); } /** * Die "E.123" ist eine Empfehlung der Internationalen Fernmeldeunion. * Dabei werden die einzelnen Bestandteile (Laenderkennzeichen, Vorwahl * und Rufnummer) durch Leerzeichen gruppiert. * * @return z.B. "+49 30 12345 67" oder "(030) 12345 67" (national) */ public String toE123String() { if (getLaenderkennzahl().isPresent()) { return toDinString().replace('-', ' '); } else { return "(" + getVorwahl() + ") " + getRufnummer().toString().replace('-', ' '); } } /** * Nach RFC 3966 wird die Telefonnummer wie E.123 dargestellt, jedoch mit * Bindestrich statt Leerzeichen. * * @return z.B. "tel:+49-30-12345-67" */ public URI toURI() { return URI.create("tel:" + toDinString().replace(' ', '-')); } private static String normalize(String nummer) { String normalized = nummer.trim(); if (normalized.startsWith("(0)")) { normalized = "0" + normalized.substring(3); } return normalized; } }