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 21.02.2017 by oboehm (ob@oasd.de) */ package de.jfachwert.post; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import de.jfachwert.Fachwert; import de.jfachwert.pruefung.exception.LocalizedIllegalArgumentException; import de.jfachwert.util.ToFachwertSerializer; import de.jfachwert.pruefung.exception.InvalidValueException; import org.apache.commons.lang3.StringUtils; import javax.validation.ValidationException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * Bei einer Adresse kann es sich um eine Wohnungsadresse oder Gebaeudeadresse * handeln. Sie besteht aus Ort, Strasse und Hausnummer. Sie unterscheidet sich * insofern von einer Anschrift, da der Name nicht Bestandteil der Adresse ist. * * @author oboehm * @since 0.2 (02.05.2017) */ @JsonSerialize(using = ToFachwertSerializer.class) public class Adresse implements Fachwert { private static final Logger LOG = Logger.getLogger(Adresse.class.getName()); private final Ort ort; private final String strasse; private final String hausnummer; /** * Zerlegt die uebergebene Adresse in ihre Einzelteile und baut daraus die * Adresse zusammen. Folgende Heuristiken werden fuer die Zerlegung * herangezogen: * <ul> * <li>Reihenfolge kann Ort, Strasse oder Strasse, Ort sein</li> * <li>Ort / Strasse werden durch Komma oder Zeilenvorschub getrennt</li> * <li>vor dem Ort steht die PLZ</li> * </ul> * * @param adresse z.B. "12345 Entenhausen, Gansstr. 23" */ public Adresse(String adresse) { this(split(adresse)); } private Adresse(String[] adresse) { this(new Ort(adresse[0]), adresse[1], adresse[2]); } /** * Erzeugt eine neue Adresse. * * @param ort the ort * @param strasse the strasse * @param hausnummer the hausnummer */ public Adresse(Ort ort, String strasse, String hausnummer) { this.ort = ort; this.strasse = strasse; this.hausnummer = hausnummer; validate(ort, strasse, hausnummer); } /** * Erzeugt eine neue Adresse. * * @param map mit den einzelnen Elementen fuer "plz", "ortsname", * "strasse" und "hausnummer". */ @JsonCreator public Adresse(Map<String, String> map) { this(new Ort(PLZ.of(map.get("plz")), map.get("ortsname")), map.get("strasse"), map.get("hausnummer")); } /** * Zerlegt die uebergebene Adresse in ihre Einzelteile und baut daraus die * Adresse zusammen. Folgende Heuristiken werden fuer die Zerlegung * herangezogen: * <ul> * <li>Reihenfolge kann Ort, Strasse oder Strasse, Ort sein</li> * <li>Ort / Strasse werden durch Komma oder Zeilenvorschub getrennt</li> * <li>vor dem Ort steht die PLZ</li> * </ul> * * @param adresse z.B. "12345 Entenhausen, Gansstr. 23" * @return Adresse */ public static Adresse of(String adresse) { return new Adresse(adresse); } /** * Liefert eine Adresse mit den uebergebenen Parametern. * * @param ort the ort * @param strasse the strasse * @param hausnummer the hausnummer * @return Adresse */ public static Adresse of(Ort ort, String strasse, String hausnummer) { return new Adresse(ort, strasse, hausnummer); } /** * Validiert die uebergebene Adresse auf moegliche Fehler. * * @param ort der Ort * @param strasse die Strasse * @param hausnummer die Hausnummer */ public static void validate(Ort ort, String strasse, String hausnummer) { if (!ort.getPLZ().isPresent()) { throw new InvalidValueException(ort, "postal_code"); } if (StringUtils.isBlank(strasse)) { throw new InvalidValueException(strasse, "street"); } if (StringUtils.isBlank(hausnummer)) { throw new InvalidValueException(hausnummer, "house_number"); } if (Character.isDigit(strasse.trim().charAt(0)) && (Character.isLetter(hausnummer.trim().charAt(0))) && (strasse.length() < hausnummer.length())) { throw new InvalidValueException(strasse + " " + hausnummer, "values_exchanged"); } } /** * Zerlegt die uebergebene Adresse in ihre Einzelteile und validiert sie. * Folgende Heuristiken werden fuer die Zerlegung herangezogen: * <ul> * <li>Reihenfolge kann Ort, Strasse oder Strasse, Ort sein</li> * <li>Ort / Strasse werden durch Komma oder Zeilenvorschub getrennt</li> * <li>vor dem Ort steht die PLZ</li> * </ul> * * @param adresse z.B. "12345 Entenhausen, Gansstr. 23" */ public static void validate(String adresse) { String[] splitted = split(adresse); Ort ort = new Ort(splitted[0]); validate(ort, splitted[1], splitted[2]); } private static String[] split(String adresse) { String[] lines = StringUtils.trimToEmpty(adresse).split("[,\\n$]"); if (lines.length != 2) { throw new LocalizedIllegalArgumentException(adresse, "address"); } List<String> splitted = new ArrayList<>(); if (hasPLZ(lines[0])) { splitted.add(lines[0].trim()); splitted.addAll(toStrasseHausnummer(lines[1])); } else { splitted.add(lines[1].trim()); splitted.addAll(toStrasseHausnummer(lines[0])); } return splitted.toArray(new String[3]); } private static boolean hasPLZ(String line) { try { Ort ort = new Ort(line); return ort.getPLZ().isPresent(); } catch (ValidationException ex) { LOG.log(Level.FINE, "no PLZ inside '" + line + "' found:", ex); return false; } } private static List<String> toStrasseHausnummer(String line) { String[] splitted = line.trim().split("\\s+"); if (splitted.length != 2) { splitted = line.split("\\s+[0-9]", 2); splitted[1] = line.substring(splitted[0].length()).trim(); } if (splitted.length != 2) { throw new InvalidValueException(line, "street_or_house_number"); } return Arrays.asList(splitted); } /** * Liefert den Ort. * * @return Ort */ public Ort getOrt() { return ort; } /** * Liefert den Ortsnamen. * * @return Ortsname */ public String getOrtsname() { return ort.getName(); } /** * Eine PLZ <em>muss</em> fuer eine Adresse vorhanden sein, sonst laesst * sich keine Aresse Anlagen. Diese wird hierueber zurueckgegeben. * * @return z.B. "80739" fuer Gerlingen */ @SuppressWarnings("squid:S3655") public PLZ getPLZ() { return ort.getPLZ().get(); } /** * Liefert die Strasse. * * @return the strasse */ public String getStrasse() { return strasse; } /** * Liefert die Strasse. * * @return Hausnummer, z.B. "10a" */ public String getHausnummer() { return hausnummer; } /** * Beim Vergleich wird zwischen Gross- und Kleinschreibung nicht * unterschieden. * * @param obj die andere Adresse * @return true oder false */ @Override public boolean equals(Object obj) { if (!(obj instanceof Adresse)) { return false; } Adresse other = (Adresse) obj; return this.ort.equals(other.ort) && (this.strasse.equalsIgnoreCase(other.strasse)) && this.hausnummer.equalsIgnoreCase(other.hausnummer); } /** * Fokus dieser hashCode-Implementierung liegt auf Einfachheit und * Performance. * * @return hashCode */ @Override public int hashCode() { return ort.hashCode() + strasse.hashCode() + hausnummer.hashCode(); } /** * Hierueber wird die Adresse, beginnend mit dem Ort, ausgegeben. * * @return z.B. "12345 Entenhausen, Gansstr. 23" */ @Override public String toString() { return this.getOrt() + ", " + this.getStrasse() + " " + this.getHausnummer(); } /** * Liefert die einzelnen Attribute einer Adresse als Map. * * @return Attribute als Map */ @Override public Map<String, Object> toMap() { Map<String, Object> map = new HashMap<>(); map.put("plz", getPLZ()); map.put("ortsname", getOrtsname()); map.put("strasse", getStrasse()); map.put("hausnummer", getHausnummer()); return map; } }