Skip to content

Commit 1f7f990

Browse files
authored
[PR] Merge pull request #81 from CSC207-2022F-UofT/patch-search
Patch search
2 parents bcfdbc6 + 03449f8 commit 1f7f990

File tree

9 files changed

+222
-79
lines changed

9 files changed

+222
-79
lines changed

src/main/java/org/hydev/mcpm/client/arguments/parsers/SearchParser.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@
44
import net.sourceforge.argparse4j.inf.Namespace;
55
import net.sourceforge.argparse4j.inf.Subparser;
66
import org.hydev.mcpm.client.commands.entries.SearchPackagesController;
7+
8+
import java.util.List;
79
import java.util.function.Consumer;
810

911
/**
1012
* SearchParser has two arguments: "type" and "text."
1113
* When the user runs the search command, the program prompts the user to specify the type of
1214
* search and the search text.
15+
*
16+
* @author Jerry Zhu (https://github.com/jerryzhu509)
1317
*/
14-
public class SearchParser implements CommandParser {
15-
private final SearchPackagesController controller;
16-
17-
public SearchParser(SearchPackagesController controller) {
18-
this.controller = controller;
19-
}
20-
18+
public record SearchParser(SearchPackagesController controller) implements CommandParser {
2119
@Override
2220
public String name() {
2321
return "search";
@@ -30,20 +28,31 @@ public String description() {
3028

3129
@Override
3230
public void configure(Subparser parser) {
33-
parser.addArgument("by").choices("name", "keyword", "command")
34-
.setDefault("name").dest("type")
35-
.help("Specifies the Type of Search");
31+
// Default search by name, if -k is specified then search by keyword
32+
var type = parser.addMutuallyExclusiveGroup();
33+
type.addArgument("-k", "--keyword").action(Arguments.storeTrue()).dest("byKeyword")
34+
.help("Search by keyword in descriptions");
35+
type.addArgument("-c", "--command").action(Arguments.storeTrue()).dest("byCommand")
36+
.help("Search by command");
37+
38+
// Content of the search
3639
parser.addArgument("text").dest("text").nargs("+")
37-
.help("Specifies the search text.");
40+
.help("Specifies the search text.");
3841
parser.addArgument("-n", "--no-cache").action(Arguments.storeTrue()).dest("noCache")
39-
.help("Specifies whether to use local cache or not.");
42+
.help("Specifies whether to use local cache or not.");
4043
}
4144

4245
@Override
4346
public void run(Namespace details, Consumer<String> log) {
44-
var t = details.getList("text");
45-
controller.searchPackages(details.getString("type"),
46-
String.join(" ", details.getList("text")),
47-
!details.getBoolean("noCache"), log);
47+
// Convert type
48+
var type = "name";
49+
if (details.getBoolean("byKeyword"))
50+
type = "keyword";
51+
if (details.getBoolean("byCommand"))
52+
type = "command";
53+
54+
// Call searchPackages
55+
List<String> t = details.getList("text");
56+
controller.searchPackages(type, String.join(" ", t), !details.getBoolean("noCache"), log);
4857
}
4958
}

src/main/java/org/hydev/mcpm/client/commands/entries/SearchPackagesController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public SearchPackagesController(SearchPackagesBoundary searcher) {
3030
* Load plugins and output status to log.
3131
*
3232
* @param type String that specifies the type of search.
33-
* @param text String that secifies the search text.
33+
* @param text String that specifies the search text.
3434
* @param noCache Specifies whether to use local cache or not.
3535
* @param log Callback for status for log events.
3636
*/

src/main/java/org/hydev/mcpm/client/database/results/SearchPackagesResult.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
public record SearchPackagesResult(SearchPackagesResult.State state, List<PluginModel> plugins) {
1515
/**
1616
* The outcome of the SearchPackagesResult.
17-
*
1817
* For INVALID_INPUT, check that your string is non-empty.
1918
*/
2019
public enum State {

src/main/java/org/hydev/mcpm/client/database/searchusecase/SearcherByCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ public List<PluginModel> getSearchList(String inp, List<PluginModel> plugins) {
5656
if (SearcherByCommand.commandMap == null) {
5757
SearcherByCommand.commandMap = constructSearchMaps(plugins);
5858
}
59-
return SearcherByCommand.commandMap.get(inp.toLowerCase());
59+
return SearcherByCommand.commandMap.getOrDefault(inp, List.of());
6060
}
6161
}

src/main/java/org/hydev/mcpm/client/database/searchusecase/SearcherByKeyword.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public Map<String, List<PluginModel>> constructSearchMaps(List<PluginModel> plug
3939
v.get().meta().description() != null && !v.get().meta().description().equals("")) {
4040
String[] keywords = v.get().meta().description()
4141
.replaceAll("[^a-zA-Z ]", "").toLowerCase().split("\\s+");
42-
for (String keyword : keywords) {
42+
for (String keyword : new HashSet<>(List.of(keywords))) {
4343
if (!models.containsKey(keyword))
4444
models.put(keyword, new ArrayList<>());
4545
models.get(keyword).add(plugin);
@@ -63,11 +63,13 @@ public List<PluginModel> getSearchList(String inp, List<PluginModel> plugins) {
6363
if (SearcherByKeyword.keywordMap == null) {
6464
SearcherByKeyword.keywordMap = constructSearchMaps(plugins);
6565
}
66-
String [] keywords = inp.toLowerCase().split(" "); // Should be a string
67-
Set<PluginModel> res = new HashSet<>(SearcherByKeyword.keywordMap.get(keywords[0]));
66+
String [] keywords = inp.split(" "); // Should be a string
67+
Set<PluginModel> res = new HashSet<>(SearcherByKeyword.keywordMap.getOrDefault(keywords[0], List.of()));
6868
for (int i = 1; i < keywords.length; i++) {
69-
List<PluginModel> pl = SearcherByKeyword.keywordMap.get(keywords[i]);
69+
List<PluginModel> pl = SearcherByKeyword.keywordMap.getOrDefault(keywords[i], List.of());
7070
res.retainAll(pl);
71+
if (res.isEmpty())
72+
return List.of();
7173
}
7274
return new ArrayList<>(res);
7375
}

src/main/java/org/hydev/mcpm/client/database/searchusecase/SearcherByName.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,14 @@ public Map<String, List<PluginModel>> constructSearchMaps(List<PluginModel> plug
4343
*
4444
* @param inp User input for the search. Should be a name as a string here.
4545
* @param plugins A list of all plugins in the database.
46-
* @return A dictionary associating a string feature of the plugins to the matching plugins.
47-
* Returns null if inp is not a string.
46+
* @return A list of plugins associated to inp.
4847
*/
4948
@Override
5049
public List<PluginModel> getSearchList(String inp, List<PluginModel> plugins) {
5150
// Instantiate if null
5251
if (SearcherByName.nameMap == null) {
5352
SearcherByName.nameMap = constructSearchMaps(plugins);
5453
}
55-
return SearcherByName.nameMap.get(inp.toLowerCase());
54+
return SearcherByName.nameMap.getOrDefault(inp, List.of());
5655
}
5756
}

src/test/java/org/hydev/mcpm/client/database/DatabaseInteractorUpdateTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public static void setup() {
4545

4646
smallInteractor = interactor(List.of(
4747
PluginMockFactory.model(1),
48-
PluginMockFactory.model(2, "name", List.of("ver.1", "ver.2")),
49-
PluginMockFactory.model(3, "test", List.of("update", "update update")),
50-
PluginMockFactory.model(4, "test", List.of("1.0", "2.0", "3.0"))
48+
PluginMockFactory.model(2, "name", null, List.of("ver.1", "ver.2"), null),
49+
PluginMockFactory.model(3, "test", null, List.of("update", "update update"), null),
50+
PluginMockFactory.model(4, "test", null, List.of("1.0", "2.0", "3.0"), null)
5151
));
5252
}
5353

src/test/java/org/hydev/mcpm/client/database/PluginMockFactory.java

Lines changed: 112 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,70 @@
22

33
import org.hydev.mcpm.client.database.fetcher.BriefFetcherListener;
44
import org.hydev.mcpm.client.database.fetcher.ConstantFetcher;
5+
import org.hydev.mcpm.client.models.PluginCommand;
56
import org.hydev.mcpm.client.models.PluginModel;
67
import org.hydev.mcpm.client.models.PluginVersion;
78
import org.hydev.mcpm.client.models.PluginYml;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Unmodifiable;
811

9-
import java.util.ArrayList;
12+
import java.util.HashMap;
1013
import java.util.List;
14+
import java.util.Map;
1115
import java.util.stream.IntStream;
1216

1317
/**
14-
* Mock factory for testing search functionality
18+
* This class creates Plugins mainly for testing purposes.
19+
*
20+
* @author Taylor Whatley
21+
* @author Jerry Zhu (<a href="https://github.com/jerryzhu509">...</a>)
1522
*/
1623
public class PluginMockFactory {
1724
private PluginMockFactory() { }
1825

26+
/**
27+
* Creates a mock PluginCommand object.
28+
*
29+
* @param description Description
30+
* @param aliases Other names
31+
* @return A Map<String, PluginCommand> object.
32+
*/
33+
public static @NotNull Map<String, PluginCommand> createCommand(String description, List<String> aliases) {
34+
var map = new HashMap<String, PluginCommand>();
35+
var pc = new PluginCommand(description, aliases, null, null);
36+
for (String alias : aliases) {
37+
map.put(alias, pc);
38+
}
39+
return map;
40+
}
41+
1942
/**
2043
* Creates a mock PluginYml object.
2144
*
2245
* @param name The name of the plugin.
2346
* @param version The version string for the plugin.
2447
* @param description The description for the plugin.
48+
* @param commands Commands for the plugin.
2549
* @return A PluginYml object.
2650
*/
27-
public static PluginYml meta(String name, String version, String description) {
51+
public static PluginYml meta(String name, String version, String description,
52+
Map<String, PluginCommand> commands) {
2853
return new PluginYml(
29-
"org." + name,
30-
name,
31-
version,
32-
description,
33-
null,
34-
null,
35-
null,
36-
null,
37-
null,
38-
null,
39-
null,
40-
null,
41-
null,
42-
null,
43-
null
54+
"org." + name,
55+
name,
56+
version,
57+
description,
58+
null,
59+
null,
60+
null,
61+
null,
62+
null,
63+
null,
64+
null,
65+
null,
66+
null,
67+
null,
68+
commands
4469
);
4570
}
4671

@@ -53,7 +78,22 @@ public static PluginYml meta(String name, String version, String description) {
5378
* @return A PluginVersion object.
5479
*/
5580
public static PluginVersion version(long id, String name, String string) {
56-
return new PluginVersion(id, 0, "", meta(name, string, null));
81+
return new PluginVersion(id, 0, "", meta(name, string, null, null));
82+
}
83+
84+
/**
85+
* Creates a mock PluginVersion object.
86+
*
87+
* @param id The version id.
88+
* @param name The plugin name (for meta).
89+
* @param string The version string (for meta).
90+
* @param description The plugin description.
91+
* @param commands The commands in the plugin.
92+
* @return A PluginVersion object.
93+
*/
94+
public static PluginVersion version(long id, String name, String string, String description,
95+
Map<String, PluginCommand> commands) {
96+
return new PluginVersion(id, 0, "", meta(name, string, description, commands));
5797
}
5898

5999

@@ -77,8 +117,8 @@ public static PluginModel model(long id) {
77117
*/
78118
public static PluginModel model(long id, String name) {
79119
return new PluginModel(
80-
id,
81-
List.of(version(id, name, "ver." + name)) // NOT SEMVER
120+
id,
121+
List.of(version(id, name, "ver." + name)) // NOT SEMVER
82122
);
83123
}
84124

@@ -88,24 +128,63 @@ public static PluginModel model(long id, String name) {
88128
*
89129
* @param id The plugin id.
90130
* @param name The plugin name.
131+
* @param description The plugin description.
91132
* @param versionNames The individual version strings for each version.
133+
* @param commands Commands for the plugin.
92134
* @return A PluginModel object.
93135
*/
94-
public static PluginModel model(long id, String name, List<String> versionNames) {
136+
public static PluginModel model(long id, String name, String description, List<String> versionNames,
137+
Map<String, PluginCommand> commands) {
95138
return new PluginModel(
96-
id,
97-
IntStream.range(0, versionNames.size())
98-
.mapToObj(i -> version(i, name, versionNames.get(i)))
99-
.toList()
139+
id,
140+
IntStream.range(0, versionNames.size())
141+
.mapToObj(i -> version(i, name, versionNames.get(i), description, commands))
142+
.toList()
100143
);
101144
}
102145

103-
public static List<PluginModel> generatePlugins() {
104-
List<PluginModel> plugins = new ArrayList<>();
105-
String[] names = {"WorldGuard"};
106-
String [] descriptions = {"Protect your server!\n" +
107-
"WorldGuard lets you and players guard areas of land against griefers and undesirables\n" +
108-
"as well as tweak and disable various gameplay features of Minecraft."};
109-
return plugins;
146+
/**
147+
* Creates a mock DatabaseInteractor object with the provided plugin list.
148+
*
149+
* @param plugins A list of plugins that the DatabaseInteractor will have access to.
150+
* @return A DatabaseInteractor object.
151+
*/
152+
public static DatabaseInteractor interactor(List<PluginModel> plugins) {
153+
var fetcher = new ConstantFetcher(plugins);
154+
var listener = new BriefFetcherListener(true);
155+
156+
return new DatabaseInteractor(fetcher, listener);
157+
}
158+
159+
160+
/**
161+
* Generates a sample list of plugins used for testing, containing the name, description, and commands.
162+
*
163+
* @return List of plugins for testing.
164+
*/
165+
public static @Unmodifiable List<PluginModel> generateTestPlugins() {
166+
String[] descriptions = {"WorldGuard lets you and players guard areas " +
167+
"of land against griefers and undesirables as well " +
168+
"as tweak and disable various gameplay features of Minecraft.",
169+
"Multiverse was created at the dawn of Bukkit multiworld support. " +
170+
"It has since then grown into a complete world management " +
171+
"solution including special treatment of your nether worlds with " +
172+
"Multiverse NetherPortals.",
173+
"Create futuristic holograms to display text and items to players!"
174+
};
175+
176+
return List.of(
177+
PluginMockFactory.model(1),
178+
PluginMockFactory.model(2, "WorldGuard",
179+
descriptions[0],
180+
List.of("1.18.2", "1.18.1"),
181+
createCommand(descriptions[0], List.of("/god", "/ungod"))),
182+
PluginMockFactory.model(3, "Multiverse-Core", descriptions[1],
183+
List.of("1.19.2", "1.19.1"),
184+
createCommand(descriptions[1],List.of("/mvlist", "/mvl"))),
185+
PluginMockFactory.model(4, "Holographic Displays", descriptions[2],
186+
List.of("1.19.2", "1.19.1", "1.19"),
187+
createCommand(descriptions[2], List.of("/hd", "/ungod")))
188+
);
110189
}
111190
}

0 commit comments

Comments
 (0)