NIFValidator.java

/*
 * Copyright (C) 2016 Alberto Irurueta Carro (alberto@irurueta.com)
 *
 * 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.
 */
package com.irurueta.commons.validators;

import java.lang.ref.SoftReference;

/**
 * Validates provided NIF (Número de Identificación Fiscal) values for Spain.
 * This class is a wrapper for the library provided by Spain's Ministerio de
 * Hacienda to validate DNI's, NIF's, NIE's, CIF's, etc.
 */
public class NIFValidator {
    /**
     * Reference to the internal validator provided by Ministerio de
     * Hacienda.
     */
    private static SoftReference<Validador> mValRef;

    /**
     * Constructor.
     */
    protected NIFValidator() {
    }

    /**
     * Checks whether provided value is a valid DNI, NIF, NIE or CIF.
     *
     * @param value DNI, NIF, NIE or CIF to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValid(final String value) {
        final int code = getOrCreateValidador().checkNif(value);
        return isValidDNICode(code) || isValidNIFCode(code) ||
                isValidNIFNonResidentsCode(code) ||
                isValidNIFMinorsCode(code) || isValidNIECode(code) ||
                isValidCIFCode(code) ||
                isValidCIFOrganizationCode(code) ||
                isValidCIFNonResidentsCode(code);
    }

    /**
     * Checks whether provided value is a valid DNI (Documento Nacional de
     * Identidad).
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidDNI(final String value) {
        return isValidDNICode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to the code of a valid DNI.
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid DNI, false otherwise.
     */
    protected static boolean isValidDNICode(final int code) {
        return code == Validador.DNI_OK;
    }

    /**
     * Checks whether provided value is a valid NIF (Número de
     * Identificación Fiscal).
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidNIF(final String value) {
        return isValidNIFCode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks if provided code corresponds to a valid NIF code.
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid NIF code, false
     * otherwise.
     */
    protected static boolean isValidNIFCode(final int code) {
        return code == Validador.NIF_OK;
    }

    /**
     * Checks whether provided value corresponds to a valid non-resident
     * NIF.
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidNIFNonResidents(final String value) {
        return isValidNIFNonResidentsCode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to a valid non-resident NIF.
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid non-resident  NIF, false
     * otherwise.
     */
    protected static boolean isValidNIFNonResidentsCode(final int code) {
        return code == Validador.NIF_NORESIDENTES;
    }

    /**
     * Checks whether provided value corresponds to a valid NIF for minors
     * having less than 14 years old.
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidNIFMinors(final String value) {
        return isValidNIFMinorsCode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to a valid NIF for minors
     * having less than 14 years old.
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid NIF for minors having
     * less than 14 years old, false otherwise.
     */
    protected static boolean isValidNIFMinorsCode(final int code) {
        return code == Validador.NIF_MENORES14ANOS;
    }

    /**
     * Checks whether provided value corresponds to a valid NIE (Número de
     * Identificación de Extranjeros).
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidNIE(final String value) {
        return isValidNIECode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to a valid NIE (Número de
     * Identificación de Extranjeros).
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid NIE, false otherwise.
     */
    protected static boolean isValidNIECode(final int code) {
        return code == Validador.NIF_EXTRANJEROS;
    }

    /**
     * Checks whether provided value corresponds to a valid CIF (Código de
     * Identificación Fiscal).
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidCIF(final String value) {
        return isValidCIFCode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to a valid CIF (Código de
     * Identificación Fiscal).
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid CIF, false otherwise.
     */
    public static boolean isValidCIFCode(final int code) {
        return code == Validador.CIF_OK;
    }

    /**
     * Checks whether provided value corresponds to a valid Organization
     * CIF.
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidCIFOrganization(final String value) {
        return isValidCIFOrganizationCode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to a valid Organization CIF
     * code.
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid Organization CIF, false
     * otherwise.
     */
    public static boolean isValidCIFOrganizationCode(final int code) {
        return code == Validador.CIF_ORGANIZACION_OK;
    }

    /**
     * Checks whether provided value corresponds to a valid non-resident
     * CIF.
     *
     * @param value value to be validated.
     * @return true if value is valid, false otherwise.
     */
    public static boolean isValidCIFNonResidents(final String value) {
        return isValidCIFNonResidentsCode(getOrCreateValidador().checkNif(value));
    }

    /**
     * Checks whether provided code corresponds to a valid non-resident CIF.
     *
     * @param code code to be validated.
     * @return true if code corresponds to a valid non-resident CIF, false
     * otherwise.
     */
    public static boolean isValidCIFNonResidentsCode(final int code) {
        return code == Validador.CIF_NORESIDENTES_OK;
    }

    /**
     * Gets or creates singleton instance to validate DNI's, NIF's, CIF's,
     * etc using the library provided by the Ministerio de Hacienda.
     *
     * @return internal validator provided by Ministerio de Hacienda.
     */
    private static synchronized Validador getOrCreateValidador() {
        if (mValRef == null || mValRef.get() == null) {
            mValRef = new SoftReference<>(new Validador());
        }
        return mValRef.get();
    }

    /**
     * Inner class to perform actual validation.
     */
    private static class Validador {
        public static final int DNI_OK = 0;
        public static final int NIF_OK = 1;
        public static final int NIF_NORESIDENTES = 2;
        public static final int NIF_MENORES14ANOS = 3;
        public static final int NIF_EXTRANJEROS = 4;
        public static final int CIF_OK = 20;
        public static final int CIF_ORGANIZACION_OK = 22;
        public static final int CIF_NORESIDENTES_OK = 23;
        private static final char[] Numeros = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
        private static final char[] Letras = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'};
        private static final char[] LetrasNIF = new char[]{'T', 'R', 'W', 'A', 'G', 'M', 'Y', 'F', 'P', 'D', 'X', 'B', 'N', 'J', 'Z', 'S', 'Q', 'V', 'H', 'L', 'C', 'K', 'E'};
        private static final char[] Letras2CIF = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
        private static final char[] LetrasCIF = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'U', 'V'};
        private static final char[] LetrasCIFORG_Y_EXTR = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'P', 'Q', 'S', 'N', 'W', 'R'};
        private static final char[] LetrasREGATRIBRENTAS = new char[]{'E', 'G', 'H', 'J', 'U', 'V'};
        private static final char[] LetrasCIFEXT = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'N', 'W'};
        private static final char[] LetrasNIFEXT = new char[]{'X', 'Y', 'Z'};

        /**
         * Checks nif
         *
         * @param var1 value to be validated.
         * @return code indicating the result of validation.
         */
        public int checkNif(final String var1) {
            return var1 != null && var1.length() == 9 ? this.vNif(var1) : -1;
        }

        @SuppressWarnings("Duplicates")
        public int vNif(String var1) {
            String var2;
            int var5 = 0;
            long var6;
            long var8;
            var1 = var1.trim();
            if (var1.length() != 9) {
                return -2;
            } else {
                char var10 = 0;
                char var11 = 0;
                int var12 = 0;
                int var13 = 0;
                int var14 = 0;
                final char[] var15 = var1.toCharArray();

                int var3;
                for (var3 = 0; var3 < 9; ++var3) {
                    final char var16 = var15[var3];
                    final int var17 = Character.getType(var16);
                    if (var17 == 1 && var12 == 0) {
                        ++var12;
                        var10 = var16;
                        var13 = var3;
                    } else if (var17 == 1 && var12 == 1) {
                        ++var12;
                        var11 = var16;
                        var14 = var3;
                    } else {
                        if (var17 == 1 && var12 == 2) {
                            return -5;
                        }

                        if (var17 != 9) {
                            return -4;
                        }
                    }
                }

                if (var12 == 0) {
                    if (var15[0] != 48) {
                        return -20;
                    } else {
                        final String var20 = var1.substring(1);
                        if (!var20.equals("11111111") && !var20.equals("22222222") && !var20.equals("33333333") && !var20.equals("44444444") && !var20.equals("55555555") && !var20.equals("66666666") && !var20.equals("77777777") && !var20.equals("88888888") && !var20.equals("99999999") && !var20.equals("00000000")) {
                            return 0;
                        } else {
                            return -21;
                        }
                    }
                } else {
                    int var19;
                    if (var12 == 1 && this.caracEnCad(LetrasCIF, var15[var13]) && var13 == 0 && Character.isDigit(var15[8])) {
                        for (var3 = 1; var3 < 8; ++var3) {
                            if (var3 != 2 && var3 != 4 && var3 != 6) {
                                var19 = (var15[var3] - 48) * 2;
                                if (var19 > 9) {
                                    var19 -= 9;
                                }

                                var5 += var19;
                            } else {
                                var5 += var15[var3] - 48;
                            }
                        }

                        var5 = 10 - var5 % 10;
                        if (var5 == 10) {
                            var5 = 0;
                        }

                        if (var5 == var15[var3] - 48) {
                            if (this.caracEnCad(LetrasREGATRIBRENTAS, var15[var13])) {
                                return 23;
                            } else {
                                return 20;
                            }
                        } else {
                            return -10;
                        }
                    } else if (var12 == 2 && this.caracEnCad(LetrasCIFORG_Y_EXTR, var15[var13]) && var13 == 0 && var14 == 8 && this.caracEnCad(Letras2CIF, var15[var14])) {
                        for (var3 = 1; var3 < 8; ++var3) {
                            if (var3 != 2 && var3 != 4 && var3 != 6) {
                                var19 = (var15[var3] - 48) * 2;
                                if (var19 > 9) {
                                    var19 -= 9;
                                }

                                var5 += var19;
                            } else {
                                var5 += var15[var3] - 48;
                            }
                        }

                        var5 = 10 - var5 % 10;
                        if (Letras2CIF[var5 - 1] == var15[var14]) {
                            if (this.caracEnCad(LetrasCIFEXT, var15[var13])) {
                                return 21;
                            } else {
                                return 22;
                            }
                        } else {
                            return -10;
                        }
                    } else if (var12 == 1 && this.caracEnCad(Letras, var15[8]) && this.caracEnCad(LetrasNIF, var15[var13]) && var13 == 8) {
                        var6 = Long.parseLong(var1.substring(0, var13));
                        var8 = var6 % 23L;
                        if (var10 == LetrasNIF[(int) var8]) {
                            if (!var1.equals("00000001R") && !var1.equals("00000000T") && !var1.equals("99999999R")) {
                                return 1;
                            } else {
                                return -1;
                            }
                        } else {
                            return -11;
                        }
                    } else if (var12 == 2 && (var15[0] == 75 || var15[0] == 76 || var15[0] == 77) && this.caracEnCad(LetrasNIF, var15[var14]) && var14 == 8) {
                        var2 = var1.substring(1, 3);
                        if (this.caracEnCad(Numeros, var2.charAt(0)) && this.caracEnCad(Numeros, var2.charAt(1))) {
                            var3 = Integer.parseInt(var2);
                            if (var3 >= 1 && var3 <= 56) {
                                var2 = var1.substring(1, var14);
                                var6 = Long.parseLong(var2);
                                var8 = var6 % 23L;
                                ++var8;
                                if (var11 == LetrasNIF[(int) (var8 - 1L)]) {
                                    return 2;
                                } else {
                                    return -11;
                                }
                            } else {
                                return -1;
                            }
                        } else {
                            return -13;
                        }
                    } else if (var1.equals("X0000000T")) {
                        return -1;
                    } else if (var12 == 2 && this.caracEnCad(LetrasNIFEXT, var15[0]) && this.caracEnCad(LetrasNIF, var15[var14]) && var14 == 8) {
                        var2 = var1.substring(1, var14);
                        var6 = Long.parseLong(var2);
                        if (var15[0] == 89) {
                            var6 += 10000000L;
                        } else if (var15[0] == 90) {
                            var6 += 20000000L;
                        }

                        var8 = var6 % 23L;
                        ++var8;
                        if (var11 == LetrasNIF[(int) (var8 - 1L)]) {
                            return 4;
                        } else {
                            return -11;
                        }
                    } else {
                        return -1;
                    }
                }
            }
        }

        private boolean caracEnCad(final char[] var1, final char var2) {
            boolean var3 = false;

            for (char aVar1 : var1) {
                if (aVar1 == var2) {
                    var3 = true;
                    break;
                }
            }

            return var3;
        }
    }
}