Java tutorial
/** * Copyright (c) 2014 Matthias Jaenicke <matthias.jaenicke@student.kit.edu>, * Matthias Plappert <undkc@student.kit.edu>, * Julien Duman <uncyc@student.kit.edu>, * Christian Dreher <uaeef@student.kit.edu>, * Wasilij Beskorovajnov <uajkm@student.kit.edu> and * Aydin Tekin <aydin.tekin@student.kit.edu> * * Released under the MIT license (refer to LICENSE.md) * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package edu.kit.iks.Cryptographics.Caesar.Experiment; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.InputStream; import javax.swing.BorderFactory; import javax.swing.JTextField; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import org.xnap.commons.i18n.I18n; import edu.kit.iks.Cryptographics.VisualizationContainerController; import edu.kit.iks.CryptographicsLib.AbstractVisualizationController; import edu.kit.iks.CryptographicsLib.AbstractVisualizationInfo; import edu.kit.iks.CryptographicsLib.AlphabetStripView; import edu.kit.iks.CryptographicsLib.Configuration; import edu.kit.iks.CryptographicsLib.ImageView; import edu.kit.iks.CryptographicsLib.KeyboardView; import edu.kit.iks.CryptographicsLib.Logger; import edu.kit.iks.CryptographicsLib.MouseClickListener; /** * Controller for the first and second step of the experiment phase. When user has to put input and * encrypt it and in the second step to decrypt a given cipher. Controls the needed elements from * {@link CryptoExperimentView} * * @author Wasilij Beskorovajnov. * */ public class CryptoExperimentController extends AbstractVisualizationController { /** * localization. */ private static I18n i18n = Configuration.getInstance().getI18n(CryptoExperimentController.class); /** * Indicates what phase of the experiment currently is. */ private boolean decryptionPhase = false; /** * When 0 user has finished the experiment successfully. */ private int editableFields; /** * Model of this controller. */ private CryptoModel model; private Element cryptoResources; /** * Constructor. * * @param visualizationInfo */ public CryptoExperimentController(AbstractVisualizationInfo visualizationInfo) { super(visualizationInfo); SAXBuilder saxBuilder = new SAXBuilder(); // obtain file object InputStream is = this.getClass().getResourceAsStream("/caesar/CaesarResources.xml"); try { // converted file to document object Document document = saxBuilder.build(is); // get root node from xml this.cryptoResources = document.getRootElement(); } catch (JDOMException | IOException e) { Logger.error(e); } } @Override public String getHelp() { if (this.decryptionPhase) { return CryptoExperimentController.i18n .tr("Remember: If you want to decrypt for example D with the key of 3." + " You need to substract 3 from the position of D in the alphabet." + " D - key = 4 - 3 = 1 = A. And if you get a negative Value add 26." + " For example: A - 8 = 1 - 8 = -7. Now add 26 + (-7) = 19 = S."); } return CryptoExperimentController.i18n .tr("Remember: If you want to encrypt for example A with the key of 3." + " You need to add 3 to the position of A in the alphabet." + " A + key = 1 + 3 = 4 = D. And if you get a value that is bigger then 26" + " then substract 26 from it. For example: S + 8 = 19 + 8 = 27." + " Now substract 26: 27 - 26 = 1 = A."); } @Override public void loadView() { this.view = new CryptoExperimentView(); this.model = CryptoModel.getInstance(); // set the initial value. this.editableFields = 2; // Create all needed ActionListener. this.generateGeneratorButtonActionListener(); // generate listener for the textfield of the key. this.generateKeyInputFocusListener(); this.generateKeyInputActionListener(); // generate listener for the literal input textfield. this.generateLiteralInputFocusListener(); this.generateLiteralInputActionListener(); // Listener for Back/next buttons. this.generateNavigationListener(); } // ------------------------------------------------------------------// // --------------------private methods-------------------------------// /** * Function for generating ActionListener for the needed input fields, after the user typed a * string to encrypt. * * @param inputChars */ private void generateIOListener(char[] inputChars) { // Generate Listener for the userOutput JTextfield for (int i = 0; i < inputChars.length; i++) { // Needed for delegating to the inner type ActionListener, when the actionEvent from the // Button "ENTER" on the KEayboard is fired. final JTextField userOutput = this.getView().getUserOutput()[i]; this.generateUserOutputFocusListener(i); this.generateUserOutputActionListener(i, userOutput); } } private void generateUserOutputActionListener(int i, final JTextField userOutput) { this.getView().getUserOutput()[i].addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (userOutput.isEditable()) { int key = 0; try { key = Integer.parseInt(CryptoExperimentController.this.getView().getKeyInput().getText()); } catch (NumberFormatException e1) { Logger.error(e1); } // If the phase is decrypting use dec, else the phase is // encrypting, therefore use enc. String encryptedOrDecryptedcipher = ""; if (CryptoExperimentController.this.decryptionPhase) { encryptedOrDecryptedcipher = CryptoExperimentController.this.getModel().dec(key, userOutput.getName()); } else { encryptedOrDecryptedcipher = CryptoExperimentController.this.getModel().enc(key, userOutput.getName()); } if ((encryptedOrDecryptedcipher).equals(userOutput.getText())) { CryptoExperimentController.this.setFeedbackImage("CaesarPositive"); // user encrypted/decrypted correctly. if ((CryptoExperimentController.this.getEditableFields() - 1) != 0) { // still some editabe fields left. CryptoExperimentController.this.notifyUserValidAction(userOutput); } else { // User encrypted all characters valid. CryptoExperimentController.this.notifyUserFinishedExperiment(userOutput); } } else { userOutput.setText(""); CryptoExperimentController.this.setFeedbackImage("CaesarNegative"); // User encrypted invalid! Need another try. CryptoExperimentController.this.notifyUserInvalidAction(userOutput); } } } }); } private void setFeedbackImage(String resourceID) { this.getView().getFeedback().removeAll(); String path = ""; try { path = CryptoExperimentController.this.getCryptoResources().getChild("CryptoResources") .getChild(resourceID).getAttributeValue("path"); } catch (NullPointerException nullException) { // Element not found. System.out.println("[NullPointerException] Ressource not found."); nullException.printStackTrace(); } GridBagConstraints imgConstraint = new GridBagConstraints(); imgConstraint.gridx = this.getView().getUserInput().length + 1; imgConstraint.gridy = 0; ImageView imageToSet = new ImageView(path); this.getView().getFeedback().add(imageToSet, imgConstraint); this.getView().validate(); this.getView().repaint(); } private void notifyUserInvalidAction(JTextField userOutput) { this.getView().getExplanations().setText(this.getModel().genRandomBlamings()); userOutput.requestFocus(); } private void notifyUserFinishedExperiment(JTextField userOutput) { userOutput.setBorder(BorderFactory.createLineBorder(Color.green)); this.getView().requestFocus(); userOutput.setEditable(false); this.getView().getAlphabet().unHighlight(userOutput.getName().charAt(0) - this.getModel().ASCII_UC_A); String notifyString = ""; if (!this.decryptionPhase) { notifyString = this.wrapHtml(CryptoExperimentController.i18n.tr("All done right!") + " " + this.getModel().genRandomGrats() + " " + CryptoExperimentController.i18n .tr("The next step is to decrypt a given message! Let's move on to decrypting.")); } else { notifyString = this.wrapHtml(CryptoExperimentController.i18n.tr("All done right!") + " " + this.getModel().genRandomGrats() + " " + CryptoExperimentController.i18n.tr( "In the next stage of the visualization you will learn how to decrypt without a key.")); } this.getView().getExplanations().setText(notifyString); this.getView().removeKeyboard(); this.getView().removeAlphabet(); this.getView().getNavigationPanel().remove(this.getView().getNextButton()); GridBagConstraints nextConst = new GridBagConstraints(); nextConst.anchor = GridBagConstraints.CENTER; nextConst.weightx = 1.0; nextConst.weighty = 0.1; nextConst.gridx = 1; nextConst.gridy = 1; nextConst.gridwidth = 26; nextConst.gridheight = 2; this.getView().add(this.getView().getNextButton(), nextConst); this.getView().requestFocus(); } private void notifyUserValidAction(JTextField userOutput) { userOutput.setBorder(BorderFactory.createLineBorder(Color.green)); userOutput.setEditable(false); this.setEditableFields(this.getEditableFields() - 1); this.getView().getExplanations().setText( this.getModel().genRandomGrats() + " " + CryptoExperimentController.i18n.trn("You have {0} left!", "You have {0} left!", this.getEditableFields(), this.getEditableFields())); this.getView().getUserOutput()[CryptoExperimentController.this.getView().getUserOutput().length - this.getEditableFields()].requestFocus(); } private void generateUserOutputFocusListener(int i) { this.getView().getUserOutput()[i].addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { JTextField output = (JTextField) e.getSource(); CryptoExperimentController.this.resetUnfocusedTextfield(); if (CryptoExperimentController.this.getView().getKeyboard() != null) { CryptoExperimentController.this.getView().removeKeyboard(); } if (output.isEditable()) { // highlights the character in the alphabet. CryptoExperimentController.this.highlightUserFocus(output); } } @Override public void focusLost(FocusEvent e) { // Nothing to do here. } }); } private void highlightUserFocus(JTextField output) { int charToEncryptAscii = output.getName().charAt(0); AlphabetStripView viewAlphabet = CryptoExperimentController.this.getView().getAlphabet(); viewAlphabet.highlight(charToEncryptAscii - CryptoExperimentController.this.getModel().ASCII_UC_A); output.setBorder(BorderFactory.createLineBorder(Color.blue, 5)); CryptoExperimentController.this.getView().createKeyboard(output, KeyboardView.CHAR_MODE); } private void resetUnfocusedTextfield() { for (JTextField outputIterator : this.getView().getUserOutput()) { if (outputIterator.getText() != null && this.getView().getAlphabet() != null) { int charToEncryptAscii = outputIterator.getName().charAt(0); AlphabetStripView viewAlphabet = this.getView().getAlphabet(); viewAlphabet.unHighlight(charToEncryptAscii - this.getModel().ASCII_UC_A); if (outputIterator.isEditable()) { outputIterator.setBorder(BorderFactory.createLineBorder(Color.darkGray)); } } } } private void generateNavigationListener() { this.getView().getBackButton().addActionListener(new ActionListener() { /* * @see java.awt.event.ActionListener#actionPerformed(java.awt .event.ActionEvent) */ @Override public void actionPerformed(ActionEvent event) { // When switching back from demonstration to // experiment in the encryption phase, the variable will remain set to true when not // reset. if (CryptoExperimentController.this.decryptionPhase) { CryptoExperimentController.this.decryptionPhase = false; } // load next state. VisualizationContainerController containerController = (VisualizationContainerController) CryptoExperimentController.this .getParentController(); containerController.presentPreviousVisualizationController(); } }); this.getView().getNextButton().addActionListener(new ActionListener() { /* * @see java.awt.event.ActionListener#actionPerformed(java.awt .event.ActionEvent) */ @Override public void actionPerformed(ActionEvent event) { if (!CryptoExperimentController.this.decryptionPhase) { CryptoExperimentController.this.getView() .remove(CryptoExperimentController.this.getView().getNextButton()); // set up the aligment of the button Next; CryptoExperimentController.this.getView().getNextButton() .setPreferredSize(new Dimension(300, 50)); CryptoExperimentController.this.getView().getNavigationPanel() .add(CryptoExperimentController.this.getView().getNextButton(), BorderLayout.EAST); // Keys bigger then 10 could be too annoying. 1 as key is also too simple. int key = CryptoExperimentController.this.getModel().generateRandomInt(2, 10); // start Decryption! if (CryptoExperimentController.this.getView().getKeyboard() != null) { CryptoExperimentController.this.getView().removeKeyboard(); } CryptoExperimentController.this.decryptionPhase = true; char[] cipher = CryptoExperimentController.this.getModel() .enc(key, CryptoExperimentController.this.getModel().genRandomPlainSequence()) .toString().toCharArray(); CryptoExperimentController.this.prepareExperimentPhase(key, cipher); // set the explanations. CryptoExperimentController.this.getView().getExplanations() .setText(CryptoExperimentController.this.wrapHtml(CryptoExperimentController.i18n.tr( "You have already learned much. But you've probably already asked yourself: How do I decrypt?") + "<br>" + CryptoExperimentController.i18n.tr( "It's simple: subtract the key from a given letter. And if you get a negative value add 26 to it."))); CryptoExperimentController.this.getView().getNextButton() .setText(CryptoExperimentController.i18n.tr("Go to Histograms")); } else { // load the previous state. VisualizationContainerController containerController = (VisualizationContainerController) CryptoExperimentController.this .getParentController(); containerController.presentNextVisualizationController(); } } }); } private void generateLiteralInputActionListener() { this.getView().getLiteralInput().addActionListener(new ActionListener() { /* * @see java.awt.event.ActionListener#actionPerformed(java.awt.event. * ActionEvent) */ @Override public void actionPerformed(ActionEvent e) { if (CryptoExperimentController.this.getView().getLiteralInput().isEditable()) { // input possible. String explanationContent = CryptoExperimentController.this.getView().getExplanations() .getText(); String input = CryptoExperimentController.this.getView().getLiteralInput().getText(); if (CryptoExperimentController.this.getModel().isInputValid(input)) { // input is valid. if ((CryptoExperimentController.this.getEditableFields() - 1) != 0) { // for proceeding key input is needed. CryptoExperimentController.this.notifyUserValidLiteralInput(explanationContent); } else { // it was the last needed input. int key = 0; try { key = Integer.parseInt( CryptoExperimentController.this.getView().getKeyInput().getText()); } catch (NumberFormatException e1) { e1.printStackTrace(); } // refactor the input into an character array. char[] inputChars = new char[input.length()]; input.getChars(0, input.length(), inputChars, 0); CryptoExperimentController.this.getView().getKeyInput().setBorder(null); CryptoExperimentController.this.getView().removeKeyboard(); CryptoExperimentController.this.prepareExperimentPhase(key, inputChars); } } else { CryptoExperimentController.this.getView().getExplanations() .setText(CryptoExperimentController.i18n .tr("Your input is invalid. Please try another one!")); } } } }); } private void notifyUserValidLiteralInput(String explanationContent) { this.getView().getExplanations().setText(explanationContent + "<br>" + CryptoExperimentController.i18n.tr("This input is ok. Now only the key is left.")); this.getView().getLiteralInput().setBorder(BorderFactory.createLineBorder(Color.green)); this.getView().getLiteralInput().setEditable(false); this.setEditableFields((this.getEditableFields() - 1)); this.getView().getKeyInput().requestFocus(); } private void generateGeneratorButtonActionListener() { this.getView().getGenerator().addMouseListener(new MouseClickListener() { @Override public void clicked(MouseEvent e) { if (CryptoExperimentController.this.getView().getNumpad() != null) { CryptoExperimentController.this.getView().removeNumpad(); } // make some preparations. char[] generatedCharacters = CryptoExperimentController.this.getModel().genRandomPlainSequence() .toCharArray(); // Keys bigger then 10 can be annoying and smaller then 2 senseless. int key = CryptoExperimentController.this.getModel().generateRandomInt(2, 10); CryptoExperimentController.this.prepareExperimentPhase(key, generatedCharacters); } }); } private void generateLiteralInputFocusListener() { this.getView().getLiteralInput().addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { if (CryptoExperimentController.this.getView().getKeyInput().getBorder() != null && CryptoExperimentController.this.getView().getKeyInput().isEditable()) { CryptoExperimentController.this.getView().getKeyInput() .setBorder(BorderFactory.createLineBorder(Color.black)); } if (CryptoExperimentController.this.getView().getKeyboard() != null) { CryptoExperimentController.this.getView().removeKeyboard(); } else if (CryptoExperimentController.this.getView().getNumpad() != null) { CryptoExperimentController.this.getView().removeNumpad(); } if (CryptoExperimentController.this.getView().getLiteralInput().isEditable()) { CryptoExperimentController.this.getView().getLiteralInput() .setBorder(BorderFactory.createLineBorder(Color.blue, 5)); CryptoExperimentController.this.getView() .createKeyboard(CryptoExperimentController.this.getView().getLiteralInput()); CryptoExperimentController.this.getView().repaint(); CryptoExperimentController.this.getView().getKeyboard().repaint(); } } @Override public void focusLost(FocusEvent e) { // Nothing to do here. } }); } private void generateKeyInputActionListener() { this.getView().getKeyInput().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (CryptoExperimentController.this.getView().getKeyInput().isEditable()) { // input still possible. String explanationContent = CryptoExperimentController.this.getView().getExplanations() .getText(); if (CryptoExperimentController.this.getView().getKeyInput().getText().length() > 0) { int key = 0; try { key = Integer .parseInt(CryptoExperimentController.this.getView().getKeyInput().getText()); } catch (NumberFormatException e1) { Logger.error(e1); } if (CryptoExperimentController.this.getModel().isKeyValid(key)) { // User typed a valid key. if ((CryptoExperimentController.this.getEditableFields() - 1) != 0) { // Only literal input needed. Notify the user. CryptoExperimentController.this.notifyOfValidKey(explanationContent); } else { CryptoExperimentController.this.getView().removeNumpad(); // The key was the last needed input. Start Experiment with the // input values. String input = CryptoExperimentController.this.getView().getLiteralInput() .getText(); char[] inputChars = new char[input.length()]; input.getChars(0, input.length(), inputChars, 0); CryptoExperimentController.this.prepareExperimentPhase(key, inputChars); } } else { CryptoExperimentController.this.getView().getExplanations().setText(explanationContent + "<br>" + CryptoExperimentController.this.wrapHtml(CryptoExperimentController.i18n .tr("This key is not valid. Please put a number between 1 and 26." + " For demonstration purposes the keys between -1 and -26 are not necessary" + " therefore not possible, but could be used in general as keys too."))); CryptoExperimentController.this.getView().getKeyInput() .setBorder(BorderFactory.createLineBorder(Color.red)); } } else { CryptoExperimentController.this.getView().getExplanations().setText(explanationContent + "<br>" + CryptoExperimentController.i18n.tr("The key field is empty!")); CryptoExperimentController.this.getView().getKeyInput() .setBorder(BorderFactory.createLineBorder(Color.red)); } } } }); } private void prepareExperimentPhase(int key, char[] input) { this.setEditableFields(input.length); // load the view! this.getView().setupExperimentCore(input, key); // Generate Listener for the userOutput JTextfield this.generateIOListener(input); this.getView().getUserOutput()[0].requestFocus(); } private void notifyOfValidKey(String explanationContent) { this.getView().getExplanations().setText(explanationContent + " " + CryptoExperimentController.i18n .tr("This key is ok. Now put your name into the bigger box to the left.")); this.getView().getKeyInput().setBorder(BorderFactory.createLineBorder(Color.green)); this.getView().getKeyInput().setEditable(false); this.setEditableFields((this.getEditableFields() - 1)); this.getView().getLiteralInput().requestFocus(); } private void generateKeyInputFocusListener() { this.getView().getKeyInput().addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { if (CryptoExperimentController.this.getView().getLiteralInput().getBorder() != null && CryptoExperimentController.this.getView().getLiteralInput().isEditable()) { CryptoExperimentController.this.getView().getLiteralInput() .setBorder(BorderFactory.createLineBorder(Color.black)); } if (CryptoExperimentController.this.getView().getKeyboard() != null) { CryptoExperimentController.this.getView().removeKeyboard(); } else if (CryptoExperimentController.this.getView().getNumpad() != null) { CryptoExperimentController.this.getView().removeNumpad(); } if (CryptoExperimentController.this.getView().getKeyInput().isEditable()) { CryptoExperimentController.this.getView().getKeyInput() .setBorder(BorderFactory.createLineBorder(Color.blue, 5)); CryptoExperimentController.this.getView() .createNumpad(CryptoExperimentController.this.getView().getKeyInput()); } } @Override public void focusLost(FocusEvent e) { // Nothing to do here. } }); } /** * @return the editableFields */ public int getEditableFields() { return this.editableFields; } /** * @return the model */ public CryptoModel getModel() { return this.model; } /** * Gets the view * * @return The view of this controller */ @Override public CryptoExperimentView getView() { return (CryptoExperimentView) this.view; } /** * @return the decryptionPhase */ public boolean isDecryptionPhase() { return this.decryptionPhase; } /** * @param editableFields * the editableFields to set */ public void setEditableFields(int editableFields) { this.editableFields = editableFields; } /** * */ public void unloadInOut() { this.getView().remove(this.getView().userCharacterIOContainer()); this.getView().setInOutPanel(null); } @Override public void unloadView() { } private String wrapHtml(String text) { return "<html><body><div width=900px>" + text + "</div></body></html>"; } /** * @return the cryptoResources */ public Element getCryptoResources() { return cryptoResources; } /** * @param cryptoResources * the cryptoResources to set */ public void setCryptoResources(Element cryptoResources) { this.cryptoResources = cryptoResources; } }