001package org.xbib.standardnumber; 002 003import org.xbib.standardnumber.check.iso7064.MOD3736; 004 005import java.util.regex.Matcher; 006import java.util.regex.Pattern; 007 008/** 009 * ISO 15706 International Standard Audiovisual Number (ISAN) 010 * 011 * The International Standard Audiovisual Number (ISAN) is a unique identifier for 012 * audiovisual works and related versions, similar to ISBN for books. 013 * 014 * It was developed within an ISO (International Organisation for Standardisation) TC46/SC9 015 * working group. ISAN is managed and run by ISAN-IA. 016 * 017 * The ISAN standard (ISO standard 15706:2002 & ISO 15706-2) is recommended or required 018 * as the audiovisual identifier of choice for producers, studios, broadcasters, 019 * Internet media providers and video games publishers who need to encode, track, and 020 * distribute video in a variety of formats. 021 * 022 * It provides a unique, internationally recognized and permanent reference number for each 023 * audiovisual work and related versions registered in the ISAN system. 024 * 025 * ISAN identifies works throughout their entire life cycle from conception, to production, 026 * to distribution and consumption. 027 * 028 * ISANs can be incorporated in both digital and physical media, such as theatrical 029 * release prints, DVDs, publications, advertising, marketing materials and packaging, 030 * as well as licensing contracts to uniquely identify works. 031 * 032 * The ISAN identifier is incorporated in many draft and final standards such as 033 * AACS, DCI, MPEG, DVB, and ATSC. 034 */ 035public class ISAN extends AbstractStandardNumber implements Comparable<ISAN>, StandardNumber { 036 037 private static final Pattern PATTERN = Pattern.compile("[\\p{Alnum}\\-]{16,34}"); 038 039 private String value; 040 041 private String formatted; 042 043 private boolean versioned; 044 045 @Override 046 public String type() { 047 return "isan"; 048 } 049 050 @Override 051 public int compareTo(ISAN isan) { 052 return isan != null ? normalizedValue().compareTo(isan.normalizedValue()) : -1; 053 } 054 055 @Override 056 public ISAN set(CharSequence value) { 057 this.value = value != null ? value.toString() : null; 058 return this; 059 } 060 061 @Override 062 public ISAN createChecksum(boolean createWithChecksum) { 063 return this; 064 } 065 066 @Override 067 public ISAN normalize() { 068 Matcher m = PATTERN.matcher(value); 069 if (m.find()) { 070 this.value = clean(value.substring(m.start(), m.end())); 071 versioned = value.length() > 17; 072 } 073 return this; 074 } 075 076 @Override 077 public boolean isValid() { 078 return value != null && !value.isEmpty() && check(); 079 } 080 081 @Override 082 public ISAN verify() throws NumberFormatException { 083 if (value == null) { 084 throw new NumberFormatException(); 085 } 086 if (!check()) { 087 throw new NumberFormatException("invalid checksum"); 088 } 089 return this; 090 } 091 092 @Override 093 public String normalizedValue() { 094 return value; 095 } 096 097 @Override 098 public String format() { 099 return formatted; 100 } 101 102 public ISAN versioned() { 103 this.versioned = true; 104 return this; 105 } 106 107 @Override 108 public ISAN reset() { 109 this.value = null; 110 this.formatted = null; 111 this.versioned = false; 112 return this; 113 } 114 115 private final static MOD3736 check = new MOD3736(); 116 117 private boolean check() { 118 if (versioned) { 119 int chk1 = value.length() >= 17 ? check.compute(value.substring(0,17)) : -1; 120 int chk2 = value.length() >= 26 ? check.compute(value.substring(0,16) + value.substring(17,26)) : -1; 121 if (chk1 != 1) { 122 return false; 123 } 124 if (chk2 != 1) { 125 return false; 126 } 127 } else { 128 int chk = check.compute(value); 129 if (chk != 1) { 130 return false; 131 } 132 } 133 return true; 134 } 135 136 private String clean(String value) { 137 StringBuilder sb = new StringBuilder(value); 138 int i = sb.indexOf("-"); 139 while (i >= 0) { 140 sb.deleteCharAt(i); 141 i = sb.indexOf("-"); 142 } 143 i = sb.indexOf(" "); 144 while (i >= 0) { 145 sb.deleteCharAt(i); 146 i = sb.indexOf(" "); 147 } 148 this.formatted = "ISAN " 149 + (sb.length() < 4 ? sb : 150 sb.substring(0,4) + "-" 151 + (sb.length() < 8 ? sb.substring(4) : 152 sb.substring(4,8) + "-" 153 + (sb.length() < 12 ? sb.substring(8) : 154 sb.substring(8,12) + "-" 155 + (sb.length() < 16 ? sb.substring(12) : 156 sb.substring(12,16) + "-" 157 + (sb.length() < 17 ? sb.substring(16) : 158 sb.substring(16,17)))))); 159 if (sb.length() > 17) { 160 this.formatted = this.formatted + "-" 161 + (sb.length() < 21 ? sb.substring(17) : 162 (sb.substring(17, 21) + "-" 163 + (sb.length() < 25 ? sb.substring(21) : 164 (sb.substring(21, 25) + "-" 165 + (sb.length() < 26 ? sb.substring(25) : 166 sb.substring(25, 26)))))); 167 } 168 return sb.toString(); 169 } 170}