/* * Copyright (C) 2014 Jörg Prante * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses * or write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. * * The interactive user interfaces in modified source and object code * versions of this program must display Appropriate Legal Notices, * as required under Section 5 of the GNU Affero General Public License. * */ package org.xbib.elasticsearch.common.standardnumber; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Global Trade Item Number (GTIN) * * GTIN describes a family of GS1 (EAN.UCC) global data structures that employ * 14 digits and can be encoded into various types of data carriers. * * Currently, GTIN is used exclusively within bar codes, but it could also be used * in other data carriers such as radio frequency identification (RFID). * The GTIN is only a term and does not impact any existing standards, nor does * it place any additional requirements on scanning hardware. * * For North American companies, the UPC is an existing form of the GTIN. * * Since 2005, EAN International and American UCC merged to GS1 and also * EAN and UPC is now named GTIN. * * The EAN/UCC-13 code is now officially called GTIN-13 (Global Trade Identifier Number). * Former 12-digit UPC codes can be converted into EAN/UCC-13 code by simply * adding a preceeding zero. * * As of January 1, 2007, the former ISBN numbers have been integrated into * the EAN/UCC-13 code. For each old ISBN-10 code, there exists a proper translation * into EAN/UCC-13 by adding "978" as prefix. * * The family of data structures comprising GTIN include: * * GTIN-8 (EAN/UCC-8): this is an 8-digit number * GTIN-12 (UPC-A): this is a 12-digit number * GTIN-13 (EAN/UCC-13): this is a 13-digit number * GTIN-14 (EAN/UCC-14 or ITF-14): this is a 14-digit number * * @see <a href="http://www.gtin.info/">GTIN info</a> */ public class GTIN extends AbstractStandardNumber implements Comparable<GTIN>, StandardNumber { private static final Pattern PATTERN = Pattern.compile("\\b[\\p{Digit}\\-]{3,18}\\b"); private String value; private boolean createWithChecksum; @Override public String type() { return "gtin"; } @Override public int compareTo(GTIN gtin) { return gtin != null ? normalizedValue().compareTo(gtin.normalizedValue()) : -1; } @Override public GTIN set(CharSequence value) { this.value = value != null ? value.toString() : null; return this; } @Override public GTIN createChecksum(boolean createWithChecksum) { this.createWithChecksum = createWithChecksum; return this; } @Override public GTIN normalize() { Matcher m = PATTERN.matcher(value); if (m.find() && value.length() >= m.end()) { this.value = dehyphenate(value.substring(m.start(), m.end())); } return this; } @Override public boolean isValid() { return value != null && !value.isEmpty() && check(); } @Override public GTIN verify() throws NumberFormatException { if (value == null || value.isEmpty()) { throw new NumberFormatException("invalid"); } if (!check()) { throw new NumberFormatException("bad checksum"); } return this; } @Override public String normalizedValue() { return value; } @Override public String format() { return value; } @Override public GTIN reset() { this.value = null; this.createWithChecksum = false; return this; } private boolean check() { int l = value.length() - 1; int checksum = 0; int weight; int val; for (int i = 0; i < l; i++) { val = value.charAt(i) - '0'; weight = i % 2 == 0 ? 1 : 3; checksum += val * weight; } int chk = 10 - checksum % 10; if (createWithChecksum) { char ch = (char)('0' + chk); value = value.substring(0, l) + ch; } return chk == (value.charAt(l) - '0'); } private String dehyphenate(String isbn) { StringBuilder sb = new StringBuilder(isbn); int i = sb.indexOf("-"); while (i >= 0) { sb.deleteCharAt(i); i = sb.indexOf("-"); } return sb.toString(); } }