implement standard json io strategy
This commit is contained in:
parent
3d7f28f8cc
commit
bc3717d874
@ -0,0 +1,21 @@
|
||||
package de.marhali.easyi18n.io.json;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import de.marhali.easyi18n.io.ArrayMapper;
|
||||
|
||||
/**
|
||||
* Map json array values.
|
||||
* @author marhali
|
||||
*/
|
||||
public class JsonArrayMapper extends ArrayMapper {
|
||||
public static String read(JsonArray array) {
|
||||
return read(array.iterator(), JsonElement::getAsString);
|
||||
}
|
||||
|
||||
public static JsonArray write(String concat) {
|
||||
JsonArray array = new JsonArray();
|
||||
write(concat, array::add);
|
||||
return array;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package de.marhali.easyi18n.io.json;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import de.marhali.easyi18n.io.IOStrategy;
|
||||
import de.marhali.easyi18n.model.SettingsState;
|
||||
import de.marhali.easyi18n.model.TranslationData;
|
||||
|
||||
import de.marhali.easyi18n.model.TranslationNode;
|
||||
import net.minidev.json.JSONObject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Strategy for simple json locale files. Each locale has its own file.
|
||||
* For example localesPath/en.json, localesPath/de.json.
|
||||
* @author marhali
|
||||
*/
|
||||
public class JsonIOStrategy implements IOStrategy {
|
||||
|
||||
private static final String FILE_EXTENSION = "json";
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
|
||||
@Override
|
||||
public boolean canUse(@NotNull Project project, @NotNull String localesPath, @NotNull SettingsState state) {
|
||||
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(localesPath));
|
||||
|
||||
if(directory == null || directory.getChildren() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(VirtualFile children : directory.getChildren()) {
|
||||
if(!children.isDirectory() && isFileRelevant(state, children)) {
|
||||
if(children.getFileType().getDefaultExtension().toLowerCase().equals(FILE_EXTENSION)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull Project project, @NotNull String localesPath,
|
||||
@NotNull SettingsState state, @NotNull Consumer<@Nullable TranslationData> result) {
|
||||
ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added)
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(localesPath));
|
||||
|
||||
if(directory == null || directory.getChildren() == null) {
|
||||
throw new IllegalArgumentException("Specified folder is invalid (" + localesPath + ")");
|
||||
}
|
||||
|
||||
TranslationData data = new TranslationData(state.isSortKeys(), state.isNestedKeys());
|
||||
|
||||
try {
|
||||
for(VirtualFile file : directory.getChildren()) {
|
||||
if(!isFileRelevant(state, file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data.addLocale(file.getNameWithoutExtension());
|
||||
|
||||
JSONObject tree = GSON.fromJson(new InputStreamReader(file.getInputStream(), file.getCharset()), JSONObject.class);
|
||||
}
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
result.accept(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull Project project, @NotNull String localesPath, @NotNull SettingsState state, @NotNull TranslationData data, @NotNull Consumer<Boolean> result) {
|
||||
|
||||
}
|
||||
}
|
73
src/main/java/de/marhali/easyi18n/io/json/JsonMapper.java
Normal file
73
src/main/java/de/marhali/easyi18n/io/json/JsonMapper.java
Normal file
@ -0,0 +1,73 @@
|
||||
package de.marhali.easyi18n.io.json;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import de.marhali.easyi18n.model.Translation;
|
||||
import de.marhali.easyi18n.model.TranslationNode;
|
||||
import de.marhali.easyi18n.util.StringUtil;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Mapper for mapping json objects into translation nodes and backwards.
|
||||
* @author marhali
|
||||
*/
|
||||
public class JsonMapper {
|
||||
|
||||
public static void read(String locale, JsonObject json, TranslationNode node) {
|
||||
for(Map.Entry<String, JsonElement> entry : json.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
JsonElement value = entry.getValue();
|
||||
|
||||
TranslationNode childNode = node.getOrCreateChildren(key);
|
||||
|
||||
if(value.isJsonObject()) {
|
||||
// Nested element - run recursively
|
||||
read(locale, value.getAsJsonObject(), childNode);
|
||||
} else {
|
||||
Translation translation = childNode.getValue();
|
||||
|
||||
String content = entry.getValue().isJsonArray()
|
||||
? JsonArrayMapper.read(value.getAsJsonArray())
|
||||
: StringUtil.escapeControls(value.getAsString(), true);
|
||||
|
||||
translation.put(locale, content);
|
||||
childNode.setValue(translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void write(String locale, JsonObject json, TranslationNode node) {
|
||||
for(Map.Entry<String, TranslationNode> entry : node.getChildren().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
TranslationNode childNode = entry.getValue();
|
||||
|
||||
if(!childNode.isLeaf()) {
|
||||
// Nested node - run recursively
|
||||
JsonObject childJson = new JsonObject();
|
||||
write(locale, childJson, childNode);
|
||||
if(childJson.size() > 0) {
|
||||
json.add(key, childJson);
|
||||
}
|
||||
} else {
|
||||
Translation translation = childNode.getValue();
|
||||
String content = translation.get(locale);
|
||||
|
||||
if(content != null) {
|
||||
if(JsonArrayMapper.isArray(content)) {
|
||||
json.add(key, JsonArrayMapper.write(content));
|
||||
} else if(NumberUtils.isNumber(content)) {
|
||||
json.add(key, new JsonPrimitive(NumberUtils.createNumber(content)));
|
||||
} else {
|
||||
json.add(key, new JsonPrimitive(StringEscapeUtils.unescapeJava(content)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package de.marhali.easyi18n.mapper;
|
||||
|
||||
import de.marhali.easyi18n.model.Translation;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Defines test cases for {@link de.marhali.easyi18n.model.TranslationNode} mapping.
|
||||
* @author marhali
|
||||
*/
|
||||
public abstract class AbstractMapperTest {
|
||||
|
||||
protected final String specialCharacters = "Special characters: äü@Öä€/$§;.-?+~#```'' end";
|
||||
protected final String arraySimple = "!arr[first;second]";
|
||||
protected final String arrayEscaped = "!arr[first\\;element;second element;third\\;element]";
|
||||
protected final String leadingSpace = " leading space";
|
||||
|
||||
@Test
|
||||
public abstract void testNonSorting();
|
||||
|
||||
@Test
|
||||
public abstract void testSorting();
|
||||
|
||||
@Test
|
||||
public abstract void testArrays();
|
||||
|
||||
@Test
|
||||
public abstract void testSpecialCharacters();
|
||||
|
||||
@Test
|
||||
public abstract void testNestedKeys();
|
||||
|
||||
@Test
|
||||
public abstract void testNonNestedKeys();
|
||||
|
||||
@Test
|
||||
public abstract void testLeadingSpace();
|
||||
|
||||
@Test
|
||||
public abstract void testNumbers();
|
||||
|
||||
protected Translation create(String content) {
|
||||
return new Translation("en", content);
|
||||
}
|
||||
}
|
153
src/test/java/de/marhali/easyi18n/mapper/JsonMapperTest.java
Normal file
153
src/test/java/de/marhali/easyi18n/mapper/JsonMapperTest.java
Normal file
@ -0,0 +1,153 @@
|
||||
package de.marhali.easyi18n.mapper;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import de.marhali.easyi18n.io.json.JsonArrayMapper;
|
||||
import de.marhali.easyi18n.io.json.JsonMapper;
|
||||
import de.marhali.easyi18n.model.TranslationData;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link de.marhali.easyi18n.io.json.JsonMapper}
|
||||
* @author marhali
|
||||
*/
|
||||
public class JsonMapperTest extends AbstractMapperTest {
|
||||
|
||||
@Override
|
||||
public void testNonSorting() {
|
||||
JsonObject input = new JsonObject();
|
||||
input.add("zulu", new JsonPrimitive("test"));
|
||||
input.add("alpha", new JsonPrimitive("test"));
|
||||
input.add("bravo", new JsonPrimitive("test"));
|
||||
|
||||
TranslationData data = new TranslationData(false, true);
|
||||
JsonMapper.read("en", input, data.getRootNode());
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Set<String> expect = new LinkedHashSet<>(Arrays.asList("zulu", "alpha", "bravo"));
|
||||
Assert.assertEquals(expect, output.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testSorting() {
|
||||
JsonObject input = new JsonObject();
|
||||
input.add("zulu", new JsonPrimitive("test"));
|
||||
input.add("alpha", new JsonPrimitive("test"));
|
||||
input.add("bravo", new JsonPrimitive("test"));
|
||||
|
||||
TranslationData data = new TranslationData(true, true);
|
||||
JsonMapper.read("en", input, data.getRootNode());
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Set<String> expect = new LinkedHashSet<>(Arrays.asList("alpha", "bravo", "zulu"));
|
||||
Assert.assertEquals(expect, output.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testArrays() {
|
||||
TranslationData data = new TranslationData(true, true);
|
||||
data.setTranslation("simple", create(arraySimple));
|
||||
data.setTranslation("escaped", create(arrayEscaped));
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Assert.assertTrue(output.get("simple").isJsonArray());
|
||||
Assert.assertEquals(arraySimple, JsonArrayMapper.read(output.get("simple").getAsJsonArray()));
|
||||
Assert.assertTrue(output.get("escaped").isJsonArray());
|
||||
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(JsonArrayMapper.read(output.get("escaped").getAsJsonArray())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testSpecialCharacters() {
|
||||
TranslationData data = new TranslationData(true, true);
|
||||
data.setTranslation("chars", create(specialCharacters));
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Assert.assertEquals(specialCharacters, output.get("chars").getAsString());
|
||||
|
||||
TranslationData input = new TranslationData(true, true);
|
||||
JsonMapper.read("en", output, input.getRootNode());
|
||||
|
||||
Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation("chars").get("en")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testNestedKeys() {
|
||||
TranslationData data = new TranslationData(true, true);
|
||||
data.setTranslation("nested.key.section", create("test"));
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Assert.assertEquals("test", output.getAsJsonObject("nested").getAsJsonObject("key").get("section").getAsString());
|
||||
|
||||
TranslationData input = new TranslationData(true, true);
|
||||
JsonMapper.read("en", output, input.getRootNode());
|
||||
|
||||
Assert.assertEquals("test", input.getTranslation("nested.key.section").get("en"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testNonNestedKeys() {
|
||||
TranslationData data = new TranslationData(true, false);
|
||||
data.setTranslation("long.key.with.many.sections", create("test"));
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Assert.assertTrue(output.has("long.key.with.many.sections"));
|
||||
|
||||
TranslationData input = new TranslationData(true, false);
|
||||
JsonMapper.read("en", output, input.getRootNode());
|
||||
|
||||
Assert.assertEquals("test", input.getTranslation("long.key.with.many.sections").get("en"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testLeadingSpace() {
|
||||
TranslationData data = new TranslationData(true, true);
|
||||
data.setTranslation("space", create(leadingSpace));
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Assert.assertEquals(leadingSpace, output.get("space").getAsString());
|
||||
|
||||
TranslationData input = new TranslationData(true, true);
|
||||
JsonMapper.read("en", output, input.getRootNode());
|
||||
|
||||
Assert.assertEquals(leadingSpace, input.getTranslation("space").get("en"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testNumbers() {
|
||||
TranslationData data = new TranslationData(true, true);
|
||||
data.setTranslation("numbered", create("15000"));
|
||||
|
||||
JsonObject output = new JsonObject();
|
||||
JsonMapper.write("en", output, data.getRootNode());
|
||||
|
||||
Assert.assertEquals(15000, output.get("numbered").getAsNumber());
|
||||
|
||||
JsonObject input = new JsonObject();
|
||||
input.addProperty("numbered", 143.23);
|
||||
JsonMapper.read("en", input, data.getRootNode());
|
||||
|
||||
Assert.assertEquals("143.23", data.getTranslation("numbered").get("en"));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user