ISBN, JAN。クラスライブラリ


半月ぶりに書きました。
ISBN-13 のチェックデジットを計算する感じの小物でも作ろうかと。
一先ずクラスライブラリだけ実装。
副産物として同じ計算式を使ってるJAN(EAN)13桁と短絡8桁や、
UPC、ITFの14桁と16桁なんかにも対応してます。


ペーパー・プロトタイピングを昨日、実装を今日に。
この前のは入門書的な1本ソースで書き上げてしまいましたが、
パッケージ分け、クラス分けはちゃんとした方が理解度が変わってきます。
違ってればちゃんとコンパイラが突っ込んで来ますから、成る程そーなのかーってなりますし。


EanTest.jar


パッケージ構成

./EanTest.java
./jp/regekatsu/utility/WpcAbstract.java
./jp/regekatsu/utility/Ean.java


./EanTest.java

/*---------------------------------------------------------------------------------
    
    class EanTest
    
    version 0.01
    May 20, 2012
    
    by DumBo
    
---------------------------------------------------------------------------------*/

import jp.regekatsu.utility.Ean;

class EanTest {
    
    public static void main(String[] args) {
        
        Ean jan8   = new Ean(Ean.CODE_JAN8);
        Ean jan13  = new Ean(Ean.CODE_JAN13);
        Ean isbn13 = new Ean(Ean.CODE_ISBN13);
        Ean upc    = new Ean(Ean.CODE_UPC);
        Ean itf14  = new Ean(Ean.CODE_ITF14);
        Ean itf16  = new Ean(Ean.CODE_ITF16);
        
        jan8.setCode("4912345");
        jan13.setCode("456995111617");
        isbn13.setCode("978123456789");
        upc.setCode("06141411234");
        itf14.setCode("1490123456789");
        itf16.setCode("010490123456789");
        
        System.out.println("JANコード(短絡タイプ8桁)  : " + jan8.getCode()   + jan8.getCheckDigit());
        System.out.println("JANコード(標準タイプ13桁) : " + jan13.getCode()  + jan13.getCheckDigit());
        System.out.println("ISBNコード(現行規格13桁)  : " + isbn13.getCode() + isbn13.getCheckDigit());
        System.out.println("UPCコード(12桁)           : " + upc.getCode()    + upc.getCheckDigit());
        System.out.println("ITF-14コード(14桁)        : " + itf14.getCode()  + itf14.getCheckDigit());
        System.out.println("ITF-16コード(16桁)        : " + itf16.getCode()  + itf16.getCheckDigit());
        
    }
    
}


./jp/regekatsu/utility/WpcAbstract.java

/*---------------------------------------------------------------------------------
    
    abstract class WpcAbstract
    
    version 0.01
    May 20, 2012
    
    by DumBo
    
---------------------------------------------------------------------------------*/

package jp.regekatsu.utility;

abstract class WpcAbstract {
    
    int codeSize;
    String code;
    
    abstract int getCodeSize();
    abstract String getCode();
    abstract void setCode(String code);
    abstract String getCheckDigit();
    
}


./jp/regekatsu/utility/Ean.java

/*---------------------------------------------------------------------------------
    
    class Ean extends WpcAbstract
    
    version 0.01
    May 20, 2012
    
    by DumBo
    
---------------------------------------------------------------------------------*/

package jp.regekatsu.utility;

public class Ean extends WpcAbstract {
    
    public static final int CODE_EAN8   = 7;
    public static final int CODE_JAN8   = CODE_EAN8;
    public static final int CODE_EAN13  = 12;
    public static final int CODE_JAN13  = CODE_EAN13;
    public static final int CODE_ISBN13 = CODE_EAN13;
    public static final int CODE_UPC    = 11;
    public static final int CODE_ITF14  = 13;
    public static final int CODE_ITF16  = 15;
    
    static final int MODULUS = 10;
    static final int WAIT    = 3;
    
    public Ean(int codeSize) {
        
        //コード桁数が0以下でないか確認する
        if(codeSize <= 0)
            throw new IllegalArgumentException("コード桁数は最低でも0より大きい数を指定してください。");
        
        this.codeSize = codeSize;
        
        String code = "";
        for(int i = 0;i < this.codeSize;i++)
            code += "0";
        this.code = code;
        
    }
    
    public int getCodeSize() {    return codeSize;    }
    
    public String getCode() {    return code;    }
    
    public void setCode(String code) {
        
        //コード桁数が設定値と一致してるか確認する。
        if(code.length() != codeSize)
            throw new IllegalArgumentException("設定した桁数と引数の桁が一致しません。");
        
        //コードが数字のみで構成されてるか確認する。
        String isNumber = "[0-9]{" + String.valueOf(codeSize) + "}";
        if(code.matches(isNumber) != true)
            throw new IllegalArgumentException("指定したコードに数字以外の文字が含まれてます。");
        
        this.code = code;
        
    }
    
    public String getCheckDigit() {
        
        /*
        計算方法
        
        1.奇数桁を全て合計して×1で総計を求める。
        2.偶数桁を全て合計して×3で総計を求める。
        3.奇数桁総計と偶数桁総計を足した数から÷10で余りを求める。
        4.10から余りを引くことでチェックデジットが求められる。
        
        ※この計算方法はJAN13桁、ISBN13桁に限らず
        JAN短絡8桁、UPC12桁、ITF14、16桁も、
        同一手順でもってチェックデジットを求めることが出来る。
        */
        
        /*
        コード桁数 + チェックデジット1桁 = 総桁数 が
        偶数なら文字列の偶数要素数(0,2,4,...)は偶数桁、
        奇数なら文字列の偶数要素数(0,2,4,...)が奇数桁となる。
        
        例)
        EAN(コード12桁 + チェックデジット1桁) 
        桁数    13  12  11  10  09  08  07  06  05  04  03  02     01
        偶奇    奇  偶  奇  偶  奇  偶  奇  偶  奇  偶  奇  偶     奇
        要素数 [00][01][02][03][04][05][06][07][08][09][10][11] + [12]
        
        UPC(コード11桁 + チェックデジット1桁)
        桁数    12  11  10  09  08  07  06  05  04  03  02     01
        偶奇    偶  奇  偶  奇  偶  奇  偶  奇  偶  奇  偶     奇
        要素数 [00][01][02][03][04][05][06][07][08][09][10] + [11]
        
        総桁数が奇数の場合は最初の要素を奇数として、
        また逆に偶数の場合は最初の要素を偶数として扱い、1つ飛ばしで計算する為に、
        コード桁数 % 2 = 奇数桁の入った要素の始点、
        (コード桁数 + チェックデジット1桁) % 2 = 偶数桁の入った要素の始点、
        として扱っている。
        */
        
        //奇数桁の計算
        int odd = 0;
        for(int i = codeSize % 2;i < codeSize;i += 2)
            odd  += Integer.parseInt(code.substring(i, i + 1));
        
        //偶数桁の計算
        int even = 0;
        for(int i = (codeSize + 1) % 2;i < codeSize;i += 2)
            even += Integer.parseInt(code.substring(i, i + 1));
        even *= WAIT;
        
        //結果の計算
        int temporaryDigit = MODULUS - ((odd + even) % MODULUS);
        
        return String.valueOf(temporaryDigit);
        
    }
    
}