/* Obscure Text #1 - Randomly Disguise Letters and Digits in Plain Text Files Written by: Keith Fenske, http://www.psc-consulting.ca/fenske/ Wednesday, 9 November 2005 Java class name: ObscureText1 Copyright (c) 2005 by Keith Fenske. Released under GNU Public License. This is a graphical Java 1.4 Swing (GUI) application to obscure words in a text file. On selected lines, lowercase letters ("a" to "z") are replaced with other random lowercase letters, uppercase letters ("A" to "Z") with random uppercase letters, and digits ("0" to "9") with other random digits. Only the standard plain text alphabet and digits are affected; accented letters and symbols are not changed. This preserves the overall appearance of the file. The intended purpose is to view a program's general structure, without being able to compile or use the source code. Input text is read from a file chosen by the user with the "Open Input File" button. Lines are selected either sequentially (for example, every tenth line) or randomly with a given probability (for example, one in ten). The changed text is shown to the user in a scrolling text area. The original input file is not changed. The user has the option of clicking on the "Save Output As" button to save the changed text to a new file. The graphical skeleton of this program can be re-used as the base code for almost any quick-and-dirty file utility that reads a text file, makes some changes (with options), and saves the changed file. GNU General Public License (GPL) -------------------------------- ObscureText1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see the http://www.gnu.org/licenses/ web page. */ import java.awt.*; // older Java GUI support import java.awt.event.*; // older Java GUI event support import java.io.*; // standard I/O import javax.swing.*; // newer Java GUI support public class ObscureText1 { /* constants */ static final String COPYRIGHT_NOTICE = "Copyright (c) 2005 by Keith Fenske. Released under GNU Public License."; static final String[] EVERY_CHOICES = {"1", "2", "3", "4", "5", "6", "10", "12"}; // frequency for selecting input lines static final int EVERY_DEFAULT = 6; // default index in static final String[] ORDER_CHOICES = {"randomly", "sequentially"}; static final int ORDER_DEFAULT = 1; // default index in static final int ORDER_RANDOM = 0; // give name to random's index number static final String PROGRAM_TITLE = "Obscure Text - by: Keith Fenske"; static final Color TEXT_COLOR = new Color(204, 255, 255); // light cyan /* class variables */ static JComboBox everyList; // drop-down list for choosing frequency static JButton exitButton; // "Exit" button static JFileChooser fileChooser; // asks for input and output file names static JFrame mainFrame; // this application's window static JButton openButton; // "Open Input File" button static JComboBox orderList; // drop-down list for random or sequential static JTextArea outputText; // generated output text static JButton saveButton; // "Save Output As" button /* main() method We run as a graphical application only. Set the window layout and then let the graphical interface run the show. */ public static void main(String[] args) { ActionListener action; // our shared action listener /* Initialize shared graphical objects. There are no non-graphical local or class variables to initialize in this application. */ action = new ObscureText1User(); // create our shared action listener fileChooser = new JFileChooser(); // create our shared file chooser /* Create the graphical interface as a series of little panels inside bigger panels. The intermediate panel names are of no lasting importance and hence are only numbered (panel1, panel2, etc). */ /* Create a vertical box to stack buttons and options. */ Box panel1 = new Box(BoxLayout.Y_AXIS); panel1.add(Box.createVerticalStrut(9)); // extra space at panel top /* Create a horizontal panel to hold the action buttons. */ JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 50, 5)); openButton = new JButton("Open Input File..."); openButton.addActionListener(action); openButton.setToolTipText("Open text file as input."); panel2.add(openButton); saveButton = new JButton("Save Output As..."); saveButton.addActionListener(action); saveButton.setToolTipText("Save obscured text in output file."); panel2.add(saveButton); exitButton = new JButton("Exit"); exitButton.addActionListener(action); exitButton.setToolTipText("Close this program."); panel2.add(exitButton); panel1.add(panel2); panel1.add(Box.createVerticalStrut(2)); // extra space between panels /* Create a horizontal panel for options. */ JPanel panel3 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5)); panel3.add(new JLabel("Obscure one in")); everyList = new JComboBox(EVERY_CHOICES); everyList.setSelectedIndex(EVERY_DEFAULT); panel3.add(everyList); panel3.add(new JLabel("lines chosen")); orderList = new JComboBox(ORDER_CHOICES); orderList.setSelectedIndex(ORDER_DEFAULT); panel3.add(orderList); panel1.add(panel3); panel1.add(Box.createVerticalStrut(1)); // extra space at panel bottom /* Put above boxed options in a panel that is centered horizontally. */ JPanel panel4 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); panel4.add(panel1); /* Create a scrolling text area to hold the generated output. */ outputText = new JTextArea(6, 30); outputText.setBackground(TEXT_COLOR); outputText.setEditable(false); // user can't change this text area outputText.setFont(new Font("Monospaced", Font.PLAIN, 16)); outputText.setLineWrap(false); // don't allow text lines to wrap outputText.setMargin(new Insets(10, 12, 10, 12)); outputText.setText( "\nRandomly disguise letters and digits in plain text files.\n" + "\nChoose your options; then open a text file for obfuscation.\n\n" + COPYRIGHT_NOTICE + "\n\n"); /* Create the main window frame for this application. Stack buttons and options above the text area. Keep text in the center so that it expands horizontally and vertically. */ mainFrame = new JFrame(PROGRAM_TITLE); Container panel5 = mainFrame.getContentPane(); // where content meets frame panel5.setLayout(new BorderLayout(5, 5)); panel5.add(panel4, BorderLayout.NORTH); panel5.add(new JScrollPane(outputText), BorderLayout.CENTER); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.setLocation(50, 50); // top left corner of application window mainFrame.setSize(700, 500); // initial size of application window mainFrame.validate(); // do the application window layout mainFrame.setVisible(true); // show the application window /* Let the graphical interface run the application now. */ } // end of main() method // ------------------------------------------------------------------------- // /* openFile() method Open a text file and change some lines according to the options selected by the user. Generate obscured (changed) text in the output text area. */ public static void openFile() { char ch; // one input character int everyValue; // how often we select a line for change int i; // index variable BufferedReader input; // input stream StringBuffer inputBuffer; // converted to a string buffer int inputLength; // length of input line int inputNumber; // input line number String inputString; // one input line as a string boolean randomFlag; // true if lines are selected randomly, false // ... if lines are selected sequentially /* Initialize local and global variables. */ inputNumber = 0; // input line number outputText.setText(""); // clear output text area /* Get options as specified by user. No need to validate these, since they are fixed strings chosen by this program (no editing allowed). */ everyValue = Integer.parseInt((String) everyList.getSelectedItem()); if (orderList.getSelectedIndex() == ORDER_RANDOM) randomFlag = true; // select lines randomly else randomFlag = false; // select lines sequentially /* Ask the user for an input file name. */ fileChooser.setDialogTitle("Open Input File..."); if (fileChooser.showOpenDialog(mainFrame) != JFileChooser.APPROVE_OPTION) return; // user cancelled file selection dialog box /* Read lines from input file. */ try { input = new BufferedReader(new FileReader(fileChooser.getSelectedFile())); // try to open input file while ((inputString = input.readLine()) != null) // try to read one input line { inputBuffer = new StringBuffer(inputString); // need buffer for changes inputLength = inputBuffer.length(); // calculate length once only inputNumber ++; // increment input line number if (((randomFlag == false) && ((inputNumber % everyValue) == 0)) || ((randomFlag == true) && ((Math.random() * everyValue) < 1.0))) { /* Change this input line. */ for (i = 0; i < inputLength; i ++) { ch = inputBuffer.charAt(i); // get one input character if ((ch >= '0') && (ch <= '9')) // change digits, if found { inputBuffer.setCharAt(i, (char) ('0' + (int) (Math.random() * 10.0))); } else if ((ch >= 'A') && (ch <= 'Z')) // change uppercase letters { inputBuffer.setCharAt(i, (char) ('A' + (int) (Math.random() * 26.0))); } else if ((ch >= 'a') && (ch <= 'z')) // change lowercase letters { inputBuffer.setCharAt(i, (char) ('a' + (int) (Math.random() * 26.0))); } else { /* Don't change any other characters. */ } } } else { /* This input line was not chosen to be changed. */ } putOutput(inputBuffer.toString()); // put output, changed or not } input.close(); // try to close input file } catch (IOException ioe) { putOutput("Can't read from input file: " + ioe.getMessage()); return; // give up and return to caller } /* Force the output text area to scroll back to the beginning. */ outputText.select(0, 0); } // end of openFile() method /* putOutput() method Append a complete line of text to the end of the output text area. We add a newline character at the end of the line, not the caller. By forcing all output to go through this same method, one complete line at a time, the generated output is cleaner and could be redirected later (if implemented). The output text area is forced to scroll to the end, after the text line is written, by selecting character positions that are much too large (and which are allowed by the definition of the JTextComponent.select() method). This is easier and faster than manipulating the scroll bars directly. */ static void putOutput(String text) { outputText.append(text + "\n"); // put caller's text into output text area outputText.select(999999999, 999999999); // force scroll to end of text } /* saveFile() method Ask the user for an output file name, create or replace that file, and copy the contents of our output text area to that file. */ static void saveFile() { FileWriter output; // output file stream /* Ask the user for an output file name. */ fileChooser.setDialogTitle("Save Output As..."); if (fileChooser.showSaveDialog(mainFrame) != JFileChooser.APPROVE_OPTION) return; // user cancelled file selection dialog box /* Write lines to output file. */ try { output = new FileWriter(fileChooser.getSelectedFile()); // try to open output file outputText.write(output); // couldn't be much easier for writing! output.close(); // try to close output file } catch (IOException ioe) { putOutput("Can't write to output file: " + ioe.getMessage()); } } // end of saveFile() method /* userButton() method This method is called by our action listener actionPerformed() to process buttons, in the context of the main ObscureText1 class. */ static void userButton(ActionEvent event) { Object source = event.getSource(); // where the event came from if (source == exitButton) // "Exit" button { System.exit(0); // exit from this application } else if (source == openButton) // "Open Input File" button { openFile(); // open input file and read text } else if (source == saveButton) // "Save Output As" button { saveFile(); // save output text in a file } else { putOutput("Error in userButton(): ActionEvent not recognized: " + event); } } // end of userButton() method } // end of ObscureText1 class // ------------------------------------------------------------------------- // /* ObscureText1User class This class listens to input from the user and passes back event parameters to static methods in the main class. */ class ObscureText1User implements ActionListener { /* button listener */ public void actionPerformed(ActionEvent event) { ObscureText1.userButton(event); } } // end of ObscureText1User class /* Copyright (c) 2005 by Keith Fenske. Released under GNU Public License. */