2024-06-27 18:03:50 +03:00
|
|
|
package codechicken.nei;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2024-12-23 12:21:28 +02:00
|
|
|
import java.util.Collections;
|
2024-06-27 18:03:50 +03:00
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
|
|
import net.minecraft.client.Minecraft;
|
|
|
|
|
import net.minecraft.client.resources.Language;
|
2024-09-28 19:05:06 +03:00
|
|
|
import net.minecraft.item.ItemStack;
|
2024-06-27 18:03:50 +03:00
|
|
|
import net.minecraft.util.EnumChatFormatting;
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
import codechicken.nei.ItemList.AllMultiItemFilter;
|
|
|
|
|
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
|
|
|
import codechicken.nei.ItemList.EverythingItemFilter;
|
2025-08-24 10:35:59 +03:00
|
|
|
import codechicken.nei.api.IRecipeFilter;
|
2024-06-27 18:03:50 +03:00
|
|
|
import codechicken.nei.api.ItemFilter;
|
2025-09-04 16:59:51 +03:00
|
|
|
import codechicken.nei.filter.AllMultiRecipeFilter;
|
|
|
|
|
import codechicken.nei.filter.AnyMultiRecipeFilter;
|
|
|
|
|
import codechicken.nei.filter.RecipeFilter;
|
|
|
|
|
import codechicken.nei.filter.RecipeFilter.FilterContext;
|
2025-08-24 10:35:59 +03:00
|
|
|
import codechicken.nei.search.ItemFilterVisitor;
|
|
|
|
|
import codechicken.nei.search.RecipeFilterVisitor;
|
|
|
|
|
import codechicken.nei.search.SearchExpressionUtils;
|
2025-09-04 16:59:51 +03:00
|
|
|
import codechicken.nei.search.SearchToken;
|
2024-06-27 18:03:50 +03:00
|
|
|
|
|
|
|
|
public class SearchTokenParser {
|
|
|
|
|
|
|
|
|
|
public enum SearchMode {
|
|
|
|
|
|
|
|
|
|
ALWAYS,
|
|
|
|
|
PREFIX,
|
|
|
|
|
NEVER;
|
|
|
|
|
|
|
|
|
|
public static SearchMode fromInt(int value) {
|
2024-08-29 08:17:00 +02:00
|
|
|
return switch (value) {
|
|
|
|
|
case 0 -> ALWAYS;
|
|
|
|
|
case 1 -> PREFIX;
|
|
|
|
|
default -> NEVER;
|
|
|
|
|
};
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static interface ISearchParserProvider {
|
|
|
|
|
|
|
|
|
|
public ItemFilter getFilter(String searchText);
|
|
|
|
|
|
|
|
|
|
public static List<Language> getAllLanguages() {
|
|
|
|
|
return new ArrayList<>(Minecraft.getMinecraft().getLanguageManager().getLanguages());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default List<Language> getMatchingLanguages() {
|
|
|
|
|
return getAllLanguages();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public char getPrefix();
|
|
|
|
|
|
|
|
|
|
public EnumChatFormatting getHighlightedColor();
|
|
|
|
|
|
2026-08-04 19:18:27 +03:00
|
|
|
default SearchMode getSearchMode() {
|
|
|
|
|
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search." + getName() + "SearchMode"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default String getName() {
|
|
|
|
|
return String.valueOf(getPrefix());
|
|
|
|
|
}
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class ProvidersCache {
|
|
|
|
|
|
|
|
|
|
public String languageCode = null;
|
|
|
|
|
public List<ISearchParserProvider> providers = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
public void clear() {
|
|
|
|
|
this.languageCode = null;
|
|
|
|
|
this.providers.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
public static class IsRegisteredItemFilter implements ItemFilter {
|
2024-09-28 19:05:06 +03:00
|
|
|
|
|
|
|
|
public ItemFilter filter;
|
|
|
|
|
|
|
|
|
|
public IsRegisteredItemFilter(ItemFilter filter) {
|
|
|
|
|
this.filter = filter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean matches(ItemStack item) {
|
|
|
|
|
return item != null && item.getItem() != null
|
|
|
|
|
&& item.getItem().delegate.name() != null
|
|
|
|
|
&& this.filter.matches(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
public static final Pattern SPACE_PATTERN = Pattern.compile(" ");
|
|
|
|
|
|
2025-01-25 21:16:28 +02:00
|
|
|
protected final LRUCache<String, ItemFilter> filtersCache = new LRUCache<>(20);
|
2025-09-04 16:59:51 +03:00
|
|
|
protected final LRUCache<String, IRecipeFilter> recipesCache = new LRUCache<>(20);
|
2024-06-27 18:03:50 +03:00
|
|
|
protected final List<ISearchParserProvider> searchProviders;
|
|
|
|
|
protected final ProvidersCache providersCache = new ProvidersCache();
|
2024-12-23 12:21:28 +02:00
|
|
|
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
|
2025-09-04 16:59:51 +03:00
|
|
|
protected String prefixes = null;
|
2024-06-27 18:03:50 +03:00
|
|
|
|
|
|
|
|
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
|
|
|
|
|
this.searchProviders = searchProviders;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SearchTokenParser() {
|
|
|
|
|
this(new ArrayList<>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void addProvider(ISearchParserProvider provider) {
|
|
|
|
|
this.searchProviders.add(provider);
|
2025-09-04 16:59:51 +03:00
|
|
|
clearCache();
|
2024-12-17 21:15:41 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-06 07:55:49 +03:00
|
|
|
public synchronized void clearCache() {
|
2025-09-04 16:59:51 +03:00
|
|
|
this.providersCache.clear();
|
2024-12-17 21:15:41 +02:00
|
|
|
this.filtersCache.clear();
|
2025-09-04 16:59:51 +03:00
|
|
|
this.recipesCache.clear();
|
|
|
|
|
this.prefixes = null;
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
public List<ISearchParserProvider> getProviders() {
|
2024-06-27 18:03:50 +03:00
|
|
|
Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage();
|
|
|
|
|
|
|
|
|
|
if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) {
|
2025-09-17 15:42:35 +03:00
|
|
|
final List<ISearchParserProvider> providersList = new ArrayList<>();
|
|
|
|
|
final Map<Character, ISearchParserProvider> providers = new HashMap<>();
|
2024-06-27 18:03:50 +03:00
|
|
|
|
|
|
|
|
for (int index = this.searchProviders.size() - 1; index >= 0; index--) {
|
|
|
|
|
ISearchParserProvider provider = this.searchProviders.get(index);
|
|
|
|
|
|
2025-05-27 00:55:37 +09:00
|
|
|
if (provider.getMatchingLanguages().contains(currentLanguage)) {
|
|
|
|
|
if (provider.getSearchMode() == SearchMode.PREFIX && !providers.containsKey(provider.getPrefix())) {
|
|
|
|
|
providers.put(provider.getPrefix(), provider);
|
|
|
|
|
} else if (provider.getSearchMode() == SearchMode.ALWAYS) {
|
2025-09-17 15:42:35 +03:00
|
|
|
providersList.add(provider);
|
2025-05-27 00:55:37 +09:00
|
|
|
}
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 15:42:35 +03:00
|
|
|
providersList.addAll(providers.values());
|
|
|
|
|
providersCache.languageCode = currentLanguage.getLanguageCode();
|
|
|
|
|
providersCache.providers = providersList;
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return providersCache.providers;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
public boolean hasRedefinedPrefix(char ch) {
|
2025-09-04 16:59:51 +03:00
|
|
|
return getPrefixes().indexOf(ch) >= 0;
|
2025-08-24 10:35:59 +03:00
|
|
|
}
|
|
|
|
|
|
2024-06-27 18:03:50 +03:00
|
|
|
public ISearchParserProvider getProvider(char ch) {
|
2025-08-24 10:35:59 +03:00
|
|
|
if (!hasRedefinedPrefix(ch)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
for (ISearchParserProvider provider : getProviders()) {
|
|
|
|
|
if (provider.getSearchMode() == SearchMode.PREFIX && getRedefinedPrefix(provider.getPrefix()) == ch) {
|
|
|
|
|
return provider;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<ItemFilter> getAlwaysProvidersFilters(String searchText) {
|
|
|
|
|
final List<ItemFilter> filters = new ArrayList<>();
|
|
|
|
|
for (ISearchParserProvider provider : getProviders()) {
|
|
|
|
|
if (provider.getSearchMode() == SearchMode.ALWAYS) {
|
|
|
|
|
filters.add(provider.getFilter(searchText));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filters;
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
2024-12-17 21:15:41 +02:00
|
|
|
public synchronized ItemFilter getFilter(String filterText) {
|
2025-09-06 23:32:14 +03:00
|
|
|
return getFilter(filterText, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public synchronized ItemFilter getFilter(String rawText, boolean skipRecipeTokens) {
|
|
|
|
|
final String filterText = EnumChatFormatting.getTextWithoutFormattingCodes(rawText).toLowerCase();
|
2025-09-04 16:59:51 +03:00
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
if (filterText == null || filterText.isEmpty()) {
|
|
|
|
|
return new EverythingItemFilter();
|
|
|
|
|
}
|
2025-09-04 16:59:51 +03:00
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
|
|
|
|
|
|
|
|
|
if (patternMode != 3) {
|
2025-09-06 23:32:14 +03:00
|
|
|
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
|
2025-09-04 16:59:51 +03:00
|
|
|
final List<ItemFilter> searchTokens = new ArrayList<>();
|
|
|
|
|
|
2025-09-06 23:32:14 +03:00
|
|
|
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
|
2025-09-04 16:59:51 +03:00
|
|
|
|
|
|
|
|
if (subQuery[1].isEmpty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final List<ItemFilter> tokens = new ArrayList<>();
|
|
|
|
|
|
2025-09-06 23:32:14 +03:00
|
|
|
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
|
|
|
|
|
|
|
|
|
|
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!contextQuery[0].isEmpty()) {
|
|
|
|
|
contextQuery[0] = contextQuery[0].substring(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
|
|
|
|
|
final ItemFilter result = token.getFilter(this);
|
2025-09-04 16:59:51 +03:00
|
|
|
|
2025-09-06 23:32:14 +03:00
|
|
|
if (result != null) {
|
|
|
|
|
tokens.add(result);
|
|
|
|
|
}
|
2025-09-04 16:59:51 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tokens.size() == 1) {
|
|
|
|
|
searchTokens.add(tokens.get(0));
|
|
|
|
|
} else if (!tokens.isEmpty()) {
|
|
|
|
|
searchTokens.add(new AllMultiItemFilter(tokens));
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-24 10:35:59 +03:00
|
|
|
|
|
|
|
|
if (searchTokens.isEmpty()) {
|
|
|
|
|
return new EverythingItemFilter();
|
|
|
|
|
} else if (searchTokens.size() == 1) {
|
|
|
|
|
return new IsRegisteredItemFilter(searchTokens.get(0));
|
|
|
|
|
} else {
|
|
|
|
|
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
return this.filtersCache.computeIfAbsent(filterText, text -> {
|
2025-09-04 16:59:51 +03:00
|
|
|
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
|
|
|
|
|
|
|
|
|
if (spaceMode == 1) {
|
|
|
|
|
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
|
|
|
|
|
}
|
2025-09-13 12:18:29 +03:00
|
|
|
if (skipRecipeTokens) {
|
|
|
|
|
return new IsRegisteredItemFilter(
|
|
|
|
|
SearchExpressionUtils.visitRecipeSearchExpression(text, new ItemFilterVisitor(this)));
|
|
|
|
|
} else {
|
|
|
|
|
return new IsRegisteredItemFilter(
|
|
|
|
|
SearchExpressionUtils.visitSearchExpression(text, new ItemFilterVisitor(this)));
|
|
|
|
|
}
|
2025-08-24 10:35:59 +03:00
|
|
|
});
|
|
|
|
|
}
|
2024-12-07 16:29:09 +02:00
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
}
|
2024-12-07 16:29:09 +02:00
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
public synchronized IRecipeFilter getRecipeFilter(String filterText) {
|
|
|
|
|
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
|
2025-09-04 16:59:51 +03:00
|
|
|
|
2025-08-24 10:35:59 +03:00
|
|
|
if (filterText == null || filterText.isEmpty()) {
|
2025-09-04 16:59:51 +03:00
|
|
|
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
|
2025-08-24 10:35:59 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
|
|
|
|
|
|
|
|
|
if (patternMode != 3) {
|
|
|
|
|
return this.recipesCache.computeIfAbsent(filterText, input -> {
|
|
|
|
|
final List<IRecipeFilter> searchTokens = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (String[] orQueryPart : splitByDelimiters(input, "|", false)) {
|
|
|
|
|
|
|
|
|
|
if (orQueryPart[1].isEmpty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final List<IRecipeFilter> contextList = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
|
|
|
|
|
|
|
|
|
|
if (contextQuery[1].isEmpty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final List<IRecipeFilter> tokens = new ArrayList<>();
|
|
|
|
|
FilterContext context = FilterContext.ANY;
|
|
|
|
|
|
|
|
|
|
if (!contextQuery[0].isEmpty()) {
|
|
|
|
|
context = FilterContext.fromChar(contextQuery[0].charAt(0));
|
|
|
|
|
contextQuery[0] = contextQuery[0].substring(1);
|
|
|
|
|
}
|
2025-08-24 10:35:59 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
|
|
|
|
|
final ItemFilter result = token.getFilter(this);
|
|
|
|
|
if (result != null) {
|
|
|
|
|
tokens.add(
|
|
|
|
|
new RecipeFilter(context, token.ignore == null || token.ignore != '!', result));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tokens.size() == 1) {
|
|
|
|
|
contextList.add(tokens.get(0));
|
|
|
|
|
} else if (!tokens.isEmpty()) {
|
|
|
|
|
contextList.add(new AllMultiRecipeFilter(tokens));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contextList.size() == 1) {
|
|
|
|
|
searchTokens.add(contextList.get(0));
|
|
|
|
|
} else if (!contextList.isEmpty()) {
|
|
|
|
|
searchTokens.add(new AllMultiRecipeFilter(contextList));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (searchTokens.isEmpty()) {
|
|
|
|
|
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
|
|
|
|
|
} else if (searchTokens.size() == 1) {
|
|
|
|
|
return searchTokens.get(0);
|
|
|
|
|
} else {
|
|
|
|
|
return new AnyMultiRecipeFilter(searchTokens);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return this.recipesCache.computeIfAbsent(filterText, text -> {
|
|
|
|
|
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
|
|
|
|
|
|
|
|
|
if (spaceMode == 1) {
|
|
|
|
|
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 12:18:29 +03:00
|
|
|
return SearchExpressionUtils.visitRecipeSearchExpression(text, new RecipeFilterVisitor(this));
|
2025-09-04 16:59:51 +03:00
|
|
|
});
|
|
|
|
|
}
|
2024-12-07 16:29:09 +02:00
|
|
|
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
2024-12-23 12:21:28 +02:00
|
|
|
public List<SearchToken> splitSearchText(String filterText) {
|
|
|
|
|
if (filterText.isEmpty()) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final List<SearchToken> tokens = new ArrayList<>();
|
|
|
|
|
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
2025-09-04 16:59:51 +03:00
|
|
|
final String prefixes = getPrefixes();
|
|
|
|
|
SearchToken lastToken = null;
|
|
|
|
|
int lastEnd = 0;
|
|
|
|
|
|
|
|
|
|
for (String[] part : splitByDelimiters(filterText, "-!" + prefixes, true)) {
|
|
|
|
|
final SearchToken token = createToken(lastEnd, part, prefixes);
|
|
|
|
|
final int length = part[0].length() + part[1].length();
|
|
|
|
|
|
|
|
|
|
if (token.isSimpleFilter() && token.rawText.isEmpty()) {
|
|
|
|
|
lastEnd += length;
|
|
|
|
|
continue;
|
2024-12-23 12:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (spaceMode == 0) { // and
|
|
|
|
|
tokens.add(lastToken = token);
|
|
|
|
|
} else if (spaceMode == 1) { // space
|
2024-12-23 12:21:28 +02:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
|
|
|
|
|
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
|
|
|
|
|
lastToken.words[0] = lastToken.rawText;
|
|
|
|
|
lastToken.end = token.end;
|
|
|
|
|
} else {
|
|
|
|
|
tokens.add(lastToken = token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (spaceMode == 2) { // and-x
|
|
|
|
|
|
|
|
|
|
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
|
|
|
|
|
final String[] newWords = new String[lastToken.words.length + token.words.length];
|
|
|
|
|
|
|
|
|
|
System.arraycopy(lastToken.words, 0, newWords, 0, lastToken.words.length);
|
|
|
|
|
System.arraycopy(token.words, 0, newWords, lastToken.words.length, token.words.length);
|
|
|
|
|
|
|
|
|
|
lastToken.words = newWords;
|
|
|
|
|
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
|
|
|
|
|
lastToken.end = token.end;
|
|
|
|
|
} else {
|
|
|
|
|
tokens.add(lastToken = token);
|
2024-12-23 12:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
lastEnd += length;
|
2024-12-23 12:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
private SearchToken createToken(int lastEnd, String[] part, String prefixes) {
|
|
|
|
|
final SearchToken token = new SearchToken();
|
|
|
|
|
String pref = part[0];
|
|
|
|
|
String query = part[1].trim();
|
|
|
|
|
|
|
|
|
|
token.start = lastEnd;
|
|
|
|
|
token.end = lastEnd + pref.length() + query.length();
|
|
|
|
|
|
|
|
|
|
if (pref.startsWith("-")) {
|
|
|
|
|
token.ignore = '-';
|
|
|
|
|
pref = pref.substring(1);
|
|
|
|
|
} else if (pref.startsWith("!")) {
|
|
|
|
|
token.ignore = '!';
|
|
|
|
|
pref = pref.substring(1);
|
2025-08-24 10:35:59 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (!pref.isEmpty()) {
|
|
|
|
|
char firstChar = pref.charAt(0);
|
2024-06-27 18:03:50 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (prefixes.indexOf(firstChar) >= 0) {
|
|
|
|
|
token.firstChar = firstChar;
|
|
|
|
|
pref = pref.substring(1);
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
String text = pref + query;
|
|
|
|
|
token.quotes = text.length() > 1 && text.startsWith("\"") && text.endsWith("\"");
|
|
|
|
|
|
|
|
|
|
if (token.quotes) {
|
|
|
|
|
text = text.substring(1, text.length() - 1);
|
2025-08-24 10:35:59 +03:00
|
|
|
}
|
2025-09-04 16:59:51 +03:00
|
|
|
|
|
|
|
|
token.rawText = text;
|
|
|
|
|
token.words = new String[] { token.quotes ? text.replaceAll("\\\\\"", "\"") : text };
|
|
|
|
|
return token;
|
2024-08-29 08:17:00 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
public List<String[]> splitByDelimiters(String input, String delimiters, boolean space) {
|
2024-06-27 18:03:50 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (delimiters == null || delimiters.isEmpty()) {
|
|
|
|
|
return Collections.singletonList(new String[] { "", input });
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
final List<String[]> tokens = new ArrayList<>();
|
|
|
|
|
final int length = input.length();
|
|
|
|
|
boolean insideQuotes = false;
|
|
|
|
|
String token = "";
|
|
|
|
|
int lastEnd = 0;
|
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
|
|
while (index < length) {
|
|
|
|
|
final char ch = input.charAt(index);
|
2024-06-27 18:03:50 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (!insideQuotes && delimiters.indexOf(ch) >= 0
|
|
|
|
|
&& (!space || index == 0 || input.charAt(index - 1) == ' ')) {
|
2024-06-27 18:03:50 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (lastEnd == index) {
|
|
|
|
|
token += String.valueOf(ch);
|
2024-06-27 18:03:50 +03:00
|
|
|
} else {
|
2025-09-04 16:59:51 +03:00
|
|
|
tokens.add(new String[] { token, input.substring(lastEnd, index) });
|
|
|
|
|
token = String.valueOf(ch);
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
lastEnd = index + 1;
|
|
|
|
|
} else if (!insideQuotes && space && ch != ' ' && (index > 0 && input.charAt(index - 1) == ' ')) {
|
|
|
|
|
tokens.add(new String[] { token, input.substring(lastEnd, index) });
|
|
|
|
|
token = "";
|
2024-06-27 18:03:50 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
lastEnd = index;
|
|
|
|
|
}
|
2024-08-29 23:14:52 +03:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
|
|
|
|
|
insideQuotes = !insideQuotes;
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
index++;
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
2024-12-23 12:21:28 +02:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
tokens.add(new String[] { token, input.substring(lastEnd) });
|
2024-12-23 12:21:28 +02:00
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
return tokens;
|
|
|
|
|
}
|
2024-12-23 12:21:28 +02:00
|
|
|
|
2026-01-26 23:08:27 +05:00
|
|
|
public String getPrefixes() {
|
2025-09-04 16:59:51 +03:00
|
|
|
if (this.prefixes == null) {
|
|
|
|
|
final StringBuilder builder = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
for (ISearchParserProvider provider : getProviders()) {
|
|
|
|
|
if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) {
|
|
|
|
|
builder.append(getRedefinedPrefix(provider.getPrefix()));
|
|
|
|
|
}
|
2024-12-23 12:21:28 +02:00
|
|
|
}
|
2025-09-04 16:59:51 +03:00
|
|
|
|
|
|
|
|
this.prefixes = builder.toString();
|
2024-12-23 12:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-04 16:59:51 +03:00
|
|
|
return this.prefixes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public char getRedefinedPrefix(char prefix) {
|
|
|
|
|
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
|
2024-12-23 12:21:28 +02:00
|
|
|
}
|
2025-09-04 16:59:51 +03:00
|
|
|
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|