    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.validator.routines.checkdigit;
    019
    020import org.apache.commons.validator.GenericValidator;
    021import org.apache.commons.validator.routines.CodeValidator;
    022
    023/**
    024 * Modulus 11 <strong>EC number</strong> Check Digit calculation/validation.
    025 *
    026 * <p>
    027 * The European Community number (EC number) is a unique seven-digit identifier that is assigned to chemical substances. For example, the EC number of arsenic
    028 * is 231-148-6:
    029 * </p>
    030 *
    031 * <p>
    032 * Check digit calculation is based on <em>modulus 11</em> with digits being weighted based on their position (from left to right).
    033 * </p>
    034 *
    035 * <p>
    036 * For further information see <a href="https://en.wikipedia.org/wiki/European_Community_number">Wikipedia - EC number</a>.
    037 * </p>
    038 *
    039 * @since 1.9.0
    040 */
    041public final class ECNumberCheckDigit extends ModulusCheckDigit {
    042
    043    private static final long serialVersionUID = 7265356024784308367L;
    044
    045    /** Singleton Check Digit instance. */
    046    private static final ECNumberCheckDigit INSTANCE = new ECNumberCheckDigit();
    047
    048    /**
    049     * EC number consists of 3 groups of numbers separated dashes (-). Example: dexamethasone is 200-003-9
    050     */
    051    private static final String GROUP = "(\\d{3})";
    052
    053    private static final String DASH = "(?:\\-)";
    054
    055    static final String EC_REGEX = "^(?:" + GROUP + DASH + GROUP + DASH + "(\\d))$";
    056
    057    private static final int EC_LEN = 7;
    058
    059    static final CodeValidator REGEX_VALIDATOR = new CodeValidator(EC_REGEX, EC_LEN, null);
    060
    061    /**
    062     * Gets the singleton instance of this validator.
    063     *
    064     * @return A singleton instance of the EC Number validator.
    065     */
    066    public static CheckDigit getInstance() {
    067        return INSTANCE;
    068    }
    069
    070    /**
    071     * Constructs a modulus 11 Check Digit routine.
    072     */
    073    private ECNumberCheckDigit() {
    074        super(MODULUS_11);
    075    }
    076
    077    /**
    078     * {@inheritDoc}
    079     */
    080    @Override
    081    public String calculate(final String code) throws CheckDigitException {
    082        if (GenericValidator.isBlankOrNull(code)) {
    083            throw new CheckDigitException("Code is missing");
    084        }
    085        final int modulusResult = INSTANCE.calculateModulus(code, false);
    086        return toCheckDigit(modulusResult);
    087    }
    088
    089    /**
    090     * {@inheritDoc}
    091     */
    092    @Override
    093    public boolean isValid(final String code) {
    094        if (GenericValidator.isBlankOrNull(code)) {
    095            return false;
    096        }
    097        final Object cde = REGEX_VALIDATOR.validate(code);
    098        if (!(cde instanceof String)) {
    099            return false;
    100        }
    101        final String validated = (String) cde;
    102        try {
    103            return INSTANCE.calculateModulus(validated, true) == Character.getNumericValue(validated.charAt(validated.length() - 1));
    104        } catch (final CheckDigitException e) {
    105            return false;
    106        }
    107    }
    108
    109    /**
    110     * Calculates the <em>weighted</em> value of a character in the code at a specified position.
    111     *
    112     * <p>
    113     * For EC number digits are weighted by their position from left to right.
    114     * </p>
    115     *
    116     * @param charValue The numeric value of the character.
    117     * @param leftPos   The position of the character in the code, counting from left to right.
    118     * @param rightPos  The position of the character in the code, counting from right to left.
    119     * @return The weighted value of the character.
    120     */
    121    @Override
    122    protected int weightedValue(final int charValue, final int leftPos, final int rightPos) {
    123        return leftPos >= EC_LEN ? 0 : charValue * leftPos;
    124    }
    125}