rewrite i18n key handling

- Translation keys are now based on KeyPath type which is simply a string list
- Handling delimiter character within a section layer is now easier
- Only the presentation layer has to deal with displaying the correct concatenated key
This commit is contained in:
marhali 2022-01-13 10:39:39 +01:00
parent 44310d5459
commit c0e385000a
35 changed files with 468 additions and 573 deletions

View File

@ -5,12 +5,14 @@
## [Unreleased]
### BREAKING CHANGES
- Translation file pattern matcher needs to be updated to <kbd>\*.*</kbd> or equivalent wildcard rule
- I18n key nesting will now escape every delimiter within a section layer (can be inverted via option)
### Added
- Full keyboard shortcut support inside tool-window
- Support for dots within key nodes in YAML files
### Changed
- Key completion inside editor suggests all keys without any logic
- Translation file pattern uses wildcard matcher instead of regex
- Improve exception handling on IO operations
- Update Qodana to latest version

View File

@ -1,5 +1,6 @@
package de.marhali.easyi18n;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.bus.BusListener;
import de.marhali.easyi18n.model.TranslationData;
@ -42,7 +43,7 @@ public class DataBus {
}
@Override
public void onFocusKey(@Nullable String key) {
public void onFocusKey(@Nullable KeyPath key) {
listener.forEach(li -> li.onFocusKey(key));
}

View File

@ -42,7 +42,7 @@ public class DataStore {
protected DataStore(@NotNull Project project) {
this.project = project;
this.data = new TranslationData(true, true); // Initialize with hard-coded configuration
this.data = new TranslationData(true); // Initialize with hard-coded configuration
this.changeListener = new FileChangeListener(project);
VirtualFileManager.getInstance().addAsyncFileListener(
@ -63,7 +63,7 @@ public class DataStore {
String localesPath = state.getLocalesPath();
if(localesPath == null || localesPath.isEmpty()) { // Populate empty instance
this.data = new TranslationData(state.isSortKeys(), state.isNestedKeys());
this.data = new TranslationData(state.isSortKeys());
return;
}
@ -73,7 +73,7 @@ public class DataStore {
strategy.read(this.project, localesPath, state, (data) -> {
this.data = data == null
? new TranslationData(state.isSortKeys(), state.isNestedKeys())
? new TranslationData(state.isSortKeys())
: data;
successResult.accept(data != null);

View File

@ -3,15 +3,20 @@ package de.marhali.easyi18n.action;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.ui.content.Content;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.service.WindowManager;
import de.marhali.easyi18n.dialog.AddDialog;
import de.marhali.easyi18n.util.PathUtil;
import de.marhali.easyi18n.util.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.TreePath;
import java.util.Objects;
import java.util.ResourceBundle;
/**
@ -27,32 +32,33 @@ public class AddAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
new AddDialog(e.getProject(), detectPreKey()).showAndHandle();
new AddDialog(Objects.requireNonNull(e.getProject()), detectPreKey(e.getProject())).showAndHandle();
}
private String detectPreKey() {
WindowManager manager = WindowManager.getInstance();
private @Nullable KeyPath detectPreKey(@NotNull Project project) {
KeyPathConverter converter = new KeyPathConverter(project);
WindowManager window = WindowManager.getInstance();
Content manager = window.getToolWindow().getContentManager().getSelectedContent();
if(manager == null) {
return null;
}
if(manager.getToolWindow().getContentManager().getSelectedContent()
.getDisplayName().equals(ResourceBundle.getBundle("messages").getString("view.tree.title"))) {
if(manager.getDisplayName().equals(
ResourceBundle.getBundle("messages").getString("view.tree.title"))) { // Tree View
TreePath path = manager.getTreeView().getTree().getSelectionPath();
TreePath path = window.getTreeView().getTree().getSelectionPath();
if(path != null) {
return TreeUtil.getFullPath(path) + PathUtil.DELIMITER;
return TreeUtil.getFullPath(path);
}
} else { // Table View
int row = manager.getTableView().getTable().getSelectedRow();
int row = window.getTableView().getTable().getSelectedRow();
if(row >= 0) {
String fullPath = String.valueOf(manager.getTableView().getTable().getValueAt(row, 0));
return fullPath + ".";
String path = String.valueOf(window.getTableView().getTable().getValueAt(row, 0));
return converter.split(path);
}
}

View File

@ -8,9 +8,10 @@ import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyedTranslation;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationCreate;
import de.marhali.easyi18n.model.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EtchedBorder;
@ -25,19 +26,23 @@ import java.util.ResourceBundle;
*/
public class AddDialog {
private final Project project;
private String preKey;
private final @NotNull Project project;
private final @NotNull KeyPathConverter converter;
private @NotNull KeyPath preKey;
private JBTextField keyTextField;
private Map<String, JBTextField> valueTextFields;
public AddDialog(Project project, String preKey) {
public AddDialog(@NotNull Project project, @Nullable KeyPath preKey) {
this(project);
this.preKey = preKey;
this.preKey = preKey == null ? new KeyPath() : preKey;
}
public AddDialog(Project project) {
public AddDialog(@NotNull Project project) {
this.project = project;
this.converter = new KeyPathConverter(project);
this.preKey = new KeyPath();
}
public void showAndHandle() {
@ -57,7 +62,8 @@ public class AddDialog {
}
});
TranslationCreate creation = new TranslationCreate(new KeyedTranslation(keyTextField.getText(), translation));
KeyedTranslation keyedTranslation = new KeyedTranslation(converter.split(keyTextField.getText()), translation);
TranslationCreate creation = new TranslationCreate(keyedTranslation);
InstanceManager.get(project).processUpdate(creation);
}
@ -67,13 +73,17 @@ public class AddDialog {
JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2, 2));
JBLabel keyLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("translation.key"));
keyTextField = new JBTextField(this.preKey);
keyTextField = new JBTextField(this.converter.concat(this.preKey));
keyLabel.setLabelFor(keyTextField);
keyPanel.add(keyLabel);
keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
keyPanel.add(keyTextField);
rootPanel.add(keyPanel);
if(!this.preKey.isEmpty()) { // Add delimiter if pre key is defined
keyTextField.setText(keyTextField.getText() + KeyPath.DELIMITER);
}
JPanel valuePanel = new JPanel(new GridLayout(0, 1, 2, 2));
valueTextFields = new HashMap<>();

View File

@ -6,12 +6,10 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyedTranslation;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationDelete;
import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.dialog.descriptor.DeleteActionDescriptor;
import de.marhali.easyi18n.model.TranslationUpdate;
import javax.swing.*;
import javax.swing.border.EtchedBorder;
@ -27,6 +25,8 @@ import java.util.ResourceBundle;
public class EditDialog {
private final Project project;
private final KeyPathConverter converter;
private final KeyedTranslation origin;
private JBTextField keyTextField;
@ -34,6 +34,7 @@ public class EditDialog {
public EditDialog(Project project, KeyedTranslation origin) {
this.project = project;
this.converter = new KeyPathConverter(project);
this.origin = origin;
}
@ -56,7 +57,7 @@ public class EditDialog {
}
});
return new KeyedTranslation(keyTextField.getText(), translation);
return new KeyedTranslation(converter.split(keyTextField.getText()), translation);
}
private DialogBuilder prepare() {
@ -65,7 +66,7 @@ public class EditDialog {
JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2,2));
JBLabel keyLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("translation.key"));
keyTextField = new JBTextField(this.origin.getKey());
keyTextField = new JBTextField(this.converter.concat(this.origin.getKey()));
keyLabel.setLabelFor(keyTextField);
keyPanel.add(keyLabel);
keyPanel.add(keyTextField);

View File

@ -51,9 +51,8 @@ public class SettingsDialog {
// Reload instance
InstanceManager manager = InstanceManager.get(project);
manager.store().loadFromPersistenceLayer((success) -> {
manager.bus().propagate().onUpdateData(manager.store().getData());
});
manager.store().loadFromPersistenceLayer((success) ->
manager.bus().propagate().onUpdateData(manager.store().getData()));
}
}

View File

@ -5,6 +5,9 @@ import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.project.Project;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.model.SettingsState;
import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.service.SettingsService;
@ -28,18 +31,21 @@ public class KeyAnnotator {
return;
}
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
String pathPrefix = SettingsService.getInstance(project).getState().getPathPrefix();
SettingsState state = SettingsService.getInstance(project).getState();
String pathPrefix = state.getPathPrefix();
String previewLocale = state.getPreviewLocale();
KeyPathConverter converter = new KeyPathConverter(project);
String searchKey = key.length() >= pathPrefix.length()
? key.substring(pathPrefix.length())
: key;
if(searchKey.startsWith(".")) {
searchKey = searchKey.substring(1);
if(searchKey.startsWith(KeyPath.DELIMITER)) {
searchKey = searchKey.substring(KeyPath.DELIMITER.length());
}
TranslationNode node = InstanceManager.get(project).store().getData().getNode(searchKey);
TranslationNode node = InstanceManager.get(project).store().getData().getNode(converter.split(searchKey));
if(node == null) { // Unknown translation. Just ignore it
return;

View File

@ -5,11 +5,13 @@ import com.intellij.codeInsight.lookup.*;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.*;
import com.intellij.util.*;
import de.marhali.easyi18n.DataStore;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.service.*;
import de.marhali.easyi18n.util.PathUtil;
import org.jetbrains.annotations.*;
import java.util.*;
@ -32,58 +34,29 @@ public class KeyCompletionProvider extends CompletionProvider<CompletionParamete
}
DataStore store = InstanceManager.get(project).store();
PathUtil pathUtil = new PathUtil(project);
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
String pathPrefix = SettingsService.getInstance(project).getState().getPathPrefix();
String path = result.getPrefixMatcher().getPrefix();
if(path.startsWith(pathPrefix)) {
path = path.substring(pathPrefix.length());
if(path.startsWith(".")) { // Remove leading dot
path = path.substring(1);
}
} else {
path = ""; // Show suggestions for root view
if(pathPrefix.length() > 0 && !pathPrefix.endsWith(KeyPath.DELIMITER)) {
pathPrefix += KeyPath.DELIMITER;
}
if(pathPrefix.length() > 0 && !pathPrefix.endsWith(".")) {
pathPrefix += ".";
}
Set<KeyPath> fullKeys = store.getData().getFullKeys();
Set<String> fullKeys = store.getData().getFullKeys();
int sections = path.split("\\.").length;
int maxSectionForwardLookup = 5;
for(String key : fullKeys) {
// Path matches
if(key.startsWith(path)) {
String[] keySections = key.split("\\.");
if(keySections.length > sections + maxSectionForwardLookup) { // Key is too deep nested
String shrinkKey = pathUtil.concat(Arrays.asList(
Arrays.copyOf(keySections, sections + maxSectionForwardLookup)
));
result.addElement(LookupElementBuilder.create(pathPrefix + shrinkKey)
.appendTailText(" I18n([])", true));
} else {
Translation translation = store.getData().getTranslation(key);
if(translation != null) {
String content = translation.get(previewLocale);
result.addElement(LookupElementBuilder.create(pathPrefix + key)
.withIcon(AllIcons.Actions.PreserveCaseHover)
.appendTailText(" I18n(" + previewLocale + ": " + content + ")", true)
);
}
}
}
for(KeyPath currentKey : fullKeys) {
result.addElement(createElement(
pathPrefix,
currentKey,
previewLocale,
Objects.requireNonNull(store.getData().getTranslation(currentKey))
));
}
}
}
private LookupElement createElement(String prefix, KeyPath path, String locale, Translation translation) {
return LookupElementBuilder.create(prefix + path.toSimpleString())
.withIcon(AllIcons.Actions.PreserveCaseHover)
.appendTailText(" I18n(" + locale + ": " + translation.get(locale) + ")", true);
}
}

View File

@ -7,12 +7,18 @@ import com.intellij.psi.impl.FakePsiElement;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.dialog.AddDialog;
import de.marhali.easyi18n.dialog.EditDialog;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.model.KeyedTranslation;
import de.marhali.easyi18n.model.Translation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Go to declaration reference for i18n keys.
* @author marhali
*/
public class KeyReference extends PsiReferenceBase<PsiElement> {
@Nullable private final String myKey;
@ -52,12 +58,14 @@ public class KeyReference extends PsiReferenceBase<PsiElement> {
@Override
public void navigate(boolean requestFocus) {
Translation translation = InstanceManager.get(getProject()).store().getData().getTranslation(getKey());
KeyPathConverter converter = new KeyPathConverter(getProject());
KeyPath path = converter.split(getKey());
Translation translation = InstanceManager.get(getProject()).store().getData().getTranslation(path);
if(translation != null) {
new EditDialog(getProject(), new KeyedTranslation(getKey(), translation)).showAndHandle();
new EditDialog(getProject(), new KeyedTranslation(path, translation)).showAndHandle();
} else {
new AddDialog(getProject(), getKey()).showAndHandle();
new AddDialog(getProject(), path).showAndHandle();
}
}

View File

@ -11,6 +11,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import de.marhali.easyi18n.DataStore;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.service.SettingsService;
@ -37,12 +38,13 @@ public class GenericFoldingBuilder extends FoldingBuilderEx {
}
DataStore store = InstanceManager.get(root.getProject()).store();
KeyPathConverter converter = new KeyPathConverter(root.getProject());
for(final PsiLiteralValue literalValue : literalValues) {
String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null;
// Undefined string literal or not a translation
if(value == null || store.getData().getTranslation(value) == null) {
if(value == null || store.getData().getTranslation(converter.split(value)) == null) {
continue;
}
@ -65,7 +67,9 @@ public class GenericFoldingBuilder extends FoldingBuilderEx {
}
DataStore store = InstanceManager.get(literalValue.getProject()).store();
Translation translation = store.getData().getTranslation(value);
KeyPathConverter converter = new KeyPathConverter(literalValue.getProject());
Translation translation = store.getData().getTranslation(converter.split(value));
if(translation == null) {
return null;

View File

@ -6,6 +6,7 @@ import com.intellij.util.ProcessingContext;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.editor.KeyReference;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.service.SettingsService;
import org.jetbrains.annotations.NotNull;
@ -38,7 +39,9 @@ public class GenericKeyReferenceContributor extends PsiReferenceContributor {
return PsiReference.EMPTY_ARRAY;
}
if(InstanceManager.get(element.getProject()).store().getData().getTranslation(value) == null) {
KeyPathConverter converter = new KeyPathConverter(element.getProject());
if(InstanceManager.get(element.getProject()).store().getData().getTranslation(converter.split(value)) == null) {
if(!KeyReference.isReferencable(value)) { // Creation policy
return PsiReference.EMPTY_ARRAY;
}

View File

@ -7,6 +7,7 @@ import com.intellij.util.ProcessingContext;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.editor.KeyReference;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.service.SettingsService;
import org.jetbrains.annotations.NotNull;
@ -45,7 +46,9 @@ public class KotlinKeyReferenceContributor extends PsiReferenceContributor {
return PsiReference.EMPTY_ARRAY;
}
if(InstanceManager.get(element.getProject()).store().getData().getNode(value) == null) {
KeyPathConverter converter = new KeyPathConverter(element.getProject());
if(InstanceManager.get(element.getProject()).store().getData().getNode(converter.split(value)) == null) {
return PsiReference.EMPTY_ARRAY;
}

View File

@ -67,7 +67,7 @@ public class JsonIOStrategy implements IOStrategy {
throw new IllegalArgumentException("Specified folder is invalid (" + localesPath + ")");
}
TranslationData data = new TranslationData(state.isSortKeys(), state.isNestedKeys());
TranslationData data = new TranslationData(state.isSortKeys());
for(VirtualFile file : directory.getChildren()) {
if(file.isDirectory() || !isFileRelevant(state, file)) {

View File

@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.io.IOStrategy;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.SettingsState;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
@ -80,7 +81,7 @@ public class ModularizedJsonIOStrategy implements IOStrategy {
throw new IllegalArgumentException("Specified folder is invalid (" + localesPath + ")");
}
TranslationData data = new TranslationData(state.isSortKeys(), state.isNestedKeys());
TranslationData data = new TranslationData(state.isSortKeys());
VirtualFile[] localeDirectories = directory.getChildren();
for(VirtualFile localeDir : localeDirectories) {
@ -95,8 +96,8 @@ public class ModularizedJsonIOStrategy implements IOStrategy {
String moduleName = module.getNameWithoutExtension();
TranslationNode moduleNode = data.getNode(moduleName) != null
? data.getNode(moduleName)
TranslationNode moduleNode = data.getNode(KeyPath.of(moduleName)) != null
? data.getNode(KeyPath.of(moduleName))
: new TranslationNode(state.isSortKeys() ? new TreeMap<>() : new LinkedHashMap<>());
try {

View File

@ -59,7 +59,7 @@ public class PropertiesIOStrategy implements IOStrategy {
throw new IllegalArgumentException("Specified folder is invalid (" + localesPath + ")");
}
TranslationData data = new TranslationData(state.isSortKeys(), state.isNestedKeys());
TranslationData data = new TranslationData(state.isSortKeys());
for(VirtualFile file : directory.getChildren()) {
if(file.isDirectory() || !isFileRelevant(state, file)) {

View File

@ -1,8 +1,8 @@
package de.marhali.easyi18n.io.properties;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.util.StringUtil;
import org.apache.commons.lang.math.NumberUtils;
@ -17,7 +17,7 @@ public class PropertiesMapper {
public static void read(String locale, SortableProperties properties, TranslationData data) {
for(Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = String.valueOf(entry.getKey());
KeyPath key = new KeyPath(String.valueOf(entry.getKey()));
Object value = entry.getValue();
Translation translation = data.getTranslation(key);
@ -36,18 +36,19 @@ public class PropertiesMapper {
}
public static void write(String locale, SortableProperties properties, TranslationData data) {
for(String key : data.getFullKeys()) {
for(KeyPath key : data.getFullKeys()) {
Translation translation = data.getTranslation(key);
if(translation != null && translation.containsKey(locale)) {
String simpleKey = key.toSimpleString();
String content = translation.get(locale);
if(PropertiesArrayMapper.isArray(content)) {
properties.put(key, PropertiesArrayMapper.write(content));
properties.put(simpleKey, PropertiesArrayMapper.write(content));
} else if(NumberUtils.isNumber(content)) {
properties.put(key, NumberUtils.createNumber(content));
properties.put(simpleKey, NumberUtils.createNumber(content));
} else {
properties.put(key, content);
properties.put(simpleKey, content);
}
}
}

View File

@ -66,7 +66,7 @@ public class YamlIOStrategy implements IOStrategy {
throw new IllegalArgumentException("Specified folder is invalid (" + localesPath + ")");
}
TranslationData data = new TranslationData(state.isSortKeys(), state.isNestedKeys());
TranslationData data = new TranslationData(state.isSortKeys());
for(VirtualFile file : directory.getChildren()) {
if(file.isDirectory() || !isFileRelevant(state, file)) {

View File

@ -2,7 +2,6 @@ package de.marhali.easyi18n.io.yaml;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.util.PathUtil;
import de.marhali.easyi18n.util.StringUtil;
import org.apache.commons.lang.StringEscapeUtils;
@ -23,10 +22,6 @@ public class YamlMapper {
public static void read(String locale, Section section, TranslationNode node) {
for(String key : section.getKeys()) {
Object value = section.getInScope(key).get();
key = StringUtil.escapeControls(
key.replace(PathUtil.DELIMITER, "\\" + PathUtil.DELIMITER), true);
TranslationNode childNode = node.getOrCreateChildren(key);
if(value instanceof MapSection) {
@ -47,9 +42,7 @@ public class YamlMapper {
public static void write(String locale, Section section, TranslationNode node) {
for(Map.Entry<String, TranslationNode> entry : node.getChildren().entrySet()) {
String key = StringEscapeUtils.unescapeJava(
entry.getKey().replace("\\" + PathUtil.DELIMITER, PathUtil.DELIMITER));
String key = entry.getKey();
TranslationNode childNode = entry.getValue();
if(!childNode.isLeaf()) {

View File

@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
/**
* Represents a full translation key with all sections.
@ -16,7 +17,7 @@ public class KeyPath extends ArrayList<String> {
public static final String DELIMITER = ".";
public static KeyPath of(String... path) {
public static KeyPath of(@NotNull String... path) {
return new KeyPath(List.of(path));
}
@ -24,7 +25,34 @@ public class KeyPath extends ArrayList<String> {
super();
}
public KeyPath(@NotNull KeyPath path, String... pathToAppend) {
this(path);
this.addAll(List.of(pathToAppend));
}
public KeyPath(@NotNull Collection<? extends String> c) {
super(c);
}
public KeyPath(@NotNull String simplePath) {
this(List.of(simplePath.split(Pattern.quote(DELIMITER))));
}
/**
* <b>Note: </b>Use {@link KeyPathConverter} if you want to keep hierarchy.
* @return simple path representation by adding delimiter between the secton nodes
*/
public String toSimpleString() {
StringBuilder builder = new StringBuilder();
for(String section : this) {
if(builder.length() > 0) {
builder.append(DELIMITER);
}
builder.append(section);
}
return builder.toString();
}
}

View File

@ -9,19 +9,19 @@ import org.jetbrains.annotations.Nullable;
*/
public class KeyedTranslation {
private @NotNull String key;
private @NotNull KeyPath key;
private @Nullable Translation translation;
public KeyedTranslation(@NotNull String key, @Nullable Translation translation) {
public KeyedTranslation(@NotNull KeyPath key, @Nullable Translation translation) {
this.key = key;
this.translation = translation;
}
public @NotNull String getKey() {
public KeyPath getKey() {
return key;
}
public void setKey(@NotNull String key) {
public void setKey(KeyPath key) {
this.key = key;
}

View File

@ -1,7 +1,5 @@
package de.marhali.easyi18n.model;
import de.marhali.easyi18n.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -11,21 +9,21 @@ import java.util.*;
* Cached translation data. The data is stored in a tree structure.
* Tree behaviour (sorted, non-sorted) can be specified via constructor.
* For more please see {@link TranslationNode}. Example tree view:
* <br/>
* user: <br/>
* -- principal: 'Principal' <br/>
* -- username: <br/>
* -- -- title: 'Username' <br/>
* auth: <br/>
* -- logout: 'Logout' <br/>
* -- login: 'Login' <br/>
*
* <pre>
* {@code
* user:
* principal: 'Principal'
* username:
* title: 'Username'
* auth:
* logout: 'Logout'
* login: 'Login'
* }
* </pre>
* @author marhali
*/
public class TranslationData {
private final PathUtil pathUtil;
@NotNull
private final Set<String> locales;
@ -36,17 +34,15 @@ public class TranslationData {
* Creates an empty instance.
* @param sort Should the translation keys be sorted alphabetically
*/
public TranslationData(boolean sort, boolean nestKeys) {
this(nestKeys, new HashSet<>(), new TranslationNode(sort ? new TreeMap<>() : new LinkedHashMap<>()));
public TranslationData(boolean sort) {
this(new HashSet<>(), new TranslationNode(sort ? new TreeMap<>() : new LinkedHashMap<>()));
}
/**
* @param nestKeys Apply key nesting. See {@link PathUtil}
* @param locales Languages which can be used for translation
* @param rootNode Translation tree structure
*/
public TranslationData(boolean nestKeys, @NotNull Set<String> locales, @NotNull TranslationNode rootNode) {
this.pathUtil = new PathUtil(nestKeys);
public TranslationData(@NotNull Set<String> locales, @NotNull TranslationNode rootNode) {
this.locales = locales;
this.rootNode = rootNode;
}
@ -76,15 +72,14 @@ public class TranslationData {
* @param fullPath Absolute translation path
* @return Translation node which leads to translations or nested child's
*/
public @Nullable TranslationNode getNode(@NotNull String fullPath) {
List<String> sections = this.pathUtil.split(fullPath);
public @Nullable TranslationNode getNode(@NotNull KeyPath fullPath) {
TranslationNode node = this.rootNode;
if(fullPath.isEmpty()) { // Return root node if empty path was supplied
return node;
}
for(String section : sections) {
for(String section : fullPath) {
if(node == null) {
return null;
}
@ -98,7 +93,7 @@ public class TranslationData {
* @param fullPath Absolute translation key path
* @return Found translation. Can be null if path is empty or is not a leaf element
*/
public @Nullable Translation getTranslation(@NotNull String fullPath) {
public @Nullable Translation getTranslation(@NotNull KeyPath fullPath) {
TranslationNode node = this.getNode(fullPath);
if(node == null || !node.isLeaf()) {
@ -109,50 +104,52 @@ public class TranslationData {
}
/**
* Create / Update or Delete a specific translation.
* The parent path of the translation will be changed if necessary.
* @param fullPath Absolute translation key path
* @param translation Translation to set. Can be null to delete the corresponding node
*/
public void setTranslation(@NotNull String fullPath, @Nullable Translation translation) {
List<String> sections = this.pathUtil.split(fullPath);
String nodeKey = sections.remove(sections.size() - 1); // Edge case last section
TranslationNode node = this.rootNode;
public void setTranslation(@NotNull KeyPath fullPath, @Nullable Translation translation) {
if(fullPath.isEmpty()) {
throw new IllegalArgumentException("Path cannot be empty");
throw new IllegalArgumentException("Key path cannot be empty");
}
for(String section : sections) { // Go to the level of the key (@nodeKey)
fullPath = new KeyPath(fullPath);
String leafKey = fullPath.remove(fullPath.size() - 1); // Extract edge section as children key of parent
TranslationNode node = this.rootNode;
for(String section : fullPath) { // Go to nested level at @leafKey
TranslationNode childNode = node.getChildren().get(section);
if(childNode == null) {
if(translation == null) { // Path should not be empty for delete
if(translation == null) { // Path must not be empty on delete
throw new IllegalArgumentException("Delete action on empty path");
}
// Created nested section
childNode = node.setChildren(section);
}
node = childNode;
}
if(translation == null) { // Delete
node.removeChildren(nodeKey);
if(translation == null) { // Delete action
node.removeChildren(leafKey);
if(node.getChildren().isEmpty() && !node.isRoot()) { // Parent is empty now. Run delete recursively
this.setTranslation(this.pathUtil.concat(sections), null);
if(node.getChildren().isEmpty() && !node.isRoot()) { // Node is empty now. Run delete recursively
this.setTranslation(fullPath, null);
}
} else { // Create or overwrite
node.setChildren(nodeKey, translation);
return;
}
// Create or overwrite
node.setChildren(leafKey, translation);
}
/**
* @return All translation keys as absolute paths (full-key)
*/
public @NotNull Set<String> getFullKeys() {
return this.getFullKeys("", this.rootNode); // Just use root node
public @NotNull Set<KeyPath> getFullKeys() {
return this.getFullKeys(new KeyPath(), this.rootNode); // Just use root node
}
/**
@ -160,15 +157,15 @@ public class TranslationData {
* @param node Node section to begin with
* @return All translation keys where the path contains the specified @parentPath
*/
public @NotNull Set<String> getFullKeys(String parentPath, TranslationNode node) {
Set<String> keys = new LinkedHashSet<>();
public @NotNull Set<KeyPath> getFullKeys(KeyPath parentPath, TranslationNode node) {
Set<KeyPath> keys = new LinkedHashSet<>();
if(node.isLeaf()) { // This node does not lead to child's - just add the key
keys.add(parentPath);
}
for(Map.Entry<String, TranslationNode> children : node.getChildren().entrySet()) {
keys.addAll(this.getFullKeys(this.pathUtil.append(parentPath, children.getKey()), children.getValue()));
keys.addAll(this.getFullKeys(new KeyPath(parentPath, children.getKey()), children.getValue()));
}
return keys;
@ -178,7 +175,6 @@ public class TranslationData {
public String toString() {
return "TranslationData{" +
"mapClass=" + rootNode.getChildren().getClass().getSimpleName() +
", pathUtil=" + pathUtil +
", locales=" + locales +
", rootNode=" + rootNode +
'}';

View File

@ -7,14 +7,13 @@ import java.util.Map;
/**
* Translation tree node. Manages child nodes which can be translations or also
* nodes which can lead to another translation or node.
* nodes which can lead to another translation or node.<br>
* Navigation inside a node can be upward and downward. To construct the full
* translation key (full-key) every parent needs to be resolved recursively.
* -
* translation key (full-key) every parent needs to be resolved recursively. <br>
* <br>
* Whether the children nodes should be sorted is determined by the parent node.
* For root nodes (empty parent) the {@link java.util.Map}-Type must be specified
* to determine which sorting should be applied.
*
* @author marhali
*/
public class TranslationNode {

View File

@ -1,6 +1,8 @@
package de.marhali.easyi18n.model.bus;
import org.jetbrains.annotations.Nullable;
import de.marhali.easyi18n.model.KeyPath;
import org.jetbrains.annotations.NotNull;
/**
* Single event listener.
@ -11,5 +13,5 @@ public interface FocusKeyListener {
* Move the specified translation key (full-key) into focus.
* @param key Absolute translation key
*/
void onFocusKey(@Nullable String key);
void onFocusKey(@NotNull KeyPath key);
}

View File

@ -28,15 +28,16 @@ import java.util.ResourceBundle;
*/
public class TableView implements BusListener {
private final JBTable table;
private final Project project;
private TableModelMapper currentMapper;
private KeyPathConverter converter;
private JPanel rootPanel;
private JPanel containerPanel;
private JBTable table;
public TableView(Project project) {
this.project = project;
@ -55,7 +56,7 @@ public class TableView implements BusListener {
return;
}
String fullPath = String.valueOf(table.getValueAt(row, 0));
KeyPath fullPath = this.converter.split(String.valueOf(this.table.getValueAt(row, 0)));
Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath);
if (translation != null) {
@ -65,7 +66,7 @@ public class TableView implements BusListener {
private void deleteSelectedRows() {
for (int selectedRow : table.getSelectedRows()) {
String fullPath = String.valueOf(table.getValueAt(selectedRow, 0));
KeyPath fullPath = this.converter.split(String.valueOf(table.getValueAt(selectedRow, 0)));
InstanceManager.get(project).processUpdate(
new TranslationDelete(new KeyedTranslation(fullPath, null))
@ -75,16 +76,19 @@ public class TableView implements BusListener {
@Override
public void onUpdateData(@NotNull TranslationData data) {
table.setModel(this.currentMapper = new TableModelMapper(data, update ->
this.converter = new KeyPathConverter(project);
table.setModel(this.currentMapper = new TableModelMapper(data, this.converter, update ->
InstanceManager.get(project).processUpdate(update)));
}
@Override
public void onFocusKey(@Nullable String key) {
public void onFocusKey(@NotNull KeyPath key) {
String concatKey = this.converter.concat(key);
int row = -1;
for (int i = 0; i < table.getRowCount(); i++) {
if (String.valueOf(table.getValueAt(i, 0)).equals(key)) {
if (table.getValueAt(i, 0).equals(concatKey)) {
row = i;
}
}

View File

@ -10,10 +10,7 @@ import com.intellij.ui.treeStructure.Tree;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.listener.ReturnKeyListener;
import de.marhali.easyi18n.model.KeyedTranslation;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationDelete;
import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.bus.BusListener;
import de.marhali.easyi18n.action.treeview.CollapseTreeViewAction;
import de.marhali.easyi18n.action.treeview.ExpandTreeViewAction;
@ -40,6 +37,8 @@ import java.util.ResourceBundle;
*/
public class TreeView implements BusListener {
private final Tree tree;
private final Project project;
private TreeModelMapper currentMapper;
@ -48,8 +47,6 @@ public class TreeView implements BusListener {
private JPanel toolBarPanel;
private JPanel containerPanel;
private Tree tree;
public TreeView(Project project) {
this.project = project;
@ -87,8 +84,8 @@ public class TreeView implements BusListener {
}
@Override
public void onFocusKey(@Nullable String key) {
if (key != null && currentMapper != null) {
public void onFocusKey(@NotNull KeyPath key) {
if (currentMapper != null) {
TreePath path = currentMapper.findTreePath(key);
this.tree.getSelectionModel().setSelectionPath(path);
@ -120,7 +117,7 @@ public class TreeView implements BusListener {
return;
}
String fullPath = TreeUtil.getFullPath(path);
KeyPath fullPath = TreeUtil.getFullPath(path);
Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath);
if (translation == null) {
@ -138,7 +135,7 @@ public class TreeView implements BusListener {
}
for (TreePath path : tree.getSelectionPaths()) {
String fullPath = TreeUtil.getFullPath(path);
KeyPath fullPath = TreeUtil.getFullPath(path);
InstanceManager.get(project).processUpdate(
new TranslationDelete(new KeyedTranslation(fullPath, null))

View File

@ -20,13 +20,18 @@ import java.util.function.Consumer;
public class TableModelMapper implements TableModel, SearchQueryListener {
private final @NotNull TranslationData data;
private final @NotNull KeyPathConverter converter;
private final @NotNull List<String> locales;
private @NotNull List<String> fullKeys;
private @NotNull List<KeyPath> fullKeys;
private final @NotNull Consumer<TranslationUpdate> updater;
public TableModelMapper(@NotNull TranslationData data, @NotNull Consumer<TranslationUpdate> updater) {
public TableModelMapper(@NotNull TranslationData data, @NotNull KeyPathConverter converter,
@NotNull Consumer<TranslationUpdate> updater) {
this.data = data;
this.converter = converter;
this.locales = new ArrayList<>(data.getLocales());
this.fullKeys = new ArrayList<>(data.getFullKeys());
@ -41,10 +46,10 @@ public class TableModelMapper implements TableModel, SearchQueryListener {
}
query = query.toLowerCase();
List<String> matches = new ArrayList<>();
List<KeyPath> matches = new ArrayList<>();
for(String key : this.data.getFullKeys()) {
if(key.toLowerCase().contains(query)) {
for(KeyPath key : this.data.getFullKeys()) {
if(this.converter.concat(key).toLowerCase().contains(query)) {
matches.add(key);
} else {
for(String content : this.data.getTranslation(key).values()) {
@ -90,11 +95,12 @@ public class TableModelMapper implements TableModel, SearchQueryListener {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
KeyPath key = this.fullKeys.get(rowIndex);
if(columnIndex == 0) { // Keys
return this.fullKeys.get(rowIndex);
return this.converter.concat(key);
}
String key = this.fullKeys.get(rowIndex);
String locale = this.locales.get(columnIndex - 1);
Translation translation = this.data.getTranslation(key);
@ -103,14 +109,14 @@ public class TableModelMapper implements TableModel, SearchQueryListener {
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
String key = String.valueOf(this.getValueAt(rowIndex, 0));
KeyPath key = this.fullKeys.get(rowIndex);
Translation translation = this.data.getTranslation(key);
if(translation == null) { // Unknown cell
return;
}
String newKey = columnIndex == 0 ? String.valueOf(aValue) : key;
KeyPath newKey = columnIndex == 0 ? this.converter.split(String.valueOf(aValue)) : key;
// Translation content update
if(columnIndex > 0) {

View File

@ -3,12 +3,8 @@ package de.marhali.easyi18n.tabs.mapper;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ui.JBColor;
import de.marhali.easyi18n.model.SettingsState;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.bus.SearchQueryListener;
import de.marhali.easyi18n.util.PathUtil;
import de.marhali.easyi18n.util.UiUtil;
import org.jetbrains.annotations.NotNull;
@ -26,12 +22,14 @@ import java.util.Map;
public class TreeModelMapper extends DefaultTreeModel implements SearchQueryListener {
private final TranslationData data;
private final KeyPathConverter converter;
private final SettingsState state;
public TreeModelMapper(TranslationData data, SettingsState state) {
super(null);
this.data = data;
this.converter = new KeyPathConverter(state.isNestedKeys());
this.state = state;
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
@ -42,7 +40,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
@Override
public void onSearchQuery(@Nullable String query) {
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
TranslationData shadow = new TranslationData(this.state.isSortKeys(), this.state.isNestedKeys());
TranslationData shadow = new TranslationData(this.state.isSortKeys());
if(query == null) {
this.generateNodes(rootNode, this.data.getRootNode());
@ -52,9 +50,9 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
query = query.toLowerCase();
for(String currentKey : this.data.getFullKeys()) {
for(KeyPath currentKey : this.data.getFullKeys()) {
Translation translation = this.data.getTranslation(currentKey);
String loweredKey = currentKey.toLowerCase();
String loweredKey = this.converter.concat(currentKey).toLowerCase();
if(query.contains(loweredKey) || loweredKey.contains(query)) {
shadow.setTranslation(currentKey, translation);
@ -100,14 +98,13 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
}
}
public @NotNull TreePath findTreePath(@NotNull String fullPath) {
List<String> sections = new PathUtil(this.state.isNestedKeys()).split(fullPath);
public @NotNull TreePath findTreePath(@NotNull KeyPath fullPath) {
List<Object> nodes = new ArrayList<>();
TreeNode currentNode = (TreeNode) this.getRoot();
nodes.add(currentNode);
for(String section : sections) {
for(String section : fullPath) {
currentNode = this.findNode(currentNode, section);
if(currentNode == null) {
@ -120,7 +117,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
return new TreePath(nodes.toArray());
}
public @Nullable DefaultMutableTreeNode findNode(@NotNull TreeNode parent, @NotNull String key) {
private @Nullable DefaultMutableTreeNode findNode(@NotNull TreeNode parent, @NotNull String key) {
for(int i = 0; i < parent.getChildCount(); i++) {
TreeNode child = parent.getChildAt(i);

View File

@ -1,77 +0,0 @@
package de.marhali.easyi18n.util;
import de.marhali.easyi18n.service.SettingsService;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
/**
* Utility tool for split and merge translation key paths.
* Some i18n implementations require to NOT nest the translation keys.
* This util takes care of this and checks the configured setting for this case.
* @author marhali
* @deprecated Replaced by KeyPathConverter
*/
@Deprecated
public class PathUtil {
public static final String DELIMITER = ".";
private final boolean nestKeys;
public PathUtil(boolean nestKeys) {
this.nestKeys = nestKeys;
}
public PathUtil(Project project) {
this.nestKeys = SettingsService.getInstance(project).getState().isNestedKeys();
}
public @NotNull List<String> split(@NotNull String path) {
// Does not contain any sections or key nesting is disabled
if(!path.contains(DELIMITER) || !nestKeys) {
return new ArrayList<>(Collections.singletonList(path));
}
return new ArrayList<>(Arrays.asList(
path.split("(?<!\\\\)" + Pattern.quote(DELIMITER))));
}
public @NotNull String concat(@NotNull List<String> sections) {
StringBuilder builder = new StringBuilder();
// For disabled key nesting this should be only one section
for(String section : sections) {
if(builder.length() > 0) {
builder.append(DELIMITER);
}
builder.append(section);
}
return builder.toString();
}
public @NotNull String append(@NotNull String parentPath, @NotNull String children) {
StringBuilder builder = new StringBuilder(parentPath);
if(builder.length() > 0) { // Only add delimiter between parent and child if parent is NOT empty
builder.append(DELIMITER);
}
return builder.append(children).toString();
}
@Override
public String toString() {
return "PathUtil{" +
"nestKeys=" + nestKeys +
'}';
}
}

View File

@ -1,6 +1,7 @@
package de.marhali.easyi18n.util;
import com.intellij.ide.projectView.PresentationData;
import de.marhali.easyi18n.model.KeyPath;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
@ -13,13 +14,13 @@ public class TreeUtil {
/**
* Constructs the full path for a given {@link TreePath}
* @param path TreePath
* @return Full key (e.g user.username.title)
* @param treePath TreePath
* @return Corresponding key path
*/
public static String getFullPath(TreePath path) {
StringBuilder builder = new StringBuilder();
public static KeyPath getFullPath(TreePath treePath) {
KeyPath keyPath = new KeyPath();
for (Object obj : path.getPath()) {
for (Object obj : treePath.getPath()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) obj;
Object value = node.getUserObject();
String section = value instanceof PresentationData ?
@ -29,13 +30,9 @@ public class TreeUtil {
continue;
}
if(builder.length() != 0) {
builder.append(PathUtil.DELIMITER);
}
builder.append(section);
keyPath.add(section);
}
return builder.toString();
return keyPath;
}
}

View File

@ -19,6 +19,6 @@ settings.path.file-pattern-tooltip=Defines a wildcard matcher to filter relevant
settings.path.prefix=Path prefix
settings.preview=Preview locale
settings.keys.sort=Sort translation keys alphabetically
settings.keys.nested=Nest translation keys if possible
settings.keys.nested=Escape delimiter character within a section layer.
settings.editor.assistance=I18n key completion, annotation and reference inside editor
error.io=Could not process file {0} with {1}. Unwanted files can be ignored via Translation file wildcard matcher option.

View File

@ -1,5 +1,6 @@
package de.marhali.easyi18n;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
@ -15,256 +16,184 @@ import java.util.*;
*/
public class TranslationDataTest {
private final int numOfTranslations = 18;
private final int numOfTranslations = 14;
private final Translation translation = new Translation("en", "test");
private void addTranslations(TranslationData data) {
data.setTranslation("zulu", new Translation("en", "test"));
data.setTranslation("gamma", new Translation("en", "test"));
data.setTranslation(KeyPath.of("zulu"), translation);
data.setTranslation(KeyPath.of("gamma"), translation);
data.setTranslation("foxtrot.super.long.key", new Translation("en", "test"));
data.setTranslation(KeyPath.of("foxtrot.super.long.key"), translation);
data.setTranslation(KeyPath.of("foxtrot", "super", "long", "key"), translation);
data.setTranslation("bravo.b", new Translation("en", "test"));
data.setTranslation("bravo.c", new Translation("en", "test"));
data.setTranslation("bravo.a", new Translation("en", "test"));
data.setTranslation("bravo.d", new Translation("en", "test"));
data.setTranslation("bravo.long.bravo", new Translation("en", "test"));
data.setTranslation("bravo.long.charlie.a", new Translation("en", "test"));
data.setTranslation("bravo.long.alpha", new Translation("en", "test"));
data.setTranslation(KeyPath.of("charlie.b", "sub"), translation);
data.setTranslation(KeyPath.of("charlie.a", "sub"), translation);
data.setTranslation("alpha.b", new Translation("en", "test"));
data.setTranslation("alpha.c", new Translation("en", "test"));
data.setTranslation("alpha.a", new Translation("en", "test"));
data.setTranslation("alpha.d", new Translation("en", "test"));
data.setTranslation(KeyPath.of("bravo.b"), translation);
data.setTranslation(KeyPath.of("bravo.c"), translation);
data.setTranslation(KeyPath.of("bravo.a"), translation);
data.setTranslation(KeyPath.of("bravo.d"), translation);
data.setTranslation("charlie.b", new Translation("en", "test"));
data.setTranslation("charlie.c", new Translation("en", "test"));
data.setTranslation("charlie.a", new Translation("en", "test"));
data.setTranslation("charlie.d", new Translation("en", "test"));
data.setTranslation(KeyPath.of("bravo", "b"), translation);
data.setTranslation(KeyPath.of("bravo", "c"), translation);
data.setTranslation(KeyPath.of("bravo", "a"), translation);
data.setTranslation(KeyPath.of("bravo", "d"), translation);
}
@Test
public void testKeySorting() {
TranslationData data = new TranslationData(true, true);
TranslationData data = new TranslationData(true);
this.addTranslations(data);
Set<String> expectation = new LinkedHashSet<>(Arrays.asList(
"alpha.a", "alpha.b", "alpha.c", "alpha.d",
"bravo.a", "bravo.b", "bravo.c", "bravo.d",
"bravo.long.alpha", "bravo.long.bravo", "bravo.long.charlie.a",
"charlie.a", "charlie.b", "charlie.c", "charlie.d",
"foxtrot.super.long.key",
"gamma",
"zulu"
Set<KeyPath> expectation = new LinkedHashSet<>(Arrays.asList(
KeyPath.of("bravo", "a"), KeyPath.of("bravo", "b"), KeyPath.of("bravo", "c"), KeyPath.of("bravo", "d"),
KeyPath.of("bravo.a"), KeyPath.of("bravo.b"), KeyPath.of("bravo.c"), KeyPath.of("bravo.d"),
KeyPath.of("charlie.a", "sub"), KeyPath.of("charlie.b", "sub"),
KeyPath.of("foxtrot", "super", "long", "key"),
KeyPath.of("foxtrot.super.long.key"),
KeyPath.of("gamma"),
KeyPath.of("zulu")
));
Assert.assertEquals(data.getFullKeys(), expectation);
Assert.assertEquals(data.getFullKeys().size(), numOfTranslations);
}
@Test
public void testKeyUnordered() {
TranslationData data = new TranslationData(false, true);
TranslationData data = new TranslationData(false);
this.addTranslations(data);
Set<String> expectation = new LinkedHashSet<>(Arrays.asList(
"zulu",
"gamma",
"foxtrot.super.long.key",
"bravo.b", "bravo.c", "bravo.a", "bravo.d",
"bravo.long.bravo", "bravo.long.charlie.a", "bravo.long.alpha",
"alpha.b", "alpha.c", "alpha.a", "alpha.d",
"charlie.b", "charlie.c", "charlie.a", "charlie.d"
Set<KeyPath> expectation = new LinkedHashSet<>(Arrays.asList(
KeyPath.of("zulu"),
KeyPath.of("gamma"),
KeyPath.of("foxtrot.super.long.key"),
KeyPath.of("foxtrot", "super", "long", "key"),
KeyPath.of("charlie.b", "sub"), KeyPath.of("charlie.a", "sub"),
KeyPath.of("bravo.b"), KeyPath.of("bravo.c"), KeyPath.of("bravo.a"), KeyPath.of("bravo.d"),
KeyPath.of("bravo", "b"), KeyPath.of("bravo", "c"), KeyPath.of("bravo", "a"), KeyPath.of("bravo", "d")
));
Assert.assertEquals(data.getFullKeys(), expectation);
Assert.assertEquals(data.getFullKeys().size(), numOfTranslations);
}
@Test
public void testKeyNesting() {
TranslationData data = new TranslationData(true, true);
public void testDelete() {
TranslationData data = new TranslationData(true);
data.setTranslation("nested.alpha", new Translation("en", "test"));
data.setTranslation("nested.bravo", new Translation("en", "test"));
data.setTranslation("other.alpha", new Translation("en", "test"));
data.setTranslation("other.bravo", new Translation("en", "test"));
data.setTranslation(KeyPath.of("alpha"), translation);
data.setTranslation(KeyPath.of("nested.alpha"), translation);
data.setTranslation(KeyPath.of("nested.long.bravo"), translation);
Assert.assertEquals(data.getRootNode().getChildren().size(), 2);
data.setTranslation(KeyPath.of("beta"), translation);
data.setTranslation(KeyPath.of("nested", "alpha"), translation);
data.setTranslation(KeyPath.of("nested", "long", "bravo"), translation);
for(TranslationNode node : data.getRootNode().getChildren().values()) {
Assert.assertFalse(node.isLeaf());
}
}
Assert.assertEquals(data.getFullKeys().size(), 6);
@Test
public void testKeyNonNested() {
TranslationData data = new TranslationData(true, false);
this.addTranslations(data);
Assert.assertEquals(data.getRootNode().getChildren().size(), this.numOfTranslations);
for(TranslationNode node : data.getRootNode().getChildren().values()) {
Assert.assertTrue(node.isLeaf());
}
}
@Test
public void testDeleteNested() {
TranslationData data = new TranslationData(true, true);
Translation value = new Translation("en", "test");
data.setTranslation("alpha", value);
data.setTranslation("nested.alpha", value);
data.setTranslation("nested.long.bravo", value);
data.setTranslation(KeyPath.of("alpha"), null);
data.setTranslation(KeyPath.of("nested.alpha"), null);
data.setTranslation(KeyPath.of("nested.long.bravo"), null);
Assert.assertEquals(data.getFullKeys().size(), 3);
data.setTranslation("alpha", null);
data.setTranslation("nested.alpha", null);
data.setTranslation("nested.long.bravo", null);
data.setTranslation(KeyPath.of("beta"), null);
data.setTranslation(KeyPath.of("nested", "alpha"), null);
data.setTranslation(KeyPath.of("nested", "long", "bravo"), null);
Assert.assertEquals(data.getFullKeys().size(), 0);
Assert.assertNull(data.getTranslation("alpha"));
Assert.assertNull(data.getTranslation("nested.alpha"));
Assert.assertNull(data.getTranslation("nested.long.bravo"));
Assert.assertNull(data.getTranslation(KeyPath.of("alpha")));
Assert.assertNull(data.getTranslation(KeyPath.of("nested.alpha")));
Assert.assertNull(data.getTranslation(KeyPath.of("nested.long.bravo")));
Assert.assertNull(data.getTranslation(KeyPath.of("beta")));
Assert.assertNull(data.getTranslation(KeyPath.of("nested", "alpha")));
Assert.assertNull(data.getTranslation(KeyPath.of("nested", "long", "bravo")));
}
@Test
public void testDeleteNonNested() {
TranslationData data = new TranslationData(true, false);
Translation value = new Translation("en", "test");
data.setTranslation("alpha", value);
data.setTranslation("nested.alpha", value);
data.setTranslation("nested.long.bravo", value);
Assert.assertEquals(data.getFullKeys().size(), 3);
data.setTranslation("alpha", null);
data.setTranslation("nested.alpha", null);
data.setTranslation("nested.long.bravo", null);
Assert.assertEquals(data.getFullKeys().size(), 0);
Assert.assertNull(data.getTranslation("alpha"));
Assert.assertNull(data.getTranslation("nested.alpha"));
Assert.assertNull(data.getTranslation("nested.long.bravo"));
}
@Test
public void testRecurseDeleteNonNested() {
TranslationData data = new TranslationData(true, false);
public void testDeleteRecursively() {
TranslationData data = new TranslationData(true);
this.addTranslations(data);
data.setTranslation("foxtrot.super.long.key", null);
data.setTranslation(KeyPath.of("foxtrot.super.long.key"), null);
data.setTranslation(KeyPath.of("foxtrot", "super", "long", "key"), null);
Assert.assertNull(data.getTranslation("foxtrot.super.long.key"));
Assert.assertNull(data.getTranslation(KeyPath.of("foxtrot.super.long.key")));
Assert.assertNull(data.getRootNode().getChildren().get("foxtrot"));
Assert.assertEquals(data.getFullKeys().size(), numOfTranslations - 2);
}
@Test
public void testRecurseDeleteNested() {
TranslationData data = new TranslationData(true, true);
this.addTranslations(data);
data.setTranslation("foxtrot.super.long.key", null);
Assert.assertNull(data.getTranslation("foxtrot.super.long.key"));
Assert.assertNull(data.getRootNode().getChildren().get("foxtrot"));
}
@Test
public void testOverwriteNonNested() {
TranslationData data = new TranslationData(true, false);
public void testOverwrite() {
TranslationData data = new TranslationData(true);
Translation before = new Translation("en", "before");
Translation after = new Translation("en", "after");
data.setTranslation("alpha", before);
data.setTranslation("nested.alpha", before);
data.setTranslation("nested.long.bravo", before);
data.setTranslation(KeyPath.of("alpha"), before);
data.setTranslation(KeyPath.of("nested.alpha"), before);
data.setTranslation(KeyPath.of("nested.long.bravo"), before);
data.setTranslation(KeyPath.of("beta"), before);
data.setTranslation(KeyPath.of("nested", "alpha"), before);
data.setTranslation(KeyPath.of("nested", "long", "bravo"), before);
Assert.assertEquals(data.getTranslation("alpha"), before);
Assert.assertEquals(data.getTranslation("alpha"), before);
Assert.assertEquals(data.getTranslation("alpha"), before);
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha")), before);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested.alpha")), before);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested.long.bravo")), before);
Assert.assertEquals(data.getTranslation(KeyPath.of("beta")), before);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "alpha")), before);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "long", "bravo")), before);
data.setTranslation("alpha", after);
data.setTranslation("nested.alpha", after);
data.setTranslation("nested.long.bravo", after);
data.setTranslation(KeyPath.of("alpha"), after);
data.setTranslation(KeyPath.of("nested.alpha"), after);
data.setTranslation(KeyPath.of("nested.long.bravo"), after);
data.setTranslation(KeyPath.of("beta"), after);
data.setTranslation(KeyPath.of("nested", "alpha"), after);
data.setTranslation(KeyPath.of("nested", "long", "bravo"), after);
Assert.assertEquals(data.getTranslation("alpha"), after);
Assert.assertEquals(data.getTranslation("alpha"), after);
Assert.assertEquals(data.getTranslation("alpha"), after);
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha")), after);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested.alpha")), after);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested.long.bravo")), after);
Assert.assertEquals(data.getTranslation(KeyPath.of("beta")), after);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "alpha")), after);
Assert.assertEquals(data.getTranslation(KeyPath.of("nested", "long", "bravo")), after);
}
@Test
public void testOverwriteNested() {
TranslationData data = new TranslationData(true, true);
public void testTransformRecursively() {
TranslationData data = new TranslationData(true);
Translation before = new Translation("en", "before");
Translation after = new Translation("en", "after");
data.setTranslation(KeyPath.of("alpha.nested.key"), translation);
data.setTranslation(KeyPath.of("alpha.other"), translation);
data.setTranslation(KeyPath.of("bravo"), translation);
data.setTranslation(KeyPath.of("alpha", "nested", "key"), translation);
data.setTranslation(KeyPath.of("alpha", "other"), translation);
data.setTranslation(KeyPath.of("charlie"), translation);
data.setTranslation("alpha", before);
data.setTranslation("nested.alpha", before);
data.setTranslation("nested.long.bravo", before);
Assert.assertEquals(6, data.getFullKeys().size());
Assert.assertEquals(data.getTranslation("alpha"), before);
Assert.assertEquals(data.getTranslation("alpha"), before);
Assert.assertEquals(data.getTranslation("alpha"), before);
data.setTranslation(KeyPath.of("alpha.nested"), translation);
data.setTranslation(KeyPath.of("alpha.other.new"), translation);
data.setTranslation(KeyPath.of("bravo"), null);
data.setTranslation(KeyPath.of("alpha", "nested"), translation);
data.setTranslation(KeyPath.of("alpha", "other", "new"), translation);
data.setTranslation(KeyPath.of("charlie"), null);
data.setTranslation("alpha", after);
data.setTranslation("nested.alpha", after);
data.setTranslation("nested.long.bravo", after);
Assert.assertEquals(6, data.getFullKeys().size());
Assert.assertEquals(data.getTranslation("alpha"), after);
Assert.assertEquals(data.getTranslation("alpha"), after);
Assert.assertEquals(data.getTranslation("alpha"), after);
}
Assert.assertNotNull(data.getTranslation(KeyPath.of("alpha.nested.key")));
Assert.assertNotNull(data.getTranslation(KeyPath.of("alpha.other")));
Assert.assertNull(data.getTranslation(KeyPath.of("bravo")));
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha.nested")), translation);
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha.other.new")), translation);
@Test
public void testRecurseTransformNested() {
TranslationData data = new TranslationData(true, true);
Translation value = new Translation("en", "test");
data.setTranslation("alpha.nested.key", value);
data.setTranslation("alpha.other", value);
data.setTranslation("bravo", value);
Assert.assertEquals(data.getFullKeys().size(), 3);
data.setTranslation("alpha.nested", value);
data.setTranslation("alpha.other.new", value);
data.setTranslation("bravo", null);
Assert.assertEquals(data.getFullKeys().size(), 2);
Assert.assertNull(data.getTranslation("alpha.nested.key"));
Assert.assertNull(data.getTranslation("alpha.other"));
Assert.assertNull(data.getTranslation("bravo"));
Assert.assertEquals(data.getTranslation("alpha.nested"), value);
Assert.assertEquals(data.getTranslation("alpha.other.new"), value);
}
@Test
public void testRecurseTransformNonNested() {
TranslationData data = new TranslationData(true, false);
Translation value = new Translation("en", "test");
data.setTranslation("alpha.nested.key", value);
data.setTranslation("alpha.other", value);
data.setTranslation("bravo", value);
Assert.assertEquals(data.getFullKeys().size(), 3);
data.setTranslation("alpha.nested", value);
data.setTranslation("alpha.other.new", value);
data.setTranslation("bravo", null);
Assert.assertEquals(data.getFullKeys().size(), 4);
Assert.assertNull(data.getTranslation("bravo"));
Assert.assertEquals(data.getTranslation("alpha.nested.key"), value);
Assert.assertEquals(data.getTranslation("alpha.other"), value);
Assert.assertEquals(data.getTranslation("alpha.nested"), value);
Assert.assertEquals(data.getTranslation("alpha.other.new"), value);
Assert.assertNull(data.getTranslation(KeyPath.of("alpha", "nested", "key")));
Assert.assertNull(data.getTranslation(KeyPath.of("alpha", "other")));
Assert.assertNull(data.getTranslation(KeyPath.of("charlie")));
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha", "nested")), translation);
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha", "other", "new")), translation);
}
}

View File

@ -6,6 +6,7 @@ import com.google.gson.JsonPrimitive;
import de.marhali.easyi18n.io.json.JsonArrayMapper;
import de.marhali.easyi18n.io.json.JsonMapper;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.TranslationData;
import org.apache.commons.lang.StringEscapeUtils;
@ -16,7 +17,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
/**
* Unit tests for {@link de.marhali.easyi18n.io.json.JsonMapper}
* Unit tests for {@link JsonMapper}.
* @author marhali
*/
public class JsonMapperTest extends AbstractMapperTest {
@ -28,7 +29,7 @@ public class JsonMapperTest extends AbstractMapperTest {
input.add("alpha", new JsonPrimitive("test"));
input.add("bravo", new JsonPrimitive("test"));
TranslationData data = new TranslationData(false, true);
TranslationData data = new TranslationData(false);
JsonMapper.read("en", input, data.getRootNode());
JsonObject output = new JsonObject();
@ -45,7 +46,7 @@ public class JsonMapperTest extends AbstractMapperTest {
input.add("alpha", new JsonPrimitive("test"));
input.add("bravo", new JsonPrimitive("test"));
TranslationData data = new TranslationData(true, true);
TranslationData data = new TranslationData(true);
JsonMapper.read("en", input, data.getRootNode());
JsonObject output = new JsonObject();
@ -57,9 +58,9 @@ public class JsonMapperTest extends AbstractMapperTest {
@Override
public void testArrays() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("simple", create(arraySimple));
data.setTranslation("escaped", create(arrayEscaped));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("simple"), create(arraySimple));
data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped));
JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode());
@ -69,81 +70,82 @@ public class JsonMapperTest extends AbstractMapperTest {
Assert.assertTrue(output.get("escaped").isJsonArray());
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(JsonArrayMapper.read(output.get("escaped").getAsJsonArray())));
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode());
Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation("simple").get("en")));
Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation("escaped").get("en")));
Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en")));
Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en")));
}
@Override
public void testSpecialCharacters() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("chars", create(specialCharacters));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("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);
TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode());
Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation("chars").get("en")));
Assert.assertEquals(specialCharacters,
StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en")));
}
@Override
public void testNestedKeys() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("nested.key.section", create("test"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("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);
TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode());
Assert.assertEquals("test", input.getTranslation("nested.key.section").get("en"));
Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "section")).get("en"));
}
@Override
public void testNonNestedKeys() {
TranslationData data = new TranslationData(true, false);
data.setTranslation("long.key.with.many.sections", create("test"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("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);
TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode());
Assert.assertEquals("test", input.getTranslation("long.key.with.many.sections").get("en"));
Assert.assertEquals("test", input.getTranslation(KeyPath.of("long.key.with.many.sections")).get("en"));
}
@Override
public void testLeadingSpace() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("space", create(leadingSpace));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("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);
TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode());
Assert.assertEquals(leadingSpace, input.getTranslation("space").get("en"));
Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en"));
}
@Override
public void testNumbers() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("numbered", create("15000"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("numbered"), create("15000"));
JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode());
@ -154,6 +156,6 @@ public class JsonMapperTest extends AbstractMapperTest {
input.addProperty("numbered", 143.23);
JsonMapper.read("en", input, data.getRootNode());
Assert.assertEquals("143.23", data.getTranslation("numbered").get("en"));
Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en"));
}
}

View File

@ -3,6 +3,7 @@ package de.marhali.easyi18n.mapper;
import de.marhali.easyi18n.io.properties.PropertiesArrayMapper;
import de.marhali.easyi18n.io.properties.PropertiesMapper;
import de.marhali.easyi18n.io.properties.SortableProperties;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.TranslationData;
import org.apache.commons.lang.StringEscapeUtils;
@ -11,7 +12,7 @@ import org.junit.Assert;
import java.util.*;
/**
* Unit tests for {@link de.marhali.easyi18n.io.properties.PropertiesMapper}
* Unit tests for {@link PropertiesMapper}.
* @author marhali
*/
public class PropertiesMapperTest extends AbstractMapperTest {
@ -23,7 +24,7 @@ public class PropertiesMapperTest extends AbstractMapperTest {
input.setProperty("alpha", "test");
input.setProperty("bravo", "test");
TranslationData data = new TranslationData(false, true);
TranslationData data = new TranslationData(false);
PropertiesMapper.read("en", input, data);
SortableProperties output = new SortableProperties(false);
@ -40,7 +41,7 @@ public class PropertiesMapperTest extends AbstractMapperTest {
input.setProperty("alpha", "test");
input.setProperty("bravo", "test");
TranslationData data = new TranslationData(true, true);
TranslationData data = new TranslationData(true);
PropertiesMapper.read("en", input, data);
SortableProperties output = new SortableProperties(true);
@ -52,9 +53,9 @@ public class PropertiesMapperTest extends AbstractMapperTest {
@Override
public void testArrays() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("simple", create(arraySimple));
data.setTranslation("escaped", create(arrayEscaped));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("simple"), create(arraySimple));
data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped));
SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data);
@ -64,83 +65,84 @@ public class PropertiesMapperTest extends AbstractMapperTest {
Assert.assertTrue(output.get("escaped") instanceof String[]);
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(PropertiesArrayMapper.read((String[]) output.get("escaped"))));
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input);
Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation("simple").get("en")));
Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation("escaped").get("en")));
Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en")));
Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en")));
}
@Override
public void testSpecialCharacters() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("chars", create(specialCharacters));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("chars"), create(specialCharacters));
SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data);
Assert.assertEquals(specialCharacters, output.get("chars"));
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input);
Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation("chars").get("en")));
Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en")));
}
@Override
public void testNestedKeys() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("nested.key.sections", create("test"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("nested", "key", "sections"), create("test"));
SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data);
Assert.assertEquals("test", output.get("nested.key.sections"));
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input);
System.out.println(input);
Assert.assertTrue(input.getRootNode().getChildren().containsKey("nested"));
Assert.assertEquals("test", input.getTranslation("nested.key.sections").get("en"));
Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "sections")).get("en"));
}
@Override
public void testNonNestedKeys() {
TranslationData data = new TranslationData(true, false);
data.setTranslation("long.key.with.many.sections", create("test"));
public void testNonNestedKeys() { // Note: Key nesting is not supported in properties file.
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test"));
SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data);
Assert.assertNotNull(output.get("long.key.with.many.sections"));
TranslationData input = new TranslationData(true, false);
TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input);
Assert.assertEquals("test", input.getRootNode().getChildren()
.get("long.key.with.many.sections").getValue().get("en"));
Assert.assertEquals("test", input.getTranslation(KeyPath.of("long", "key", "with", "many", "sections")).get("en"));
}
@Override
public void testLeadingSpace() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("space", create(leadingSpace));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("space"), create(leadingSpace));
SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data);
Assert.assertEquals(leadingSpace, output.get("space"));
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input);
Assert.assertEquals(leadingSpace, input.getTranslation("space").get("en"));
Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en"));
}
@Override
public void testNumbers() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("numbered", create("15000"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("numbered"), create("15000"));
SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data);
@ -151,6 +153,6 @@ public class PropertiesMapperTest extends AbstractMapperTest {
input.put("numbered", 143.23);
PropertiesMapper.read("en", input, data);
Assert.assertEquals("143.23", data.getTranslation("numbered").get("en"));
Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en"));
}
}

View File

@ -2,6 +2,7 @@ package de.marhali.easyi18n.mapper;
import de.marhali.easyi18n.io.yaml.YamlArrayMapper;
import de.marhali.easyi18n.io.yaml.YamlMapper;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.TranslationData;
import org.apache.commons.lang.StringEscapeUtils;
@ -15,7 +16,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
/**
* Unit tests for {@link de.marhali.easyi18n.io.yaml.YamlMapper}
* Unit tests for {@link YamlMapper}.
* @author marhali
*/
public class YamlMapperTest extends AbstractMapperTest {
@ -27,7 +28,7 @@ public class YamlMapperTest extends AbstractMapperTest {
input.set("alpha", "test");
input.set("bravo", "test");
TranslationData data = new TranslationData(false, true);
TranslationData data = new TranslationData(false);
YamlMapper.read("en", input, data.getRootNode());
Section output = new MapSection();
@ -44,7 +45,7 @@ public class YamlMapperTest extends AbstractMapperTest {
input.set("alpha", "test");
input.set("bravo", "test");
TranslationData data = new TranslationData(true, true);
TranslationData data = new TranslationData(true);
YamlMapper.read("en", input, data.getRootNode());
Section output = new MapSection();
@ -56,9 +57,9 @@ public class YamlMapperTest extends AbstractMapperTest {
@Override
public void testArrays() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("simple", create(arraySimple));
data.setTranslation("escaped", create(arrayEscaped));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("simple"), create(arraySimple));
data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped));
Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode());
@ -68,81 +69,82 @@ public class YamlMapperTest extends AbstractMapperTest {
Assert.assertTrue(output.isList("escaped"));
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(YamlArrayMapper.read(output.getList("escaped").get())));
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode());
Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation("simple").get("en")));
Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation("escaped").get("en")));
Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en")));
Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en")));
}
@Override
public void testSpecialCharacters() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("chars", create(specialCharacters));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("chars"), create(specialCharacters));
Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode());
Assert.assertEquals(specialCharacters, output.getString("chars").get());
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode());
Assert.assertEquals(specialCharacters, StringEscapeUtils.unescapeJava(input.getTranslation("chars").get("en")));
Assert.assertEquals(specialCharacters,
StringEscapeUtils.unescapeJava(input.getTranslation(KeyPath.of("chars")).get("en")));
}
@Override
public void testNestedKeys() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("nested.key.section", create("test"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("nested", "key", "section"), create("test"));
Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode());
Assert.assertEquals("test", output.getString("nested.key.section").get());
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode());
Assert.assertEquals("test", input.getTranslation("nested.key.section").get("en"));
Assert.assertEquals("test", input.getTranslation(KeyPath.of("nested", "key", "section")).get("en"));
}
@Override
public void testNonNestedKeys() {
TranslationData data = new TranslationData(true, false);
data.setTranslation("long.key.with.many.sections", create("test"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test"));
Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode());
Assert.assertTrue(output.getKeys().contains("long.key.with.many.sections"));
TranslationData input = new TranslationData(true, false);
TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode());
Assert.assertEquals("test", input.getTranslation("long.key.with.many.sections").get("en"));
Assert.assertEquals("test", input.getTranslation(KeyPath.of("long.key.with.many.sections")).get("en"));
}
@Override
public void testLeadingSpace() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("space", create(leadingSpace));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("space"), create(leadingSpace));
Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode());
Assert.assertEquals(leadingSpace, output.getString("space").get());
TranslationData input = new TranslationData(true, true);
TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode());
Assert.assertEquals(leadingSpace, input.getTranslation("space").get("en"));
Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en"));
}
@Override
public void testNumbers() {
TranslationData data = new TranslationData(true, true);
data.setTranslation("numbered", create("15000"));
TranslationData data = new TranslationData(true);
data.setTranslation(KeyPath.of("numbered"), create("15000"));
Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode());
@ -153,6 +155,6 @@ public class YamlMapperTest extends AbstractMapperTest {
input.set("numbered", 143.23);
YamlMapper.read("en", input, data.getRootNode());
Assert.assertEquals("143.23", data.getTranslation("numbered").get("en"));
Assert.assertEquals("143.23", data.getTranslation(KeyPath.of("numbered")).get("en"));
}
}