001package org.xbib.standardnumber; 002 003import java.util.regex.Matcher; 004import java.util.regex.Pattern; 005 006/** 007 * Global Trade Item Number (GTIN) 008 * 009 * GTIN describes a family of GS1 (EAN.UCC) global data structures that employ 010 * 14 digits and can be encoded into various types of data carriers. 011 * 012 * Currently, GTIN is used exclusively within bar codes, but it could also be used 013 * in other data carriers such as radio frequency identification (RFID). 014 * The GTIN is only a term and does not impact any existing standards, nor does 015 * it place any additional requirements on scanning hardware. 016 * 017 * For North American companies, the UPC is an existing form of the GTIN. 018 * 019 * Since 2005, EAN International and American UCC merged to GS1 and also 020 * EAN and UPC is now named GTIN. 021 * 022 * The EAN/UCC-13 code is now officially called GTIN-13 (Global Trade Identifier Number). 023 * Former 12-digit UPC codes can be converted into EAN/UCC-13 code by simply 024 * adding a preceeding zero. 025 * 026 * As of January 1, 2007, the former ISBN numbers have been integrated into 027 * the EAN/UCC-13 code. For each old ISBN-10 code, there exists a proper translation 028 * into EAN/UCC-13 by adding "978" as prefix. 029 * 030 * The family of data structures comprising GTIN include: 031 * 032 * GTIN-8 (EAN/UCC-8): this is an 8-digit number 033 * GTIN-12 (UPC-A): this is a 12-digit number 034 * GTIN-13 (EAN/UCC-13): this is a 13-digit number 035 * GTIN-14 (EAN/UCC-14 or ITF-14): this is a 14-digit number 036 * 037 * @see <a href="http://www.gtin.info/">GTIN info</a> 038 */ 039public class GTIN extends AbstractStandardNumber implements Comparable<GTIN>, StandardNumber { 040 041 private static final Pattern PATTERN = Pattern.compile("\\b[\\p{Digit}\\-]{3,18}\\b"); 042 043 private String value; 044 045 private boolean createWithChecksum; 046 047 @Override 048 public String type() { 049 return "gtin"; 050 } 051 052 @Override 053 public int compareTo(GTIN gtin) { 054 return gtin != null ? normalizedValue().compareTo(gtin.normalizedValue()) : -1; 055 } 056 057 @Override 058 public GTIN set(CharSequence value) { 059 this.value = value != null ? value.toString() : null; 060 return this; 061 } 062 063 @Override 064 public GTIN createChecksum(boolean createWithChecksum) { 065 this.createWithChecksum = createWithChecksum; 066 return this; 067 } 068 069 @Override 070 public GTIN normalize() { 071 Matcher m = PATTERN.matcher(value); 072 if (m.find() && value.length() >= m.end()) { 073 this.value = dehyphenate(value.substring(m.start(), m.end())); 074 } 075 return this; 076 } 077 078 @Override 079 public boolean isValid() { 080 return value != null && !value.isEmpty() && check(); 081 } 082 083 @Override 084 public GTIN verify() throws NumberFormatException { 085 if (value == null || value.isEmpty()) { 086 throw new NumberFormatException("invalid"); 087 } 088 if (!check()) { 089 throw new NumberFormatException("bad checksum"); 090 } 091 return this; 092 } 093 094 @Override 095 public String normalizedValue() { 096 return value; 097 } 098 099 @Override 100 public String format() { 101 return value; 102 } 103 104 @Override 105 public GTIN reset() { 106 this.value = null; 107 this.createWithChecksum = false; 108 return this; 109 } 110 111 private boolean check() { 112 int l = value.length() - 1; 113 int checksum = 0; 114 int weight; 115 int val; 116 for (int i = 0; i < l; i++) { 117 val = value.charAt(i) - '0'; 118 weight = i % 2 == 0 ? 1 : 3; 119 checksum += val * weight; 120 } 121 int chk = 10 - checksum % 10; 122 if (createWithChecksum) { 123 char ch = (char)('0' + chk); 124 value = value.substring(0, l) + ch; 125 } 126 return chk == (value.charAt(l) - '0'); 127 } 128 129 private String dehyphenate(String isbn) { 130 StringBuilder sb = new StringBuilder(isbn); 131 int i = sb.indexOf("-"); 132 while (i >= 0) { 133 sb.deleteCharAt(i); 134 i = sb.indexOf("-"); 135 } 136 return sb.toString(); 137 } 138}