/* * 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; /** * ISO 21047 International Standard Text Code (ISTC) * * The International Standard Text Code (ISTC) is a numbering system for the unique identification * of text-based works; the term “work” can refer to any content appearing in conventional * printed books, audio-books, static e-books or enhanced digital books, as well as content * which might appear in a newspaper or journal. * * The ISTC provides sales analysis systems, retail websites, library catalogs and other * bibliographic systems with a method of automatically linking together publications * of the “same content” and/or “related content”, thus improving discoverability of * products and efficiencies. * * An ISTC number is the link between a user’s search for a piece of content and the * ultimate sale or loan of a publication. * * The standard was formally published in March 2009 * * Checksum algorithm is ISO 7064 MOD 16/3 * */ public class ISTC extends AbstractStandardNumber implements Comparable<ISTC>, StandardNumber { private static final Pattern PATTERN = Pattern.compile("^[\\p{Alnum}\\-\\s]{12,24}"); private String value; private String formatted; private boolean createWithChecksum; @Override public String type() { return "istc"; } @Override public int compareTo(ISTC istc) { return istc != null ? normalizedValue().compareTo(istc.normalizedValue()) : -1; } @Override public ISTC set(CharSequence value) { this.value = value != null ? value.toString() : null; return this; } @Override public ISTC createChecksum(boolean createWithChecksum) { this.createWithChecksum = createWithChecksum; return this; } @Override public ISTC normalize() { Matcher m = PATTERN.matcher(value); if (m.find()) { this.value = clean(value.substring(m.start(), m.end())); } return this; } @Override public boolean isValid() { return value != null && !value.isEmpty() && check(); } @Override public ISTC verify() throws NumberFormatException { if (value == null || value.isEmpty()) { throw new NumberFormatException("invalid"); } if (!check()) { throw new NumberFormatException("bad createChecksum"); } return this; } @Override public String normalizedValue() { return value; } @Override public String format() { return formatted; } @Override public ISTC reset() { this.value = null; this.formatted = null; this.createWithChecksum = false; return this; } private boolean check() { int l = value.length() - 1; int checksum = 0; int weight; int factor; int val; for (int i = 0; i < l; i++) { val = value.charAt(i); if (val >= 'A' && val <= 'Z') { val = 10 + (val - 'A'); } if (val >= '0' && val <= '9') { val = val - '0'; } factor = i % 4 < 2 ? 1 : 5; weight = (12 - 2 * (i % 4)) - factor; // --> 11,9,3,1 checksum += val * weight; } int chk = checksum % 16; if (createWithChecksum) { char ch = chk > 9 ? (char)(10 + (chk - 'A')) : (char)('0' + chk); value = value.substring(0, l) + ch; } char digit = value.charAt(l); int chk2 = (digit >= '0' && digit <= '9') ? digit - '0' : digit -'A' + 10; return chk == chk2; } private String clean(String raw) { if (raw == null) { return null; } StringBuilder sb = new StringBuilder(raw); int i = sb.indexOf("-"); while (i >= 0) { sb.deleteCharAt(i); i = sb.indexOf("-"); } i = sb.indexOf(" "); while (i >= 0) { sb.deleteCharAt(i); i = sb.indexOf(" "); } if (sb.indexOf("ISTC") == 0) { sb = new StringBuilder(sb.substring(4)); } if (sb.length() > 15) { this.formatted = "ISTC " + sb.substring(0,3) + "-" + sb.substring(3,7) + "-" + sb.substring(7,15) + "-" + sb.substring(15); } return sb.toString(); } }