diff --git a/src/main/java/de/marhali/easyi18n/data/DataStore.java b/src/main/java/de/marhali/easyi18n/data/DataStore.java index 482f06e..50f5eba 100644 --- a/src/main/java/de/marhali/easyi18n/data/DataStore.java +++ b/src/main/java/de/marhali/easyi18n/data/DataStore.java @@ -10,10 +10,12 @@ import de.marhali.easyi18n.model.TranslationDelete; import de.marhali.easyi18n.model.TranslationUpdate; import de.marhali.easyi18n.util.IOUtil; import de.marhali.easyi18n.util.TranslationsUtil; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * Singleton service to manage localized messages. @@ -51,14 +53,19 @@ public class DataStore { } else { TranslatorIO io = IOUtil.determineFormat(localesPath); - translations = io.read(localesPath); - } - // Propagate changes - synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery)); + io.read(localesPath, (translations) -> { + if(translations != null) { // Read was successful + this.translations = translations; + + // Propagate changes + synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery)); + } + }); + } } - public void saveToDisk() { + public void saveToDisk(@NotNull Consumer callback) { String localesPath = SettingsService.getInstance(project).getState().getLocalesPath(); if(localesPath == null || localesPath.isEmpty()) { // Cannot save without valid path @@ -66,7 +73,7 @@ public class DataStore { } TranslatorIO io = IOUtil.determineFormat(localesPath); - io.save(translations); + io.save(translations, localesPath, callback); } public void searchBeyKey(String fullPath) { @@ -105,9 +112,12 @@ public class DataStore { node.setValue(update.getChange().getTranslations()); } - // Propagate changes and save them - synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery)); - saveToDisk(); + // Persist changes and propagate them on success + saveToDisk(success -> { + if(success) { + synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery)); + } + }); } public Translations getTranslations() { diff --git a/src/main/java/de/marhali/easyi18n/io/translator/JsonTranslatorIO.java b/src/main/java/de/marhali/easyi18n/io/translator/JsonTranslatorIO.java index 59e970b..7b3259e 100644 --- a/src/main/java/de/marhali/easyi18n/io/translator/JsonTranslatorIO.java +++ b/src/main/java/de/marhali/easyi18n/io/translator/JsonTranslatorIO.java @@ -3,16 +3,21 @@ package de.marhali.easyi18n.io.translator; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import de.marhali.easyi18n.data.LocalizedNode; import de.marhali.easyi18n.data.Translations; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; +import java.util.function.Consumer; /** * Implementation for JSON translation files. @@ -20,31 +25,75 @@ import java.util.*; */ public class JsonTranslatorIO implements TranslatorIO { + private static final String FILE_EXTENSION = "json"; + @Override - public Translations read(String directoryPath) throws IOException { - VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath)); + public void read(@NotNull String directoryPath, @NotNull Consumer callback) { + 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 + ")"); - } + if(directory == null || directory.getChildren() == null) { + throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")"); + } - VirtualFile[] files = directory.getChildren(); + VirtualFile[] files = directory.getChildren(); - List locales = new ArrayList<>(); - LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>()); + List locales = new ArrayList<>(); + LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>()); - for(VirtualFile file : files) { - locales.add(file.getNameWithoutExtension()); - JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream())).getAsJsonObject(); - readTree(file.getNameWithoutExtension(), tree, nodes); - } + try { + for(VirtualFile file : files) { + locales.add(file.getNameWithoutExtension()); + JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream())).getAsJsonObject(); + readTree(file.getNameWithoutExtension(), tree, nodes); + } - return new Translations(locales, nodes); + callback.accept(new Translations(locales, nodes)); + + } catch(IOException e) { + e.printStackTrace(); + callback.accept(null); + } + }); } @Override - public void save(Translations translations) { - System.out.println("TODO: save"); + public void save(@NotNull Translations translations, @NotNull String directoryPath, @NotNull Consumer callback) { + ApplicationManager.getApplication().runWriteAction(() -> { + for(String locale : translations.getLocales()) { + JsonElement content = writeTree(locale, new JsonObject(), translations.getNodes()); + + String fullPath = directoryPath + "/" + locale + "." + FILE_EXTENSION; + VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(new File(fullPath)); + + try { + file.setBinaryContent(content.toString().getBytes()); + callback.accept(true); + + } catch (IOException e) { + e.printStackTrace(); + callback.accept(false); + } + } + }); + } + + private JsonElement writeTree(String locale, JsonObject parent, LocalizedNode data) { + if(data.isLeaf() && !data.getKey().equals(LocalizedNode.ROOT_KEY)) { + if(data.getValue().get(locale) != null) { // Translation is defined - track it + return new JsonPrimitive(data.getValue().get(locale)); + } + + } else { + for(LocalizedNode children : data.getChildren()) { + JsonObject childrenObject = new JsonObject(); + parent.add(children.getKey(), childrenObject); + + parent.add(children.getKey(), writeTree(locale, childrenObject, children)); + } + } + + return parent; } private void readTree(String locale, JsonObject json, LocalizedNode data) { @@ -73,4 +122,4 @@ public class JsonTranslatorIO implements TranslatorIO { } } } -} +} \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/io/translator/TranslatorIO.java b/src/main/java/de/marhali/easyi18n/io/translator/TranslatorIO.java index 1ed8a7f..586e172 100644 --- a/src/main/java/de/marhali/easyi18n/io/translator/TranslatorIO.java +++ b/src/main/java/de/marhali/easyi18n/io/translator/TranslatorIO.java @@ -2,7 +2,9 @@ package de.marhali.easyi18n.io.translator; import de.marhali.easyi18n.data.Translations; -import java.io.IOException; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; /** * Interface to retrieve and save localized messages. @@ -13,16 +15,16 @@ public interface TranslatorIO { /** * Reads localized messages from the persistence layer. - * @param directoryPath The full path from the parent directory which holds the different locale files. - * @return Translations model - * Example entry: username.title => [DE:Benutzername, EN:Username] + * @param directoryPath The full path for the directory which holds all locale files + * @param callback Contains loaded translations. Will be called after io operation. Content might be null on failure. */ - Translations read(String directoryPath) throws IOException; + void read(@NotNull String directoryPath, @NotNull Consumer callback); /** - * Writes the provided messages to the persistence layer. - * @param translations Translatons model to save - * @see #read(String) More information regards the data map + * Writes the provided messages (translations) to the persistence layer. + * @param translations Translations instance to save + * @param directoryPath The full path for the directory which holds all locale files + * @param callback Will be called after io operation. Can be used to determine if action was successful(true) or not */ - void save(Translations translations); + void save(@NotNull Translations translations, @NotNull String directoryPath, @NotNull Consumer callback); } \ No newline at end of file