2024-06-27 18:03:50 +03:00
|
|
|
package codechicken.nei;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.HashMap;
|
2024-12-07 16:29:09 +02:00
|
|
|
import java.util.LinkedHashMap;
|
2024-06-27 18:03:50 +03:00
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
2024-12-07 16:29:09 +02:00
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
2024-06-27 18:03:50 +03:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
import codechicken.nei.ItemList.AllMultiItemFilter;
|
|
|
|
|
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
|
|
|
import codechicken.nei.ItemList.EverythingItemFilter;
|
|
|
|
|
import codechicken.nei.ItemList.NegatedItemFilter;
|
|
|
|
|
import codechicken.nei.ItemList.NothingItemFilter;
|
|
|
|
|
import codechicken.nei.api.ItemFilter;
|
2024-08-29 08:17:00 +02:00
|
|
|
import gnu.trove.map.TCharCharMap;
|
|
|
|
|
import gnu.trove.map.hash.TCharCharHashMap;
|
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();
|
|
|
|
|
|
|
|
|
|
public SearchMode getSearchMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class ProvidersCache {
|
|
|
|
|
|
|
|
|
|
public String languageCode = null;
|
|
|
|
|
public List<ISearchParserProvider> providers = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
public void clear() {
|
|
|
|
|
this.languageCode = null;
|
|
|
|
|
this.providers.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-28 19:05:06 +03:00
|
|
|
private static class IsRegisteredItemFilter implements ItemFilter {
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-07 16:29:09 +02:00
|
|
|
private static class ItemFilterCache implements ItemFilter {
|
|
|
|
|
|
|
|
|
|
public ItemFilter filter;
|
|
|
|
|
private final ItemStackMap<Boolean> states = new ItemStackMap<>();
|
|
|
|
|
private final ReentrantLock lock = new ReentrantLock();
|
|
|
|
|
|
|
|
|
|
public ItemFilterCache(ItemFilter filter) {
|
|
|
|
|
this.filter = filter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean matches(ItemStack item) {
|
|
|
|
|
lock.lock();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Boolean match = states.get(item);
|
|
|
|
|
|
|
|
|
|
if (match == null) {
|
|
|
|
|
states.put(item, match = this.filter.matches(item));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return match;
|
|
|
|
|
} catch (Throwable th) {
|
|
|
|
|
th.printStackTrace();
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
lock.unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static final LinkedHashMap<String, ItemFilter> filtersCache = new LinkedHashMap<>() {
|
|
|
|
|
|
|
|
|
|
private static final long serialVersionUID = 1042213947848622164L;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected boolean removeEldestEntry(Map.Entry<String, ItemFilter> eldest) {
|
|
|
|
|
return size() > 20;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-27 18:03:50 +03:00
|
|
|
protected final List<ISearchParserProvider> searchProviders;
|
|
|
|
|
protected final ProvidersCache providersCache = new ProvidersCache();
|
2024-08-29 08:17:00 +02:00
|
|
|
protected final TCharCharMap prefixRedefinitions = new TCharCharHashMap();
|
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);
|
|
|
|
|
this.providersCache.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected List<ISearchParserProvider> getProviders() {
|
|
|
|
|
Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage();
|
|
|
|
|
|
|
|
|
|
if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) {
|
|
|
|
|
Map<Character, ISearchParserProvider> providers = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
for (int index = this.searchProviders.size() - 1; index >= 0; index--) {
|
|
|
|
|
ISearchParserProvider provider = this.searchProviders.get(index);
|
|
|
|
|
|
|
|
|
|
if (!providers.containsKey(provider.getPrefix())
|
|
|
|
|
&& provider.getMatchingLanguages().contains(currentLanguage)) {
|
|
|
|
|
providers.put(provider.getPrefix(), provider);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
providersCache.providers = new ArrayList<>(providers.values());
|
|
|
|
|
providersCache.languageCode = currentLanguage.getLanguageCode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return providersCache.providers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ISearchParserProvider getProvider(char ch) {
|
|
|
|
|
return getProviders().stream()
|
2024-08-29 08:17:00 +02:00
|
|
|
.filter(
|
|
|
|
|
provider -> provider.getSearchMode() == SearchMode.PREFIX
|
|
|
|
|
&& getRedefinedPrefix(provider.getPrefix()) == ch)
|
2024-06-27 18:03:50 +03:00
|
|
|
.findFirst().orElse(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ItemFilter getFilter(String filterText) {
|
2024-12-07 16:29:09 +02:00
|
|
|
|
|
|
|
|
if (!filtersCache.containsKey(filterText)) {
|
|
|
|
|
final String[] parts = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase()
|
|
|
|
|
.split("\\|");
|
|
|
|
|
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(this::parseSearchText).filter(s -> s != null)
|
|
|
|
|
.collect(Collectors.toCollection(ArrayList::new));
|
|
|
|
|
|
|
|
|
|
if (searchTokens.isEmpty()) {
|
|
|
|
|
filtersCache.put(filterText, new EverythingItemFilter());
|
|
|
|
|
} else if (searchTokens.size() == 1) {
|
|
|
|
|
filtersCache.put(filterText, new IsRegisteredItemFilter(new ItemFilterCache(searchTokens.get(0))));
|
|
|
|
|
} else {
|
|
|
|
|
filtersCache.put(
|
|
|
|
|
filterText,
|
|
|
|
|
new IsRegisteredItemFilter(new ItemFilterCache(new AnyMultiItemFilter(searchTokens))));
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
2024-12-07 16:29:09 +02:00
|
|
|
|
|
|
|
|
return filtersCache.get(filterText);
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Pattern getSplitPattern() {
|
2024-08-29 08:17:00 +02:00
|
|
|
StringBuilder prefixes = new StringBuilder().append('\0');
|
2024-06-27 18:03:50 +03:00
|
|
|
|
|
|
|
|
for (ISearchParserProvider provider : getProviders()) {
|
|
|
|
|
if (provider.getSearchMode() == SearchMode.PREFIX) {
|
2024-08-29 08:17:00 +02:00
|
|
|
prefixes.append(getRedefinedPrefix(provider.getPrefix()));
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Pattern.compile("((-*)([" + Pattern.quote(prefixes.toString()) + "]*)(\\\".*?(?:\\\"|$)|\\S+))");
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-13 23:00:06 +03:00
|
|
|
public char getRedefinedPrefix(char prefix) {
|
2024-08-29 08:17:00 +02:00
|
|
|
if (this.prefixRedefinitions.containsKey(prefix)) {
|
|
|
|
|
return this.prefixRedefinitions.get(prefix);
|
|
|
|
|
}
|
|
|
|
|
return prefix;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 18:03:50 +03:00
|
|
|
private ItemFilter parseSearchText(String filterText) {
|
|
|
|
|
|
|
|
|
|
if (filterText.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final Matcher filterMatcher = getSplitPattern().matcher(filterText);
|
|
|
|
|
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
|
|
|
|
|
|
|
|
|
|
while (filterMatcher.find()) {
|
|
|
|
|
boolean ignore = "-".equals(filterMatcher.group(2));
|
|
|
|
|
String firstChar = filterMatcher.group(3);
|
|
|
|
|
String token = filterMatcher.group(4);
|
|
|
|
|
boolean quotes = token.length() > 1 && token.startsWith("\"") && token.endsWith("\"");
|
|
|
|
|
|
|
|
|
|
if (quotes) {
|
|
|
|
|
token = token.substring(1, token.length() - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!token.isEmpty()) {
|
|
|
|
|
ItemFilter result = parseToken(firstChar, token);
|
|
|
|
|
|
|
|
|
|
if (ignore) {
|
|
|
|
|
searchTokens.filters.add(new NegatedItemFilter(result));
|
|
|
|
|
} else {
|
|
|
|
|
searchTokens.filters.add(result);
|
|
|
|
|
}
|
|
|
|
|
} else if (!ignore) {
|
|
|
|
|
searchTokens.filters.add(new NothingItemFilter());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return searchTokens;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ItemFilter parseToken(String firstChar, String token) {
|
|
|
|
|
final ISearchParserProvider provider = firstChar.isEmpty() ? null : this.getProvider(firstChar.charAt(0));
|
|
|
|
|
|
|
|
|
|
if (provider == null || provider.getSearchMode() == SearchMode.NEVER) {
|
|
|
|
|
final List<ItemFilter> filters = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (ISearchParserProvider _provider : getProviders()) {
|
|
|
|
|
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
|
2024-08-29 23:14:52 +03:00
|
|
|
ItemFilter filter = _provider.getFilter(token);
|
|
|
|
|
|
|
|
|
|
if (filter != null) {
|
|
|
|
|
filters.add(filter);
|
|
|
|
|
}
|
2024-06-27 18:03:50 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters);
|
|
|
|
|
} else {
|
|
|
|
|
return provider.getFilter(token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|