Rework IO system

- Write function added for JSON
- Encapsulated IO operations with Application Actions to meet Intellij Guidelines
This commit is contained in:
Marcel Haßlinger 2021-03-13 23:31:58 +01:00
parent 3eb83ef955
commit 41ee5d3a03
3 changed files with 96 additions and 35 deletions

View File

@ -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);
}
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<Boolean> 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
// Persist changes and propagate them on success
saveToDisk(success -> {
if(success) {
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery));
saveToDisk();
}
});
}
public Translations getTranslations() {

View File

@ -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,8 +25,11 @@ import java.util.*;
*/
public class JsonTranslatorIO implements TranslatorIO {
private static final String FILE_EXTENSION = "json";
@Override
public Translations read(String directoryPath) throws IOException {
public void read(@NotNull String directoryPath, @NotNull Consumer<Translations> callback) {
ApplicationManager.getApplication().runReadAction(() -> {
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath));
if(directory == null || directory.getChildren() == null) {
@ -33,18 +41,59 @@ public class JsonTranslatorIO implements TranslatorIO {
List<String> locales = new ArrayList<>();
LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>());
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<Boolean> 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) {

View File

@ -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<Translations> 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<Boolean> callback);
}