001package org.xbib.standardnumber;
002
003import java.util.regex.Matcher;
004import java.util.regex.Pattern;
005
006/**
007 * ISO 15707 International Standard Musical Work Code (ISWC)
008 *
009 * International Standard Musical Work Code (ISWC) is a unique identifier for
010 * musical works, similar to ISBN.
011 *
012 * Its primary purpose is in collecting society administration, and to clearly identify works in
013 * legal contracts. It would also be useful in library cataloging.
014 *
015 * Due to the fact that a musical work can have multiple authors, it is inevitable that,
016 * on rare occasions, a duplicate ISWC might exist and might not be detected immediately.
017 *
018 * Because of the existing business practices among collecting societies, it is not possible
019 * to simply declare an ISWC as obsolete. In such cases, as soon as they are identified,
020 * the system will deal with duplicate registrations by linking such registration records
021 * in the ISWC database.
022 *
023 */
024public class ISWC extends AbstractStandardNumber implements Comparable<ISWC>, StandardNumber {
025
026    private static final Pattern PATTERN = Pattern.compile("[\\p{Alnum}\\-\\s]{10,13}");
027
028    private String value;
029
030    private String formatted;
031
032    @Override
033    public String type() {
034        return "iswc";
035    }
036
037    @Override
038    public int compareTo(ISWC iswc) {
039        return iswc != null ? normalizedValue().compareTo(iswc.normalizedValue()) : -1;
040    }
041
042    @Override
043    public ISWC set(CharSequence value) {
044        this.value = value != null ? value.toString() : null;
045        return this;
046    }
047
048    @Override
049    public ISWC createChecksum(boolean createChecksum) {
050        return this;
051    }
052
053    @Override
054    public ISWC normalize() {
055        Matcher m = PATTERN.matcher(value);
056        if (m.find()) {
057            this.value = clean(value.substring(m.start(), m.end()));
058        }
059        return this;
060    }
061
062    @Override
063    public boolean isValid() {
064        return value != null && !value.isEmpty() && check();
065    }
066
067    @Override
068    public ISWC verify() throws NumberFormatException {
069        if (value == null || value.isEmpty()) {
070            throw new NumberFormatException("invalid");
071        }
072        if (!check()) {
073            throw new NumberFormatException("bad createChecksum");
074        }
075        return this;
076    }
077
078    @Override
079    public String normalizedValue() {
080        return value;
081    }
082
083    @Override
084    public String format() {
085        return formatted;
086    }
087
088    @Override
089    public ISWC reset() {
090        this.value = null;
091        this.formatted = null;
092        return this;
093    }
094
095    private boolean check() {
096        int l = value.length();
097        int checksum = 1;
098        int val;
099        int weight;
100        for (int i = 1; i < l; i++) {
101            val = value.charAt(i) - '0';
102            weight = i < l - 1 ? i : 1;
103            checksum += val * weight;
104        }
105        int chk = checksum % 10;
106        return chk == 0;
107    }
108
109    private String clean(String raw) {
110        if (raw == null) {
111            return null;
112        }
113        StringBuilder sb = new StringBuilder(raw);
114        int i = sb.indexOf("-");
115        while (i >= 0) {
116            sb.deleteCharAt(i);
117            i = sb.indexOf("-");
118        }
119        i = sb.indexOf(" ");
120        while (i >= 0) {
121            sb.deleteCharAt(i);
122            i = sb.indexOf(" ");
123        }
124        if (sb.indexOf("ISWC") == 0) {
125            sb = new StringBuilder(sb.substring(4));
126        }
127        if (sb.length() > 10) {
128            this.formatted = "ISWC "
129                + "T-"
130                + sb.substring(1,10) + "-"
131                + sb.substring(10,11);
132        }
133        return sb.toString();
134    }
135}