From 8a71899904a7e93a03e1c8d13debbd13a858829e Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Tue, 2 May 2017 11:27:29 +0200 Subject: [PATCH] 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(); + } }