package mods.eln.i18n; import java.io.File; import java.io.IOException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; class SourceCodeParser { private static final Pattern JAVA_TR_PATTERN = Pattern.compile("(?:tr|TR)\\s*\\(\\s*\"(.*?)\"\\s*[,)]"); private static final Pattern JAVA_FORGE_PATTERN = Pattern.compile("TR_([A-Z]*)\\s*\\(\\s*(?:I18N.)?Type.(.*?)\\s*,\\s\"(.*?)\"\\s*\\)"); private static final String MULTIPLE_LOCATIONS = "Appearing in multiple source files"; private SourceCodeParser() { } static Map<String, Set<TranslationItem>> parseSourceFolder(final File file) throws IOException { Map<String, Set<TranslationItem>> strings = new TreeMap<String, Set<TranslationItem>>(); strings.put(MULTIPLE_LOCATIONS, new TreeSet<TranslationItem>()); parseSourceFolderRecursive(file, strings); return strings; } private static void parseSourceFolderRecursive(final File folder, final Map<String, Set<TranslationItem>> strings) throws IOException { // Check that arguments are valid. if (folder != null && folder.exists()) { // Do for each file. for (final File file : folder.listFiles()) { // If the file is a directory, call the method recursively. if (file.isDirectory()) { parseSourceFolderRecursive(file, strings); // If it is a file and has the file extension .java, parse the Java file. } else if (file.isFile() && file.getName().endsWith(".java")) { System.out.println("Parsing Java source file: " + file.getName() + "..."); parseJavaFile(file, strings); // If it is a file and has the file extension .kt, parse the Kotlin file. } else if (file.isFile() && file.getName().endsWith(".kt")) { System.out.println("Parsing Kotlin source file: " + file.getName() + "..."); parseKotlinFile(file, strings); } } } } private static void parseJavaFile(final File file, final Map<String, Set<TranslationItem>> strings) throws IOException { // Load file into memory. final String content = new Scanner(file).useDelimiter("\\Z").next(); final Set<TranslationItem> textsToTranslate = new TreeSet<TranslationItem>(); // Find all matches for Java style translations. final Matcher trMatcher = JAVA_TR_PATTERN.matcher(content); while (trMatcher.find()) { final TranslationItem textToTranslate = new TranslationItem(trMatcher.group(1)); System.out.println(" " + textToTranslate.getKey()); if (!isStringAlreadyPresent(textToTranslate, strings)) { textsToTranslate.add(textToTranslate); } } final Matcher forgeMatcher = JAVA_FORGE_PATTERN.matcher(content); while (forgeMatcher.find()) { final String property = forgeMatcher.group(1).toLowerCase(); final I18N.Type type = I18N.Type.valueOf(forgeMatcher.group(2)); final String text = forgeMatcher.group(3); final TranslationItem textToTranslate = new TranslationItem(type.getPrefix() + I18N.encodeLangKey(text, type.isWhitespacesInFileReplaced()) + "." + property, text); System.out.println(" " + textToTranslate.getKey()); if (!isStringAlreadyPresent(textToTranslate, strings)) { textsToTranslate.add(textToTranslate); } } // If there were translations for that file, add the list of translations to the map. if (!textsToTranslate.isEmpty()) { strings.put(file.getPath(), textsToTranslate); } } private static void parseKotlinFile(final File file, final Map<String, Set<TranslationItem>> strings) throws IOException { // TODO: This is unlikely to work perfectly. It'll do for now. parseJavaFile(file, strings); } private static boolean isStringAlreadyPresent(final TranslationItem string, final Map<String, Set<TranslationItem>> strings) { for (final String fileName : strings.keySet()) { if (MULTIPLE_LOCATIONS.equals(fileName)) { if (strings.get(fileName).contains(string)) return true; } else { if (strings.get(fileName).remove(string)) { strings.get(MULTIPLE_LOCATIONS).add(string); return true; } } } return false; } }