From 5f1d96d91d51bb5fd0664a14b2196bca0819fcf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Ha=C3=9Flinger?= Date: Sat, 24 Apr 2021 21:16:42 +0200 Subject: [PATCH] Add basic support for modularized / splitted json files | Fixes Issue #4 --- .../io/implementation/JsonTranslatorIO.java | 72 ++--------- .../ModularizedJsonTranslatorIO.java | 112 ++++++++++++++++++ .../java/de/marhali/easyi18n/util/IOUtil.java | 6 + .../de/marhali/easyi18n/util/JsonUtil.java | 83 +++++++++++++ 4 files changed, 214 insertions(+), 59 deletions(-) create mode 100644 src/main/java/de/marhali/easyi18n/io/implementation/ModularizedJsonTranslatorIO.java create mode 100644 src/main/java/de/marhali/easyi18n/util/JsonUtil.java diff --git a/src/main/java/de/marhali/easyi18n/io/implementation/JsonTranslatorIO.java b/src/main/java/de/marhali/easyi18n/io/implementation/JsonTranslatorIO.java index 75b14d6..9a2840d 100644 --- a/src/main/java/de/marhali/easyi18n/io/implementation/JsonTranslatorIO.java +++ b/src/main/java/de/marhali/easyi18n/io/implementation/JsonTranslatorIO.java @@ -8,6 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.TranslatorIO; import de.marhali.easyi18n.model.LocalizedNode; import de.marhali.easyi18n.model.Translations; +import de.marhali.easyi18n.util.JsonUtil; import org.jetbrains.annotations.NotNull; @@ -44,8 +45,11 @@ public class JsonTranslatorIO implements TranslatorIO { try { for(VirtualFile file : files) { locales.add(file.getNameWithoutExtension()); - JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream(), file.getCharset())).getAsJsonObject(); - readTree(file.getNameWithoutExtension(), tree, nodes); + + JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream(), + file.getCharset())).getAsJsonObject(); + + JsonUtil.readTree(file.getNameWithoutExtension(), tree, nodes); } callback.accept(new Translations(locales, nodes)); @@ -65,13 +69,16 @@ public class JsonTranslatorIO implements TranslatorIO { try { for(String locale : translations.getLocales()) { JsonObject content = new JsonObject(); - writeTree(locale, content, translations.getNodes()); - //JsonElement content = writeTree(locale, new JsonObject(), translations.getNodes()); + JsonUtil.writeTree(locale, content, translations.getNodes()); String fullPath = directoryPath + "/" + locale + "." + FILE_EXTENSION; - VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(new File(fullPath)); + File file = new File(fullPath); + boolean created = file.createNewFile(); - file.setBinaryContent(gson.toJson(content).getBytes(file.getCharset())); + VirtualFile vf = created ? LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file) + : LocalFileSystem.getInstance().findFileByIoFile(file); + + vf.setBinaryContent(gson.toJson(content).getBytes(vf.getCharset())); } // Successfully saved @@ -83,57 +90,4 @@ public class JsonTranslatorIO implements TranslatorIO { } }); } - - private void writeTree(String locale, JsonObject parent, LocalizedNode node) { - if(node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) { - if(node.getValue().get(locale) != null) { - parent.add(node.getKey(), new JsonPrimitive(node.getValue().get(locale))); - } - - } else { - for(LocalizedNode children : node.getChildren()) { - if(children.isLeaf()) { - writeTree(locale, parent, children); - } else { - JsonObject childrenJson = new JsonObject(); - writeTree(locale, childrenJson, children); - if(childrenJson.size() > 0) { - parent.add(children.getKey(), childrenJson); - } - } - } - } - } - - private void readTree(String locale, JsonObject json, LocalizedNode data) { - for(Map.Entry entry : json.entrySet()) { - String key = entry.getKey(); - - try { - // Try to go one level deeper - JsonObject childObject = entry.getValue().getAsJsonObject(); - - LocalizedNode childrenNode = data.getChildren(key); - - if(childrenNode == null) { - childrenNode = new LocalizedNode(key, new ArrayList<>()); - data.addChildren(childrenNode); - } - - readTree(locale, childObject, childrenNode); - - } catch(IllegalStateException e) { // Reached end for this node - LocalizedNode leafNode = data.getChildren(key); - - if(leafNode == null) { - leafNode = new LocalizedNode(key, new HashMap<>()); - data.addChildren(leafNode); - } - - Map messages = leafNode.getValue(); - messages.put(locale, entry.getValue().getAsString()); - leafNode.setValue(messages); - } - } - } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/io/implementation/ModularizedJsonTranslatorIO.java b/src/main/java/de/marhali/easyi18n/io/implementation/ModularizedJsonTranslatorIO.java new file mode 100644 index 0000000..00c0e37 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/io/implementation/ModularizedJsonTranslatorIO.java @@ -0,0 +1,112 @@ +package de.marhali.easyi18n.io.implementation; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; + +import de.marhali.easyi18n.io.TranslatorIO; +import de.marhali.easyi18n.model.LocalizedNode; +import de.marhali.easyi18n.model.Translations; +import de.marhali.easyi18n.util.JsonUtil; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * IO operations for splitted / modularized json files. Each locale can have multiple translation files. + * @author marhali + */ +public class ModularizedJsonTranslatorIO implements TranslatorIO { + + private static final String FILE_EXTENSION = "json"; + + @Override + public void read(@NotNull String directoryPath, @NotNull Consumer callback) { + ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added) + + ApplicationManager.getApplication().runReadAction(() -> { + VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath)); + + if(directory == null || directory.getChildren() == null) { + throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")"); + } + + VirtualFile[] localeDirectories = directory.getChildren(); + + List locales = new ArrayList<>(); + LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>()); + + try { + for(VirtualFile localeDir : localeDirectories) { + String locale = localeDir.getName(); + locales.add(locale); + + // Read all json modules + for(VirtualFile module : localeDir.getChildren()) { + JsonObject tree = JsonParser.parseReader(new InputStreamReader(module.getInputStream(), + module.getCharset())).getAsJsonObject(); + + String moduleName = module.getNameWithoutExtension(); + LocalizedNode moduleNode = nodes.getChildren(moduleName); + + if(moduleNode == null) { // Create module / sub node + moduleNode = new LocalizedNode(moduleName, new ArrayList<>()); + nodes.addChildren(moduleNode); + } + + JsonUtil.readTree(locale, tree, moduleNode); + } + } + + callback.accept(new Translations(locales, nodes)); + + } catch(IOException e) { + e.printStackTrace(); + callback.accept(null); + } + }); + } + + @Override + public void save(@NotNull Translations translations, @NotNull String directoryPath, @NotNull Consumer callback) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + ApplicationManager.getApplication().runWriteAction(() -> { + try { + for(String locale : translations.getLocales()) { + // Use top level children as modules + for (LocalizedNode module : translations.getNodes().getChildren()) { + JsonObject content = new JsonObject(); + JsonUtil.writeTree(locale, content, module); + + String fullPath = directoryPath + "/" + locale + "/" + module.getKey() + "." + FILE_EXTENSION; + File file = new File(fullPath); + boolean created = file.createNewFile(); + + VirtualFile vf = created ? LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file) + : LocalFileSystem.getInstance().findFileByIoFile(file); + + vf.setBinaryContent(gson.toJson(content).getBytes(vf.getCharset())); + } + } + + // Successfully saved + callback.accept(true); + + } catch(IOException e) { + e.printStackTrace(); + callback.accept(false); + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/util/IOUtil.java b/src/main/java/de/marhali/easyi18n/util/IOUtil.java index 7ee00e6..bdb8838 100644 --- a/src/main/java/de/marhali/easyi18n/util/IOUtil.java +++ b/src/main/java/de/marhali/easyi18n/util/IOUtil.java @@ -3,6 +3,7 @@ package de.marhali.easyi18n.util; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.io.implementation.JsonTranslatorIO; +import de.marhali.easyi18n.io.implementation.ModularizedJsonTranslatorIO; import de.marhali.easyi18n.io.implementation.PropertiesTranslatorIO; import de.marhali.easyi18n.io.TranslatorIO; @@ -36,6 +37,11 @@ public class IOUtil { throw new IllegalStateException("Could not determine i18n format. At least one locale file must be defined"); } + // Split files - Should be always JSON + if(any.get().isDirectory()) { + return new ModularizedJsonTranslatorIO(); + } + switch (any.get().getFileType().getDefaultExtension().toLowerCase()) { case "json": return new JsonTranslatorIO(); diff --git a/src/main/java/de/marhali/easyi18n/util/JsonUtil.java b/src/main/java/de/marhali/easyi18n/util/JsonUtil.java new file mode 100644 index 0000000..145f4b3 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/util/JsonUtil.java @@ -0,0 +1,83 @@ +package de.marhali.easyi18n.util; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import de.marhali.easyi18n.model.LocalizedNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * Json tree utilities for writing and reading {@link LocalizedNode}'s + * @author marhali + */ +public class JsonUtil { + + /** + * Creates a {@link JsonObject} based from an {@link LocalizedNode} + * @param locale Current locale + * @param parent Parent json. Can be an entire json document + * @param node The node instance + */ + public static void writeTree(String locale, JsonObject parent, LocalizedNode node) { + if(node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) { + if(node.getValue().get(locale) != null) { + parent.add(node.getKey(), new JsonPrimitive(node.getValue().get(locale))); + } + + } else { + for(LocalizedNode children : node.getChildren()) { + if(children.isLeaf()) { + writeTree(locale, parent, children); + } else { + JsonObject childrenJson = new JsonObject(); + writeTree(locale, childrenJson, children); + if(childrenJson.size() > 0) { + parent.add(children.getKey(), childrenJson); + } + } + } + } + } + + /** + * Reads a {@link JsonObject} and writes the tree into the provided {@link LocalizedNode} + * @param locale Current locale + * @param json Json to read + * @param data Node. Can be a root node + */ + public static void readTree(String locale, JsonObject json, LocalizedNode data) { + for(Map.Entry entry : json.entrySet()) { + String key = entry.getKey(); + + try { + // Try to go one level deeper + JsonObject childObject = entry.getValue().getAsJsonObject(); + + LocalizedNode childrenNode = data.getChildren(key); + + if(childrenNode == null) { + childrenNode = new LocalizedNode(key, new ArrayList<>()); + data.addChildren(childrenNode); + } + + readTree(locale, childObject, childrenNode); + + } catch(IllegalStateException e) { // Reached end for this node + LocalizedNode leafNode = data.getChildren(key); + + if(leafNode == null) { + leafNode = new LocalizedNode(key, new HashMap<>()); + data.addChildren(leafNode); + } + + Map messages = leafNode.getValue(); + messages.put(locale, entry.getValue().getAsString()); + leafNode.setValue(messages); + } + } + } +} \ No newline at end of file