001package org.xbib.standardnumber; 002 003 004import java.util.regex.Matcher; 005import java.util.regex.Pattern; 006 007/** 008 * Pica Productie Nummer 009 * 010 * A catalog record numbering system, uniquely identifying records, used by PICA 011 * (Project voor geIntegreerde Catalogus Automatisering) integrated library systems. 012 * 013 */ 014public class PPN extends AbstractStandardNumber implements Comparable<PPN>, StandardNumber { 015 016 private final static Pattern PATTERN = Pattern.compile("[\\p{Digit}]{3,10}\\-{0,1}[\\p{Digit}xX]{1}\\b"); 017 018 private String value; 019 020 private String formatted; 021 022 private boolean createWithChecksum; 023 024 @Override 025 public String type() { 026 return "ppn"; 027 } 028 029 @Override 030 public PPN set(CharSequence value) { 031 this.value = value != null ? value.toString() : null; 032 return this; 033 } 034 035 @Override 036 public int compareTo(PPN o) { 037 return value != null ? normalizedValue().compareTo(o.normalizedValue()) : -1; 038 } 039 040 @Override 041 public String normalizedValue() { 042 return value; 043 } 044 045 @Override 046 public PPN createChecksum(boolean createWithChecksum) { 047 this.createWithChecksum = createWithChecksum; 048 return this; 049 } 050 051 @Override 052 public PPN normalize() { 053 Matcher m = PATTERN.matcher(value); 054 if (m.find()) { 055 this.value = dehyphenate(value.substring(m.start(), m.end())); 056 } 057 return this; 058 } 059 060 @Override 061 public boolean isValid() { 062 return value != null && !value.isEmpty() && check(); 063 } 064 065 @Override 066 public PPN verify() throws NumberFormatException { 067 if (value == null || value.isEmpty()) { 068 throw new NumberFormatException("invalid"); 069 } 070 if (!check()) { 071 throw new NumberFormatException("bad checksum"); 072 } 073 return this; 074 } 075 076 @Override 077 public String format() { 078 if (formatted == null) { 079 StringBuilder sb = new StringBuilder(value); 080 this.formatted = sb.insert(sb.length()-1,"-").toString(); 081 } 082 return formatted; 083 } 084 085 @Override 086 public PPN reset() { 087 this.value = null; 088 this.formatted = null; 089 this.createWithChecksum = false; 090 return this; 091 } 092 093 private boolean check() { 094 int checksum = 0; 095 int weight = 2; 096 int val; 097 int l = value.length() - 1; 098 for (int i = l - 1; i >= 0; i--) { 099 val = value.charAt(i) - '0'; 100 checksum += val * weight++; 101 } 102 if (createWithChecksum) { 103 char ch = checksum % 11 == 10 ? 'X' : (char)('0' + (checksum % 11)); 104 value = value.substring(0, l) + ch; 105 } 106 return 11 - checksum % 11 == 107 (value.charAt(l) == 'X' || value.charAt(l) == 'x' ? 10 : value.charAt(l) - '0'); 108 } 109 110 private String dehyphenate(String value) { 111 StringBuilder sb = new StringBuilder(value); 112 int i = sb.indexOf("-"); 113 while (i >= 0) { 114 sb.deleteCharAt(i); 115 i = sb.indexOf("-"); 116 } 117 return sb.toString(); 118 } 119}