001 002package org.xbib.standardnumber; 003 004import java.util.regex.Matcher; 005import java.util.regex.Pattern; 006 007/** 008 * ISO 3297 International Standard Serial Number (ISSN) 009 * 010 * Z39.50 BIB-1 Use Attribute 8 011 * 012 * The International Standard Serial Number (ISSN) is a unique 013 * eight-digit number used to identify a print or electronic periodical 014 * publication. The ISSN system was adopted as international standard 015 * ISO 3297 in 1975. The ISO subcommittee TC 46/SC 9 is responsible 016 * for the standard. 017 * 018 * Quoted from http://www.issn.org/2-22636-All-about-ISSN.php 019 * 020 * The ISSN (International Standard Serial Number) is an eight-digit number 021 * which identifies periodical publications as such, including electronic 022 * serials. 023 * 024 * The ISSN is a numeric code which is used as an identifier: it has no 025 * signification in itself and does not contain in itself any information 026 * referring to the origin or contents of the publication. 027 * 028 * The ISSN takes the form of the acronym ISSN followed by two groups 029 * of four digits, separated by a hyphen. The eighth character is a 030 * control digit calculated according to a modulo 11 algorithm on 031 * the basis of the 7 preceding digits; this eighth control character 032 * may be an "X" if the result of the computing is equal to "10", 033 * in order to avoid any ambiguity. 034 * 035 * The ISSN is linked to a standardized form of the title of the 036 * identified serial, known as the "key title", which repeats 037 * the title of the publication, qualifying it with additional elements 038 * in order to distinguish it from other publications having identical 039 * titles. 040 * 041 * If the title of the publication changes in any significant way, 042 * a new ISSN must be assigned in order to correspond to this new form 043 * of title and avoid any confusion. A serial publication whose 044 * title is modified several times in the course of its existence 045 * will be assigned each time a new ISSN, thus allowing precise 046 * identification of each form of the title : in fact it is then 047 * considered that they are different publications even if there 048 * is a logical link between them. 049 * 050 * Contrary to other types of publications, the world of serial 051 * publications is particularly changeable and complex : 052 * the lifetime of a title may be extremely short; many publications 053 * may be part of a complex set of relationships, etc. 054 * These particularities themselves necessitated the introduction 055 * of the ISSN. 056 * 057 */ 058public class ISSN extends AbstractStandardNumber implements Comparable<ISSN>, StandardNumber { 059 060 private final static Pattern PATTERN = Pattern.compile("[0-9xX\\-]{8,9}"); 061 062 private String value; 063 064 private String formatted; 065 066 private boolean createWithChecksum; 067 068 @Override 069 public String type() { 070 return "issn"; 071 } 072 073 @Override 074 public int compareTo(ISSN issn) { 075 return value != null ? normalizedValue().compareTo(issn.normalizedValue()) : -1; 076 } 077 078 @Override 079 public ISSN set(CharSequence value) { 080 this.value = value != null ? value.toString() : null; 081 return this; 082 } 083 084 @Override 085 public ISSN createChecksum(boolean createWithChecksum) { 086 this.createWithChecksum = createWithChecksum; 087 return this; 088 } 089 090 091 @Override 092 public ISSN normalize() { 093 Matcher m = PATTERN.matcher(value); 094 if (m.find() && value.length() >= m.end()) { 095 this.value = dehyphenate(value.substring(m.start(), m.end())); 096 } 097 return this; 098 } 099 100 @Override 101 public boolean isValid() { 102 return value != null && !value.isEmpty() && check(); 103 } 104 105 @Override 106 public ISSN verify() throws NumberFormatException { 107 if (value == null || value.isEmpty()) { 108 throw new NumberFormatException("invalid"); 109 } 110 if (!check()) { 111 throw new NumberFormatException("bad createChecksum"); 112 } 113 return this; 114 } 115 116 /** 117 * Returns the value representation of the standard number 118 * @return value 119 */ 120 @Override 121 public String normalizedValue() { 122 return value; 123 } 124 125 /** 126 * Format this number 127 * 128 * @return the formatted number 129 */ 130 @Override 131 public String format() { 132 return formatted; 133 } 134 135 @Override 136 public ISSN reset() { 137 this.value = null; 138 this.formatted = null; 139 this.createWithChecksum = false; 140 return this; 141 } 142 143 public GTIN toGTIN() throws NumberFormatException { 144 return new GTIN().set("977" + value.substring(0, 7) + "000").createChecksum(true).normalize().verify(); 145 } 146 147 public GTIN toGTIN(String additionalCode) throws NumberFormatException { 148 // "977" + ISSN + add-on + placeholder for createChecksum 149 return new GTIN().set("977" + value.substring(0, 7) + additionalCode + "0").createChecksum(true).normalize().verify(); 150 } 151 152 private boolean check() { 153 int l = createWithChecksum ? value.length() : value.length() - 1; 154 int checksum = 0; 155 int weight; 156 int val; 157 for (int i = 0; i < l; i++) { 158 val = value.charAt(i) - '0'; 159 weight = 8 - i; 160 checksum += weight * val; 161 } 162 int chk = checksum % 11; 163 char p = chk == 0 ? '0' : chk == 1 ? 'X' : (char)((11-chk) + '0'); 164 return p == Character.toUpperCase(value.charAt(l)); 165 } 166 167 private String dehyphenate(String isbn) { 168 StringBuilder sb = new StringBuilder(isbn); 169 int i = sb.indexOf("-"); 170 while (i > 0) { 171 sb.deleteCharAt(i); 172 i = sb.indexOf("-"); 173 } 174 if (sb.length() > 7) { 175 this.formatted = sb.substring(0, 4) + "-" + sb.substring(4, 8); 176 } 177 return sb.toString(); 178 } 179 180}