/* * [Quoter.java] * * Summary: Performs various cleanups and conversions on text. * * 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.0 1998-12-25 initial release. * 1.1 1998-12-28 handle empty or null clipboard better. * handle Latin1, Windows and IBM OEM character set encodings. * 1.2 1999-09-10 add Java source code strings as target. * 1.3 1999-09-11 allow plain, just strip control chars * trim lead/trail spaces. * in Applet, choice changes are now Live and * trigger a convert action. * 1.4 1999-09-12 UPPER, lower and Title case conversion, * text column alignment * Java source alignment. * 1.5 1999-09-12 allow Applet command line parameter to simulate * Applet mode when running as application. * fix bug in translate table that was sometimes stripping \ns. * 1.6 1999-09-13 clearer Choice descriptions. * 1.7 1999-09-18 add collapse multiple spaces * 1.8 1999-12-02 high chars -> \ u xxxx * 1.9 2000-05-15 HTML converts runs of blank lines to * 2.0 2000-08-02 Funduc Regex converters * fixed bug in Java string generator for \ u x x x x * 2.1 2001-02-02 make sure + always gets quoted in Funduc search strings * reorganise the code around TextProcessor and Translator * base classes. * 2.2 2001-02-02 avoid surrounding results in quotes * 2.3 2002-03-06 preserve line breaks in generated HTML * now see and marking line breaks. * It is easier to take them out than add them manually. * 2.4 2002-06-20 strip HTML tags and entities * 2.5 2002-08-08 convert to Java style search/replace regexes. * 2.6 2003-06-06 fix bug, excess in to HTML output. * 2.7 2004-05-30 cleverer way of handling newlines when stripping tags to more * closely parallel what the way the text was rendered HTML. * added an about box. * handle tabs with \t in Java string literals * 2.8 2004-06-01 handle tabs with \t in Java string literals * 2.9 2004-06-01 no longer use \' in "...", just plain ' * 3.0 2004-06-20 strip HTML tags will not be fooled by tags similar to standard ones. * 3.1 2005-07-30 use ANT, document encodings better. * more entities, use standard Entities package. * 3.2 2005-09-03 expand size of window slightly * 3.3 2006-03-05 reformat with IntelliJ, add javadoc. * Convert to JDK 1.5 and swing with enums. * 3.4 2006-03-07 remove translate feature from screen. * add char[] transform * 3.5 2007-03-26 fixes a bug in StripEntities that was mishandling * &#xffff; * 3.6 2007-04-06 tidy code. proper size when run as application. * tidy code. Convert AlignJava to an enum-based * finite state machine. * 3.7 2007-04-28 new logo, PAD. * 3.8 2007-05-02 add Vslick regex support * 3.9 2007-12-16 add support for encode/decode URL * 4.0 2008-01-29 add > as a reserve character in Java regex. * 4.1 2008-02-09 remove =<> as Java regex search reserved chars * 4.2 2008-04-07 add build to title, tidy code, correct spelling, convert Align to use enum. * Application mode now displays raw and cooked. Add Paste button. * 4.3 2008-08-06 add support for XML strip tags and entities, insert entities. * 4.4 2009-02-24 add Java string quoting to Java search/replace regexes. * 4.5 2009-02-26 add both Java string quoting and plain for Java search/regexes. * 4.6 2009-11-11 add . to list of quoted chars in Java regex. add ToCSV. * 4.7 2011-01-03 add span processor * 4.8 2011-11-15 add flow by stripping newline chars * 4.9 2011-11-17 add swap button, now signed * 5.0 2011-11-18 regex span now does both includes and excludes and displays the range two ways. * 5.1 2012-01-13 configurable look and feel * 5.2 2012-12-17 convert Java String literals back to plain strings. * 5.3 2014-07-01 now show three Regex variants at once. * 5.4 2014-07-24 correct the three Regex variants and improve the spacing. * 5.5 2014-09-13 add hex display of codepoints */ package com.mindprod.quoter; 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.JComboBox; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JScrollPane; import javax.swing.JTextArea; 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; import static com.mindprod.quoter.TargetOption.STRIP_NEWLINES; import static com.mindprod.quoter.TrimOption.TRIM; import static java.lang.System.*; /** * Performs various cleanups and conversions on text. *

* * @author Roedy Green, Canadian Mind Products * @version 5.5 2014-09-13 add hex display of codepoints * @noinspection FieldCanBeLocal * @since 1998-12-25 */ public final class Quoter extends JApplet implements ClipboardOwner { /** * true if want extra debugging info. * * @noinspection WeakerAccess */ static final boolean DEBUGGING = false; /** * true can do copy/paste. Always now. Previously could not as unsigned Applets. */ private static final boolean canPaste = true; /** * height of Applet box in pixels. has no effect on application width. Does not include surrounding frame. Height * to use in <applet tag. * * @noinspection UnusedDeclaration */ private static final int APPLET_HEIGHT = 528; /** * Width of Applet box in pixels, has no effect on application width. Height to use in <applet tag. * * @noinspection UnusedDeclaration */ private static final int APPLET_WIDTH = 636; private static final int FIRST_COPYRIGHT_YEAR = 1998; /** * non-displaying copyright. * * @noinspection 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 = "2014-09-13"; private static final String TITLE_STRING = "Quoter Amanuensis"; private static final String VERSION_STRING = "5.5"; private static final Color BACKGROUND_FOR_BOX = Color.WHITE; private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 )/* dark green */; private static final Color FOREGROUND_FOR_TEXT = Color.BLACK; /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * for for titles and About 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 everything button. */ private JButton clearButton; /** * convert text button. */ private JButton convertButton; /** * paste button. */ private JButton pasteButton; /** * swap input and output. */ private JButton swapButton; /** * Control to select what convert text into. */ private JComboBox targetChoice; /** * control to select what sort of blank trimming is wanted. */ private JComboBox trimChoice; /** * instructions on what to d. */ private JLabel instructions; /** * title of Applet. */ private JLabel title; /** * title, second line */ private JLabel title2; /** * converted text. */ private JTextArea cookedTextArea; /** * text input to be converted. */ private JTextArea rawTextArea; /** * what to convert the text into. */ private TargetOption targetOption; /** * which post processing to do, align, case, blank collapse. */ private TextProcessor postprocessor; /** * which preprocessing to use, usually just trim. */ private TextProcessor preprocessor; /** * which translation to use, char by char to a string. */ private Translator translator; /** * how to trim the text. */ private TrimOption trimOption; /** * 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 ); rawTextArea = new JTextArea( "", 3, 50 ); rawTextArea.setEditable( true ); rawTextArea.setForeground( FOREGROUND_FOR_TEXT ); rawTextArea.setBackground( BACKGROUND_FOR_BOX ); cookedTextArea = new JTextArea( "", 3, 50 ); cookedTextArea.setEditable( false ); cookedTextArea.setForeground( FOREGROUND_FOR_TEXT ); cookedTextArea.setBackground( BACKGROUND_FOR_BOX ); decideFonts(); targetChoice = new JComboBox<>(); // add legal choices to choice box in same order as enums. // We add the enums themselves, not the descriptive Strings. for ( TargetOption possibility : TargetOption.values() ) { targetChoice.addItem( possibility ); } targetChoice.setSelectedItem( targetOption ); // set initial value targetChoice.setMaximumRowCount( TargetOption.values().length ); trimChoice = new JComboBox<>(); for ( TrimOption possibility : TrimOption.values() ) { trimChoice.addItem( possibility ); } trimChoice.setSelectedItem( trimOption ); trimChoice.setMaximumRowCount( TrimOption.values().length ); add( trimChoice ); clearButton = new JEButton( "Clear" ); clearButton.setToolTipText( "Clear everything" ); swapButton = new JEButton( "Swap" ); swapButton.setToolTipText( "Swap the lower and upper panel contents so you can apply another transform to the output." ); convertButton = new JEButton( "Convert" ); convertButton.setToolTipText( "Convert text in upper panel" ); if ( canPaste ) { // can't paste the clipboard in an unsigned Applet pasteButton = new JEButton( "Convert Clipboard" ); pasteButton.setToolTipText( "Paste and convert clipboard" ); } instructions = new JLabel( "", JLabel.CENTER ); instructions.setFont( FontFactory.build( "Dialog", Font.PLAIN, 11 ) ); instructions.setBackground( BACKGROUND_FOR_BOX ); 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 ) { // open aboutbox frame new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Does various text manipulations.", "In application mode, the clipboard is automatic.", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "QUOTER", "1.8" ); } } ); } /** * action when Convert button pressed This is a method of quoter, not TheListener. * * @noinspection ConstantConditions */ private void convertAction() { /* get user's selections and decide work to be done */ getSelections(); /** * The original format of the clipboard before conversion. Lines in the * clipboard are separated with \n characters. There may or may not be a * final \n. I have only tested this code under NT. I am not sure if * this is universal. */ // as Applet, must rely on manual cut paste to raw and cooked // TextAreas. final String raw = rawTextArea.getText(); if ( DEBUGGING ) { // dump the raw input to view. final char[] ca = raw.toCharArray(); for ( char aCa : ca ) { out.println( aCa + " " + ( int ) aCa ); } // end for } // end if debugging /** * The converted form of the clipboard after conversion. */ String cooked = raw; // process the contents of the clipboard // This the guts of the program if ( preprocessor != null ) { cooked = preprocessor.process( cooked ); } if ( translator != null ) { cooked = translator.process( cooked ); } if ( postprocessor != null ) { cooked = postprocessor.process( cooked ); } cookedTextArea.setText( cooked ); if ( canPaste ) { // can't poke the clipboard in an unsigned Applet ClipboardPoker.setClip( cooked, this ); } // end else } // end ConvertAction /** * choose fonts. */ private void decideFonts() { final Font font = targetOption.getFont(); rawTextArea.setFont( font ); cookedTextArea.setFont( font ); } // end decideFonts /** * decide on the instructions to the user. */ private void decideInstructions() { instructions .setText( canPaste ? targetOption.getApplicationInstructions() : targetOption.getAppletInstructions() ); } /** * decide post processing needed. */ private void decidePostprocessor() { postprocessor = targetOption.getProcessor(); } /** * decide trim preprocessing needed. */ private void decidePreprocessor() { preprocessor = trimOption.getProcessor(); } /** * decide basic translation. */ private void decideTranslator() { translator = targetOption.getTranslator(); } /** * Get the state of the various choice boxes and set up corresponding internal variables to plan the translations * and processing we will do. This controls the main application logic. */ private void getSelections() { // get contents of multiple choice fields on screen targetOption = ( TargetOption ) targetChoice.getSelectedItem(); trimOption = ( TrimOption ) trimChoice.getSelectedItem(); decideInstructions(); decidePreprocessor(); decideTranslator(); decidePostprocessor(); decideFonts(); } // end getSelections /** * register listeners */ private void hookListeners() { // REGISTER LISTENER final TheListener theListener = new TheListener(); clearButton.addActionListener( theListener ); swapButton.addActionListener( theListener ); convertButton.addActionListener( theListener ); if ( canPaste ) { pasteButton.addActionListener( theListener ); } targetChoice.addActionListener( theListener ); trimChoice.addActionListener( theListener ); } /** * layout the components * * @param contentPane where to place the Components */ private void layoutComponents( Container contentPane ) { // Applet Gridbag: // 0------1---2---- 3---- // title------------title2- 0 // --------raw------------ 1 // -------cooked---------- 2 // targetChoice trimchoice 3 // clear swap paste convert 4 // -----instructions------- 5 // 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, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title2, new GridBagConstraints( 2, 0, 2, 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 // put inside a scroller contentPane.add( new JScrollPane( rawTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ), new GridBagConstraints( 0, 1, 4, 1, 0.5, 0.5, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 0, 10, 10, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( new JScrollPane( cookedTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ), new GridBagConstraints( 0, 2, 4, 1, 0.5, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 0, 10, 10, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( targetChoice, new GridBagConstraints( 0, 3, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 0, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( trimChoice, new GridBagConstraints( 2, 3, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 0, 5, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( clearButton, new GridBagConstraints( 0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, 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( swapButton, new GridBagConstraints( 1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 10, 5, 5 ), 0, 0 ) ); if ( canPaste ) { // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( pasteButton, new GridBagConstraints( 2, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 5, 5, 5, 5 ), 0, 0 ) ); } // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( convertButton, new GridBagConstraints( 3, 4, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 5, 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, 5, 4, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 0, 10, 10, 10 ), 0, 0 ) ); } /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { HybridJ.fireup( new Quoter(), 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. */ @Override public void destroy() { clearButton = null; convertButton = null; cookedTextArea = null; instructions = null; pasteButton = null; postprocessor = null; preprocessor = null; rawTextArea = null; swapButton = null; targetChoice = null; targetOption = null; title2 = null; title = null; translator = null; trimChoice = null; trimOption = 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 ) ) { // effectively abort return; } buildMenu(); Container contentPane = this.getContentPane(); contentPane.setLayout( new GridBagLayout() ); contentPane.setBackground( BACKGROUND_FOR_BOX ); targetOption = STRIP_NEWLINES; trimOption = TRIM; buildComponents(); getSelections(); layoutComponents( contentPane ); hookListeners(); 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 datatransfer.ClipboardOwner. * * @param clipboard clipboard handle. * @param contents contents of clipboard. */ public final void lostOwnership( Clipboard clipboard, Transferable contents ) { // we don't care } /** * Inner class to handle button events. */ @SuppressWarnings( { "WeakerAccess" } ) final class TheListener implements ActionListener { public void actionPerformed( ActionEvent event ) { final Object source = event.getSource(); if ( source == convertButton || source == targetChoice || source == trimChoice ) { convertAction(); } else if ( source == pasteButton ) { // automatically paste clipboard into upper window, then convert. rawTextArea.setText( ClipboardPoker.getClip( Quoter.this ) ); convertAction(); } else if ( source == clearButton ) { rawTextArea.setText( null ); cookedTextArea.setText( null ); } else if ( source == swapButton ) { final String oldRaw = rawTextArea.getText(); final String oldCooked = cookedTextArea.getText(); rawTextArea.setText( oldCooked ); cookedTextArea.setText( oldRaw ); } } // end actionPerformed } // end TheListener inner Class }