All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.jfachwert.post.Ort.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2017-2023 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 orimplied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * (c)reated 13.04.17 by oliver ([email protected])
 */
package de.jfachwert.post

import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
import de.jfachwert.KFachwert
import de.jfachwert.KSimpleValidator
import de.jfachwert.Text
import de.jfachwert.pruefung.LengthValidator
import de.jfachwert.pruefung.NullValidator
import de.jfachwert.pruefung.exception.LocalizedIllegalArgumentException
import de.jfachwert.pruefung.exception.ValidationException
import org.apache.commons.lang3.StringUtils
import java.util.*
import java.util.logging.Level
import java.util.logging.Logger

/**
 * Ein Ort (oder auch Ortschaft) ist eine Stadt oder Gemeinde. Ein Ort hat
 * i.d.R. eine Postleitzahl (PLZ). Diese ist aber in dieser Klasse optional,
 * sodass man einen Ort auch ohne eine PLZ einsetzen kann.
 *
 * @author oboehm
 * @since 0.2.0 (13.04.2017)
 */
@JsonSerialize(using = ToStringSerializer::class)
open class Ort
/**
 * Hierueber kann ein Ort mit PLZ angelegt werden.
 *
 * @param plz       Postleitzahl des Ortes
 * @param name      Name des Ortes
 * @param validator Validator fuer die Ueberpruefung des Ortes
 */
@JvmOverloads constructor(private val plz: PLZ?, name: String, validator: KSimpleValidator = VALIDATOR) : KFachwert {

    /**
     * Liefert den Ortsnamen zurueck.
     *
     * @return den Ortsnamen
     */
    val name: String

    /**
     * Hierueber kann ein Ort (mit oder ohne PLZ) angelegt werden.
     *
     * @param name des Ortes
     */
    constructor(name: String) : this(split(name)) {}

    private constructor(values: Array) : this(if (values[0].isEmpty()) null else PLZ(values[0]), values[1]) {}

    init {
        this.name = verify(name, validator)
    }

    /**
     * Da die Postleitzahl optional ist, wird sie auch als [Optional]
     * zurueckgegeben.
     *
     * @return die PLZ
     */
    val pLZ: Optional
        get() = if (plz == null) {
            Optional.empty()
        } else {
            Optional.of(plz)
        }

    /**
     * Hier wird ein logischer Vergleich vorgenommen, ob der andere Ort
     * der gleiche Ort ist. Kennzeichnend dafuer ist die PLZ. Solange die
     * PLZ die gleiche ist, darf der Ort unterschiedlich geschrieben sein
     * (Bsp.: "73730 Esslingen" und "73730 Esslingen am Necker" werden als
     * gleich angesehen.
     *
     * @param other der andere Ort
     * @return true, falls es der gleiche Ort ist
     */
    override fun equals(other: Any?): Boolean {
        if (other !is Ort) {
            return false
        }
        val thisName = Text.replaceUmlaute(name)
        val otherName = Text.replaceUmlaute(other.name)
        return if (plz == null || other.plz == null) {
            thisName.equals(otherName, ignoreCase = true)
        } else {
            plz == other.plz &&
                    Character.toLowerCase(thisName[0]) == Character.toLowerCase(otherName[0])
        }
    }

    /**
     * Im Gegensatz zur [.equals]-Methode muss hier der andere
     * Ort exakt uebereinstimmen. D.h. Sowohl in der PLZ als auch im Namen.
     *
     * @param other der andere Ort
     * @return true bei exakter Gleichheit
     * @since 2.1
     */
    fun equalsExact(other: Ort): Boolean {
        return plz == other.plz && name == other.name
    }

    /**
     * Da die PLZ optional ist, kann die PLZ nicht fuer den Hashcode
     * herangezogen werden. Und auch beim Ort wird es schwierig, da er
     * unterschiedlich geschrieben werden kann (mit Umlaute oder ohne
     * Umlaute). Fuer den Hashcode wird daher ausgegangen, dass der
     * erste Buchstabe auch bei unterschiedlicher Schreibweise immer
     * gleich ist.
     *
     * @return hashcode
     */
    override fun hashCode(): Int {
        return Character.toUpperCase(Text.replaceUmlaute(name)[0]).code
    }

    /**
     * Liefert den Orstnamen als Ergebnis.
     *
     * @return Ortsname
     */
    override fun toString(): String {
        return if (plz == null) {
            name
        } else {
            plz.toString() + " " + name
        }
    }



    companion object {

        private val VALIDATOR: KSimpleValidator = LengthValidator(1)
        private val log = Logger.getLogger(Ort::class.java.name)

        /** Null-Wert fuer Initialisierung.  */
        @JvmField
        val NULL = Ort(PLZ.NULL, "", NullValidator())

        /**
         * Hierueber kann ein Ort (mit oder ohne PLZ) angelegt werden.
         *
         * @param name des Ortes
         * @return Ort
         */
        @JvmStatic
        fun of(name: String): Ort {
            return Ort(name)
        }

        /**
         * Hierueber kann ein Ort mit PLZ angelegt werden.
         *
         * @param plz Postleitzahl des Ortes
         * @param name Name des Ortes
         * @return Ort
         */
        @JvmStatic
        fun of(plz: PLZ, name: String): Ort {
            return Ort(plz, name)
        }

        /**
         * Ein Orstname muss mindestens aus einem Zeichen bestehen. Allerdings
         * koennte der ueberbebene Name auch die PLZ noch beinhalten. Dies wird
         * bei der Validierung beruecksichtigt.
         *
         * @param name der Ortsname (mit oder ohne PLZ)
         * @return der validierte Ortsname zur Weiterverabeitung
         */
        fun validate(name: String): String {
            return validate(name, VALIDATOR)
        }

        private fun validate(name: String, validator: KSimpleValidator): String {
            val splitted = split(name)
            val ortsname = splitted[1]
            validator.validate(ortsname)
            return name
        }

        private fun verify(name: String, validator: KSimpleValidator): String {
            return try {
                validate(name, validator)
            } catch (ex: ValidationException) {
                throw LocalizedIllegalArgumentException(ex)
            }
        }

        private fun split(name: String): Array {
            val input = StringUtils.trimToEmpty(name)
            val splitted = arrayOf("", input)
            if (input.contains(" ")) {
                try {
                    val plz = PLZ.Validator().validate(StringUtils.substringBefore(input, " "))
                    splitted[0] = plz
                    splitted[1] = StringUtils.substringAfter(input, " ").trim { it <= ' ' }
                } catch (ex: ValidationException) {
                    log.log(Level.FINE, "keine PLZ in '$name' gefunden.")
                    log.log(Level.FINER, "Details:", ex)
                }
            }
            return splitted
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy