/* * [ISBN.java] * * Summary: Applet to validate and format ISBNs (International Standard Book Numbers). * * Copyright: (c) 1998-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: * 1.1 1998-12-28 handle empty or null clipboard better. * 1.2 1998-12-31 handle variable first dash position. * 1.3 1999-01-07 check for invalid publisher number. * correctly handle group codes 80.. 99990. * 1.4 2000-06-07 looser publisher number checks on high groups. * No longer attempt to place dash between * publisher and title for groups other than 0 and 1. * 1.5 2001-02-09 check on group number * 1.6 2002-02-17 add support for 1-4000-4728-5, new ranges * formerly illegal. * update list of valid groups. * Fix bug that was marking some groups ending in 9 as invalid. * 1.7 2005-06-18 add about box, tidy code, use stomp * minor change in hyphenation rules. * precise list of valid groups, generated from web page. * reorganised code in validate class to more cleanly handle * lead and trail junk. Now works better when ISBN has surrounding junk. * 1.8 2005-06-19 methods no longer preserve enclosing junk. * 1.9 2005-12-16 adds Javadoc * 2.0 2006-03-05 reformat with IntelliJ and add Javadoc. * 2.1 2007-08-24 convert to JDK 1.5. handle ISBN-13 * 2.2 2008-04-04 add build number to title, tidy code, fix spelling errors. * 2.3 2009-03-16 tidy code, rename some methods. * 2.4 2011-09-30 now display isbn-10 and isbn-13 both with and without dashes. * 2.5 2011-12-03 configurable look and feel */ package com.mindprod.isbn; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.ClipboardPoker; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.JEButton; import com.mindprod.common18.Laf; import com.mindprod.common18.VersionCheck; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JTextArea; import javax.swing.JTextField; import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * Applet to validate and format ISBNs (International Standard Book Numbers). *
* The ISBN Amanuensis will help you proofread, validate, convert * and tidy ISBN (International Standard Book Number) * references in your HTML or other documentation. * * Description of the hyphenation rules. * * http://www.isbn.org/standards/home/isbn/international/hyphenation-instructions.asp * * his program does not deal with the new 13-digit bar-code ISBNs. * * Here are some sample ISBNs correctly formatted: * * 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 * * Unfortunately the program cannot correctly place the dash * between publisher and title for groups other than 0 and 1. * * e.g. it does not currently hyphenate these Brazilian ISBNs correctly * 85-7393-116-7 * 85-216-0551-X * * The lead 0 is significant. ISBNs always contain 10 or 13 digits. * * It takes the rawISBN ISBN (which may contain dashes, lead, trail * or embedded spaces or other miscellaneous junk), validates * the check digit and insert the dashes in the proper places * using a set of rules described at * * Just paste the rawISBN ISBN into the left window, click Tidy * then copy the tidied ISBN from the right window. * * When you run the ISBN Amanuensis as an application, it is * even more automatic. You don't need to do any manual pasting * and copying. Just click Tidy to tidy the current contents of * the clipboard. The tidied ISBN replaces the old the rawISBN text * clipboard contents. This automatic feature is not available * in the Applet version because the Applet sandbox considers * it a security risk. * * @author Roedy Green, Canadian Mind Products * @version 2.5 2011-12-03 configurable look and feel * @see com.mindprod.isbn.ISBNValidate * @since 1998 */ public final class ISBN extends JApplet implements ClipboardOwner { // todo: // prepare a CSV file of triples, low, high, pattern. eg. // 000000000, 199999999, 0-00-bbbbbb-x. The pattern can be formed with 0-9 a-z. - is dash. // low and high are 9 digits with out dashs, without the check digit. // must be sorted by low, much cover all numbers. // Gets turned into a binary search list. // The list might be automatically generated from ISBN standards XML file. // May use same table for 10 and 13 or something similar for 13, with not all numbers covered. // or could just have a big list of examples, including on for first and last of a range. // If put extras not needed in, they would be deleted. // Read a binary resourc eof band objests : low value, high value, array of offsets to insert -. // scheme could handle variable # of dashes in future. // instead of absolute offsets in undashed or dashed version where - go, might code // the length of each chunk between dashes. Would be easier to proofread/debug. // could also build by strings rather than char by char. /** * height of Applet box in pixels. Does not include surrounding frame. */ private static final int APPLET_HEIGHT = 380; /** * Width of Applet box in pixels. */ private static final int APPLET_WIDTH = 640; private static final int FIRST_COPYRIGHT_YEAR = 1998; /** * undisplayed copyright notice */ @SuppressWarnings( { "UnusedDeclaration" } ) private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; private static final String RELEASE_DATE = "2011-12-03"; private static final String TITLE_STRING = "CMP ISBN Validator"; private static final String VERSION_STRING = "2.5"; private static final Color BACKGROUND_FOR_APPLET = new Color( 0xf6fff6 ); private static final Color BACKGROUND_FOR_EDITABLE = Color.WHITE; private static final Color BACKGROUND_FOR_UNEDITABLE = new Color( 0xdef9ff ); private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 ); private static final Color FOREGROUND_FOR_LABEL = new Color( 0x0000b0 ); /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); private static final Font FONT_FOR_LABELS = FontFactory.build( "Dialog", Font.BOLD, 15 ); /** * for for titles and buttons buttons */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); /** * for for title second line */ private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * clear ISBN */ private JButton clearButton; /** * paste ISBN from clipboard */ private JButton pasteButton; /** * tidy the ISBN */ private JButton tidyButton; /** * label for isbn10 */ private JLabel isbn10Lab; /** * label for isbn13 */ private JLabel isbn13Lab; /** * label for raw isbn */ private JLabel isbnRawLab; /** * title */ private JLabel title; /** * title, second line */ private JLabel title2; /** * instructions */ private JTextArea instructions; /** * result ISBN-13 with dashes. */ private JTextField cookedISBN10; /** * result ISBN-13 with dashes. */ private JTextField cookedISBN13; /** * result ISBN-13 without dashes. */ private JTextField plainISBN10; /** * result ISBN-13 without dashes. */ private JTextField plainISBN13; /** * where user enters rawISBN ISBN10 or 13 with or without dashes. */ private JTextField rawISBN; /** * allocate all components */ private void buildComponents() { title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); title2 = new JLabel( "released:" + RELEASE_DATE + " build:" + Build.BUILD_NUMBER ); title2.setFont( FONT_FOR_TITLE2 ); title2.setForeground( FOREGROUND_FOR_TITLE ); // With Applet we have two text windows you must manually cut/paste, // with Clear and Tidy Button. // With application, you have no windows, just the Tidy Button. isbnRawLab = new JLabel( "raw" ); isbnRawLab.setFont( FONT_FOR_LABELS ); isbnRawLab.setForeground( FOREGROUND_FOR_LABEL ); rawISBN = new JTextField( "", 13 + 4 + 2 ); rawISBN.setEditable( true ); rawISBN.setFont( FontFactory.build( "Dialog", Font.PLAIN, 15 ) ); rawISBN.setBackground( BACKGROUND_FOR_EDITABLE ); isbn10Lab = new JLabel( "ISBN-10" ); isbn10Lab.setFont( FONT_FOR_LABELS ); isbn10Lab.setForeground( FOREGROUND_FOR_LABEL ); plainISBN10 = new JTextField( "", 10 + 3 + 2 ); plainISBN10.setEditable( false ); plainISBN10.setBackground( BACKGROUND_FOR_UNEDITABLE ); plainISBN10.setFont( FontFactory.build( "Dialog", Font.PLAIN, 15 ) ); cookedISBN10 = new JTextField( "", 10 + 3 + 2 ); cookedISBN10.setEditable( false ); cookedISBN10.setBackground( BACKGROUND_FOR_UNEDITABLE ); cookedISBN10.setFont( FontFactory.build( "Dialog", Font.PLAIN, 15 ) ); isbn13Lab = new JLabel( "ISBN-13" ); isbn13Lab.setFont( FONT_FOR_LABELS ); isbn13Lab.setForeground( FOREGROUND_FOR_LABEL ); plainISBN13 = new JTextField( "", 13 + 4 + 2 ); plainISBN13.setEditable( false ); plainISBN13.setBackground( BACKGROUND_FOR_UNEDITABLE ); plainISBN13.setFont( FontFactory.build( "Dialog", Font.PLAIN, 15 ) ); cookedISBN13 = new JTextField( "", 13 + 4 + 2 ); cookedISBN13.setEditable( false ); cookedISBN13.setBackground( BACKGROUND_FOR_UNEDITABLE ); cookedISBN13.setFont( FontFactory.build( "Dialog", Font.PLAIN, 15 ) ); clearButton = new JEButton( "Clear" ); clearButton.setToolTipText( "Clear everything" ); pasteButton = new JEButton( "Paste" ); pasteButton.setToolTipText( "Paste ISBN, convert and copy resulting tidied ISBN-13 back to the clipboard." ); // three lines tidyButton = new JEButton( "Tidy | Convert | Validate" ); tidyButton.setToolTipText( "Convert, Tidy and validate the raw ISBN" ); instructions = new JTextArea( "" ); instructions.setEditable( false ); instructions.setMargin( new Insets( 2, 2, 2, 2 ) ); instructions.setBackground( BACKGROUND_FOR_UNEDITABLE ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); } /** * build a menu with Look & Feel and About across the top */ private void buildMenu() { // turn on anti-alias System.setProperty( "swing.aatext", "true" ); final JMenuBar menubar = new JMenuBar(); setJMenuBar( menubar ); final JMenu lafMenu = Laf.buildLookAndFeelMenu(); if ( lafMenu != null ) { menubar.add( lafMenu ); } final JMenu menuHelp = new JMenu( "Help" ); menubar.add( menuHelp ); final JMenuItem aboutItem = new JMenuItem( "About" ); menuHelp.add( aboutItem ); aboutItem.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Convert, Tidy and Validate the ISBN-10 and ISBN-13", "numbers used to identify books", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "ISBN", "1.8" ); } } ); } /** * display isbn in 10 and 13 form, with and without dashes. * * @param raw the raw input isbn may or may not have dashes */ private void displayResults( final String raw ) { try { rawISBN.setText( raw ); final String stripped = ISBNValidate.tidyISBN10or13RemovingDashes( raw ); rawISBN.setText( stripped ); final String isbn10WithoutDashes; final String isbn13WithoutDashes; final String isbn13WithDashes; final String isbn10WithDashes; switch ( stripped.length() ) { case 10: isbn10WithoutDashes = stripped; isbn13WithoutDashes = ISBNValidate.isbn10To13( isbn10WithoutDashes ); isbn10WithDashes = ISBNValidate.tidyISBN10or13InsertingDashes( isbn10WithoutDashes ); isbn13WithDashes = ISBNValidate.tidyISBN10or13InsertingDashes( isbn13WithoutDashes ); plainISBN10.setText( isbn10WithoutDashes ); plainISBN13.setText( isbn13WithoutDashes ); cookedISBN10.setText( isbn10WithDashes ); cookedISBN13.setText( isbn13WithDashes ); break; case 13: isbn13WithoutDashes = stripped; isbn10WithoutDashes = ISBNValidate.isbn13To10( isbn13WithoutDashes ); isbn13WithDashes = ISBNValidate.tidyISBN10or13InsertingDashes( isbn13WithoutDashes ); isbn10WithDashes = isbn10WithoutDashes.equals( "n/a" ) ? "n/a" : ISBNValidate.tidyISBN10or13InsertingDashes( isbn10WithoutDashes ); plainISBN10.setText( isbn10WithoutDashes ); plainISBN13.setText( isbn13WithoutDashes ); cookedISBN10.setText( isbn10WithDashes ); cookedISBN13.setText( isbn13WithDashes ); break; default: throw new IllegalArgumentException( "ISBN must be 10 or 13 digits long." ); } setDefaultInstructions(); } catch ( IllegalArgumentException oops ) { plainISBN10.setText( null ); plainISBN13.setText( null ); cookedISBN10.setText( null ); cookedISBN13.setText( null ); instructions.setText( oops.getMessage() ); } } /** * hook up ActionListener for Clear button */ private void hookClearButton() { clearButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { rawISBN.setText( null ); plainISBN10.setText( null ); plainISBN13.setText( null ); cookedISBN10.setText( null ); cookedISBN13.setText( null ); } } ); } /** * hook up ActionListener for Paste button */ private void hookPasteButton() { pasteButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { displayResults( ClipboardPoker.getClip( ISBN.this ) ); ClipboardPoker.setClip( cookedISBN13.getText(), ISBN.this ); } } ); } /** * hook up ActionListener for Tidy button */ private void hookTidyButton() { tidyButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { displayResults( rawISBN.getText() ); } } ); } /** * layout fields in a GridBag * * @param contentPane contentPane of JApplet */ private void layoutFields( Container contentPane ) { // Applet GridBag: // ----- 0 - 1--- --------- 2 ----- // --------title-------- title2------ 0 // raw ---rawISBN ---- ---------- 2 // isbn10 --plain10 -- --cooked-10- 3 // inbn13 --plain13-- --cooked-13--- 4 // Clear-------- tidy--- --(paste)- 5 // -----instructions---------------- 6 // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title2, new GridBagConstraints( 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 0, 5, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( isbnRawLab, new GridBagConstraints( 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( rawISBN, new GridBagConstraints( 1, 2, 1, 1, 50.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( isbn10Lab, new GridBagConstraints( 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( plainISBN10, new GridBagConstraints( 1, 3, 1, 1, 50.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( cookedISBN10, new GridBagConstraints( 2, 3, 1, 1, 50.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( isbn13Lab, new GridBagConstraints( 0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( plainISBN13, new GridBagConstraints( 1, 4, 1, 1, 50.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( cookedISBN13, new GridBagConstraints( 2, 4, 1, 1, 50.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( clearButton, new GridBagConstraints( 0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( tidyButton, new GridBagConstraints( 1, 5, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 10, 5, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( pasteButton, new GridBagConstraints( 2, 5, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 5, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions, new GridBagConstraints( 0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( 5, 10, 10, 10 ), 0, 0 ) ); } /** * set default instructions on how to work the Applet */ private void setDefaultInstructions() { instructions.setText( "Ctrl-C the raw ISBN-10 or 13 to the clipboard;\n" + "click Paste;\n" + "then Ctrl-V the tidied ISBN-13 to your application." ); } /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { // app is smaller than Applet since fewer fields. HybridJ.fireup( new ISBN(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } // end main /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ public void destroy() { clearButton = null; plainISBN10 = null; plainISBN13 = null; cookedISBN10 = null; cookedISBN13 = null; instructions = null; isbn10Lab = null; isbn13Lab = null; isbnRawLab = null; pasteButton = null; rawISBN = null; tidyButton = null; title2 = null; title = null; } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) ) { return; } buildMenu(); Container contentPane = this.getContentPane(); contentPane.setBackground( BACKGROUND_FOR_APPLET ); contentPane.setLayout( new GridBagLayout() ); buildComponents(); setDefaultInstructions(); layoutFields( contentPane ); // REGISTER LISTENERS hookClearButton(); hookTidyButton(); hookPasteButton(); this.validate(); this.setVisible( true ); } // end init /** * If we put data in the Clipboard, and somebody else changes the clipboard, we lose ownership and are notified * here. Satisfies the requirement of this class to implement java.awt.datatransfer.ClipboardOwner. * * @param clipboard the clipboard for this Applet * @param contents The contents of the clipboard. */ public void lostOwnership( Clipboard clipboard, Transferable contents ) { // we don't care. } }