/*
* Copyright 2013, 2014 Deutsche Nationalbibliothek
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.culturegraph.mf.metamorph.functions;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.culturegraph.mf.metamorph.api.helpers.AbstractSimpleStatelessFunction;
/**
* Offers ISBN conversions.
*
* @author Markus Michael Geipel
*/
public final class ISBN extends AbstractSimpleStatelessFunction {
private static final String CHECK = "0123456789X0";
private static final String ISBN10 = "isbn10";
private static final String ISBN13 = "isbn13";
private static final Pattern ISBN_PATTERN = Pattern.compile("[\\dX]+");
private static final Pattern DIRT_PATTERN = Pattern.compile("[\\.\\-]");
private static final int ISBN10_SIZE = 10;
private static final int ISBN10_MAGIC = 10;
private static final int ISBN13_SIZE = 13;
private static final int ISBN13_MAGIC = 3;
private static final int ISBN10_MOD = 11;
private static final int ISBN13_MOD = 10;
private boolean to10;
private boolean to13;
private boolean verifyCheckDigit;
private String errorString;
public void setErrorString(final String errorString) {
this.errorString = errorString;
}
@Override
public String process(final String value) {
String result = cleanse(value);
final int size = result.length();
if (verifyCheckDigit && !isValid(result)) {
result = errorString;
} else if (!(size == ISBN10_SIZE || size == ISBN13_SIZE)) {
result = errorString;
} else {
if (to10 && ISBN13_SIZE == size) {
result = isbn13to10(result);
} else if (to13 && ISBN10_SIZE == size) {
result = isbn10to13(result);
}
}
return result;
}
public static String cleanse(final String isbn) {
String normValue = isbn.replace('x', 'X');
normValue = DIRT_PATTERN.matcher(normValue).replaceAll("");
final Matcher matcher = ISBN_PATTERN.matcher(normValue);
if (matcher.find()) {
return matcher.group();
}
return "";
}
public void setTo(final String toString) {
to13 = toString.equalsIgnoreCase(ISBN13);
to10 = toString.equalsIgnoreCase(ISBN10);
}
private static char check10(final String isbn10Data) {
assert isbn10Data.length() == ISBN10_SIZE - 1;
int check = 0;
for (int i = 0; i < ISBN10_SIZE - 1; ++i) {
final int digit = charToInt(isbn10Data.charAt(i));
check = check + (ISBN10_MAGIC - i) * digit;
}
check = ISBN10_MOD - (check % ISBN10_MOD);
return CHECK.charAt(check);
}
private static char check13(final String isbn13Data) {
assert isbn13Data.length() == ISBN13_SIZE - 1;
int accumulator = 0;
for (int i = 0; i < ISBN13_SIZE - 1; ++i) {
final int digit = charToInt(isbn13Data.charAt(i));
if ((i % 2) == 0) {
accumulator = accumulator + digit;
} else {
accumulator = accumulator + ISBN13_MAGIC * digit;
}
}
accumulator = accumulator % ISBN13_MOD;
if (accumulator != 0) {
accumulator = ISBN13_MOD - accumulator;
}
return CHECK.charAt(accumulator);
}
private static int charToInt(final char cha) {
return (byte) cha - (byte) '0';
}
public static String isbn13to10(final String isbn) {
if (isbn.length() != ISBN13_SIZE) {
throw new IllegalArgumentException(
"isbn must be 13 characters long");
}
final String isbn10Data = isbn.substring(3, 12);
return isbn10Data + check10(isbn10Data);
}
public static String isbn10to13(final String isbn) {
if (isbn.length() != ISBN10_SIZE) {
throw new IllegalArgumentException(
"isbn must be 10 characters long");
}
final String isbn13Data = "978" + isbn.substring(0, ISBN10_SIZE - 1);
return isbn13Data + check13(isbn13Data);
}
public static boolean isValid(final String isbn) {
boolean result = false;
if (isbn.length() == ISBN10_SIZE) {
result = check10(isbn.substring(0, ISBN10_SIZE - 1)) == isbn
.charAt(ISBN10_SIZE - 1);
} else if (isbn.length() == ISBN13_SIZE) {
result = check13(isbn.substring(0, ISBN13_SIZE - 1)) == isbn
.charAt(ISBN13_SIZE - 1);
}
return result;
}
public void setVerifyCheckDigit(final String verifyCheckDigit) {
this.verifyCheckDigit = "true".equals(verifyCheckDigit);
}
}