/* * 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.standardnumber; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ISO 3297 International Standard Serial Number (ISSN) * * Z39.50 BIB-1 Use Attribute 8 * * The International Standard Serial Number (ISSN) is a unique * eight-digit number used to identify a print or electronic periodical * publication. The ISSN system was adopted as international standard * ISO 3297 in 1975. The ISO subcommittee TC 46/SC 9 is responsible * for the standard. * * Quoted from http://www.issn.org/2-22636-All-about-ISSN.php * * The ISSN (International Standard Serial Number) is an eight-digit number * which identifies periodical publications as such, including electronic * serials. * * The ISSN is a numeric code which is used as an identifier: it has no * signification in itself and does not contain in itself any information * referring to the origin or contents of the publication. * * The ISSN takes the form of the acronym ISSN followed by two groups * of four digits, separated by a hyphen. The eighth character is a * control digit calculated according to a modulo 11 algorithm on * the basis of the 7 preceding digits; this eighth control character * may be an "X" if the result of the computing is equal to "10", * in order to avoid any ambiguity. * * The ISSN is linked to a standardized form of the title of the * identified serial, known as the "key title", which repeats * the title of the publication, qualifying it with additional elements * in order to distinguish it from other publications having identical * titles. * * If the title of the publication changes in any significant way, * a new ISSN must be assigned in order to correspond to this new form * of title and avoid any confusion. A serial publication whose * title is modified several times in the course of its existence * will be assigned each time a new ISSN, thus allowing precise * identification of each form of the title : in fact it is then * considered that they are different publications even if there * is a logical link between them. * * Contrary to other types of publications, the world of serial * publications is particularly changeable and complex : * the lifetime of a title may be extremely short; many publications * may be part of a complex set of relationships, etc. * These particularities themselves necessitated the introduction * of the ISSN. * */ public class ISSN extends AbstractStandardNumber implements Comparable<ISSN>, StandardNumber { private final static Pattern PATTERN = Pattern.compile("[0-9]{4}\\-?[0-9]{3}[0-9xX]"); private String value; private String formatted; private boolean createWithChecksum; @Override public String type() { return "issn"; } @Override public int compareTo(ISSN issn) { return value != null ? normalizedValue().compareTo(issn.normalizedValue()) : -1; } @Override public ISSN set(CharSequence value) { this.value = value != null ? value.toString() : null; return this; } @Override public ISSN createChecksum(boolean createWithChecksum) { this.createWithChecksum = createWithChecksum; return this; } @Override public ISSN normalize() { Matcher m = PATTERN.matcher(value); this.value = m.find() ? dehyphenate(value.substring(m.start(), m.end())) : null; return this; } @Override public boolean isValid() { return value != null && !value.isEmpty() && check(); } @Override public ISSN verify() throws NumberFormatException { if (value == null || value.isEmpty()) { throw new NumberFormatException("invalid"); } if (!check()) { throw new NumberFormatException("bad checksum"); } return this; } /** * Returns the value representation of the standard number * @return value */ @Override public String normalizedValue() { return value; } /** * Format this number * * @return the formatted number */ @Override public String format() { return formatted; } @Override public ISSN reset() { this.value = null; this.formatted = null; this.createWithChecksum = false; return this; } public GTIN toGTIN() throws NumberFormatException { return new GTIN().set("977" + value.substring(0, 7) + "000").createChecksum(true).normalize().verify(); } public GTIN toGTIN(String additionalCode) throws NumberFormatException { // "977" + ISSN + add-on + placeholder for createChecksum return new GTIN().set("977" + value.substring(0, 7) + additionalCode + "0").createChecksum(true).normalize().verify(); } private boolean check() { int l = createWithChecksum ? value.length() : value.length() - 1; int checksum = 0; int weight; int val; for (int i = 0; i < l; i++) { val = value.charAt(i) - '0'; weight = 8 - i; checksum += weight * val; } int chk = checksum % 11; char p = chk == 0 ? '0' : chk == 1 ? 'X' : (char)((11-chk) + '0'); return p == Character.toUpperCase(value.charAt(l)); } private String dehyphenate(String isbn) { StringBuilder sb = new StringBuilder(isbn); int i = sb.indexOf("-"); while (i > 0) { sb.deleteCharAt(i); i = sb.indexOf("-"); } if (sb.length() > 7) { this.formatted = sb.substring(0, 4) + "-" + sb.substring(4, 8); } return sb.toString(); } }