From 8a71899904a7e93a03e1c8d13debbd13a858829e Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Tue, 2 May 2017 11:27:29 +0200 Subject: [PATCH 1/3] Added translate from tengwar to english. --- src/net/agspace/Window.java | 43 ++++-- src/net/agspace/translate/Translator.java | 170 +++++++++++++++++++--- 2 files changed, 174 insertions(+), 39 deletions(-) diff --git a/src/net/agspace/Window.java b/src/net/agspace/Window.java index 107dba7..ea07466 100644 --- a/src/net/agspace/Window.java +++ b/src/net/agspace/Window.java @@ -17,6 +17,7 @@ import java.util.ArrayList; public class Window { + //List of files that can be imported. public static final List OPEN_FILE_EXTENSIONS = new ArrayList<>(); static { @@ -39,18 +40,16 @@ public class Window { private boolean isLive = false; - public Window() { - + Window() { + //Load the font into the system. GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Font tengwar_annatar = null; try { - tengwar_annatar = Font.createFont(Font.TRUETYPE_FONT, this.getClass().getClassLoader().getResourceAsStream("resources/tngan.ttf")); + ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, this.getClass().getClassLoader().getResourceAsStream("resources/tngan.ttf"))); } catch (FontFormatException e) { - e.printStackTrace(); + System.err.println("Font format exception!"); } catch (IOException e) { - e.printStackTrace(); + System.err.println("IOException!"); } - ge.registerFont(tengwar_annatar); //Explicit translation from english to tengwar. toTengwarButton.addActionListener(new ActionListener() { @@ -59,6 +58,7 @@ public class Window { tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText())); } }); + //Clear both text areas. clearButton.addActionListener(new ActionListener() { @Override @@ -67,10 +67,12 @@ public class Window { inputTextArea.setText(null); } }); + //Translate tengwar to english. toEnglishButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { + inputTextArea.setText(Translator.translateToEnglish(tengwarTextArea.getText())); } }); } @@ -101,20 +103,31 @@ public class Window { this.inputTextArea.requestFocus(); } - public JPanel getMainPanel(){ + JPanel getMainPanel(){ return this.mainPanel; } + /** + * If the english text is changed, and live is selected, then update the tengwar text. + */ private void onEnglishTextChanged(){ - if (isLive) + if (isLive && inputTextArea.hasFocus()) tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText())); } + /** + * If tengwar text is changed, and live is selected, then update the english text. + */ + private void onTengwarTextChanged(){ + if (isLive && tengwarTextArea.hasFocus()) + inputTextArea.setText(Translator.translateToEnglish(tengwarTextArea.getText())); + } + /** * Toggle the live button and disable translate buttons if needed. * @param e event generated by the check box. */ - public void onLiveToggled(ActionEvent e){ + void onLiveToggled(ActionEvent e){ if (((JCheckBoxMenuItem) e.getSource()).getState()){ //Deactivate buttons, set live to true. toTengwarButton.setEnabled(false); @@ -131,7 +144,7 @@ public class Window { /** * What to do if the user clicks the 'import' button. */ - public void onImportClicked(){ + void onImportClicked(){ JFileChooser fileChooser = new JFileChooser(System.getProperty("user.documents")); fileChooser.setFileFilter(new FileFilter() { @Override @@ -171,7 +184,7 @@ public class Window { /** * What to do when the user wants to save their document. */ - public void onSaveClicked(){ + void onSaveClicked(){ JFileChooser fileChooser = new JFileChooser(System.getProperty("user.documents")); int result = fileChooser.showSaveDialog(this.mainPanel); if (result == JFileChooser.APPROVE_OPTION){ @@ -186,7 +199,7 @@ public class Window { /** * Attempt to open the pdf in the browser. */ - public void onEnglishModeAboutClicked(){ + void onEnglishModeAboutClicked(){ if (Desktop.isDesktopSupported()){ try{ Desktop.getDesktop().browse(new URI("https://github.com/andrewlalis/TengwarTranslator/blob/master/src/resources/EnglishOneToOneTengwarV2-1.pdf")); @@ -199,7 +212,7 @@ public class Window { /** * Attempt to open Tengwar wikipedia page. */ - public void onTengwarAboutClicked(){ + void onTengwarAboutClicked(){ if (Desktop.isDesktopSupported()){ try{ Desktop.getDesktop().browse(new URI("https://en.wikipedia.org/wiki/Tengwar")); @@ -212,7 +225,7 @@ public class Window { /** * Attempt to open my github page. */ - public void onAboutMeClicked(){ + void onAboutMeClicked(){ if (Desktop.isDesktopSupported()){ try{ Desktop.getDesktop().browse(new URI("https://github.com/andrewlalis")); diff --git a/src/net/agspace/translate/Translator.java b/src/net/agspace/translate/Translator.java index 7f9c2b5..88e38b2 100644 --- a/src/net/agspace/translate/Translator.java +++ b/src/net/agspace/translate/Translator.java @@ -1,28 +1,12 @@ package net.agspace.translate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Created by Andrew's Computer on 23-Apr-17. */ public class Translator { - private enum CHAR_TYPE { - CONSONANT, - COMPOUND, - ALTERNATE, - VOWEL, - NUMBER, - CARRIER, - PUNCTUATION, - BAR, - S_CURL, - UNKNOWN, - } - //Consonant variables. private static final char B = 'w'; private static final char C = 'c'; @@ -184,7 +168,6 @@ public class Translator { alternateChars.put('s', S_ALT); alternateChars.put('z', Z_ALT); alternateChars.put('w', W_ALT); - //alternateChars.put('r', R_ALT); R alt is used if followed by a vowel. alternateChars.put('y', Y_ALT); } @@ -372,6 +355,92 @@ public class Translator { return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'); } + /** + * Determines if a tengwar character is a vowel. + * @param c tengwar char to check. + * @return true if vowel, false if not. + */ + private static boolean isVowelTengwar(char c){ + for (Character[] ch : vowelChars.values()){ + for (char cha : ch){ + if (cha == c) + return true; + } + } + return false; + } + + /** + * Returns the english vowel representation of a tengwar vowel. + * @param tengwarVowel The tengwar vowel to decipher. + * @return english vowel char. + */ + private static char getEnglishVowel(char tengwarVowel){ + if (!isVowelTengwar(tengwarVowel)){ + return 0; + } + for (Character[] ch : vowelChars.values()){ + for (char cha : ch){ + if (cha == tengwarVowel) { + return getKeyByValue(vowelChars, ch); + } + } + } + return 0; + } + + /** + * Determines if a tengwar char is a silent E. + * @param tengwarChar the tengwar char to evaluate. + * @return True if it is a silent E, false otherwise. + */ + private static boolean isSilentE(char tengwarChar){ + return tengwarChar == E_UNDER_1 + || tengwarChar == E_UNDER_2 + || tengwarChar == E_UNDER_3 + || tengwarChar == E_UNDER_4 + || tengwarChar == E_LAMBE; + } + + /** + * Returns a character or string that is a literal translation from tengwar. + * @param tengwarChar the tengwar char to translate. + * @return english representation of the tengwar. + */ + private static String getEnglishLiteral(char tengwarChar){ + if (consonantChars.containsValue(tengwarChar)){ + return getKeyByValue(consonantChars, tengwarChar).toString(); + } else if (compoundChars.containsValue(tengwarChar)){ + return getKeyByValue(compoundChars, tengwarChar); + } else if (alternateChars.containsValue(tengwarChar)) { + return getKeyByValue(alternateChars, tengwarChar).toString(); + } else if (tengwarChar == R_ALT){ + return "r"; + } else if (numberChars.containsValue(tengwarChar)){ + return getKeyByValue(numberChars, tengwarChar).toString(); + } else if (punctuationChars.containsValue(tengwarChar)){ + return getKeyByValue(punctuationChars, tengwarChar).toString(); + } + return null; + } + + /** + * Returns the key for a specified value in a 1 to 1 map. + * @param map The 1 to 1 map to use. + * @param value Value to return the key of. + * @param Key type of the map. + * @param Value type of the map. + * @return Key associated with value. + */ + private static T getKeyByValue(Map map, E value) { + for (Map.Entry entry : map.entrySet()) { + if (Objects.equals(value, entry.getValue())) { + return entry.getKey(); + } + } + return null; + } + /** * Translate an english string to tengwar. * @param englishString english string. @@ -380,19 +449,14 @@ public class Translator { public static String translateToTengwar(String englishString){ StringBuilder result = new StringBuilder(englishString.length()); String input = englishString.toLowerCase().trim(); - for (int i = 0; i < input.length(); i++){ //Attempt to get previous, current, and next char. char currentChar = input.charAt(i); - char previousChar = 0; char nextChar = 0; - try{ - previousChar = input.charAt(i-1); - } catch (IndexOutOfBoundsException e){ - } try{ nextChar = input.charAt(i+1); } catch (IndexOutOfBoundsException e){ + //Do nothing, since this may happen. } //Test if the current char is a vowel. if (isVowel(currentChar)){ @@ -429,6 +493,7 @@ public class Translator { } result.append(tengwarCharToBeAdded); result.append(getAppropriateVowel(tengwarCharToBeAdded, currentChar)); + //Bar gets added after the vowel. if (needsBar) result.append(bars.get(barSizes.get(tengwarCharToBeAdded))); i++; @@ -483,4 +548,61 @@ public class Translator { } return result.toString(); } + + /** + * Translates a tengwar string to english. + * @param tengwarString tengwar string. + * @return english string. + */ + public static String translateToEnglish(String tengwarString){ + StringBuilder result = new StringBuilder(tengwarString.length()+tengwarString.length()/2); + for (int i = 0; i < tengwarString.length(); i++){ + char currentChar = tengwarString.charAt(i); + char nextChar = 0; + char secondNextChar = 0; + try{ + nextChar = tengwarString.charAt(i+1); + } catch (IndexOutOfBoundsException e){ + //Do nothing, this is fine. + } + try{ + secondNextChar = tengwarString.charAt(i+2); + } catch (IndexOutOfBoundsException e){ + //Do nothing, this is fine. + } + String currentLiteral = getEnglishLiteral(currentChar); + System.out.println("At: "+i+" Literal: "+currentLiteral); + //Check if the current character is a literal translation. + if (currentLiteral != null){ + //Check if the next character is a vowel that should be placed before a character. + if (isVowelTengwar(nextChar) && !isSilentE(nextChar)){ + result.append(getEnglishVowel(nextChar)); + result.append(currentLiteral); + i++; + //Check for a double-bar, and then add whatever the current char is. + if (bars.contains(secondNextChar)){ + result.append(currentLiteral); + i++; + } + } else if (bars.contains(nextChar)){ + result.append(currentLiteral); + i++; + } else { + //Just append the current literal, no skipping. + result.append(currentLiteral); + } + } else { + //Current character is not a literal translation, so it is an E_UNDER, or carrier. + if (carriers.contains(currentChar)){ + //If carrier, append the following vowel. + result.append(getEnglishVowel(nextChar)); + i++; + } else if (isSilentE(currentChar)) { + result.append('e'); + } + } + + } + return result.toString(); + } } -- 2.34.1 From 84a1cb36d46c9bdf821e07027f73ceca06c81845 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Tue, 2 May 2017 11:47:08 +0200 Subject: [PATCH 2/3] Fixed some issues and replaced implementations with Lambda expressions. --- src/net/agspace/Main.java | 61 +++++------------------ src/net/agspace/Window.java | 47 +++++++++-------- src/net/agspace/translate/Translator.java | 1 - 3 files changed, 35 insertions(+), 74 deletions(-) diff --git a/src/net/agspace/Main.java b/src/net/agspace/Main.java index f5c5f13..3ac603b 100644 --- a/src/net/agspace/Main.java +++ b/src/net/agspace/Main.java @@ -4,10 +4,8 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.IOException; /** * @author Andrew Lalis @@ -15,8 +13,8 @@ import java.io.IOException; */ public class Main { - public static final String TITLE = "Tengwar Typewriter"; - public static final String ICON_PATH = "resources/icon.png"; + private static final String TITLE = "Tengwar Typewriter"; + private static final String ICON_PATH = "resources/icon.png"; public static void main(String[] args){ JFrame f = new JFrame(TITLE); @@ -31,31 +29,16 @@ public class Main { JMenu fileMenu = new JMenu("File"); //Save Item. JMenuItem saveItem = new JMenuItem("Save"); - saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)); - saveItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - window.onSaveClicked(); - } - }); + saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); + saveItem.addActionListener(e -> window.onSaveClicked()); fileMenu.add(saveItem); //Import Item. JMenuItem importItem = new JMenuItem("Import"); - importItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - window.onImportClicked(); - } - }); + importItem.addActionListener(e -> window.onImportClicked()); fileMenu.add(importItem); //Exit Item. JMenuItem exitItem = new JMenuItem("Exit"); - exitItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - f.dispose(); - } - }); + exitItem.addActionListener(e -> f.dispose()); fileMenu.add(exitItem); menuBar.add(fileMenu); //Edit menu. @@ -63,49 +46,29 @@ public class Main { //Live checkbox JCheckBoxMenuItem liveCheckBox = new JCheckBoxMenuItem("Live"); liveCheckBox.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.CTRL_MASK)); - liveCheckBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - window.onLiveToggled(e); - } - }); + liveCheckBox.addActionListener(e -> window.onLiveToggled(e)); editMenu.add(liveCheckBox); menuBar.add(editMenu); //About Menu. JMenu aboutMenu = new JMenu("About"); //Tengwar Item. JMenuItem tengwarItem = new JMenuItem("Tengwar"); - tengwarItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - window.onTengwarAboutClicked(); - } - }); + tengwarItem.addActionListener(e -> window.onTengwarAboutClicked()); aboutMenu.add(tengwarItem); //English Mode. JMenuItem englishModeItem = new JMenuItem("English Mode"); - englishModeItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - window.onEnglishModeAboutClicked(); - } - }); + englishModeItem.addActionListener(e -> window.onEnglishModeAboutClicked()); aboutMenu.add(englishModeItem); //About the author. JMenuItem aboutMeItem = new JMenuItem("About the Author"); - aboutMeItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - window.onAboutMeClicked(); - } - }); + aboutMeItem.addActionListener(e -> window.onAboutMeClicked()); aboutMenu.add(aboutMeItem); menuBar.add(aboutMenu); f.setJMenuBar(menuBar); f.pack(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { diff --git a/src/net/agspace/Window.java b/src/net/agspace/Window.java index ea07466..b7009e6 100644 --- a/src/net/agspace/Window.java +++ b/src/net/agspace/Window.java @@ -11,14 +11,13 @@ import java.awt.event.*; import java.io.*; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.Files; import java.util.List; import java.util.ArrayList; public class Window { //List of files that can be imported. - public static final List OPEN_FILE_EXTENSIONS = new ArrayList<>(); + private static final List OPEN_FILE_EXTENSIONS = new ArrayList<>(); static { OPEN_FILE_EXTENSIONS.add("txt"); @@ -52,29 +51,16 @@ public class Window { } //Explicit translation from english to tengwar. - toTengwarButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText())); - } - }); + toTengwarButton.addActionListener(e -> tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText()))); //Clear both text areas. - clearButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - tengwarTextArea.setText(null); - inputTextArea.setText(null); - } + clearButton.addActionListener(e -> { + tengwarTextArea.setText(null); + inputTextArea.setText(null); }); //Translate tengwar to english. - toEnglishButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - inputTextArea.setText(Translator.translateToEnglish(tengwarTextArea.getText())); - } - }); + toEnglishButton.addActionListener(e -> inputTextArea.setText(Translator.translateToEnglish(tengwarTextArea.getText()))); } private void createUIComponents() { @@ -97,6 +83,22 @@ public class Window { } }); this.tengwarTextArea = new JTextArea(); + tengwarTextArea.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + onTengwarTextChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + onTengwarTextChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + onTengwarTextChanged(); + } + }); this.toTengwarButton = new JButton(); this.toEnglishButton = new JButton(); this.clearButton = new JButton(); @@ -152,10 +154,7 @@ public class Window { if (f.isDirectory()) return true; String extension = Utils.getExtension(f); - if (extension != null) - return OPEN_FILE_EXTENSIONS.contains(extension.toLowerCase()); - else - return false; + return extension != null && OPEN_FILE_EXTENSIONS.contains(extension.toLowerCase()); } @Override diff --git a/src/net/agspace/translate/Translator.java b/src/net/agspace/translate/Translator.java index 88e38b2..430eaf7 100644 --- a/src/net/agspace/translate/Translator.java +++ b/src/net/agspace/translate/Translator.java @@ -571,7 +571,6 @@ public class Translator { //Do nothing, this is fine. } String currentLiteral = getEnglishLiteral(currentChar); - System.out.println("At: "+i+" Literal: "+currentLiteral); //Check if the current character is a literal translation. if (currentLiteral != null){ //Check if the next character is a vowel that should be placed before a character. -- 2.34.1 From e8dcd6fbdb1637a4ef9d9a0b20082fe35d30cac7 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Thu, 4 May 2017 08:09:23 +0200 Subject: [PATCH 3/3] Added line in readme about tengwar->english --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f770c1e..fd22609 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Since Tengwar is inherently a phonetic script, converting english to Tengwar wou ![Tengwar](https://puu.sh/vtyfi/598ad704e1.png "Tengwar Characters") +To translate from Tengwar back to English is much simpler, but because Tengwar characters do use capitalization, the resulting string of text will be in only lower-case letters. + ### To-Do List * Implement s-curls. -- 2.34.1