/* * [ISBNValidate.java] * * Summary: Library of methods to validate and format ISBNs (International Standard Book Numbers). * * Copyright: (c) 1999-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 2.3 2009-03-16 tidy code, rename some methods. */ package com.mindprod.isbn; import java.util.BitSet; import static java.lang.System.*; /** * Library of methods to validate and format ISBNs (International Standard Book Numbers). *
* Typical valid ISBNs look like this: * group/country-publisher-title#-checkdigit * examples of good formatted * ISBNs 0-00-000000-0 0-200-00000-4 0-7000-0000-3 * 0-85000-000-9 0-900000-00-7 0-9500000-0-0 0-9999999-9-0 1-00-000000-1 1-09-999999-5 1-100-00000-3 1-399-99999-0 * 1-4000-0000-9 1-4999-9999-2 1-5000-0000-0 1-5499-9999-0 1-55000-000-4 1-86979-999-2 1-869800-00-1 1-9990000-0-5 * 2-00000000-2 2-95000000-2 3-00000000-3 3-99999999-3 4-00000000-4 4-95000000-4 5-00000000-5 5-95000000-5 7-00000000-7 * 7-20000000-0 7-95000000-7 80-0000000-8 93-9999999-8 950-000000-8 950-999999-7 9949-99999-5 9950-00000-9 9959-99999-8 * 9960-00000-1 9989-99999-6 99901-9601-X * * examples of bad ISBNs 6000000006 6950000006 6999999996 9400000006 9419000001 * 949999999X 992999999X 9930000003 9934000008 9939999992 9940000006 9999900003 9999999999 * The lead 0 is significant. They always contain 10 digits, possibly with the last check digit character being X. * * ISBN-13, for now always begin 978- and use a different check digit mechanism. It has a numeric checkdigit. * * @author Roedy Green, Canadian Mind Products * @version 2.3 2009-03-16 tidy code, rename some methods. * @since 1999 */ public final class ISBNValidate { private static final boolean DEBUGGING = false; /** * define bandd of IBSN numbers formatted differently to format an ISBN, after categorising it into a range of * numbers. * Number in table is high+1 for the band. * Even illegal bands get formatted in some way */ private static final int[] BANDS = { // --------0 0-00-bbbbbb-x group 0 00.......19 1st 3rd 9th digit +20000000, // --------1 0-200-bbbbb-x 200......699 " 4th " +70000000, // --------2 0-7000-bbbb-x 7000.....8499 " 5th " +85000000, // --------3 0-85000-bbb-x 85000....89999 " 6th " +90000000, // --------4 0-900000-bb-x 900000...949999 " 7th " +95000000, // --------5 0-9500000-b-x 9500000..9999999 " 8th " 100000000, // --------6 1-00-bbbbbb-x group 1 00.......09 1st 3rd 9th digit 110000000, // --------7 1-100-bbbbb-x 100......399 " 4th " 140000000, // --------8 1-4000-bbbb-x 4000.....5499 " 5th " 155000000, // --------9 1-55000-bbb-x 55000....86979 1st 6th 9th digit 186980000, // -------10 1-869800-bb-x 869800...998999 " 7th " 199900000, // -------11 1-1999000-b-x 9990000..9999999 " 8th " 200000000, // -------12 2-rrrrrrrr-x groups 2 .. 7 1st 9th digit 800000000, // -------13 80-rrrrrrr-x groups 80 .. 94 2nd 9th digit 950000000, // -------14 950-rrrrrr-x groups 950 .. 993 3rd 9th digit 994000000, // -------15 9940-rrrrr-x groups 9940 .. 9989 4th 9th digit 999000000, // -------16 groups 99900 .. 99999 5th 9th digit 1000000000 }; /** * List of legal groups The following table gives the range remainder distribution of the group identifiers: for * pub+title 0 - 7 9 80 - 94 8 950 - 993 7 9940 - 9989 6 99900 - 99999 5 Group is the part of the number before the * first dash. as of 2005-06-19 only used to build bit map lookup see legal list at: * http://www.isbn-international.org/en/identifiers/allidentifiers.html * * We convert he list to a bit map indexed by the first 5 digits to say whether an ISBN-10 could start with those 5 * digits. For 13-digit ISBNs, we may eventually need different group tables for different 3-digit prefixes. */ private static final int[] VALID_GROUPS = { 0, 1, 2, 3, 4, 5, 7, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 98, 600, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 9940, 9941, 9942, 9943, 9944, 9945, 9946, 9947, 9948, 9949, 9950, 9951, 9952, 9953, 9954, 9955, 9956, 9957, 9958, 9959, 9960, 9961, 9962, 9963, 9964, 9965, 9966, 9967, 9968, 9970, 9971, 9972, 9973, 9974, 9975, 9976, 9977, 9978, 9979, 9980, 9981, 9982, 9983, 9984, 9985, 9986, 9987, 9988, 9989, 99901, 99902, 99903, 99905, 99906, 99908, 99909, 99910, 99911, 99912, 99913, 99914, 99915, 99916, 99917, 99918, 99919, 99920, 99921, 99922, 99923, 99924, 99925, 99926, 99927, 99928, 99929, 99930, 99931, 99932, 99933, 99934, 99935, 99936, 99937, 99938, 99939, 99940, 99941, 99942, 99943, 99944, 99945, 99946, 99947, 99948, 99949, 99950, 99951, 99952, 99953, 99954, 99955 }; /** * bit map of all valid groups, indexed by first 5 digits of ISBN currently all groups are valid, index into it to * see if that group is valid. */ private static BitSet groupValidLookup; static { buildGroupValidLookup(); } /** * Build a BitSet to check if a group is valid, static initialisation */ private static void buildGroupValidLookup() { groupValidLookup = new BitSet( 100000 ); for ( int group : VALID_GROUPS ) { // how many digits wide int width = Integer.toString( group ).length(); switch ( width ) { case 5: groupValidLookup.set( group ); break; case 4: for ( int j = group * 10; j <= group * 10 + 9; j++ ) { groupValidLookup.set( j ); } break; case 3: for ( int j = group * 100; j <= group * 100 + 99; j++ ) { groupValidLookup.set( j ); } break; case 2: for ( int j = group * 1000; j <= group * 1000 + 999; j++ ) { groupValidLookup.set( j ); } break; case 1: for ( int j = group * 10000; j <= group * 10000 + 9999; j++ ) { groupValidLookup.set( j ); } break; default: throw new IllegalArgumentException( "bug: bad VALID_GROUPS table" ); } // end switch } // end for } /** * Calculate the expected ISBN-10 checkdigit from the first 9 digits. * * @param dashlessISBN9 first 9 digits expressed as String. May not contain any extra lead/trail chars. * * @return just the check digit as a char. */ private static char calcISBN10CheckDigit( String dashlessISBN9 ) { if ( dashlessISBN9.length() != 9 ) { throw new IllegalArgumentException( "ISBN-9 must be 9 digits." ); } int sum = 0; /* 0 1 2 3 4 5 6 7 8|9 ISBN 9 9 9 0 1 9 6 0 1|X Weighting 10 9 8 7 6 5 4 3 2|1 Values 90 + 81 + 72 + 0 + 6 + 45 + 24 + 0 + 2 = 320 mod 11 = 1 divide by 11 and get remainder. If 0, checkdigit is 0 if 1 the checkdigit is X, otherwise it is 10 - remainder. */ // not your usual odd-even. work right to left, slot 8, 7 ... 0 for ( int weight = 2; weight <= 10; weight++ ) { // works even though 10th digit not there yet int digit = dashlessISBN9.charAt( 10 - weight ) - '0'; sum += digit * weight; } // end for final int remainder = sum % 11; // sum 0->0, 1->10(X), 2->9. 3->8, 4->7, 5->6, 6->5, 7->4, 8->3, 9->2, // 10->1 switch ( remainder ) { case 0: return '0'; case 1: return 'X'; default: return ( char ) ( 11 - remainder + '0' ); } } // end calcISBN10CheckDigit /** * Calculate the expected checkdigit for an ISBN-13. http://www.isbn.org/standards/home/isbn/transition.asp uses * mod-10 checkdigit with weight 3. * * @param dashlessISBN12 first 12 digits expressed as String. May not contain any extra lead/trail chars. * * @return just the check digit as a char. always a digit '0' to '9' */ private static char calcISBN13CheckDigit( String dashlessISBN12 ) { /* 0 1 2 3 4 5 6 7 8 9 A B|C ISBN 9 7 8 0 9 4 0 0 1 6 7 3|6 Weighting 1 3 1 3 1 3 1 3 1 3 1 3|1 Values 9 + 21 + 8 + 0 + 9 + 12 + 0 + 0 + 1 + 18 + 7 + 9 = 94 mod 10 = 4, 10 - 4 = 6 divide by 10 and get remainder. If 0, checkdigit is 0, otherwise it is 10 - remainder. */ if ( dashlessISBN12.length() != 12 ) { throw new IllegalArgumentException( "ISBN-12 must be 12 digits." ); } int sum = 0; // work left to right, start with weight 1, alternating weight 3 for ( int i = 0; i < 12; i++ ) { // extract digit final int digit = dashlessISBN12.charAt( i ) - '0'; // even cols weight 1, odd cols weight 3 sum += ( i & 1 ) != 0 ? digit * 3 : digit; } final int remainder = sum % 10; return ( remainder == 0 ) ? '0' : ( char ) ( '0' + 10 - remainder ); } // end calcISBN13CheckDigit /** * Some group numbers are invalid. * * @param isbn10 isbn, no dashes, 10 chars long, 0-9 possibly last char X. */ @SuppressWarnings( { "BooleanMethodIsAlwaysInverted" } ) private static void ensureISBN10GroupValid( String isbn10 ) { // first 5 digits int group; try { group = Integer.parseInt( isbn10.substring( 0, 5 ) ); } catch ( NumberFormatException e ) { throw new IllegalArgumentException( "Bad group number" ); } if ( !groupValidLookup.get( group ) ) { throw new IllegalArgumentException( "Bad group number" ); } } // end isGroupValid /** * Some publisher numbers could be invalid. They would be too long to leave room for a book number. * * @param dashlessISBN10 isbn, no dashes, 10 chars long, 0-9 possibly last char X. */ @SuppressWarnings( { "BooleanMethodIsAlwaysInverted" } ) private static void ensureISBN10PublisherValid( String dashlessISBN10 ) { try { Integer.parseInt( dashlessISBN10.substring( 0, 9 ) ); } catch ( NumberFormatException e ) { throw new IllegalArgumentException( "Bad publisher" ); } // The no known invalid publisher numbers. // There are none in group 0 & 1. // in other groups I don't know. } /** * Inserts dashes between group, publisher, title and checkdigit. * see http://isbn.org/standards/home/isbn/international/hyphenation-instructions.asp * For correct presentation, the 10 digits of an ISBN must be divided, by hyphens, into four parts: * * Part 1: The country or group of countries identifier * * Part 2: The publisher identifier * * Part 3: The title identifier * * Part 4: The check digit Group * * Where to put the dashes depends on the range of number. There are 12 different patterns. for ISBN 10. * We format the last 10 digits of an ISBN-13 the same way we would an ISBN-10 with a lead 978-. * Common usage is to drop the dashes for ISBN-13. People find them too complicated to do properly. * We don't because it makes them easier to read. * * @param dashlessISBN10or13 isbn, no dashes, 10 chars long, 0-9 possibly last char X. must be a valid ISBN number. * * @return ISBN with dashed inserted. */ private static String insertDashesInISBN10or13( String dashlessISBN10or13 ) { // first 9 digits int range; boolean isISBN13; final int length = dashlessISBN10or13.length(); try { switch ( length ) { case 10: range = Integer.parseInt( dashlessISBN10or13.substring( 0, 9 ) ); isISBN13 = false; break; case 13: range = Integer.parseInt( dashlessISBN10or13.substring( 3, 12 ) ); isISBN13 = true; break; default: return dashlessISBN10or13; } } catch ( NumberFormatException e ) { return dashlessISBN10or13; } // 0-based where first dash goes int whereFirstDash = -1; // 0-based where middle dash goes, -1 = none int whereMidDash = -1; // never varies since always single-digit check digit. // 0-based where last dash goes final int whereLastDash = 8; boolean tryAgain = false; do { // figure out where middle dash goes int band = 0; for ( int i = 0; i < BANDS.length; i++ ) { if ( range < BANDS[ i ] ) { band = i; break; } // end if } // end for switch ( band ) { // 1st 3rd 9th digit case 0: /* publisher 00 .. 19 : 0-00-bbbbbb-x */ case 6: /* publisher 10 .. 09 : 1-00-bbbbbb-x */ whereFirstDash = 0; whereMidDash = whereFirstDash + 2; break; // 1st 4th 9th digit case 1: /* publisher 200 .. 699 : 0-200-bbbbb-x */ case 7: /* publisher 100.. 399 : 1-100-bbbbb-x */ whereFirstDash = 0; whereMidDash = whereFirstDash + 3; break; // 1st 5th 9th digit case 2: /* publisher 7000 .. 8499 : 0-7000-bbbb-x */ case 8: /* publisher 4000.. 5499 : 1-4000-bbbb-x */ whereFirstDash = 0; whereMidDash = whereFirstDash + 4; break; // 1st 6th 9th digit case 3: /* publisher 85000 .. 89999 : 0-85000-bbb-x */ case 9: /* publisher 55000 .. 86979 : 0-54999-bbb-x */ whereFirstDash = 0; whereMidDash = whereFirstDash + 5; break; // 1st 7th 9th digit case 4: /* publisher 900000 .. 94999 : 0-90000-bb-x */ case 10: /* publisher 869800 .. 998999 : 0-869800-bb-x */ whereFirstDash = 0; whereMidDash = whereFirstDash + 6; break; // 1st 8th 9th digit case 5: /* publisher 9500000 .. 9999999 : 0-950000-b-x */ case 11: /* publisher 9990000 .. 9999999 : 0-9990000-b-x */ whereFirstDash = 0; whereMidDash = whereFirstDash + 7; break; // 1st 9th digit case 12:/* group codes 2 .. 7 : 2-rrrrrrrr-x */ whereFirstDash = 0; /* leave out dash between publisher and title */ break; // 2nd 9th digit case 13:/* group codes 80 .. 94: 80-rrrrrrr-x */ whereFirstDash = 1; /* leave out dash between publisher and title */ break; // 3rd 9th digit case 14:/* group codes 950..993 : 930-rrrrrr-x */ whereFirstDash = 2; /* leave out dash between publisher and title */ break; // 4th 9th digit case 15:/* group codes 9940 .. 9989 : 9940-rrrrr-x */ whereFirstDash = 3; /* leave out dash between publisher and title */ break; // 5th 9th digit case 16:/* group codes 99900 .. 99999 : 99900-rrrr-x */ whereFirstDash = 4; /* leave out dash between publisher and title */ break; } // end switch } while ( tryAgain ); // insert the three dashes StringBuilder cooked = new StringBuilder( 13 + 4 ); if ( isISBN13 ) { cooked.append( dashlessISBN10or13.substring( 0, 3 ) ); cooked.append( '-' ); } for ( int i = 0; i < 10; i++ ) { char c = dashlessISBN10or13.charAt( isISBN13 ? i + 3 : i ); cooked.append( c ); if ( i == whereFirstDash || i == whereMidDash || i == whereLastDash ) { cooked.append( '-' ); } } // end for return cooked.toString(); } // end insertDashesInISBN10or13 /** * strips out extraneous characters, leaving 10 digits (possibly X for last) stores lead and trail junk around * number for later use by sandwich. * * @param rawISBN10or13 string with lead and trail and embedded junk, e.g. punctuation, spaces. * * @return just the digits and X, ideally 10 or 13 digits worth. */ private static String strip( String rawISBN10or13 ) { // find where where lead junk ends boolean foundNumbers = false; for ( int i = 0; i < rawISBN10or13.length(); i++ ) { char c = rawISBN10or13.charAt( i ); if ( "0123456789".indexOf( c ) >= 0 ) { // We have found the beginning of the ISBN // Should really just have to look for 0, 1 or 7, but // user might think you can leave off lead 0. rawISBN10or13 = rawISBN10or13.substring( i ); foundNumbers = true; break; } } // end for if ( !foundNumbers ) { return ""; } // find where where trailing junk ends // can't run off the end of the loop for ( int i = rawISBN10or13.length() - 1; i >= 0; i-- ) { char c = rawISBN10or13.charAt( i ); if ( "0123456789Xx".indexOf( c ) >= 0 ) { // we have found the end of the ISBN rawISBN10or13 = rawISBN10or13.substring( 0, i + 1 ); break; } } // end for // remove embedded junk StringBuilder cooked = new StringBuilder( rawISBN10or13.length() ); for ( int i = 0; i < rawISBN10or13.length(); i++ ) { char c = rawISBN10or13.charAt( i ); if ( "0123456789".indexOf( c ) >= 0 ) { cooked.append( c ); } else if ( "Xx".indexOf( c ) >= 0 ) { cooked.append( 'X' ); } else { /* ignore */ } } // end for if ( cooked.length() == 14 && cooked.charAt( 13 ) == 'X' ) { cooked.delete( 13, 13 ); } return cooked.toString(); } // end strip /** * Calculate the expected ISBN-13 checkdigit from the first 12 digits. inserts hyphens. * * @param dashlessISBN12 first 12 digits expressed as String. May not contain any extra lead/trail chars. * * @return complete ISBN with checkdigit without dashes.. */ @SuppressWarnings( { "WeakerAccess" } ) public static String appendCheckDigitToISBN12( String dashlessISBN12 ) { if ( dashlessISBN12.length() != 12 ) { throw new IllegalArgumentException( "ISBN-12 must be 12 chars long without dashes, without check digit" ); } return dashlessISBN12 + calcISBN13CheckDigit( dashlessISBN12 ); } // end appendCheckDigitToISBN12 /** * Calculate the expected ISBN-10 checkdigit from the first 12 digits. inserts hyphens. * * @param dashlessISBN9 first 9 digits expressed as String. May not contain any extra lead/trail chars. * * @return complete ISBN-10 with checkdigit without dashes. */ @SuppressWarnings( { "WeakerAccess" } ) public static String appendCheckDigitToISBN9( String dashlessISBN9 ) { if ( dashlessISBN9.length() != 9 ) { throw new IllegalArgumentException( "ISBN-9 must be 9 chars long without dashes, without check digit" ); } return dashlessISBN9 + calcISBN10CheckDigit( dashlessISBN9 ); } // end appendCheckDigitToISBN9 /** * Checks if ISBN-10 is valid. * * @param dashlessISBN10 isbn, no dashes, 10 chars long, 0-9 possibly last char X. */ @SuppressWarnings( { "WeakerAccess" } ) public static void ensureISBN10Valid( String dashlessISBN10 ) { if ( dashlessISBN10.length() != 10 ) { throw new IllegalArgumentException( "ISBN-10 must be 10 digits long" ); } if ( dashlessISBN10.charAt( 9 ) == 'x' ) { throw new IllegalArgumentException( "ISBN-10 should end in a capital X" ); } ensureISBN10GroupValid( dashlessISBN10 ); ensureISBN10PublisherValid( dashlessISBN10 ); if ( !isISBN10CheckDigitValid( dashlessISBN10 ) ) { throw new IllegalArgumentException( "Bad Check Digit" ); } } /** * Checks if ISBN-13 is valid, throws Illegal ArgumenException if not. * * @param dashlessISBN13 isbn, no dashes, 13 chars long */ @SuppressWarnings( { "WeakerAccess" } ) public static void ensureISBN13Valid( String dashlessISBN13 ) { if ( dashlessISBN13 == null ) { throw new IllegalArgumentException( "ISBN-13 null." ); } if ( dashlessISBN13.length() != 13 ) { throw new IllegalArgumentException( "ISBN-13 must be 13 digits." ); } final String lead = dashlessISBN13.substring( 0, 3 ); if ( !( lead.equals( "978" ) || lead.equals( "979" ) ) ) { throw new IllegalArgumentException( "ISBN-13 must begin with 978 or 979." ); } final String isbn10Part = dashlessISBN13.substring( 3, 13 ); ensureISBN10GroupValid( isbn10Part ); // ensure publisher is ok ensureISBN10PublisherValid( isbn10Part ); // ensure check digit ok if ( !isISBN13CheckDigitValid( dashlessISBN13 ) ) { throw new IllegalArgumentException( "Bad check digit." ); } } /** * Validates checkdigit for an ISBN-10 * * @param dashlessISBN10 isbn, no dashes, 10 chars long, 0-9 possibly last char X. * * @return true if check digit is correct. * * see isbn.org for * details. Weights are 10 9 8 7 6 5 4 3 2 1 mod 11, X represents modulo 10. sum mod 11 must be 0. */ @SuppressWarnings( { "BooleanMethodIsAlwaysInverted", "WeakerAccess" } ) public static boolean isISBN10CheckDigitValid( String dashlessISBN10 ) { if ( dashlessISBN10.length() != 10 ) { throw new IllegalArgumentException( "ISBN-10 must be 10 digits long." ); } return ( calcISBN10CheckDigit( dashlessISBN10.substring( 0, 9 ) ) == dashlessISBN10.charAt( 9 ) ); } // end isISBN10CheckDigitValid /** * Validates checkdigit on ISBN-13 * * @param dashlessISBN13 isbn, no dashes, 13chars long, 0-9 * * @return true if check digit is correct. * * see isbn.org for * details. Weights are 10 9 8 7 6 5 4 3 2 1 mod 11, X represents modulo 10. sum mod 11 must be 0. */ @SuppressWarnings( { "BooleanMethodIsAlwaysInverted", "WeakerAccess" } ) public static boolean isISBN13CheckDigitValid( String dashlessISBN13 ) { if ( dashlessISBN13.length() != 13 ) { throw new IllegalArgumentException( "ISBN-13 must be 13 digits long." ); } return ( calcISBN13CheckDigit( dashlessISBN13.substring( 0, 12 ) ) == dashlessISBN13.charAt( 12 ) ); } // end isISBN13CheckDigitValid /** * converts old ISBN-10 to ISBN-13 form * * @param dashlessISBN10 ISBN:10 without dashes, with check digit. * * @return complete ISBN-13 with checkdigit without dashes. */ public static String isbn10To13( String dashlessISBN10 ) { if ( dashlessISBN10.length() != 10 ) { throw new IllegalArgumentException( "ISBN-10 must be 10 chars long without dashes, with check digit" ); } return appendCheckDigitToISBN12( "978" + dashlessISBN10.substring( 0, 9 ) ); } /** * converts new ISBN-13 to old ISBN-10 form * * @param dashlessISBN13 ISBN:13 without dashes, with check digit. * * @return complete ISBN-10 with checkdigit without dashes. Returns "n/a" if there is no equivalent ISBN-10 code. */ public static String isbn13To10( String dashlessISBN13 ) { if ( dashlessISBN13.length() != 13 ) { throw new IllegalArgumentException( "ISBN-13 must be 13 chars long without dashes, with check digit." ); } if ( dashlessISBN13.substring( 0, 3 ).equals( "978" ) ) { return appendCheckDigitToISBN9( dashlessISBN13.substring( 3, 12 ) ); } else { return ( "n/a" ); } } /** * test harness * * @param args not used. */ public static void main( String[] args ) { if ( DEBUGGING ) { String[] tests = { // 9 digits long. We append a good check digit before // testing "000000000", "039306163", /* 0-393-06163-9 978-0-393-06163-5 Making of the Fittest */ "094001673", "157675414", "020000000", "061868000", /* 0-618-68000-4 978-0-618-68000-9 Dawkins */ "070000000", "085000000", "090000000", "094001673", /* 0-940016-73-7 978-0-940016-73-6 isbn for dummies example*/ "095000000", "099999999", "100000000", "109999999", "110000000", "139999999", "140000000", "149999999", "150000000", "154999999", "155000000", "186979999", "186980000", "199900000", "200000000", "295000000", "300000000", "399999999", "400000000", "495000000", "500000000", "595000000", "600000000", "700000000", "720000000", "795000000", "800000000", "939999999", "950000000", "950999999", "994000000", "994999999", "995000000", "995999999", "996000000", "998999999", "999019601", "999555555" }; try { for ( String test : tests ) { out.println( ISBNValidate.appendCheckDigitToISBN9( test ) ); } for ( String test : tests ) { out.println( ISBNValidate.appendCheckDigitToISBN12( "978" + test ) ); } out.println( "isbn13To10" ); out.println( isbn13To10( "9780618680009" ) ); out.println( "isbn10To13" ); out.println( isbn10To13( "0618680004" ) ); out.println( "tidyISBN10or13InsertingDashes" ); out.println( tidyISBN10or13InsertingDashes( "9780618680009" ) ); out.println( tidyISBN10or13InsertingDashes( "0618680004" ) ); out.println( "tidyISBN10or13RemovingDashes" ); out.println( tidyISBN10or13RemovingDashes( "9780618680009" ) ); out.println( tidyISBN10or13RemovingDashes( "0618680004" ) ); out.println( "calcISBN13CheckDigit" ); out.println( calcISBN13CheckDigit( "978061868000" ) ); out.println( "calcISBN10CheckDigit" ); out.println( calcISBN10CheckDigit( "061868000" ) ); out.println( "isISBN13CheckDigitValid" ); out.println( isISBN13CheckDigitValid( "9780618680009" ) ); out.println( "isISBN10CheckDigitValid" ); out.println( isISBN10CheckDigitValid( "0618680004" ) ); } catch ( Exception e ) { err.println(); e.printStackTrace( err ); } } // end if debugging } /** * TOP LEVEL method, invoked when user clicks TIDY. Tidies up an ISBN by first removing stray dashes and * non-numerics. Validates checksum, checks publisher number, and inserts dashes where they belong. Discards * whatever it finds before or after the ISBN. * * @param rawISBN10or13 rawISBN text to be tidied, possibly with extra dashes, spaces or other junk chars. * * @return tidied dashed ISBN with lead and trail junk discarded. or an error message if the ISBN is invalid. */ public static String tidyISBN10or13InsertingDashes( String rawISBN10or13 ) { if ( rawISBN10or13 == null || rawISBN10or13.length() == 0 ) { throw new IllegalArgumentException( "no ISBN" ); } // strip lead, trail and embedded junk including dashes final String dashlessISBN = strip( rawISBN10or13 ); final int length = dashlessISBN.length(); switch ( length ) { case 10: ensureISBN10Valid( dashlessISBN ); return insertDashesInISBN10or13( dashlessISBN ); case 13: ensureISBN13Valid( dashlessISBN ); return insertDashesInISBN10or13( dashlessISBN ); default: throw new IllegalArgumentException( "ISBN [" + rawISBN10or13 + "] must be 10 or 13 digits long." ); } } // end tidyISBN10or13InsertingDashes /** * TOP LEVEL method, used by Amazon book macro to get ISBN without dashes.. Tidies up an ISBN by removing all dashes * and non-numerics. Validates checksum, checks publisher number, Discards whatever it finds before or after the * ISBN e.g. * * @param rawISBN10or13 rawISBN text to be tidied, may contain spaces, dashes or other junk chars. * * @return tidied dash-free ISBN with lead and trail junk discarded. or an error message if the ISBN is invalid, * will be either 10 on 13 long depending on input. */ public static String tidyISBN10or13RemovingDashes( String rawISBN10or13 ) { if ( rawISBN10or13 == null || rawISBN10or13.length() == 0 ) { throw new IllegalArgumentException( "no ISBN" ); } // strip lead, trail and embedded junk including dashes final String dashlessISBN = strip( rawISBN10or13 ); final int length = dashlessISBN.length(); switch ( length ) { case 10: ensureISBN10Valid( dashlessISBN ); return dashlessISBN; case 13: ensureISBN13Valid( dashlessISBN ); return dashlessISBN; default: throw new IllegalArgumentException( "ISBN [" + rawISBN10or13 + "] must be 10 or 13 digits long." ); } } // end tidyISBN10or13RemovingDashes }