Newer
Older
book-metadata-service / src / main / java / ru / mcs / metadatabook / util / IsbnValidator.java
package ru.mcs.metadatabook.util;

/**
 * Валидация и нормализация ISBN-10/ISBN-13 по контрольной сумме.
 *
 * Нормализация (убрать дефисы/пробелы) нужна не ради экономии места в БД
 * (VARCHAR(4000) — variable-length, разница в 3-4 байта на строку ничтожна),
 * а ради корректной дедупликации: "978-5-389-14385-2" и "9785389143852" —
 * одна и та же книга, но как РАЗНЫЕ строки для persist()/findByIndexedField
 * они создают два независимых дубля в mt_data (см. баг с "Дорога" Маккарти).
 *
 * Проверка по контрольной сумме также защищает от лишних внешних HTTP-вызовов:
 * без нормализации+валидации любая случайная 10-13-значная строка уходила бы
 * в Open Library/Google Books/Sigla как "похоже на ISBN", тратя их квоту впустую.
 */
public final class IsbnValidator {

    private IsbnValidator() {}

    /** Убирает всё, кроме цифр и X/x, приводит X к верхнему регистру. */
    public static String normalize(String raw) {
        if (raw == null) return "";
        return raw.replaceAll("[^0-9Xx]", "").toUpperCase();
    }

    public static boolean isValid(String normalizedIsbn) {
        if (normalizedIsbn == null) return false;
        return switch (normalizedIsbn.length()) {
            case 10 -> isValidIsbn10(normalizedIsbn);
            case 13 -> isValidIsbn13(normalizedIsbn);
            default -> false;
        };
    }

    private static boolean isValidIsbn10(String isbn) {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            char c = isbn.charAt(i);
            int digit;
            if (c == 'X') {
                if (i != 9) return false; // X допустим только последним символом
                digit = 10;
            } else if (Character.isDigit(c)) {
                digit = c - '0';
            } else {
                return false;
            }
            sum += digit * (10 - i);
        }
        return sum % 11 == 0;
    }

    private static boolean isValidIsbn13(String isbn) {
        int sum = 0;
        for (int i = 0; i < 13; i++) {
            char c = isbn.charAt(i);
            if (!Character.isDigit(c)) return false;
            int digit = c - '0';
            sum += (i % 2 == 0) ? digit : digit * 3;
        }
        return sum % 10 == 0;
    }
}