Improved search feature.
- Search is now more like osu!, where subsequent search terms limit the existing result list (as opposed to further expanding it). - Replaced global tag HashMap with String instance variables, since the previous implementation is incompatible with the above change (resulting in slightly higher memory usage and search times). Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
cb396e0d20
commit
0a5e7f66ec
|
@ -63,7 +63,7 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
public String creator = ""; // beatmap creator
|
public String creator = ""; // beatmap creator
|
||||||
public String version = ""; // beatmap difficulty
|
public String version = ""; // beatmap difficulty
|
||||||
public String source = ""; // song source
|
public String source = ""; // song source
|
||||||
// public String[] tags; // song tags, for searching -> different structure
|
public String tags = ""; // song tags, for searching
|
||||||
public int beatmapID = 0; // beatmap ID
|
public int beatmapID = 0; // beatmap ID
|
||||||
public int beatmapSetID = 0; // beatmap set ID
|
public int beatmapSetID = 0; // beatmap set ID
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,7 @@ package itdelatrisu.opsu;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.Iterator;
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indexed, expanding, doubly-linked list data type for song groups.
|
* Indexed, expanding, doubly-linked list data type for song groups.
|
||||||
|
@ -54,12 +53,6 @@ public class OsuGroupList {
|
||||||
*/
|
*/
|
||||||
private int mapCount = 0;
|
private int mapCount = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Song tags.
|
|
||||||
* Each tag value is a HashSet which points song group ArrayLists.
|
|
||||||
*/
|
|
||||||
private HashMap<String, HashSet<ArrayList<OsuFile>>> tags;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current list of nodes.
|
* Current list of nodes.
|
||||||
* (For searches; otherwise, a pointer to parsedNodes.)
|
* (For searches; otherwise, a pointer to parsedNodes.)
|
||||||
|
@ -83,7 +76,6 @@ public class OsuGroupList {
|
||||||
public OsuGroupList() {
|
public OsuGroupList() {
|
||||||
parsedNodes = new ArrayList<OsuGroupNode>();
|
parsedNodes = new ArrayList<OsuGroupNode>();
|
||||||
nodes = parsedNodes;
|
nodes = parsedNodes;
|
||||||
tags = new HashMap<String, HashSet<ArrayList<OsuFile>>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,21 +97,6 @@ public class OsuGroupList {
|
||||||
*/
|
*/
|
||||||
public int getMapCount() { return mapCount; }
|
public int getMapCount() { return mapCount; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a tag.
|
|
||||||
* @param tag the tag string (key)
|
|
||||||
* @param osuFiles the song group associated with the tag (value)
|
|
||||||
*/
|
|
||||||
public void addTag(String tag, ArrayList<OsuFile> osuFiles) {
|
|
||||||
tag = tag.toLowerCase();
|
|
||||||
HashSet<ArrayList<OsuFile>> tagSet = tags.get(tag);
|
|
||||||
if (tagSet == null) {
|
|
||||||
tagSet = new HashSet<ArrayList<OsuFile>>();
|
|
||||||
tags.put(tag, tagSet);
|
|
||||||
}
|
|
||||||
tagSet.add(osuFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the OsuGroupNode at an index, disregarding expansions.
|
* Returns the OsuGroupNode at an index, disregarding expansions.
|
||||||
*/
|
*/
|
||||||
|
@ -272,7 +249,7 @@ public class OsuGroupList {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new list of song groups in which each group contains a match to a search query.
|
* Creates a new list of song groups in which each group contains a match to a search query.
|
||||||
* @param query the search query (tag terms separated by spaces)
|
* @param query the search query (terms separated by spaces)
|
||||||
* @return false if query is the same as the previous one, true otherwise
|
* @return false if query is the same as the previous one, true otherwise
|
||||||
*/
|
*/
|
||||||
public boolean search(String query) {
|
public boolean search(String query) {
|
||||||
|
@ -284,48 +261,30 @@ public class OsuGroupList {
|
||||||
if (lastQuery != null && query.equals(lastQuery))
|
if (lastQuery != null && query.equals(lastQuery))
|
||||||
return false;
|
return false;
|
||||||
lastQuery = query;
|
lastQuery = query;
|
||||||
|
String[] terms = query.split("\\s+");
|
||||||
|
|
||||||
// if empty query, reset to original list
|
// if empty query, reset to original list
|
||||||
if (query.isEmpty()) {
|
if (query.isEmpty() || terms.length < 1) {
|
||||||
nodes = parsedNodes;
|
nodes = parsedNodes;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag search: check if each word is contained in global tag structure
|
// build list from first search term
|
||||||
HashSet<ArrayList<OsuFile>> taggedGroups = new HashSet<ArrayList<OsuFile>>();
|
|
||||||
String[] terms = query.split("\\s+");
|
|
||||||
for (String term : terms) {
|
|
||||||
if (tags.containsKey(term))
|
|
||||||
taggedGroups.addAll(tags.get(term)); // add all matches
|
|
||||||
}
|
|
||||||
|
|
||||||
// traverse parsedNodes, comparing each element with the query
|
|
||||||
nodes = new ArrayList<OsuGroupNode>();
|
nodes = new ArrayList<OsuGroupNode>();
|
||||||
for (OsuGroupNode node : parsedNodes) {
|
for (OsuGroupNode node : parsedNodes) {
|
||||||
// search: tags
|
if (node.matches(terms[0])) {
|
||||||
if (taggedGroups.contains(node.osuFiles)) {
|
|
||||||
nodes.add(node);
|
nodes.add(node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
OsuFile osu = node.osuFiles.get(0);
|
|
||||||
|
|
||||||
// search: title, artist, creator, source, version (first OsuFile)
|
|
||||||
if (osu.title.toLowerCase().contains(query) ||
|
|
||||||
osu.artist.toLowerCase().contains(query) ||
|
|
||||||
osu.creator.toLowerCase().contains(query) ||
|
|
||||||
osu.source.toLowerCase().contains(query) ||
|
|
||||||
osu.version.toLowerCase().contains(query)) {
|
|
||||||
nodes.add(node);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// search: versions (all OsuFiles)
|
// remove nodes from list if they don't match all remaining terms
|
||||||
for (int i = 1; i < node.osuFiles.size(); i++) {
|
for (int i = 1; i < terms.length; i++) {
|
||||||
if (node.osuFiles.get(i).version.toLowerCase().contains(query)) {
|
Iterator<OsuGroupNode> iter = nodes.iterator();
|
||||||
nodes.add(node);
|
while (iter.hasNext()) {
|
||||||
break;
|
OsuGroupNode node = iter.next();
|
||||||
}
|
if (!node.matches(terms[i]))
|
||||||
|
iter.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,4 +186,32 @@ public class OsuGroupNode implements Comparable<OsuGroupNode> {
|
||||||
else
|
else
|
||||||
return osuFiles.get(osuFileIndex).toString();
|
return osuFiles.get(osuFileIndex).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the node matches a given search query.
|
||||||
|
* @param query the search term
|
||||||
|
* @return true if title, artist, creator, source, version, or tag matches query
|
||||||
|
*/
|
||||||
|
public boolean matches(String query) {
|
||||||
|
OsuFile osu = osuFiles.get(0);
|
||||||
|
|
||||||
|
// search: title, artist, creator, source, version, tags (first OsuFile)
|
||||||
|
if (osu.title.toLowerCase().contains(query) ||
|
||||||
|
osu.artist.toLowerCase().contains(query) ||
|
||||||
|
osu.creator.toLowerCase().contains(query) ||
|
||||||
|
osu.source.toLowerCase().contains(query) ||
|
||||||
|
osu.version.toLowerCase().contains(query) ||
|
||||||
|
osu.tags.contains(query))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// search: version, tags (remaining OsuFiles)
|
||||||
|
for (int i = 1; i < osuFiles.size(); i++) {
|
||||||
|
osu = osuFiles.get(i);
|
||||||
|
if (osu.version.toLowerCase().contains(query) ||
|
||||||
|
osu.tags.contains(query))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -124,7 +124,6 @@ public class OsuParser {
|
||||||
*/
|
*/
|
||||||
private static OsuFile parseFile(File file, ArrayList<OsuFile> osuFiles, boolean parseObjects) {
|
private static OsuFile parseFile(File file, ArrayList<OsuFile> osuFiles, boolean parseObjects) {
|
||||||
OsuFile osu = new OsuFile(file);
|
OsuFile osu = new OsuFile(file);
|
||||||
String tags = ""; // parse at end, only if valid OsuFile
|
|
||||||
|
|
||||||
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
|
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
|
||||||
|
|
||||||
|
@ -258,7 +257,7 @@ public class OsuParser {
|
||||||
osu.source = tokens[1];
|
osu.source = tokens[1];
|
||||||
break;
|
break;
|
||||||
case "Tags":
|
case "Tags":
|
||||||
tags = tokens[1];
|
osu.tags = tokens[1].toLowerCase();
|
||||||
break;
|
break;
|
||||||
case "BeatmapID":
|
case "BeatmapID":
|
||||||
osu.beatmapID = Integer.parseInt(tokens[1]);
|
osu.beatmapID = Integer.parseInt(tokens[1]);
|
||||||
|
@ -417,12 +416,6 @@ public class OsuParser {
|
||||||
if (osu.combo == null)
|
if (osu.combo == null)
|
||||||
osu.combo = Utils.DEFAULT_COMBO;
|
osu.combo = Utils.DEFAULT_COMBO;
|
||||||
|
|
||||||
// add tags
|
|
||||||
if (!tags.isEmpty()) {
|
|
||||||
for (String tag : tags.split(" "))
|
|
||||||
Opsu.groups.addTag(tag, osuFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse hit objects now?
|
// parse hit objects now?
|
||||||
if (parseObjects)
|
if (parseObjects)
|
||||||
parseHitObjects(osu);
|
parseHitObjects(osu);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user