{
super();
}
+ public KeyPath(@NotNull KeyPath path, String... pathToAppend) {
+ this(path);
+ this.addAll(List.of(pathToAppend));
+ }
+
public KeyPath(@NotNull Collection extends String> c) {
super(c);
}
+
+ public KeyPath(@NotNull String simplePath) {
+ this(List.of(simplePath.split(Pattern.quote(DELIMITER))));
+ }
+
+ /**
+ * Note: 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();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java b/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java
index 97a415a..04a409f 100644
--- a/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java
+++ b/src/main/java/de/marhali/easyi18n/model/KeyedTranslation.java
@@ -9,19 +9,19 @@ import org.jetbrains.annotations.Nullable;
*/
public class KeyedTranslation {
- private @NotNull String key;
+ private @NotNull KeyPath key;
private @Nullable Translation translation;
- public KeyedTranslation(@NotNull String key, @Nullable Translation translation) {
+ public KeyedTranslation(@NotNull KeyPath key, @Nullable Translation translation) {
this.key = key;
this.translation = translation;
}
- public @NotNull String getKey() {
+ public KeyPath getKey() {
return key;
}
- public void setKey(@NotNull String key) {
+ public void setKey(KeyPath key) {
this.key = key;
}
diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationData.java b/src/main/java/de/marhali/easyi18n/model/TranslationData.java
index ba042b2..087048b 100644
--- a/src/main/java/de/marhali/easyi18n/model/TranslationData.java
+++ b/src/main/java/de/marhali/easyi18n/model/TranslationData.java
@@ -1,7 +1,5 @@
package de.marhali.easyi18n.model;
-import de.marhali.easyi18n.util.PathUtil;
-
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,21 +9,21 @@ import java.util.*;
* Cached translation data. The data is stored in a tree structure.
* Tree behaviour (sorted, non-sorted) can be specified via constructor.
* For more please see {@link TranslationNode}. Example tree view:
- *
- * user:
- * -- principal: 'Principal'
- * -- username:
- * -- -- title: 'Username'
- * auth:
- * -- logout: 'Logout'
- * -- login: 'Login'
- *
+ *
+ * {@code
+ * user:
+ * principal: 'Principal'
+ * username:
+ * title: 'Username'
+ * auth:
+ * logout: 'Logout'
+ * login: 'Login'
+ * }
+ *
* @author marhali
*/
public class TranslationData {
- private final PathUtil pathUtil;
-
@NotNull
private final Set locales;
@@ -36,17 +34,15 @@ public class TranslationData {
* Creates an empty instance.
* @param sort Should the translation keys be sorted alphabetically
*/
- public TranslationData(boolean sort, boolean nestKeys) {
- this(nestKeys, new HashSet<>(), new TranslationNode(sort ? new TreeMap<>() : new LinkedHashMap<>()));
+ public TranslationData(boolean sort) {
+ this(new HashSet<>(), new TranslationNode(sort ? new TreeMap<>() : new LinkedHashMap<>()));
}
/**
- * @param nestKeys Apply key nesting. See {@link PathUtil}
* @param locales Languages which can be used for translation
* @param rootNode Translation tree structure
*/
- public TranslationData(boolean nestKeys, @NotNull Set locales, @NotNull TranslationNode rootNode) {
- this.pathUtil = new PathUtil(nestKeys);
+ public TranslationData(@NotNull Set locales, @NotNull TranslationNode rootNode) {
this.locales = locales;
this.rootNode = rootNode;
}
@@ -76,15 +72,14 @@ public class TranslationData {
* @param fullPath Absolute translation path
* @return Translation node which leads to translations or nested child's
*/
- public @Nullable TranslationNode getNode(@NotNull String fullPath) {
- List sections = this.pathUtil.split(fullPath);
+ public @Nullable TranslationNode getNode(@NotNull KeyPath fullPath) {
TranslationNode node = this.rootNode;
if(fullPath.isEmpty()) { // Return root node if empty path was supplied
return node;
}
- for(String section : sections) {
+ for(String section : fullPath) {
if(node == null) {
return null;
}
@@ -98,7 +93,7 @@ public class TranslationData {
* @param fullPath Absolute translation key path
* @return Found translation. Can be null if path is empty or is not a leaf element
*/
- public @Nullable Translation getTranslation(@NotNull String fullPath) {
+ public @Nullable Translation getTranslation(@NotNull KeyPath fullPath) {
TranslationNode node = this.getNode(fullPath);
if(node == null || !node.isLeaf()) {
@@ -109,50 +104,52 @@ public class TranslationData {
}
/**
+ * Create / Update or Delete a specific translation.
+ * The parent path of the translation will be changed if necessary.
* @param fullPath Absolute translation key path
* @param translation Translation to set. Can be null to delete the corresponding node
*/
- public void setTranslation(@NotNull String fullPath, @Nullable Translation translation) {
- List sections = this.pathUtil.split(fullPath);
- String nodeKey = sections.remove(sections.size() - 1); // Edge case last section
- TranslationNode node = this.rootNode;
-
+ public void setTranslation(@NotNull KeyPath fullPath, @Nullable Translation translation) {
if(fullPath.isEmpty()) {
- throw new IllegalArgumentException("Path cannot be empty");
+ throw new IllegalArgumentException("Key path cannot be empty");
}
- for(String section : sections) { // Go to the level of the key (@nodeKey)
+ fullPath = new KeyPath(fullPath);
+ String leafKey = fullPath.remove(fullPath.size() - 1); // Extract edge section as children key of parent
+ TranslationNode node = this.rootNode;
+
+ for(String section : fullPath) { // Go to nested level at @leafKey
TranslationNode childNode = node.getChildren().get(section);
if(childNode == null) {
- if(translation == null) { // Path should not be empty for delete
+ if(translation == null) { // Path must not be empty on delete
throw new IllegalArgumentException("Delete action on empty path");
}
- // Created nested section
childNode = node.setChildren(section);
}
node = childNode;
}
- if(translation == null) { // Delete
- node.removeChildren(nodeKey);
+ if(translation == null) { // Delete action
+ node.removeChildren(leafKey);
- if(node.getChildren().isEmpty() && !node.isRoot()) { // Parent is empty now. Run delete recursively
- this.setTranslation(this.pathUtil.concat(sections), null);
+ if(node.getChildren().isEmpty() && !node.isRoot()) { // Node is empty now. Run delete recursively
+ this.setTranslation(fullPath, null);
}
-
- } else { // Create or overwrite
- node.setChildren(nodeKey, translation);
+ return;
}
+
+ // Create or overwrite
+ node.setChildren(leafKey, translation);
}
/**
* @return All translation keys as absolute paths (full-key)
*/
- public @NotNull Set getFullKeys() {
- return this.getFullKeys("", this.rootNode); // Just use root node
+ public @NotNull Set getFullKeys() {
+ return this.getFullKeys(new KeyPath(), this.rootNode); // Just use root node
}
/**
@@ -160,15 +157,15 @@ public class TranslationData {
* @param node Node section to begin with
* @return All translation keys where the path contains the specified @parentPath
*/
- public @NotNull Set getFullKeys(String parentPath, TranslationNode node) {
- Set keys = new LinkedHashSet<>();
+ public @NotNull Set getFullKeys(KeyPath parentPath, TranslationNode node) {
+ Set keys = new LinkedHashSet<>();
if(node.isLeaf()) { // This node does not lead to child's - just add the key
keys.add(parentPath);
}
for(Map.Entry children : node.getChildren().entrySet()) {
- keys.addAll(this.getFullKeys(this.pathUtil.append(parentPath, children.getKey()), children.getValue()));
+ keys.addAll(this.getFullKeys(new KeyPath(parentPath, children.getKey()), children.getValue()));
}
return keys;
@@ -178,7 +175,6 @@ public class TranslationData {
public String toString() {
return "TranslationData{" +
"mapClass=" + rootNode.getChildren().getClass().getSimpleName() +
- ", pathUtil=" + pathUtil +
", locales=" + locales +
", rootNode=" + rootNode +
'}';
diff --git a/src/main/java/de/marhali/easyi18n/model/TranslationNode.java b/src/main/java/de/marhali/easyi18n/model/TranslationNode.java
index dc91f3b..744cad7 100644
--- a/src/main/java/de/marhali/easyi18n/model/TranslationNode.java
+++ b/src/main/java/de/marhali/easyi18n/model/TranslationNode.java
@@ -7,14 +7,13 @@ import java.util.Map;
/**
* Translation tree node. Manages child nodes which can be translations or also
- * nodes which can lead to another translation or node.
+ * nodes which can lead to another translation or node.
* 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.
+ *
* Whether the children nodes should be sorted is determined by the parent node.
* For root nodes (empty parent) the {@link java.util.Map}-Type must be specified
* to determine which sorting should be applied.
- *
* @author marhali
*/
public class TranslationNode {
diff --git a/src/main/java/de/marhali/easyi18n/model/bus/FocusKeyListener.java b/src/main/java/de/marhali/easyi18n/model/bus/FocusKeyListener.java
index ce3069f..6cae087 100644
--- a/src/main/java/de/marhali/easyi18n/model/bus/FocusKeyListener.java
+++ b/src/main/java/de/marhali/easyi18n/model/bus/FocusKeyListener.java
@@ -1,6 +1,8 @@
package de.marhali.easyi18n.model.bus;
-import org.jetbrains.annotations.Nullable;
+import de.marhali.easyi18n.model.KeyPath;
+
+import org.jetbrains.annotations.NotNull;
/**
* Single event listener.
@@ -11,5 +13,5 @@ public interface FocusKeyListener {
* Move the specified translation key (full-key) into focus.
* @param key Absolute translation key
*/
- void onFocusKey(@Nullable String key);
+ void onFocusKey(@NotNull KeyPath key);
}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/tabs/TableView.java b/src/main/java/de/marhali/easyi18n/tabs/TableView.java
index 94b4f22..596d846 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/TableView.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/TableView.java
@@ -28,15 +28,16 @@ import java.util.ResourceBundle;
*/
public class TableView implements BusListener {
+ private final JBTable table;
+
private final Project project;
private TableModelMapper currentMapper;
+ private KeyPathConverter converter;
private JPanel rootPanel;
private JPanel containerPanel;
- private JBTable table;
-
public TableView(Project project) {
this.project = project;
@@ -55,7 +56,7 @@ public class TableView implements BusListener {
return;
}
- String fullPath = String.valueOf(table.getValueAt(row, 0));
+ KeyPath fullPath = this.converter.split(String.valueOf(this.table.getValueAt(row, 0)));
Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath);
if (translation != null) {
@@ -65,7 +66,7 @@ public class TableView implements BusListener {
private void deleteSelectedRows() {
for (int selectedRow : table.getSelectedRows()) {
- String fullPath = String.valueOf(table.getValueAt(selectedRow, 0));
+ KeyPath fullPath = this.converter.split(String.valueOf(table.getValueAt(selectedRow, 0)));
InstanceManager.get(project).processUpdate(
new TranslationDelete(new KeyedTranslation(fullPath, null))
@@ -75,16 +76,19 @@ public class TableView implements BusListener {
@Override
public void onUpdateData(@NotNull TranslationData data) {
- table.setModel(this.currentMapper = new TableModelMapper(data, update ->
+ this.converter = new KeyPathConverter(project);
+
+ table.setModel(this.currentMapper = new TableModelMapper(data, this.converter, update ->
InstanceManager.get(project).processUpdate(update)));
}
@Override
- public void onFocusKey(@Nullable String key) {
+ public void onFocusKey(@NotNull KeyPath key) {
+ String concatKey = this.converter.concat(key);
int row = -1;
for (int i = 0; i < table.getRowCount(); i++) {
- if (String.valueOf(table.getValueAt(i, 0)).equals(key)) {
+ if (table.getValueAt(i, 0).equals(concatKey)) {
row = i;
}
}
diff --git a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java
index d49f907..71957ab 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java
@@ -10,10 +10,7 @@ import com.intellij.ui.treeStructure.Tree;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.listener.ReturnKeyListener;
-import de.marhali.easyi18n.model.KeyedTranslation;
-import de.marhali.easyi18n.model.Translation;
-import de.marhali.easyi18n.model.TranslationData;
-import de.marhali.easyi18n.model.TranslationDelete;
+import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.bus.BusListener;
import de.marhali.easyi18n.action.treeview.CollapseTreeViewAction;
import de.marhali.easyi18n.action.treeview.ExpandTreeViewAction;
@@ -40,6 +37,8 @@ import java.util.ResourceBundle;
*/
public class TreeView implements BusListener {
+ private final Tree tree;
+
private final Project project;
private TreeModelMapper currentMapper;
@@ -48,8 +47,6 @@ public class TreeView implements BusListener {
private JPanel toolBarPanel;
private JPanel containerPanel;
- private Tree tree;
-
public TreeView(Project project) {
this.project = project;
@@ -87,8 +84,8 @@ public class TreeView implements BusListener {
}
@Override
- public void onFocusKey(@Nullable String key) {
- if (key != null && currentMapper != null) {
+ public void onFocusKey(@NotNull KeyPath key) {
+ if (currentMapper != null) {
TreePath path = currentMapper.findTreePath(key);
this.tree.getSelectionModel().setSelectionPath(path);
@@ -120,7 +117,7 @@ public class TreeView implements BusListener {
return;
}
- String fullPath = TreeUtil.getFullPath(path);
+ KeyPath fullPath = TreeUtil.getFullPath(path);
Translation translation = InstanceManager.get(project).store().getData().getTranslation(fullPath);
if (translation == null) {
@@ -138,7 +135,7 @@ public class TreeView implements BusListener {
}
for (TreePath path : tree.getSelectionPaths()) {
- String fullPath = TreeUtil.getFullPath(path);
+ KeyPath fullPath = TreeUtil.getFullPath(path);
InstanceManager.get(project).processUpdate(
new TranslationDelete(new KeyedTranslation(fullPath, null))
diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java
index f434c5b..209f6b0 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java
@@ -20,13 +20,18 @@ import java.util.function.Consumer;
public class TableModelMapper implements TableModel, SearchQueryListener {
private final @NotNull TranslationData data;
+ private final @NotNull KeyPathConverter converter;
+
private final @NotNull List locales;
- private @NotNull List fullKeys;
+ private @NotNull List fullKeys;
private final @NotNull Consumer updater;
- public TableModelMapper(@NotNull TranslationData data, @NotNull Consumer updater) {
+ public TableModelMapper(@NotNull TranslationData data, @NotNull KeyPathConverter converter,
+ @NotNull Consumer updater) {
this.data = data;
+ this.converter = converter;
+
this.locales = new ArrayList<>(data.getLocales());
this.fullKeys = new ArrayList<>(data.getFullKeys());
@@ -41,10 +46,10 @@ public class TableModelMapper implements TableModel, SearchQueryListener {
}
query = query.toLowerCase();
- List matches = new ArrayList<>();
+ List matches = new ArrayList<>();
- for(String key : this.data.getFullKeys()) {
- if(key.toLowerCase().contains(query)) {
+ for(KeyPath key : this.data.getFullKeys()) {
+ if(this.converter.concat(key).toLowerCase().contains(query)) {
matches.add(key);
} else {
for(String content : this.data.getTranslation(key).values()) {
@@ -90,11 +95,12 @@ public class TableModelMapper implements TableModel, SearchQueryListener {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
+ KeyPath key = this.fullKeys.get(rowIndex);
+
if(columnIndex == 0) { // Keys
- return this.fullKeys.get(rowIndex);
+ return this.converter.concat(key);
}
- String key = this.fullKeys.get(rowIndex);
String locale = this.locales.get(columnIndex - 1);
Translation translation = this.data.getTranslation(key);
@@ -103,14 +109,14 @@ public class TableModelMapper implements TableModel, SearchQueryListener {
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
- String key = String.valueOf(this.getValueAt(rowIndex, 0));
+ KeyPath key = this.fullKeys.get(rowIndex);
Translation translation = this.data.getTranslation(key);
if(translation == null) { // Unknown cell
return;
}
- String newKey = columnIndex == 0 ? String.valueOf(aValue) : key;
+ KeyPath newKey = columnIndex == 0 ? this.converter.split(String.valueOf(aValue)) : key;
// Translation content update
if(columnIndex > 0) {
diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java
index 621bb10..4049e10 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java
@@ -3,12 +3,8 @@ package de.marhali.easyi18n.tabs.mapper;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ui.JBColor;
-import de.marhali.easyi18n.model.SettingsState;
-import de.marhali.easyi18n.model.Translation;
-import de.marhali.easyi18n.model.TranslationData;
-import de.marhali.easyi18n.model.TranslationNode;
+import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.model.bus.SearchQueryListener;
-import de.marhali.easyi18n.util.PathUtil;
import de.marhali.easyi18n.util.UiUtil;
import org.jetbrains.annotations.NotNull;
@@ -26,12 +22,14 @@ import java.util.Map;
public class TreeModelMapper extends DefaultTreeModel implements SearchQueryListener {
private final TranslationData data;
+ private final KeyPathConverter converter;
private final SettingsState state;
public TreeModelMapper(TranslationData data, SettingsState state) {
super(null);
this.data = data;
+ this.converter = new KeyPathConverter(state.isNestedKeys());
this.state = state;
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
@@ -42,7 +40,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
@Override
public void onSearchQuery(@Nullable String query) {
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
- TranslationData shadow = new TranslationData(this.state.isSortKeys(), this.state.isNestedKeys());
+ TranslationData shadow = new TranslationData(this.state.isSortKeys());
if(query == null) {
this.generateNodes(rootNode, this.data.getRootNode());
@@ -52,9 +50,9 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
query = query.toLowerCase();
- for(String currentKey : this.data.getFullKeys()) {
+ for(KeyPath currentKey : this.data.getFullKeys()) {
Translation translation = this.data.getTranslation(currentKey);
- String loweredKey = currentKey.toLowerCase();
+ String loweredKey = this.converter.concat(currentKey).toLowerCase();
if(query.contains(loweredKey) || loweredKey.contains(query)) {
shadow.setTranslation(currentKey, translation);
@@ -100,14 +98,13 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
}
}
- public @NotNull TreePath findTreePath(@NotNull String fullPath) {
- List sections = new PathUtil(this.state.isNestedKeys()).split(fullPath);
+ public @NotNull TreePath findTreePath(@NotNull KeyPath fullPath) {
List