From a34ae7e02fad91f18d13a26390153ca9258d5007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Ha=C3=9Flinger?= Date: Tue, 9 Nov 2021 19:35:37 +0100 Subject: [PATCH] introduce full-text-search inside i18n tool window --- .../de/marhali/easyi18n/tabs/TableView.java | 2 +- .../de/marhali/easyi18n/tabs/TreeView.java | 16 ++++-- .../tabs/mapper/TableModelMapper.java | 29 ++++++++-- .../easyi18n/tabs/mapper/TreeModelMapper.java | 55 +++++++++++++++++-- 4 files changed, 84 insertions(+), 18 deletions(-) diff --git a/src/main/java/de/marhali/easyi18n/tabs/TableView.java b/src/main/java/de/marhali/easyi18n/tabs/TableView.java index 8437d05..340d3a5 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/TableView.java +++ b/src/main/java/de/marhali/easyi18n/tabs/TableView.java @@ -97,9 +97,9 @@ public class TableView implements BusListener { @Override public void onSearchQuery(@Nullable String query) { - // TODO: handle search functionality if(this.currentMapper != null) { this.currentMapper.onSearchQuery(query); + this.table.updateUI(); } } diff --git a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java index 726526b..55bce06 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java +++ b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java @@ -41,14 +41,14 @@ public class TreeView implements BusListener { private final Project project; + private TreeModelMapper currentMapper; + private JPanel rootPanel; private JPanel toolBarPanel; private JPanel containerPanel; private Tree tree; - private TreeModelMapper mapper; - public TreeView(Project project) { this.project = project; @@ -81,19 +81,23 @@ public class TreeView implements BusListener { @Override public void onUpdateData(@NotNull TranslationData data) { - tree.setModel(this.mapper = new TreeModelMapper(data, SettingsService.getInstance(project).getState(), null)); + tree.setModel(this.currentMapper = new TreeModelMapper(data, SettingsService.getInstance(project).getState())); } @Override public void onFocusKey(@Nullable String key) { - if(key != null && mapper != null) { - this.tree.scrollPathToVisible(mapper.findTreePath(key)); + if(key != null && currentMapper != null) { + this.tree.scrollPathToVisible(currentMapper.findTreePath(key)); } } @Override public void onSearchQuery(@Nullable String query) { - // TODO: handle search functionality + if(this.currentMapper != null) { + this.currentMapper.onSearchQuery(query); + this.expandAll().run(); + this.tree.updateUI(); + } } private void handlePopup(MouseEvent e) { 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 29aa273..5a79f8e 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java +++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java @@ -1,10 +1,8 @@ package de.marhali.easyi18n.tabs.mapper; import de.marhali.easyi18n.model.*; - -import de.marhali.easyi18n.model.bus.BusListener; import de.marhali.easyi18n.model.bus.SearchQueryListener; -import de.marhali.easyi18n.model.bus.UpdateDataListener; + import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,7 +10,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.function.Consumer; @@ -24,7 +21,7 @@ public class TableModelMapper implements TableModel, SearchQueryListener { private final @NotNull TranslationData data; private final @NotNull List locales; - private final @NotNull List fullKeys; + private @NotNull List fullKeys; private final @NotNull Consumer updater; @@ -38,7 +35,27 @@ public class TableModelMapper implements TableModel, SearchQueryListener { @Override public void onSearchQuery(@Nullable String query) { - this.fullKeys = new ArrayList<>(); + if(query == null) { // Reset + this.fullKeys = new ArrayList<>(this.data.getFullKeys()); + return; + } + + query = query.toLowerCase(); + List matches = new ArrayList<>(); + + for(String key : this.data.getFullKeys()) { + if(key.toLowerCase().contains(query)) { + matches.add(key); + } else { + for(String content : this.data.getTranslation(key).values()) { + if(content.toLowerCase().contains(query)) { + matches.add(key); + } + } + } + } + + this.fullKeys = matches; } @Override 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 1c08dac..d1e4670 100644 --- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java +++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java @@ -6,6 +6,7 @@ import com.intellij.ui.JBColor; import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.model.TranslationNode; +import de.marhali.easyi18n.model.bus.SearchQueryListener; import de.marhali.easyi18n.util.PathUtil; import de.marhali.easyi18n.util.UiUtil; @@ -20,31 +21,46 @@ import java.util.Map; * Mapping {@link TranslationData} to {@link TreeModel}. * @author marhali */ -public class TreeModelMapper extends DefaultTreeModel { +public class TreeModelMapper extends DefaultTreeModel implements SearchQueryListener { private final TranslationData data; private final SettingsState state; - public TreeModelMapper(TranslationData data, SettingsState state, String searchQuery) { + public TreeModelMapper(TranslationData data, SettingsState state) { super(null); this.data = data; this.state = state; DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); - this.generateNodes(rootNode, this.data.getRootNode()); + this.generateNodes(rootNode, this.data.getRootNode(), null); super.setRoot(rootNode); } - private void generateNodes(DefaultMutableTreeNode parent, TranslationNode translationNode) { + @Override + public void onSearchQuery(@Nullable String query) { + DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); + this.generateNodes(rootNode, this.data.getRootNode(), query); + super.setRoot(rootNode); + } + + private void generateNodes(@NotNull DefaultMutableTreeNode parent, + @NotNull TranslationNode translationNode, @Nullable String searchQuery) { for(Map.Entry entry : translationNode.getChildren().entrySet()) { String key = entry.getKey(); TranslationNode childTranslationNode = entry.getValue(); + if(searchQuery != null) { + searchQuery = searchQuery.toLowerCase(); + if(!this.isApplicable(key, childTranslationNode, searchQuery)) { + continue; + } + } + if(!childTranslationNode.isLeaf()) { // Nested node - run recursively DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(key); - this.generateNodes(childNode, childTranslationNode); + this.generateNodes(childNode, childTranslationNode, searchQuery); parent.add(childNode); } else { String previewLocale = this.state.getPreviewLocale(); @@ -63,6 +79,35 @@ public class TreeModelMapper extends DefaultTreeModel { } } + /** + * Checks if the provided tree (@node) is applicable for the search string. + * A full-text-search is applied and section keys and every value will be evaluated. + * @param key Section key + * @param node Node which has @key as key + * @param searchQuery Search query to search for + * @return True if this node or ANY child is relevant for the search context + */ + private boolean isApplicable(@NotNull String key, @NotNull TranslationNode node, @NotNull String searchQuery) { + if(key.toLowerCase().contains(searchQuery)) { + return true; + } + + if(!node.isLeaf()) { + for(Map.Entry entry : node.getChildren().entrySet()) { + if(this.isApplicable(entry.getKey(), entry.getValue(), searchQuery)) { + return true; + } + } + } else { + for(String content : node.getValue().values()) { + if(content.toLowerCase().contains(searchQuery)) { + return true; + } + } + } + + return false; + } public @NotNull TreePath findTreePath(@NotNull String fullPath) { List sections = new PathUtil(this.state.isNestedKeys()).split(fullPath);