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.model.TranslationUpdate;
import de.marhali.easyi18n.util.IOUtil; import de.marhali.easyi18n.util.IOUtil;
import de.marhali.easyi18n.util.TranslationsUtil; import de.marhali.easyi18n.util.TranslationsUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
/** /**
* Singleton service to manage localized messages. * Singleton service to manage localized messages.
@ -51,14 +53,19 @@ public class DataStore {
} else { } else {
TranslatorIO io = IOUtil.determineFormat(localesPath); TranslatorIO io = IOUtil.determineFormat(localesPath);
translations = io.read(localesPath);
}
// Propagate changes io.read(localesPath, (translations) -> {
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery)); 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(); String localesPath = SettingsService.getInstance(project).getState().getLocalesPath();
if(localesPath == null || localesPath.isEmpty()) { // Cannot save without valid path if(localesPath == null || localesPath.isEmpty()) { // Cannot save without valid path
@ -66,7 +73,7 @@ public class DataStore {
} }
TranslatorIO io = IOUtil.determineFormat(localesPath); TranslatorIO io = IOUtil.determineFormat(localesPath);
io.save(translations); io.save(translations, localesPath, callback);
} }
public void searchBeyKey(String fullPath) { public void searchBeyKey(String fullPath) {
@ -105,9 +112,12 @@ public class DataStore {
node.setValue(update.getChange().getTranslations()); node.setValue(update.getChange().getTranslations());
} }
// Propagate changes and save them // Persist changes and propagate them on success
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery)); saveToDisk(success -> {
saveToDisk(); if(success) {
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery));
}
});
} }
public Translations getTranslations() { public Translations getTranslations() {

View File

@ -3,16 +3,21 @@ package de.marhali.easyi18n.io.translator;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; 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.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.data.LocalizedNode; import de.marhali.easyi18n.data.LocalizedNode;
import de.marhali.easyi18n.data.Translations; import de.marhali.easyi18n.data.Translations;
import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
/** /**
* Implementation for JSON translation files. * Implementation for JSON translation files.
@ -20,31 +25,75 @@ import java.util.*;
*/ */
public class JsonTranslatorIO implements TranslatorIO { public class JsonTranslatorIO implements TranslatorIO {
private static final String FILE_EXTENSION = "json";
@Override @Override
public Translations read(String directoryPath) throws IOException { public void read(@NotNull String directoryPath, @NotNull Consumer<Translations> callback) {
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath)); ApplicationManager.getApplication().runReadAction(() -> {
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath));
if(directory == null || directory.getChildren() == null) { if(directory == null || directory.getChildren() == null) {
throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")"); throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")");
} }
VirtualFile[] files = directory.getChildren(); VirtualFile[] files = directory.getChildren();
List<String> locales = new ArrayList<>(); List<String> locales = new ArrayList<>();
LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>()); LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>());
for(VirtualFile file : files) { try {
locales.add(file.getNameWithoutExtension()); for(VirtualFile file : files) {
JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream())).getAsJsonObject(); locales.add(file.getNameWithoutExtension());
readTree(file.getNameWithoutExtension(), tree, nodes); 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 @Override
public void save(Translations translations) { public void save(@NotNull Translations translations, @NotNull String directoryPath, @NotNull Consumer<Boolean> callback) {
System.out.println("TODO: save"); 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) { private void readTree(String locale, JsonObject json, LocalizedNode data) {
@ -73,4 +122,4 @@ public class JsonTranslatorIO implements TranslatorIO {
} }
} }
} }
} }

View File

@ -2,7 +2,9 @@ package de.marhali.easyi18n.io.translator;
import de.marhali.easyi18n.data.Translations; 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. * Interface to retrieve and save localized messages.
@ -13,16 +15,16 @@ public interface TranslatorIO {
/** /**
* Reads localized messages from the persistence layer. * Reads localized messages from the persistence layer.
* @param directoryPath The full path from the parent directory which holds the different locale files. * @param directoryPath The full path for the directory which holds all locale files
* @return Translations model * @param callback Contains loaded translations. Will be called after io operation. Content might be null on failure.
* Example entry: username.title => [DE:Benutzername, EN:Username]
*/ */
Translations read(String directoryPath) throws IOException; void read(@NotNull String directoryPath, @NotNull Consumer<Translations> callback);
/** /**
* Writes the provided messages to the persistence layer. * Writes the provided messages (translations) to the persistence layer.
* @param translations Translatons model to save * @param translations Translations instance to save
* @see #read(String) More information regards the data map * @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);
} }