de.jfachwert.pruefung.NumberValidator.java Source code

Java tutorial

Introduction

Here is the source code for de.jfachwert.pruefung.NumberValidator.java

Source

/*
 * 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 30.08.2017 by oboehm (ob@oasd.de)
 */
package de.jfachwert.pruefung;

import de.jfachwert.SimpleValidator;
import de.jfachwert.pruefung.exception.InvalidValueException;
import de.jfachwert.pruefung.exception.LocalizedArithmeticException;
import org.apache.commons.lang3.Range;

import javax.validation.ValidationException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Locale;

/**
 * Der NumberValidator ueberprueft eine uebergebene {@link Number}, ob sie
 * im erlaubten Wertebereich liegt.
 *
 * @author oboehm
 * @since 0.4 (30.08.2017)
 */
public class NumberValidator implements SimpleValidator<String> {

    private static final String NUMBER = "number";
    private final Range<BigDecimal> range;

    /** Wenn man keine obere Grenze angeben will, nimmt man diesen Wert. */
    public static final BigDecimal INFINITE = BigDecimal.valueOf(Long.MAX_VALUE);

    /**
     * Als Default werden alle numerischen Werte zugelassen.
     */
    public NumberValidator() {
        this(INFINITE.negate(), INFINITE);
    }

    /**
     * Instanziert einen Validator, der prueft, ob ein Wert zwischen den
     * vorgegebenen Grenzen liegt.
     *
     * @param min untere Grenze
     * @param max obere Grenze
     */
    public NumberValidator(long min, long max) {
        this(BigDecimal.valueOf(min), BigDecimal.valueOf(max));
    }

    /**
     * Instanziert einen Validator, der prueft, ob ein Wert zwischen den
     * vorgegebenen Grenzen liegt.
     *
     * @param min untere Grenze
     * @param max obere Grenze
     */
    public NumberValidator(BigDecimal min, BigDecimal max) {
        this.range = Range.between(min, max);
    }

    /**
     * Wenn der uebergebene Wert gueltig ist, soll er unveraendert zurueckgegeben werden, damit er anschliessend von der
     * aufrufenden Methode weiterverarbeitet werden kann. Ist der Wert nicht gueltig, soll eine {@link
     * ValidationException} geworfen werden.
     *
     * @param value Wert, der validiert werden soll
     * @return Wert selber, wenn er gueltig ist
     */
    @Override
    public String validate(String value) {
        String normalized = normalize(value);
        try {
            BigDecimal n = new BigDecimal(normalized);
            if (range.contains(n)) {
                return normalized;
            }
        } catch (NumberFormatException ex) {
            throw new InvalidValueException(value, NUMBER, ex);
        }
        throw new InvalidValueException(value, NUMBER, range);
    }

    /**
     * Normalisiert einen String, sodass er zur Generierung einer Zahl
     * herangezogen werden kann.
     * 
     * @param value z.B. "1,234.5"
     * @return normalisiert, z.B. "1234.5"
     */
    public String normalize(String value) {
        if (!value.matches("[\\d,.]+([eE]\\d+)?")) {
            throw new InvalidValueException(value, NUMBER);
        }
        Locale locale = guessLocale(value);
        DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(locale);
        df.setParseBigDecimal(true);
        try {
            return df.parse(value).toString();
        } catch (ParseException ex) {
            throw new InvalidValueException(value, NUMBER, ex);
        }
    }

    private static Locale guessLocale(String value) {
        return value.matches("\\d+(\\.\\d{3})*(,\\d+)?") ? Locale.GERMAN : Locale.ENGLISH;
    }

    /**
     * Verifiziert die uebergebene Nummer, ob sie eine gueltige Nummer und
     * nicht unendlich oder 'NaN' ist. Falls die Nummer unendlich oder 'NaN'
     * ist, wird eine {@link ArithmeticException} geworfen.
     *
     * @param number zu pruefende Nummer
     * @return die Nummer selbst zur Weiterverarbeitung
     */
    public Number verifyNumber(Number number) {
        if ((number instanceof Double) || (number instanceof Float)) {
            double dValue = number.doubleValue();
            if (Double.isNaN(dValue) || Double.isInfinite(dValue)) {
                throw new LocalizedArithmeticException(dValue, NUMBER);
            }
        }
        return number;
    }

}