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] ## [Unreleased]
### BREAKING CHANGES ### BREAKING CHANGES
- Translation file pattern matcher needs to be updated to <kbd>\*.*</kbd> or equivalent wildcard rule - 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 ### Added
- Full keyboard shortcut support inside tool-window - Full keyboard shortcut support inside tool-window
- Support for dots within key nodes in YAML files - Support for dots within key nodes in YAML files
### Changed ### Changed
- Key completion inside editor suggests all keys without any logic
- Translation file pattern uses wildcard matcher instead of regex - Translation file pattern uses wildcard matcher instead of regex
- Improve exception handling on IO operations - Improve exception handling on IO operations
- Update Qodana to latest version - Update Qodana to latest version

View File

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

View File

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

View File

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

View File

@ -8,9 +8,10 @@ import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField; import com.intellij.ui.components.JBTextField;
import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyedTranslation; import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationCreate; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EtchedBorder; import javax.swing.border.EtchedBorder;
@ -25,19 +26,23 @@ import java.util.ResourceBundle;
*/ */
public class AddDialog { public class AddDialog {
private final Project project; private final @NotNull Project project;
private String preKey; private final @NotNull KeyPathConverter converter;
private @NotNull KeyPath preKey;
private JBTextField keyTextField; private JBTextField keyTextField;
private Map<String, JBTextField> valueTextFields; private Map<String, JBTextField> valueTextFields;
public AddDialog(Project project, String preKey) { public AddDialog(@NotNull Project project, @Nullable KeyPath preKey) {
this(project); 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.project = project;
this.converter = new KeyPathConverter(project);
this.preKey = new KeyPath();
} }
public void showAndHandle() { 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); InstanceManager.get(project).processUpdate(creation);
} }
@ -67,13 +73,17 @@ public class AddDialog {
JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2, 2)); JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2, 2));
JBLabel keyLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("translation.key")); 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); keyLabel.setLabelFor(keyTextField);
keyPanel.add(keyLabel); keyPanel.add(keyLabel);
keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
keyPanel.add(keyTextField); keyPanel.add(keyTextField);
rootPanel.add(keyPanel); 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)); JPanel valuePanel = new JPanel(new GridLayout(0, 1, 2, 2));
valueTextFields = new HashMap<>(); 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.JBLabel;
import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField; import com.intellij.ui.components.JBTextField;
import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyedTranslation; import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationDelete;
import de.marhali.easyi18n.dialog.descriptor.DeleteActionDescriptor; import de.marhali.easyi18n.dialog.descriptor.DeleteActionDescriptor;
import de.marhali.easyi18n.model.TranslationUpdate;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EtchedBorder; import javax.swing.border.EtchedBorder;
@ -27,6 +25,8 @@ import java.util.ResourceBundle;
public class EditDialog { public class EditDialog {
private final Project project; private final Project project;
private final KeyPathConverter converter;
private final KeyedTranslation origin; private final KeyedTranslation origin;
private JBTextField keyTextField; private JBTextField keyTextField;
@ -34,6 +34,7 @@ public class EditDialog {
public EditDialog(Project project, KeyedTranslation origin) { public EditDialog(Project project, KeyedTranslation origin) {
this.project = project; this.project = project;
this.converter = new KeyPathConverter(project);
this.origin = origin; 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() { private DialogBuilder prepare() {
@ -65,7 +66,7 @@ public class EditDialog {
JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2,2)); JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2,2));
JBLabel keyLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("translation.key")); 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); keyLabel.setLabelFor(keyTextField);
keyPanel.add(keyLabel); keyPanel.add(keyLabel);
keyPanel.add(keyTextField); keyPanel.add(keyTextField);

View File

@ -51,9 +51,8 @@ public class SettingsDialog {
// Reload instance // Reload instance
InstanceManager manager = InstanceManager.get(project); InstanceManager manager = InstanceManager.get(project);
manager.store().loadFromPersistenceLayer((success) -> { manager.store().loadFromPersistenceLayer((success) ->
manager.bus().propagate().onUpdateData(manager.store().getData()); 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 com.intellij.openapi.project.Project;
import de.marhali.easyi18n.InstanceManager; 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.model.TranslationNode;
import de.marhali.easyi18n.service.SettingsService; import de.marhali.easyi18n.service.SettingsService;
@ -28,18 +31,21 @@ public class KeyAnnotator {
return; return;
} }
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale(); SettingsState state = SettingsService.getInstance(project).getState();
String pathPrefix = SettingsService.getInstance(project).getState().getPathPrefix(); String pathPrefix = state.getPathPrefix();
String previewLocale = state.getPreviewLocale();
KeyPathConverter converter = new KeyPathConverter(project);
String searchKey = key.length() >= pathPrefix.length() String searchKey = key.length() >= pathPrefix.length()
? key.substring(pathPrefix.length()) ? key.substring(pathPrefix.length())
: key; : key;
if(searchKey.startsWith(".")) { if(searchKey.startsWith(KeyPath.DELIMITER)) {
searchKey = searchKey.substring(1); 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 if(node == null) { // Unknown translation. Just ignore it
return; return;

View File

@ -5,11 +5,13 @@ import com.intellij.codeInsight.lookup.*;
import com.intellij.icons.AllIcons; import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.*; import com.intellij.openapi.project.*;
import com.intellij.util.*; import com.intellij.util.*;
import de.marhali.easyi18n.DataStore; import de.marhali.easyi18n.DataStore;
import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.service.*; import de.marhali.easyi18n.service.*;
import de.marhali.easyi18n.util.PathUtil;
import org.jetbrains.annotations.*; import org.jetbrains.annotations.*;
import java.util.*; import java.util.*;
@ -32,58 +34,29 @@ public class KeyCompletionProvider extends CompletionProvider<CompletionParamete
} }
DataStore store = InstanceManager.get(project).store(); DataStore store = InstanceManager.get(project).store();
PathUtil pathUtil = new PathUtil(project);
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale(); String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
String pathPrefix = SettingsService.getInstance(project).getState().getPathPrefix(); String pathPrefix = SettingsService.getInstance(project).getState().getPathPrefix();
String path = result.getPrefixMatcher().getPrefix(); if(pathPrefix.length() > 0 && !pathPrefix.endsWith(KeyPath.DELIMITER)) {
pathPrefix += KeyPath.DELIMITER;
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(".")) { Set<KeyPath> fullKeys = store.getData().getFullKeys();
pathPrefix += ".";
}
Set<String> fullKeys = store.getData().getFullKeys(); for(KeyPath currentKey : fullKeys) {
result.addElement(createElement(
int sections = path.split("\\.").length; pathPrefix,
int maxSectionForwardLookup = 5; currentKey,
previewLocale,
for(String key : fullKeys) { Objects.requireNonNull(store.getData().getTranslation(currentKey))
// 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)
);
}
}
}
} }
} }
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.InstanceManager;
import de.marhali.easyi18n.dialog.AddDialog; import de.marhali.easyi18n.dialog.AddDialog;
import de.marhali.easyi18n.dialog.EditDialog; 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.KeyedTranslation;
import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.Translation;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/**
* Go to declaration reference for i18n keys.
* @author marhali
*/
public class KeyReference extends PsiReferenceBase<PsiElement> { public class KeyReference extends PsiReferenceBase<PsiElement> {
@Nullable private final String myKey; @Nullable private final String myKey;
@ -52,12 +58,14 @@ public class KeyReference extends PsiReferenceBase<PsiElement> {
@Override @Override
public void navigate(boolean requestFocus) { 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) { if(translation != null) {
new EditDialog(getProject(), new KeyedTranslation(getKey(), translation)).showAndHandle(); new EditDialog(getProject(), new KeyedTranslation(path, translation)).showAndHandle();
} else { } 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.DataStore;
import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.service.SettingsService; import de.marhali.easyi18n.service.SettingsService;
@ -37,12 +38,13 @@ public class GenericFoldingBuilder extends FoldingBuilderEx {
} }
DataStore store = InstanceManager.get(root.getProject()).store(); DataStore store = InstanceManager.get(root.getProject()).store();
KeyPathConverter converter = new KeyPathConverter(root.getProject());
for(final PsiLiteralValue literalValue : literalValues) { for(final PsiLiteralValue literalValue : literalValues) {
String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null; String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null;
// Undefined string literal or not a translation // 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; continue;
} }
@ -65,7 +67,9 @@ public class GenericFoldingBuilder extends FoldingBuilderEx {
} }
DataStore store = InstanceManager.get(literalValue.getProject()).store(); 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) { if(translation == null) {
return null; return null;

View File

@ -6,6 +6,7 @@ import com.intellij.util.ProcessingContext;
import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.editor.KeyReference; import de.marhali.easyi18n.editor.KeyReference;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.service.SettingsService; import de.marhali.easyi18n.service.SettingsService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -38,7 +39,9 @@ public class GenericKeyReferenceContributor extends PsiReferenceContributor {
return PsiReference.EMPTY_ARRAY; 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 if(!KeyReference.isReferencable(value)) { // Creation policy
return PsiReference.EMPTY_ARRAY; return PsiReference.EMPTY_ARRAY;
} }

View File

@ -7,6 +7,7 @@ import com.intellij.util.ProcessingContext;
import de.marhali.easyi18n.InstanceManager; import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.editor.KeyReference; import de.marhali.easyi18n.editor.KeyReference;
import de.marhali.easyi18n.model.KeyPathConverter;
import de.marhali.easyi18n.service.SettingsService; import de.marhali.easyi18n.service.SettingsService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -45,7 +46,9 @@ public class KotlinKeyReferenceContributor extends PsiReferenceContributor {
return PsiReference.EMPTY_ARRAY; 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; return PsiReference.EMPTY_ARRAY;
} }

View File

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

View File

@ -59,7 +59,7 @@ public class PropertiesIOStrategy implements IOStrategy {
throw new IllegalArgumentException("Specified folder is invalid (" + localesPath + ")"); 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()) { for(VirtualFile file : directory.getChildren()) {
if(file.isDirectory() || !isFileRelevant(state, file)) { if(file.isDirectory() || !isFileRelevant(state, file)) {

View File

@ -1,8 +1,8 @@
package de.marhali.easyi18n.io.properties; package de.marhali.easyi18n.io.properties;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.util.StringUtil; import de.marhali.easyi18n.util.StringUtil;
import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.math.NumberUtils;
@ -17,7 +17,7 @@ public class PropertiesMapper {
public static void read(String locale, SortableProperties properties, TranslationData data) { public static void read(String locale, SortableProperties properties, TranslationData data) {
for(Map.Entry<Object, Object> entry : properties.entrySet()) { 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(); Object value = entry.getValue();
Translation translation = data.getTranslation(key); Translation translation = data.getTranslation(key);
@ -36,18 +36,19 @@ public class PropertiesMapper {
} }
public static void write(String locale, SortableProperties properties, TranslationData data) { 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); Translation translation = data.getTranslation(key);
if(translation != null && translation.containsKey(locale)) { if(translation != null && translation.containsKey(locale)) {
String simpleKey = key.toSimpleString();
String content = translation.get(locale); String content = translation.get(locale);
if(PropertiesArrayMapper.isArray(content)) { if(PropertiesArrayMapper.isArray(content)) {
properties.put(key, PropertiesArrayMapper.write(content)); properties.put(simpleKey, PropertiesArrayMapper.write(content));
} else if(NumberUtils.isNumber(content)) { } else if(NumberUtils.isNumber(content)) {
properties.put(key, NumberUtils.createNumber(content)); properties.put(simpleKey, NumberUtils.createNumber(content));
} else { } 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 + ")"); 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()) { for(VirtualFile file : directory.getChildren()) {
if(file.isDirectory() || !isFileRelevant(state, file)) { 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.Translation;
import de.marhali.easyi18n.model.TranslationNode; import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.util.PathUtil;
import de.marhali.easyi18n.util.StringUtil; import de.marhali.easyi18n.util.StringUtil;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
@ -23,10 +22,6 @@ public class YamlMapper {
public static void read(String locale, Section section, TranslationNode node) { public static void read(String locale, Section section, TranslationNode node) {
for(String key : section.getKeys()) { for(String key : section.getKeys()) {
Object value = section.getInScope(key).get(); Object value = section.getInScope(key).get();
key = StringUtil.escapeControls(
key.replace(PathUtil.DELIMITER, "\\" + PathUtil.DELIMITER), true);
TranslationNode childNode = node.getOrCreateChildren(key); TranslationNode childNode = node.getOrCreateChildren(key);
if(value instanceof MapSection) { if(value instanceof MapSection) {
@ -47,9 +42,7 @@ public class YamlMapper {
public static void write(String locale, Section section, TranslationNode node) { public static void write(String locale, Section section, TranslationNode node) {
for(Map.Entry<String, TranslationNode> entry : node.getChildren().entrySet()) { for(Map.Entry<String, TranslationNode> entry : node.getChildren().entrySet()) {
String key = StringEscapeUtils.unescapeJava( String key = entry.getKey();
entry.getKey().replace("\\" + PathUtil.DELIMITER, PathUtil.DELIMITER));
TranslationNode childNode = entry.getValue(); TranslationNode childNode = entry.getValue();
if(!childNode.isLeaf()) { if(!childNode.isLeaf()) {

View File

@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.regex.Pattern;
/** /**
* Represents a full translation key with all sections. * 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 final String DELIMITER = ".";
public static KeyPath of(String... path) { public static KeyPath of(@NotNull String... path) {
return new KeyPath(List.of(path)); return new KeyPath(List.of(path));
} }
@ -24,7 +25,34 @@ public class KeyPath extends ArrayList<String> {
super(); super();
} }
public KeyPath(@NotNull KeyPath path, String... pathToAppend) {
this(path);
this.addAll(List.of(pathToAppend));
}
public KeyPath(@NotNull Collection<? extends String> c) { public KeyPath(@NotNull Collection<? extends String> c) {
super(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 { public class KeyedTranslation {
private @NotNull String key; private @NotNull KeyPath key;
private @Nullable Translation translation; private @Nullable Translation translation;
public KeyedTranslation(@NotNull String key, @Nullable Translation translation) { public KeyedTranslation(@NotNull KeyPath key, @Nullable Translation translation) {
this.key = key; this.key = key;
this.translation = translation; this.translation = translation;
} }
public @NotNull String getKey() { public KeyPath getKey() {
return key; return key;
} }
public void setKey(@NotNull String key) { public void setKey(KeyPath key) {
this.key = key; this.key = key;
} }

View File

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

View File

@ -7,14 +7,13 @@ import java.util.Map;
/** /**
* Translation tree node. Manages child nodes which can be translations or also * 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 * 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. * 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 * For root nodes (empty parent) the {@link java.util.Map}-Type must be specified
* to determine which sorting should be applied. * to determine which sorting should be applied.
*
* @author marhali * @author marhali
*/ */
public class TranslationNode { public class TranslationNode {

View File

@ -1,6 +1,8 @@
package de.marhali.easyi18n.model.bus; 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. * Single event listener.
@ -11,5 +13,5 @@ public interface FocusKeyListener {
* Move the specified translation key (full-key) into focus. * Move the specified translation key (full-key) into focus.
* @param key Absolute translation key * @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 { public class TableView implements BusListener {
private final JBTable table;
private final Project project; private final Project project;
private TableModelMapper currentMapper; private TableModelMapper currentMapper;
private KeyPathConverter converter;
private JPanel rootPanel; private JPanel rootPanel;
private JPanel containerPanel; private JPanel containerPanel;
private JBTable table;
public TableView(Project project) { public TableView(Project project) {
this.project = project; this.project = project;
@ -55,7 +56,7 @@ public class TableView implements BusListener {
return; 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); Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath);
if (translation != null) { if (translation != null) {
@ -65,7 +66,7 @@ public class TableView implements BusListener {
private void deleteSelectedRows() { private void deleteSelectedRows() {
for (int selectedRow : table.getSelectedRows()) { 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( InstanceManager.get(project).processUpdate(
new TranslationDelete(new KeyedTranslation(fullPath, null)) new TranslationDelete(new KeyedTranslation(fullPath, null))
@ -75,16 +76,19 @@ public class TableView implements BusListener {
@Override @Override
public void onUpdateData(@NotNull TranslationData data) { 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))); InstanceManager.get(project).processUpdate(update)));
} }
@Override @Override
public void onFocusKey(@Nullable String key) { public void onFocusKey(@NotNull KeyPath key) {
String concatKey = this.converter.concat(key);
int row = -1; int row = -1;
for (int i = 0; i < table.getRowCount(); i++) { 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; row = i;
} }
} }

View File

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

View File

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

View File

@ -3,12 +3,8 @@ package de.marhali.easyi18n.tabs.mapper;
import com.intellij.ide.projectView.PresentationData; import com.intellij.ide.projectView.PresentationData;
import com.intellij.ui.JBColor; import com.intellij.ui.JBColor;
import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
import de.marhali.easyi18n.model.bus.SearchQueryListener; import de.marhali.easyi18n.model.bus.SearchQueryListener;
import de.marhali.easyi18n.util.PathUtil;
import de.marhali.easyi18n.util.UiUtil; import de.marhali.easyi18n.util.UiUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -26,12 +22,14 @@ import java.util.Map;
public class TreeModelMapper extends DefaultTreeModel implements SearchQueryListener { public class TreeModelMapper extends DefaultTreeModel implements SearchQueryListener {
private final TranslationData data; private final TranslationData data;
private final KeyPathConverter converter;
private final SettingsState state; private final SettingsState state;
public TreeModelMapper(TranslationData data, SettingsState state) { public TreeModelMapper(TranslationData data, SettingsState state) {
super(null); super(null);
this.data = data; this.data = data;
this.converter = new KeyPathConverter(state.isNestedKeys());
this.state = state; this.state = state;
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
@ -42,7 +40,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
@Override @Override
public void onSearchQuery(@Nullable String query) { public void onSearchQuery(@Nullable String query) {
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
TranslationData shadow = new TranslationData(this.state.isSortKeys(), this.state.isNestedKeys()); TranslationData shadow = new TranslationData(this.state.isSortKeys());
if(query == null) { if(query == null) {
this.generateNodes(rootNode, this.data.getRootNode()); this.generateNodes(rootNode, this.data.getRootNode());
@ -52,9 +50,9 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
query = query.toLowerCase(); query = query.toLowerCase();
for(String currentKey : this.data.getFullKeys()) { for(KeyPath currentKey : this.data.getFullKeys()) {
Translation translation = this.data.getTranslation(currentKey); Translation translation = this.data.getTranslation(currentKey);
String loweredKey = currentKey.toLowerCase(); String loweredKey = this.converter.concat(currentKey).toLowerCase();
if(query.contains(loweredKey) || loweredKey.contains(query)) { if(query.contains(loweredKey) || loweredKey.contains(query)) {
shadow.setTranslation(currentKey, translation); shadow.setTranslation(currentKey, translation);
@ -100,14 +98,13 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
} }
} }
public @NotNull TreePath findTreePath(@NotNull String fullPath) { public @NotNull TreePath findTreePath(@NotNull KeyPath fullPath) {
List<String> sections = new PathUtil(this.state.isNestedKeys()).split(fullPath);
List<Object> nodes = new ArrayList<>(); List<Object> nodes = new ArrayList<>();
TreeNode currentNode = (TreeNode) this.getRoot(); TreeNode currentNode = (TreeNode) this.getRoot();
nodes.add(currentNode); nodes.add(currentNode);
for(String section : sections) { for(String section : fullPath) {
currentNode = this.findNode(currentNode, section); currentNode = this.findNode(currentNode, section);
if(currentNode == null) { if(currentNode == null) {
@ -120,7 +117,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
return new TreePath(nodes.toArray()); 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++) { for(int i = 0; i < parent.getChildCount(); i++) {
TreeNode child = parent.getChildAt(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; package de.marhali.easyi18n.util;
import com.intellij.ide.projectView.PresentationData; import com.intellij.ide.projectView.PresentationData;
import de.marhali.easyi18n.model.KeyPath;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
@ -13,13 +14,13 @@ public class TreeUtil {
/** /**
* Constructs the full path for a given {@link TreePath} * Constructs the full path for a given {@link TreePath}
* @param path TreePath * @param treePath TreePath
* @return Full key (e.g user.username.title) * @return Corresponding key path
*/ */
public static String getFullPath(TreePath path) { public static KeyPath getFullPath(TreePath treePath) {
StringBuilder builder = new StringBuilder(); KeyPath keyPath = new KeyPath();
for (Object obj : path.getPath()) { for (Object obj : treePath.getPath()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) obj; DefaultMutableTreeNode node = (DefaultMutableTreeNode) obj;
Object value = node.getUserObject(); Object value = node.getUserObject();
String section = value instanceof PresentationData ? String section = value instanceof PresentationData ?
@ -29,13 +30,9 @@ public class TreeUtil {
continue; continue;
} }
if(builder.length() != 0) { keyPath.add(section);
builder.append(PathUtil.DELIMITER);
}
builder.append(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.path.prefix=Path prefix
settings.preview=Preview locale settings.preview=Preview locale
settings.keys.sort=Sort translation keys alphabetically 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 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. 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; package de.marhali.easyi18n;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation; import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode; import de.marhali.easyi18n.model.TranslationNode;
@ -15,256 +16,184 @@ import java.util.*;
*/ */
public class TranslationDataTest { 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) { private void addTranslations(TranslationData data) {
data.setTranslation("zulu", new Translation("en", "test")); data.setTranslation(KeyPath.of("zulu"), translation);
data.setTranslation("gamma", new Translation("en", "test")); 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(KeyPath.of("charlie.b", "sub"), translation);
data.setTranslation("bravo.c", new Translation("en", "test")); data.setTranslation(KeyPath.of("charlie.a", "sub"), translation);
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("alpha.b", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo.b"), translation);
data.setTranslation("alpha.c", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo.c"), translation);
data.setTranslation("alpha.a", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo.a"), translation);
data.setTranslation("alpha.d", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo.d"), translation);
data.setTranslation("charlie.b", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo", "b"), translation);
data.setTranslation("charlie.c", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo", "c"), translation);
data.setTranslation("charlie.a", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo", "a"), translation);
data.setTranslation("charlie.d", new Translation("en", "test")); data.setTranslation(KeyPath.of("bravo", "d"), translation);
} }
@Test @Test
public void testKeySorting() { public void testKeySorting() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
this.addTranslations(data); this.addTranslations(data);
Set<String> expectation = new LinkedHashSet<>(Arrays.asList( Set<KeyPath> expectation = new LinkedHashSet<>(Arrays.asList(
"alpha.a", "alpha.b", "alpha.c", "alpha.d", KeyPath.of("bravo", "a"), KeyPath.of("bravo", "b"), KeyPath.of("bravo", "c"), KeyPath.of("bravo", "d"),
"bravo.a", "bravo.b", "bravo.c", "bravo.d", KeyPath.of("bravo.a"), KeyPath.of("bravo.b"), KeyPath.of("bravo.c"), KeyPath.of("bravo.d"),
"bravo.long.alpha", "bravo.long.bravo", "bravo.long.charlie.a", KeyPath.of("charlie.a", "sub"), KeyPath.of("charlie.b", "sub"),
"charlie.a", "charlie.b", "charlie.c", "charlie.d", KeyPath.of("foxtrot", "super", "long", "key"),
"foxtrot.super.long.key", KeyPath.of("foxtrot.super.long.key"),
"gamma", KeyPath.of("gamma"),
"zulu" KeyPath.of("zulu")
)); ));
Assert.assertEquals(data.getFullKeys(), expectation); Assert.assertEquals(data.getFullKeys(), expectation);
Assert.assertEquals(data.getFullKeys().size(), numOfTranslations);
} }
@Test @Test
public void testKeyUnordered() { public void testKeyUnordered() {
TranslationData data = new TranslationData(false, true); TranslationData data = new TranslationData(false);
this.addTranslations(data); this.addTranslations(data);
Set<String> expectation = new LinkedHashSet<>(Arrays.asList( Set<KeyPath> expectation = new LinkedHashSet<>(Arrays.asList(
"zulu", KeyPath.of("zulu"),
"gamma", KeyPath.of("gamma"),
"foxtrot.super.long.key", KeyPath.of("foxtrot.super.long.key"),
"bravo.b", "bravo.c", "bravo.a", "bravo.d", KeyPath.of("foxtrot", "super", "long", "key"),
"bravo.long.bravo", "bravo.long.charlie.a", "bravo.long.alpha", KeyPath.of("charlie.b", "sub"), KeyPath.of("charlie.a", "sub"),
"alpha.b", "alpha.c", "alpha.a", "alpha.d", KeyPath.of("bravo.b"), KeyPath.of("bravo.c"), KeyPath.of("bravo.a"), KeyPath.of("bravo.d"),
"charlie.b", "charlie.c", "charlie.a", "charlie.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(), expectation);
Assert.assertEquals(data.getFullKeys().size(), numOfTranslations);
} }
@Test @Test
public void testKeyNesting() { public void testDelete() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("nested.alpha", new Translation("en", "test")); data.setTranslation(KeyPath.of("alpha"), translation);
data.setTranslation("nested.bravo", new Translation("en", "test")); data.setTranslation(KeyPath.of("nested.alpha"), translation);
data.setTranslation("other.alpha", new Translation("en", "test")); data.setTranslation(KeyPath.of("nested.long.bravo"), translation);
data.setTranslation("other.bravo", new Translation("en", "test"));
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.assertEquals(data.getFullKeys().size(), 6);
Assert.assertFalse(node.isLeaf());
}
}
@Test data.setTranslation(KeyPath.of("alpha"), null);
public void testKeyNonNested() { data.setTranslation(KeyPath.of("nested.alpha"), null);
TranslationData data = new TranslationData(true, false); data.setTranslation(KeyPath.of("nested.long.bravo"), null);
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);
Assert.assertEquals(data.getFullKeys().size(), 3); Assert.assertEquals(data.getFullKeys().size(), 3);
data.setTranslation("alpha", null); data.setTranslation(KeyPath.of("beta"), null);
data.setTranslation("nested.alpha", null); data.setTranslation(KeyPath.of("nested", "alpha"), null);
data.setTranslation("nested.long.bravo", null); data.setTranslation(KeyPath.of("nested", "long", "bravo"), null);
Assert.assertEquals(data.getFullKeys().size(), 0); Assert.assertEquals(data.getFullKeys().size(), 0);
Assert.assertNull(data.getTranslation("alpha"));
Assert.assertNull(data.getTranslation("nested.alpha")); Assert.assertNull(data.getTranslation(KeyPath.of("alpha")));
Assert.assertNull(data.getTranslation("nested.long.bravo")); 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 @Test
public void testDeleteNonNested() { public void testDeleteRecursively() {
TranslationData data = new TranslationData(true, false); TranslationData data = new TranslationData(true);
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);
this.addTranslations(data); 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.assertNull(data.getRootNode().getChildren().get("foxtrot"));
Assert.assertEquals(data.getFullKeys().size(), numOfTranslations - 2);
} }
@Test @Test
public void testRecurseDeleteNested() { public void testOverwrite() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(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);
Translation before = new Translation("en", "before"); Translation before = new Translation("en", "before");
Translation after = new Translation("en", "after"); Translation after = new Translation("en", "after");
data.setTranslation("alpha", before); data.setTranslation(KeyPath.of("alpha"), before);
data.setTranslation("nested.alpha", before); data.setTranslation(KeyPath.of("nested.alpha"), before);
data.setTranslation("nested.long.bravo", 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(KeyPath.of("alpha")), before);
Assert.assertEquals(data.getTranslation("alpha"), before); Assert.assertEquals(data.getTranslation(KeyPath.of("nested.alpha")), before);
Assert.assertEquals(data.getTranslation("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(KeyPath.of("alpha"), after);
data.setTranslation("nested.alpha", after); data.setTranslation(KeyPath.of("nested.alpha"), after);
data.setTranslation("nested.long.bravo", 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(KeyPath.of("alpha")), after);
Assert.assertEquals(data.getTranslation("alpha"), after); Assert.assertEquals(data.getTranslation(KeyPath.of("nested.alpha")), after);
Assert.assertEquals(data.getTranslation("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 @Test
public void testOverwriteNested() { public void testTransformRecursively() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
Translation before = new Translation("en", "before"); data.setTranslation(KeyPath.of("alpha.nested.key"), translation);
Translation after = new Translation("en", "after"); 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); Assert.assertEquals(6, data.getFullKeys().size());
data.setTranslation("nested.alpha", before);
data.setTranslation("nested.long.bravo", before);
Assert.assertEquals(data.getTranslation("alpha"), before); data.setTranslation(KeyPath.of("alpha.nested"), translation);
Assert.assertEquals(data.getTranslation("alpha"), before); data.setTranslation(KeyPath.of("alpha.other.new"), translation);
Assert.assertEquals(data.getTranslation("alpha"), before); 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); Assert.assertEquals(6, data.getFullKeys().size());
data.setTranslation("nested.alpha", after);
data.setTranslation("nested.long.bravo", after);
Assert.assertEquals(data.getTranslation("alpha"), after); Assert.assertNotNull(data.getTranslation(KeyPath.of("alpha.nested.key")));
Assert.assertEquals(data.getTranslation("alpha"), after); Assert.assertNotNull(data.getTranslation(KeyPath.of("alpha.other")));
Assert.assertEquals(data.getTranslation("alpha"), after); 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 Assert.assertNull(data.getTranslation(KeyPath.of("alpha", "nested", "key")));
public void testRecurseTransformNested() { Assert.assertNull(data.getTranslation(KeyPath.of("alpha", "other")));
TranslationData data = new TranslationData(true, true); Assert.assertNull(data.getTranslation(KeyPath.of("charlie")));
Assert.assertEquals(data.getTranslation(KeyPath.of("alpha", "nested")), translation);
Translation value = new Translation("en", "test"); Assert.assertEquals(data.getTranslation(KeyPath.of("alpha", "other", "new")), translation);
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);
} }
} }

View File

@ -6,6 +6,7 @@ import com.google.gson.JsonPrimitive;
import de.marhali.easyi18n.io.json.JsonArrayMapper; import de.marhali.easyi18n.io.json.JsonArrayMapper;
import de.marhali.easyi18n.io.json.JsonMapper; import de.marhali.easyi18n.io.json.JsonMapper;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationData;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
@ -16,7 +17,7 @@ import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
/** /**
* Unit tests for {@link de.marhali.easyi18n.io.json.JsonMapper} * Unit tests for {@link JsonMapper}.
* @author marhali * @author marhali
*/ */
public class JsonMapperTest extends AbstractMapperTest { public class JsonMapperTest extends AbstractMapperTest {
@ -28,7 +29,7 @@ public class JsonMapperTest extends AbstractMapperTest {
input.add("alpha", new JsonPrimitive("test")); input.add("alpha", new JsonPrimitive("test"));
input.add("bravo", 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()); JsonMapper.read("en", input, data.getRootNode());
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
@ -45,7 +46,7 @@ public class JsonMapperTest extends AbstractMapperTest {
input.add("alpha", new JsonPrimitive("test")); input.add("alpha", new JsonPrimitive("test"));
input.add("bravo", 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()); JsonMapper.read("en", input, data.getRootNode());
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
@ -57,9 +58,9 @@ public class JsonMapperTest extends AbstractMapperTest {
@Override @Override
public void testArrays() { public void testArrays() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("simple", create(arraySimple)); data.setTranslation(KeyPath.of("simple"), create(arraySimple));
data.setTranslation("escaped", create(arrayEscaped)); data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped));
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode()); JsonMapper.write("en", output, data.getRootNode());
@ -69,81 +70,82 @@ public class JsonMapperTest extends AbstractMapperTest {
Assert.assertTrue(output.get("escaped").isJsonArray()); Assert.assertTrue(output.get("escaped").isJsonArray());
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(JsonArrayMapper.read(output.get("escaped").getAsJsonArray()))); 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()); JsonMapper.read("en", output, input.getRootNode());
Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation("simple").get("en"))); Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en")));
Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation("escaped").get("en"))); Assert.assertTrue(JsonArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en")));
} }
@Override @Override
public void testSpecialCharacters() { public void testSpecialCharacters() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("chars", create(specialCharacters)); data.setTranslation(KeyPath.of("chars"), create(specialCharacters));
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode()); JsonMapper.write("en", output, data.getRootNode());
Assert.assertEquals(specialCharacters, output.get("chars").getAsString()); Assert.assertEquals(specialCharacters, output.get("chars").getAsString());
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode()); 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 @Override
public void testNestedKeys() { public void testNestedKeys() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("nested.key.section", create("test")); data.setTranslation(KeyPath.of("nested", "key", "section"), create("test"));
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode()); JsonMapper.write("en", output, data.getRootNode());
Assert.assertEquals("test", output.getAsJsonObject("nested").getAsJsonObject("key").get("section").getAsString()); 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()); 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 @Override
public void testNonNestedKeys() { public void testNonNestedKeys() {
TranslationData data = new TranslationData(true, false); TranslationData data = new TranslationData(true);
data.setTranslation("long.key.with.many.sections", create("test")); data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test"));
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode()); JsonMapper.write("en", output, data.getRootNode());
Assert.assertTrue(output.has("long.key.with.many.sections")); 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()); 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 @Override
public void testLeadingSpace() { public void testLeadingSpace() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("space", create(leadingSpace)); data.setTranslation(KeyPath.of("space"), create(leadingSpace));
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode()); JsonMapper.write("en", output, data.getRootNode());
Assert.assertEquals(leadingSpace, output.get("space").getAsString()); Assert.assertEquals(leadingSpace, output.get("space").getAsString());
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
JsonMapper.read("en", output, input.getRootNode()); 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 @Override
public void testNumbers() { public void testNumbers() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("numbered", create("15000")); data.setTranslation(KeyPath.of("numbered"), create("15000"));
JsonObject output = new JsonObject(); JsonObject output = new JsonObject();
JsonMapper.write("en", output, data.getRootNode()); JsonMapper.write("en", output, data.getRootNode());
@ -154,6 +156,6 @@ public class JsonMapperTest extends AbstractMapperTest {
input.addProperty("numbered", 143.23); input.addProperty("numbered", 143.23);
JsonMapper.read("en", input, data.getRootNode()); 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.PropertiesArrayMapper;
import de.marhali.easyi18n.io.properties.PropertiesMapper; import de.marhali.easyi18n.io.properties.PropertiesMapper;
import de.marhali.easyi18n.io.properties.SortableProperties; import de.marhali.easyi18n.io.properties.SortableProperties;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationData;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
@ -11,7 +12,7 @@ import org.junit.Assert;
import java.util.*; import java.util.*;
/** /**
* Unit tests for {@link de.marhali.easyi18n.io.properties.PropertiesMapper} * Unit tests for {@link PropertiesMapper}.
* @author marhali * @author marhali
*/ */
public class PropertiesMapperTest extends AbstractMapperTest { public class PropertiesMapperTest extends AbstractMapperTest {
@ -23,7 +24,7 @@ public class PropertiesMapperTest extends AbstractMapperTest {
input.setProperty("alpha", "test"); input.setProperty("alpha", "test");
input.setProperty("bravo", "test"); input.setProperty("bravo", "test");
TranslationData data = new TranslationData(false, true); TranslationData data = new TranslationData(false);
PropertiesMapper.read("en", input, data); PropertiesMapper.read("en", input, data);
SortableProperties output = new SortableProperties(false); SortableProperties output = new SortableProperties(false);
@ -40,7 +41,7 @@ public class PropertiesMapperTest extends AbstractMapperTest {
input.setProperty("alpha", "test"); input.setProperty("alpha", "test");
input.setProperty("bravo", "test"); input.setProperty("bravo", "test");
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
PropertiesMapper.read("en", input, data); PropertiesMapper.read("en", input, data);
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
@ -52,9 +53,9 @@ public class PropertiesMapperTest extends AbstractMapperTest {
@Override @Override
public void testArrays() { public void testArrays() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("simple", create(arraySimple)); data.setTranslation(KeyPath.of("simple"), create(arraySimple));
data.setTranslation("escaped", create(arrayEscaped)); data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped));
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data); PropertiesMapper.write("en", output, data);
@ -64,83 +65,84 @@ public class PropertiesMapperTest extends AbstractMapperTest {
Assert.assertTrue(output.get("escaped") instanceof String[]); Assert.assertTrue(output.get("escaped") instanceof String[]);
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(PropertiesArrayMapper.read((String[]) output.get("escaped")))); 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); PropertiesMapper.read("en", output, input);
Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation("simple").get("en"))); Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en")));
Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation("escaped").get("en"))); Assert.assertTrue(PropertiesArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en")));
} }
@Override @Override
public void testSpecialCharacters() { public void testSpecialCharacters() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("chars", create(specialCharacters)); data.setTranslation(KeyPath.of("chars"), create(specialCharacters));
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data); PropertiesMapper.write("en", output, data);
Assert.assertEquals(specialCharacters, output.get("chars")); Assert.assertEquals(specialCharacters, output.get("chars"));
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input); 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 @Override
public void testNestedKeys() { public void testNestedKeys() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("nested.key.sections", create("test")); data.setTranslation(KeyPath.of("nested", "key", "sections"), create("test"));
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data); PropertiesMapper.write("en", output, data);
Assert.assertEquals("test", output.get("nested.key.sections")); Assert.assertEquals("test", output.get("nested.key.sections"));
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input); PropertiesMapper.read("en", output, input);
System.out.println(input);
Assert.assertTrue(input.getRootNode().getChildren().containsKey("nested")); 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 @Override
public void testNonNestedKeys() { public void testNonNestedKeys() { // Note: Key nesting is not supported in properties file.
TranslationData data = new TranslationData(true, false); TranslationData data = new TranslationData(true);
data.setTranslation("long.key.with.many.sections", create("test")); data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test"));
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data); PropertiesMapper.write("en", output, data);
Assert.assertNotNull(output.get("long.key.with.many.sections")); 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); PropertiesMapper.read("en", output, input);
Assert.assertEquals("test", input.getRootNode().getChildren() Assert.assertEquals("test", input.getTranslation(KeyPath.of("long", "key", "with", "many", "sections")).get("en"));
.get("long.key.with.many.sections").getValue().get("en"));
} }
@Override @Override
public void testLeadingSpace() { public void testLeadingSpace() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("space", create(leadingSpace)); data.setTranslation(KeyPath.of("space"), create(leadingSpace));
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data); PropertiesMapper.write("en", output, data);
Assert.assertEquals(leadingSpace, output.get("space")); Assert.assertEquals(leadingSpace, output.get("space"));
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
PropertiesMapper.read("en", output, input); PropertiesMapper.read("en", output, input);
Assert.assertEquals(leadingSpace, input.getTranslation("space").get("en")); Assert.assertEquals(leadingSpace, input.getTranslation(KeyPath.of("space")).get("en"));
} }
@Override @Override
public void testNumbers() { public void testNumbers() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("numbered", create("15000")); data.setTranslation(KeyPath.of("numbered"), create("15000"));
SortableProperties output = new SortableProperties(true); SortableProperties output = new SortableProperties(true);
PropertiesMapper.write("en", output, data); PropertiesMapper.write("en", output, data);
@ -151,6 +153,6 @@ public class PropertiesMapperTest extends AbstractMapperTest {
input.put("numbered", 143.23); input.put("numbered", 143.23);
PropertiesMapper.read("en", input, data); 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.YamlArrayMapper;
import de.marhali.easyi18n.io.yaml.YamlMapper; import de.marhali.easyi18n.io.yaml.YamlMapper;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationData;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
@ -15,7 +16,7 @@ import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
/** /**
* Unit tests for {@link de.marhali.easyi18n.io.yaml.YamlMapper} * Unit tests for {@link YamlMapper}.
* @author marhali * @author marhali
*/ */
public class YamlMapperTest extends AbstractMapperTest { public class YamlMapperTest extends AbstractMapperTest {
@ -27,7 +28,7 @@ public class YamlMapperTest extends AbstractMapperTest {
input.set("alpha", "test"); input.set("alpha", "test");
input.set("bravo", "test"); input.set("bravo", "test");
TranslationData data = new TranslationData(false, true); TranslationData data = new TranslationData(false);
YamlMapper.read("en", input, data.getRootNode()); YamlMapper.read("en", input, data.getRootNode());
Section output = new MapSection(); Section output = new MapSection();
@ -44,7 +45,7 @@ public class YamlMapperTest extends AbstractMapperTest {
input.set("alpha", "test"); input.set("alpha", "test");
input.set("bravo", "test"); input.set("bravo", "test");
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
YamlMapper.read("en", input, data.getRootNode()); YamlMapper.read("en", input, data.getRootNode());
Section output = new MapSection(); Section output = new MapSection();
@ -56,9 +57,9 @@ public class YamlMapperTest extends AbstractMapperTest {
@Override @Override
public void testArrays() { public void testArrays() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("simple", create(arraySimple)); data.setTranslation(KeyPath.of("simple"), create(arraySimple));
data.setTranslation("escaped", create(arrayEscaped)); data.setTranslation(KeyPath.of("escaped"), create(arrayEscaped));
Section output = new MapSection(); Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode()); YamlMapper.write("en", output, data.getRootNode());
@ -68,81 +69,82 @@ public class YamlMapperTest extends AbstractMapperTest {
Assert.assertTrue(output.isList("escaped")); Assert.assertTrue(output.isList("escaped"));
Assert.assertEquals(arrayEscaped, StringEscapeUtils.unescapeJava(YamlArrayMapper.read(output.getList("escaped").get()))); 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()); YamlMapper.read("en", output, input.getRootNode());
Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation("simple").get("en"))); Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(KeyPath.of("simple")).get("en")));
Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation("escaped").get("en"))); Assert.assertTrue(YamlArrayMapper.isArray(input.getTranslation(KeyPath.of("escaped")).get("en")));
} }
@Override @Override
public void testSpecialCharacters() { public void testSpecialCharacters() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("chars", create(specialCharacters)); data.setTranslation(KeyPath.of("chars"), create(specialCharacters));
Section output = new MapSection(); Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode()); YamlMapper.write("en", output, data.getRootNode());
Assert.assertEquals(specialCharacters, output.getString("chars").get()); Assert.assertEquals(specialCharacters, output.getString("chars").get());
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode()); 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 @Override
public void testNestedKeys() { public void testNestedKeys() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("nested.key.section", create("test")); data.setTranslation(KeyPath.of("nested", "key", "section"), create("test"));
Section output = new MapSection(); Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode()); YamlMapper.write("en", output, data.getRootNode());
Assert.assertEquals("test", output.getString("nested.key.section").get()); 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()); 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 @Override
public void testNonNestedKeys() { public void testNonNestedKeys() {
TranslationData data = new TranslationData(true, false); TranslationData data = new TranslationData(true);
data.setTranslation("long.key.with.many.sections", create("test")); data.setTranslation(KeyPath.of("long.key.with.many.sections"), create("test"));
Section output = new MapSection(); Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode()); YamlMapper.write("en", output, data.getRootNode());
Assert.assertTrue(output.getKeys().contains("long.key.with.many.sections")); 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()); 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 @Override
public void testLeadingSpace() { public void testLeadingSpace() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("space", create(leadingSpace)); data.setTranslation(KeyPath.of("space"), create(leadingSpace));
Section output = new MapSection(); Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode()); YamlMapper.write("en", output, data.getRootNode());
Assert.assertEquals(leadingSpace, output.getString("space").get()); Assert.assertEquals(leadingSpace, output.getString("space").get());
TranslationData input = new TranslationData(true, true); TranslationData input = new TranslationData(true);
YamlMapper.read("en", output, input.getRootNode()); 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 @Override
public void testNumbers() { public void testNumbers() {
TranslationData data = new TranslationData(true, true); TranslationData data = new TranslationData(true);
data.setTranslation("numbered", create("15000")); data.setTranslation(KeyPath.of("numbered"), create("15000"));
Section output = new MapSection(); Section output = new MapSection();
YamlMapper.write("en", output, data.getRootNode()); YamlMapper.write("en", output, data.getRootNode());
@ -153,6 +155,6 @@ public class YamlMapperTest extends AbstractMapperTest {
input.set("numbered", 143.23); input.set("numbered", 143.23);
YamlMapper.read("en", input, data.getRootNode()); 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"));
} }
} }