001package org.xbib.standardnumber; 002 003import java.util.regex.Matcher; 004import java.util.regex.Pattern; 005 006/** 007 * ISO 15420 Universal Product Code (UPC) 008 * 009 * The Universal Product Code (UPC) is a barcode symbology (i.e., a specific type of barcode) 010 * that is widely used in the United States, Canada, the United Kingdom, Australia, 011 * New Zealand and in other countries for tracking trade items in stores. 012 * Its most common form, the UPC-A, consists of 12 numerical digits, which are uniquely 013 * assigned to each trade item. 014 * 015 * Along with the related EAN barcode, the UPC is the barcode mainly used for scanning 016 * of trade items at the point of sale, per GS1 specifications. 017 * 018 * UPC data structures are a component of GTINs (Global Trade Item Numbers). 019 * 020 * All of these data structures follow the global GS1 specification which bases on 021 * international standards. 022 * 023 */ 024public class UPC extends AbstractStandardNumber implements Comparable<UPC>, StandardNumber { 025 026 private static final Pattern PATTERN = Pattern.compile("[\\p{Digit}]{0,12}"); 027 028 private String value; 029 030 private boolean createWithChecksum; 031 032 @Override 033 public String type() { 034 return "upc"; 035 } 036 037 @Override 038 public int compareTo(UPC upc) { 039 return upc != null ? normalizedValue().compareTo(upc.normalizedValue()) : -1; 040 } 041 042 @Override 043 public UPC set(CharSequence value) { 044 this.value = value != null ? value.toString() : null; 045 return this; 046 } 047 048 @Override 049 public UPC createChecksum(boolean createWithChecksum) { 050 this.createWithChecksum = createWithChecksum; 051 return this; 052 } 053 054 @Override 055 public UPC normalize() { 056 Matcher m = PATTERN.matcher(value); 057 if (m.find()) { 058 this.value = value.substring(m.start(), m.end()); 059 } 060 return this; 061 } 062 063 @Override 064 public boolean isValid() { 065 return value != null && !value.isEmpty() && check(); 066 } 067 068 @Override 069 public UPC verify() throws NumberFormatException { 070 if (value == null || value.isEmpty()) { 071 throw new NumberFormatException("invalid"); 072 } 073 if (!check()) { 074 throw new NumberFormatException("bad checksum"); 075 } 076 return this; 077 } 078 079 @Override 080 public String normalizedValue() { 081 return value; 082 } 083 084 @Override 085 public String format() { 086 return value; 087 } 088 089 @Override 090 public UPC reset() { 091 this.value = null; 092 this.createWithChecksum = false; 093 return this; 094 } 095 096 private boolean check() { 097 int l = value.length() - 1; 098 int checksum = 0; 099 int weight; 100 int val; 101 for (int i = 0; i < l; i++) { 102 val = value.charAt(i) - '0'; 103 weight = i%2 == 0 ? 3 : 1; 104 checksum += val * weight; 105 } 106 int chk = 10 - checksum % 10; 107 if (createWithChecksum) { 108 char ch = (char)('0' + chk); 109 value = value.substring(0, l) + ch; 110 } 111 return chk == (value.charAt(l) - '0'); 112 } 113}