diff --git a/src/main/java/codechicken/nei/CollapsibleItems.java b/src/main/java/codechicken/nei/CollapsibleItems.java index 79edb7fc..9274dc48 100644 --- a/src/main/java/codechicken/nei/CollapsibleItems.java +++ b/src/main/java/codechicken/nei/CollapsibleItems.java @@ -18,12 +18,12 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.EverythingItemFilter; +import codechicken.nei.ItemList.NothingItemFilter; import codechicken.nei.PresetsList.Preset; import codechicken.nei.PresetsList.PresetMode; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.EverythingItemFilter; -import codechicken.nei.filter.NothingItemFilter; import codechicken.nei.util.ItemStackFilterParser; public class CollapsibleItems { diff --git a/src/main/java/codechicken/nei/FormattedTextField.java b/src/main/java/codechicken/nei/FormattedTextField.java index 707dd5a2..686e53cb 100644 --- a/src/main/java/codechicken/nei/FormattedTextField.java +++ b/src/main/java/codechicken/nei/FormattedTextField.java @@ -38,6 +38,10 @@ public class FormattedTextField extends GuiTextField { this.formatter = formatter; } + public TextFormatter getFormatter() { + return this.formatter; + } + public void setPlaceholder(String placeholder) { this.placeholder = placeholder; } diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index d7032685..e027d525 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -1,6 +1,7 @@ package codechicken.nei; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -10,12 +11,14 @@ import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinWorkerThread; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import net.minecraft.client.Minecraft; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IIcon; import com.google.common.collect.ArrayListMultimap; @@ -25,7 +28,6 @@ import codechicken.nei.ThreadOperationTimer.TimeoutException; import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemFilter.ItemFilterProvider; import codechicken.nei.api.ItemInfo; -import codechicken.nei.filter.AllMultiItemFilter; import codechicken.nei.recipe.InformationHandler; import codechicken.nei.search.TooltipFilter; @@ -53,6 +55,117 @@ public class ItemList { */ public static boolean loadFinished; + public static class EverythingItemFilter implements ItemFilter { + + @Override + public boolean matches(ItemStack item) { + return true; + } + } + + public static class NothingItemFilter implements ItemFilter { + + @Override + public boolean matches(ItemStack item) { + return false; + } + } + + public static class NegatedItemFilter implements ItemFilter { + + public ItemFilter filter; + + public NegatedItemFilter(ItemFilter filter) { + this.filter = filter; + } + + @Override + public boolean matches(ItemStack item) { + return this.filter == null || !this.filter.matches(item); + } + } + + public static class PatternItemFilter implements ItemFilter { + + public Pattern pattern; + + public PatternItemFilter(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean matches(ItemStack item) { + String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName()); + + if (displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find()) { + return true; + } + + if (item.hasDisplayName()) { + displayName = EnumChatFormatting + .getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item)); + + return displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find(); + } + + return false; + } + } + + public static class AllMultiItemFilter implements ItemFilter { + + public List filters; + + public AllMultiItemFilter(List filters) { + this.filters = filters; + } + + public AllMultiItemFilter(ItemFilter... filters) { + this(new LinkedList<>(Arrays.asList(filters))); + } + + public AllMultiItemFilter() { + this(new LinkedList<>()); + } + + @Override + public boolean matches(ItemStack item) { + for (ItemFilter filter : filters) try { + if (filter != null && !filter.matches(item)) return false; + } catch (Exception e) { + NEIClientConfig.logger + .error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e); + } + + return true; + } + } + + public static class AnyMultiItemFilter implements ItemFilter { + + public List filters; + + public AnyMultiItemFilter(List filters) { + this.filters = filters; + } + + public AnyMultiItemFilter() { + this(new LinkedList<>()); + } + + @Override + public boolean matches(ItemStack item) { + for (ItemFilter filter : filters) try { + if (filter != null && filter.matches(item)) return true; + } catch (Exception e) { + NEIClientConfig.logger + .error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e); + } + + return false; + } + } + public static interface ItemsLoadedCallback { public void itemsLoaded(); diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index d17f8efd..058f98fb 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -561,7 +561,7 @@ public class NEIClientConfig { @Override public boolean onClick(int button) { - // SearchField.searchParser.clearCache(); + SearchField.searchParser.clearCache(); return super.onClick(button); } @@ -722,7 +722,7 @@ public class NEIClientConfig { }; Map charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType()); SearchField.searchParser.prefixRedefinitions.putAll(charMap); - SearchField.searchParser.updateRedefinedPrefixes(); + SearchField.searchParser.clearCache(); } catch (JsonParseException e) { logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e); } diff --git a/src/main/java/codechicken/nei/PresetsList.java b/src/main/java/codechicken/nei/PresetsList.java index a639728b..6a01d55f 100644 --- a/src/main/java/codechicken/nei/PresetsList.java +++ b/src/main/java/codechicken/nei/PresetsList.java @@ -83,17 +83,20 @@ public class PresetsList { } @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { + public boolean matches(IRecipeHandler handler, int recipeIndex) { - if (matchPositionedStack(ingredients, false)) { + if (matchPositionedStack(handler.getIngredientStacks(recipeIndex), false)) { return false; } + final PositionedStack result = handler.getResultStack(recipeIndex); + if (result != null && matchPositionedStack(result)) { return true; } + final List others = handler.getOtherStacks(recipeIndex); + if (!others.isEmpty() && matchPositionedStack(others, true)) { return true; } diff --git a/src/main/java/codechicken/nei/RecipeSearchField.java b/src/main/java/codechicken/nei/RecipeSearchField.java index 1393f729..822990c4 100644 --- a/src/main/java/codechicken/nei/RecipeSearchField.java +++ b/src/main/java/codechicken/nei/RecipeSearchField.java @@ -20,9 +20,11 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP @Override protected void initInternalTextField() { - field = new GuiSearchField(); + final GuiSearchField field = new GuiSearchField(); field.setMaxStringLength(maxSearchLength); field.setCursorPositionZero(); + ((SearchTextFormatter) field.getFormatter()).setContextToken("<>"); + this.field = field; } public boolean isVisible() { diff --git a/src/main/java/codechicken/nei/SearchField.java b/src/main/java/codechicken/nei/SearchField.java index 00e31c1c..818d20a4 100644 --- a/src/main/java/codechicken/nei/SearchField.java +++ b/src/main/java/codechicken/nei/SearchField.java @@ -13,13 +13,13 @@ import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.fluids.FluidStack; import codechicken.lib.gui.GuiDraw; +import codechicken.nei.ItemList.NothingItemFilter; import codechicken.nei.SearchTokenParser.ISearchParserProvider; import codechicken.nei.SearchTokenParser.SearchMode; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider; import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemFilter.ItemFilterProvider; -import codechicken.nei.filter.NothingItemFilter; import codechicken.nei.recipe.StackInfo; import codechicken.nei.util.TextHistory; diff --git a/src/main/java/codechicken/nei/SearchTextFormatter.java b/src/main/java/codechicken/nei/SearchTextFormatter.java index ffb67033..ff74a440 100644 --- a/src/main/java/codechicken/nei/SearchTextFormatter.java +++ b/src/main/java/codechicken/nei/SearchTextFormatter.java @@ -1,23 +1,26 @@ package codechicken.nei; -import java.util.List; -import java.util.StringJoiner; - import net.minecraft.util.EnumChatFormatting; import codechicken.nei.FormattedTextField.TextFormatter; -import codechicken.nei.SearchTokenParser.SearchToken; import codechicken.nei.search.SearchExpressionFormatVisitor; import codechicken.nei.search.SearchExpressionUtils; +import codechicken.nei.search.SearchToken; public class SearchTextFormatter implements TextFormatter { protected final SearchTokenParser searchParser; + protected String contextToken = ""; public SearchTextFormatter(SearchTokenParser searchParser) { this.searchParser = searchParser; } + public void setContextToken(String contextToken) { + this.contextToken = contextToken; + } + + @Override public String format(String text) { final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode"); if (patternMode == 3) { @@ -28,48 +31,61 @@ public class SearchTextFormatter implements TextFormatter { final SearchExpressionFormatVisitor visitor = new SearchExpressionFormatVisitor(searchParser); return SearchExpressionUtils.visitSearchExpression(text, visitor); } else { - final String[] parts = (text + "| ").split("\\|"); - StringJoiner formattedText = new StringJoiner(SearchExpressionUtils.HIGHLIGHTS.OR + "|"); + final StringBuilder formattedText = new StringBuilder(); - for (int i = 0; i < parts.length - 1; i++) { - final String filterText = parts[i]; - final List tokens = searchParser.splitSearchText(filterText); - StringBuilder formattedPart = new StringBuilder(); - int startIndex = 0; + for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) { + final String filterText = orQuery[1]; - for (SearchToken token : tokens) { - formattedPart.append(filterText.substring(startIndex, token.start)); - EnumChatFormatting tokenColor = EnumChatFormatting.RESET; + formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]); - if (token.firstChar != null) { - tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor(); + for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) { + + if (!contextPart[0].isEmpty()) { + formattedText.append( + SearchExpressionUtils.HIGHLIGHTS.RECIPE + contextPart[0] + EnumChatFormatting.RESET); } - if (token.ignore) { - formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + "-"); + if (contextPart[1].isEmpty()) { + continue; } - if (token.firstChar != null) { - formattedPart.append(tokenColor + String.valueOf(token.firstChar)); + int startIndex = 0; + + for (SearchToken token : searchParser.splitSearchText(contextPart[1])) { + formattedText.append(contextPart[1].substring(startIndex, token.start)); + EnumChatFormatting tokenColor = EnumChatFormatting.RESET; + + if (token.firstChar != null) { + tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor(); + } + + if (token.ignore != null) { + formattedText + .append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + String.valueOf(token.ignore)); + } + + if (token.firstChar != null) { + formattedText.append(tokenColor + String.valueOf(token.firstChar)); + } + + if (token.quotes) { + formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\""); + } + + if (!token.rawText.isEmpty()) { + formattedText.append(tokenColor + token.rawText); + } + + if (token.quotes) { + formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\""); + } + + startIndex = token.end; } - if (token.quotes) { - formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\""); - } - - if (!token.rawText.isEmpty()) { - formattedPart.append(tokenColor + token.rawText); - } - - if (token.quotes) { - formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\""); - } - - startIndex = token.end; + formattedText.append(contextPart[1].substring(startIndex, contextPart[1].length())); } - formattedPart.append(filterText.substring(startIndex, filterText.length())); - formattedText.add(formattedPart); } return formattedText.toString(); diff --git a/src/main/java/codechicken/nei/SearchTokenParser.java b/src/main/java/codechicken/nei/SearchTokenParser.java index 25600461..d50fe890 100644 --- a/src/main/java/codechicken/nei/SearchTokenParser.java +++ b/src/main/java/codechicken/nei/SearchTokenParser.java @@ -1,33 +1,30 @@ package codechicken.nei; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -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; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.EverythingItemFilter; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AllMultiItemFilter; -import codechicken.nei.filter.AnyItemRecipeFilter; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.EverythingItemFilter; -import codechicken.nei.filter.NegatedItemFilter; -import codechicken.nei.filter.NothingItemFilter; +import codechicken.nei.filter.AllMultiRecipeFilter; +import codechicken.nei.filter.AnyMultiRecipeFilter; +import codechicken.nei.filter.RecipeFilter; +import codechicken.nei.filter.RecipeFilter.FilterContext; import codechicken.nei.search.ItemFilterVisitor; import codechicken.nei.search.RecipeFilterVisitor; import codechicken.nei.search.SearchExpressionUtils; +import codechicken.nei.search.SearchToken; public class SearchTokenParser { @@ -46,20 +43,6 @@ public class SearchTokenParser { } } - public static class SearchToken { - - public boolean ignore = false; - public boolean quotes = false; - public Character firstChar = null; - - public String[] words; - - public String rawText = ""; - public int start = 0; - public int end = 0; - - } - public static interface ISearchParserProvider { public ItemFilter getFilter(String searchText); @@ -109,34 +92,33 @@ public class SearchTokenParser { public static final Pattern SPACE_PATTERN = Pattern.compile(" "); protected final LRUCache filtersCache = new LRUCache<>(20); + protected final LRUCache recipesCache = new LRUCache<>(20); protected final List searchProviders; - protected final Set redefinedPrefixes = new HashSet<>(); protected final ProvidersCache providersCache = new ProvidersCache(); protected final Map prefixRedefinitions = new HashMap<>(); + protected String prefixes = null; public SearchTokenParser(List searchProviders) { this.searchProviders = searchProviders; - updateRedefinedPrefixes(); } public SearchTokenParser() { this(new ArrayList<>()); - updateRedefinedPrefixes(); } public void addProvider(ISearchParserProvider provider) { this.searchProviders.add(provider); - updateRedefinedPrefixes(provider); - this.providersCache.clear(); - this.filtersCache.clear(); + clearCache(); } public void clearCache() { + this.providersCache.clear(); this.filtersCache.clear(); - updateRedefinedPrefixes(); + this.recipesCache.clear(); + this.prefixes = null; } - protected List getProviders() { + public List getProviders() { Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage(); if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) { @@ -164,7 +146,7 @@ public class SearchTokenParser { } public boolean hasRedefinedPrefix(char ch) { - return redefinedPrefixes.contains(ch); + return getPrefixes().indexOf(ch) >= 0; } public ISearchParserProvider getProvider(char ch) { @@ -192,17 +174,39 @@ public class SearchTokenParser { public synchronized ItemFilter getFilter(String filterText) { filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase(); + if (filterText == null || filterText.isEmpty()) { return new EverythingItemFilter(); } - final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode"); + final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode"); if (patternMode != 3) { return this.filtersCache.computeIfAbsent(filterText, text -> { - final String[] parts = text.split("\\|"); - final List searchTokens = Arrays.stream(parts).map(this::parseSearchText) - .filter(s -> s != null).collect(Collectors.toList()); + final List searchTokens = new ArrayList<>(); + + for (String[] subQuery : splitByDelimiters(text, "|", false)) { + + if (subQuery[1].isEmpty()) { + continue; + } + + final List tokens = new ArrayList<>(); + + for (SearchToken token : splitSearchText(subQuery[1])) { + final ItemFilter result = token.getFilter(this); + + if (result != null) { + tokens.add(result); + } + } + + if (tokens.size() == 1) { + searchTokens.add(tokens.get(0)); + } else if (!tokens.isEmpty()) { + searchTokens.add(new AllMultiItemFilter(tokens)); + } + } if (searchTokens.isEmpty()) { return new EverythingItemFilter(); @@ -213,183 +217,256 @@ public class SearchTokenParser { } }); } else { - if (spaceMode == 1) { - filterText = SearchTokenParser.SPACE_PATTERN.matcher(filterText).replaceAll("\\\\ "); - } return this.filtersCache.computeIfAbsent(filterText, text -> { - final ItemFilterVisitor visitor = new ItemFilterVisitor(this); - final ItemFilter searchToken = SearchExpressionUtils.visitSearchExpression(text, visitor); + final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode"); - return new IsRegisteredItemFilter(searchToken); + if (spaceMode == 1) { + text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ "); + } + + return new IsRegisteredItemFilter( + SearchExpressionUtils.visitSearchExpression(text, new ItemFilterVisitor(this))); }); } } public synchronized IRecipeFilter getRecipeFilter(String filterText) { - final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode"); - if (patternMode != 3) { - return new AnyItemRecipeFilter(getFilter(filterText)); - } filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase(); + if (filterText == null || filterText.isEmpty()) { - return new AnyItemRecipeFilter(new EverythingItemFilter()); - } - final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode"); - - if (spaceModeEnabled == 1) { - filterText = SearchTokenParser.SPACE_PATTERN.matcher(filterText).replaceAll("\\\\ "); + return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter()); } - final RecipeFilterVisitor visitor = new RecipeFilterVisitor(this); - return SearchExpressionUtils.visitSearchExpression(filterText, visitor); + final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode"); + + if (patternMode != 3) { + return this.recipesCache.computeIfAbsent(filterText, input -> { + final List searchTokens = new ArrayList<>(); + + for (String[] orQueryPart : splitByDelimiters(input, "|", false)) { + + if (orQueryPart[1].isEmpty()) { + continue; + } + + final List contextList = new ArrayList<>(); + + for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) { + + if (contextQuery[1].isEmpty()) { + continue; + } + + final List tokens = new ArrayList<>(); + FilterContext context = FilterContext.ANY; + + if (!contextQuery[0].isEmpty()) { + context = FilterContext.fromChar(contextQuery[0].charAt(0)); + contextQuery[0] = contextQuery[0].substring(1); + } + + 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("\\\\ "); + } + + return SearchExpressionUtils.visitSearchExpression(text, new RecipeFilterVisitor(this)); + }); + } } public List splitSearchText(String filterText) { - if (filterText.isEmpty()) { return Collections.emptyList(); } final List tokens = new ArrayList<>(); - final String prefixes = getPrefixes(); final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode"); - // The regular expression first tries to match a string that starts with a space or the beginning of the string, - // followed by a sequence of characters that do not contain special characters -"@#$%&, and ends with a space - // and the characters -"@#$%& or the end of the string. - final String patternPart1 = "(?(^|\\s+)(?:[^" + Pattern.quote(" -\"" + prefixes) - + "]).+?(?=\\s+[" - + Pattern.quote("-\"" + prefixes) - + "]|$))"; - // If the first condition is not met, it tries to match a sequence of - characters, followed by @#$%& - // characters, then either a quoted string or non-space characters. - final String patternPart2 = "((?-*)(?[" + Pattern.quote(prefixes) - + "]*)(?\\\".*?(?:\\\"|$)|\\S+\\s*))"; - final Pattern pattern = Pattern.compile(spaceMode == 0 ? patternPart2 : (patternPart1 + "|" + patternPart2)); - final Matcher filterMatcher = pattern.matcher(filterText); + final String prefixes = getPrefixes(); + SearchToken lastToken = null; + int lastEnd = 0; - while (filterMatcher.find()) { - String firstChar = filterMatcher.group("firstChar"); - SearchToken token = new SearchToken(); - token.start = filterMatcher.start(); - token.end = filterMatcher.end(); - token.ignore = "-".equals(filterMatcher.group("ignore")); - token.rawText = spaceMode == 0 ? null : filterMatcher.group("tokenA"); + for (String[] part : splitByDelimiters(filterText, "-!" + prefixes, true)) { + final SearchToken token = createToken(lastEnd, part, prefixes); + final int length = part[0].length() + part[1].length(); - if (firstChar != null && !firstChar.isEmpty()) { - token.firstChar = firstChar.charAt(0); + if (token.isSimpleFilter() && token.rawText.isEmpty()) { + lastEnd += length; + continue; } - if (token.rawText == null) { // spaceMode == 0 - token.rawText = filterMatcher.group("tokenB"); - token.quotes = token.rawText.length() > 1 && token.rawText.startsWith("\"") - && token.rawText.endsWith("\""); + if (spaceMode == 0) { // and + tokens.add(lastToken = token); + } else if (spaceMode == 1) { // space - if (token.quotes) { - token.rawText = token.rawText.substring(1, token.rawText.length() - 1); + 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); } - token.words = new String[] { token.rawText.trim() }; - } else if (spaceMode == 2) { - token.words = token.rawText.trim().split("\\s+"); - } else { - token.words = new String[] { token.rawText.trim() }; } - tokens.add(token); + lastEnd += length; } return tokens; } - private String getPrefixes() { - final StringBuilder builder = new StringBuilder(); - for (Character redefinedPrefix : redefinedPrefixes) { - builder.append(redefinedPrefix); + 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); } - return builder.toString(); + + if (!pref.isEmpty()) { + char firstChar = pref.charAt(0); + + if (prefixes.indexOf(firstChar) >= 0) { + token.firstChar = firstChar; + pref = pref.substring(1); + } + } + + String text = pref + query; + token.quotes = text.length() > 1 && text.startsWith("\"") && text.endsWith("\""); + + if (token.quotes) { + text = text.substring(1, text.length() - 1); + } + + token.rawText = text; + token.words = new String[] { token.quotes ? text.replaceAll("\\\\\"", "\"") : text }; + return token; + } + + public List splitByDelimiters(String input, String delimiters, boolean space) { + + if (delimiters == null || delimiters.isEmpty()) { + return Collections.singletonList(new String[] { "", input }); + } + + final List 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); + + if (!insideQuotes && delimiters.indexOf(ch) >= 0 + && (!space || index == 0 || input.charAt(index - 1) == ' ')) { + + if (lastEnd == index) { + token += String.valueOf(ch); + } else { + tokens.add(new String[] { token, input.substring(lastEnd, index) }); + token = String.valueOf(ch); + } + + lastEnd = index + 1; + } else if (!insideQuotes && space && ch != ' ' && (index > 0 && input.charAt(index - 1) == ' ')) { + tokens.add(new String[] { token, input.substring(lastEnd, index) }); + token = ""; + + lastEnd = index; + } + + if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) { + insideQuotes = !insideQuotes; + } + + index++; + } + + tokens.add(new String[] { token, input.substring(lastEnd) }); + + return tokens; + } + + private String getPrefixes() { + if (this.prefixes == null) { + final StringBuilder builder = new StringBuilder(); + + for (ISearchParserProvider provider : getProviders()) { + if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) { + builder.append(getRedefinedPrefix(provider.getPrefix())); + } + } + + this.prefixes = builder.toString(); + } + + return this.prefixes; } public char getRedefinedPrefix(char prefix) { return this.prefixRedefinitions.getOrDefault(prefix, prefix); } - public void updateRedefinedPrefixes() { - this.redefinedPrefixes.clear(); - for (ISearchParserProvider provider : getProviders()) { - if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) { - redefinedPrefixes.add(getRedefinedPrefix(provider.getPrefix())); - } - } - this.redefinedPrefixes.add('\0'); - } - - public void updateRedefinedPrefixes(ISearchParserProvider provider) { - if (provider.getSearchMode() == SearchMode.PREFIX) { - this.redefinedPrefixes.add(getRedefinedPrefix(provider.getPrefix())); - } - } - - private ItemFilter parseSearchText(String filterText) { - - if (filterText.isEmpty()) { - return null; - } - - final AllMultiItemFilter searchTokens = new AllMultiItemFilter(); - final List tokens = splitSearchText(filterText); - - for (SearchToken token : tokens) { - if (!token.rawText.isEmpty()) { - ItemFilter result = parseToken(token); - - if (token.ignore) { - searchTokens.filters.add(new NegatedItemFilter(result)); - } else { - searchTokens.filters.add(result); - } - } else if (!token.ignore) { - searchTokens.filters.add(new NothingItemFilter()); - } - } - - return searchTokens; - } - - private ItemFilter parseToken(SearchToken token) { - final ISearchParserProvider provider = token.firstChar == null ? null : this.getProvider(token.firstChar); - - if (provider == null || provider.getSearchMode() == SearchMode.NEVER) { - final List filters = new ArrayList<>(); - - for (ISearchParserProvider _provider : getProviders()) { - if (_provider.getSearchMode() == SearchMode.ALWAYS) { - AllMultiItemFilter filter = generateFilters(_provider, token.words); - - if (!filter.filters.isEmpty()) { - filters.add(filter); - } - } - } - - return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters); - } else { - return generateFilters(provider, token.words); - } - } - - private AllMultiItemFilter generateFilters(ISearchParserProvider provider, String[] words) { - final AllMultiItemFilter filters = new AllMultiItemFilter(); - - for (String work : words) { - final ItemFilter filter = provider.getFilter(work); - - if (filter != null) { - filters.filters.add(filter); - } - } - - return filters; - } } diff --git a/src/main/java/codechicken/nei/SubsetWidget.java b/src/main/java/codechicken/nei/SubsetWidget.java index 21363810..97e8305d 100644 --- a/src/main/java/codechicken/nei/SubsetWidget.java +++ b/src/main/java/codechicken/nei/SubsetWidget.java @@ -32,13 +32,13 @@ import com.google.gson.JsonParser; import codechicken.core.gui.GuiScrollSlot; import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.NothingItemFilter; import codechicken.nei.SearchTokenParser.ISearchParserProvider; import codechicken.nei.SearchTokenParser.SearchMode; import codechicken.nei.api.API; import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemFilter.ItemFilterProvider; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.NothingItemFilter; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerTooltipHandler; import codechicken.nei.recipe.GuiCraftingRecipe; diff --git a/src/main/java/codechicken/nei/api/IRecipeFilter.java b/src/main/java/codechicken/nei/api/IRecipeFilter.java index bf880bb2..0ef52cda 100644 --- a/src/main/java/codechicken/nei/api/IRecipeFilter.java +++ b/src/main/java/codechicken/nei/api/IRecipeFilter.java @@ -1,8 +1,5 @@ package codechicken.nei.api; -import java.util.List; - -import codechicken.nei.PositionedStack; import codechicken.nei.recipe.IRecipeHandler; public interface IRecipeFilter { @@ -12,7 +9,6 @@ public interface IRecipeFilter { public IRecipeFilter getRecipeFilter(); } - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others); + public boolean matches(IRecipeHandler handler, int recipeIndex); } diff --git a/src/main/java/codechicken/nei/api/ItemInfo.java b/src/main/java/codechicken/nei/api/ItemInfo.java index 7e8bd74c..023493b9 100644 --- a/src/main/java/codechicken/nei/api/ItemInfo.java +++ b/src/main/java/codechicken/nei/api/ItemInfo.java @@ -44,6 +44,8 @@ import com.google.common.collect.ArrayListMultimap; import codechicken.core.featurehack.GameDataManipulator; import codechicken.nei.InfiniteStackSizeHandler; import codechicken.nei.InfiniteToolHandler; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.PatternItemFilter; import codechicken.nei.ItemMobSpawner; import codechicken.nei.ItemStackMap; import codechicken.nei.ItemStackSet; @@ -56,8 +58,6 @@ import codechicken.nei.config.ArrayDumper; import codechicken.nei.config.HandlerDumper; import codechicken.nei.config.ItemPanelDumper; import codechicken.nei.config.RegistryDumper; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.PatternItemFilter; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.recipe.BrewingRecipeHandler; import codechicken.nei.recipe.RecipeItemInputHandler; diff --git a/src/main/java/codechicken/nei/config/preset/RightPanel.java b/src/main/java/codechicken/nei/config/preset/RightPanel.java index f48e2004..8f52c200 100644 --- a/src/main/java/codechicken/nei/config/preset/RightPanel.java +++ b/src/main/java/codechicken/nei/config/preset/RightPanel.java @@ -16,6 +16,9 @@ import codechicken.core.gui.GuiWidget; import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.CollapsibleItems; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.NegatedItemFilter; import codechicken.nei.ItemsGrid; import codechicken.nei.ItemsGrid.ItemsGridSlot; import codechicken.nei.NEIClientConfig; @@ -25,9 +28,6 @@ import codechicken.nei.PresetsList.Preset; import codechicken.nei.SearchField.GuiSearchField; import codechicken.nei.TextField; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AllMultiItemFilter; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.NegatedItemFilter; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerTooltipHandler; import codechicken.nei.util.TextHistory; diff --git a/src/main/java/codechicken/nei/filter/AllIngredientsRecipeFilter.java b/src/main/java/codechicken/nei/filter/AllIngredientsRecipeFilter.java deleted file mode 100644 index 7df00d66..00000000 --- a/src/main/java/codechicken/nei/filter/AllIngredientsRecipeFilter.java +++ /dev/null @@ -1,23 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.IRecipeFilter; -import codechicken.nei.api.ItemFilter; -import codechicken.nei.recipe.IRecipeHandler; - -public class AllIngredientsRecipeFilter extends AllRecipeFilter implements IRecipeFilter { - - public AllIngredientsRecipeFilter(ItemFilter filter) { - super(filter); - } - - @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { - - return allMatch(ingredients); - } - -} diff --git a/src/main/java/codechicken/nei/filter/AllMultiItemFilter.java b/src/main/java/codechicken/nei/filter/AllMultiItemFilter.java deleted file mode 100644 index f9499397..00000000 --- a/src/main/java/codechicken/nei/filter/AllMultiItemFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package codechicken.nei.filter; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.NEIClientConfig; -import codechicken.nei.api.ItemFilter; - -public class AllMultiItemFilter implements ItemFilter { - - public final List filters; - - public AllMultiItemFilter(List filters) { - this.filters = filters; - } - - public AllMultiItemFilter(ItemFilter... filters) { - this(new LinkedList<>(Arrays.asList(filters))); - } - - public AllMultiItemFilter() { - this(new LinkedList<>()); - } - - @Override - public boolean matches(ItemStack item) { - for (ItemFilter filter : filters) try { - if (filter != null && !filter.matches(item)) return false; - } catch (Exception e) { - NEIClientConfig.logger - .error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e); - } - - return true; - } -} diff --git a/src/main/java/codechicken/nei/filter/AllMultiRecipeFilter.java b/src/main/java/codechicken/nei/filter/AllMultiRecipeFilter.java index 9ecfb0ad..8554b9a8 100644 --- a/src/main/java/codechicken/nei/filter/AllMultiRecipeFilter.java +++ b/src/main/java/codechicken/nei/filter/AllMultiRecipeFilter.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.List; import codechicken.nei.NEIClientConfig; -import codechicken.nei.PositionedStack; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.recipe.IRecipeHandler; @@ -26,11 +25,10 @@ public class AllMultiRecipeFilter implements IRecipeFilter { } @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { + public boolean matches(IRecipeHandler handler, int recipeIndex) { for (IRecipeFilter filter : filters) { try { - if (filter != null && !filter.matches(handler, ingredients, result, others)) return false; + if (filter != null && !filter.matches(handler, recipeIndex)) return false; } catch (Exception e) { NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e); } diff --git a/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java b/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java index ae76600a..a7ded945 100644 --- a/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java +++ b/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java @@ -1,23 +1,44 @@ package codechicken.nei.filter; -import java.util.List; +import net.minecraft.item.ItemStack; import codechicken.nei.PositionedStack; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.ItemFilter; import codechicken.nei.recipe.IRecipeHandler; -public class AllOthersRecipeFilter extends AllRecipeFilter implements IRecipeFilter { +public class AllOthersRecipeFilter implements IRecipeFilter { + + private final ItemFilter filter; public AllOthersRecipeFilter(ItemFilter filter) { - super(filter); + this.filter = filter; } @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { + public boolean matches(IRecipeHandler handler, int recipeIndex) { - return allMatch(others); + for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) { + if (!match(pStack)) { + return false; + } + } + + return true; + } + + protected boolean match(PositionedStack pStack) { + if (pStack == null) { + return false; + } + + for (ItemStack stack : pStack.items) { + if (filter.matches(stack)) { + return true; + } + } + + return false; } } diff --git a/src/main/java/codechicken/nei/filter/AllRecipeFilter.java b/src/main/java/codechicken/nei/filter/AllRecipeFilter.java deleted file mode 100644 index d1c507a6..00000000 --- a/src/main/java/codechicken/nei/filter/AllRecipeFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.ItemFilter; - -public abstract class AllRecipeFilter { - - private final ItemFilter filter; - - protected AllRecipeFilter(ItemFilter filter) { - this.filter = filter; - } - - protected boolean allMatch(List items) { - for (PositionedStack pStack : items) { - if (!allMatch(pStack)) { - return false; - } - } - return true; - } - - protected boolean allMatch(PositionedStack pStack) { - if (pStack == null) { - return false; - } - - for (ItemStack stack : pStack.items) { - if (!filter.matches(stack)) { - return false; - } - } - - return true; - } - -} diff --git a/src/main/java/codechicken/nei/filter/AllSmartResultRecipeFilter.java b/src/main/java/codechicken/nei/filter/AllSmartResultRecipeFilter.java deleted file mode 100644 index cdfb08fd..00000000 --- a/src/main/java/codechicken/nei/filter/AllSmartResultRecipeFilter.java +++ /dev/null @@ -1,27 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.IRecipeFilter; -import codechicken.nei.api.ItemFilter; -import codechicken.nei.recipe.IRecipeHandler; - -public class AllSmartResultRecipeFilter extends AllRecipeFilter implements IRecipeFilter { - - public AllSmartResultRecipeFilter(ItemFilter filter) { - super(filter); - } - - @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { - if (result != null) { - return allMatch(result); - } else { - // GT recipes use others as a result - return allMatch(others); - } - } - -} diff --git a/src/main/java/codechicken/nei/filter/AnyIngredientsRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnyIngredientsRecipeFilter.java deleted file mode 100644 index e80d48c4..00000000 --- a/src/main/java/codechicken/nei/filter/AnyIngredientsRecipeFilter.java +++ /dev/null @@ -1,23 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.IRecipeFilter; -import codechicken.nei.api.ItemFilter; -import codechicken.nei.recipe.IRecipeHandler; - -public class AnyIngredientsRecipeFilter extends AnyRecipeFilter implements IRecipeFilter { - - public AnyIngredientsRecipeFilter(ItemFilter filter) { - super(filter); - } - - @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { - - return anyMatch(ingredients); - } - -} diff --git a/src/main/java/codechicken/nei/filter/AnyItemRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnyItemRecipeFilter.java deleted file mode 100644 index 9f32f04b..00000000 --- a/src/main/java/codechicken/nei/filter/AnyItemRecipeFilter.java +++ /dev/null @@ -1,22 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.IRecipeFilter; -import codechicken.nei.api.ItemFilter; -import codechicken.nei.recipe.IRecipeHandler; - -public class AnyItemRecipeFilter extends AnyRecipeFilter implements IRecipeFilter { - - public AnyItemRecipeFilter(ItemFilter filter) { - super(filter); - } - - @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { - return anyMatch(ingredients) || anyMatch(result) || anyMatch(others); - } - -} diff --git a/src/main/java/codechicken/nei/filter/AnyMultiItemFilter.java b/src/main/java/codechicken/nei/filter/AnyMultiItemFilter.java deleted file mode 100644 index 7fa68fb1..00000000 --- a/src/main/java/codechicken/nei/filter/AnyMultiItemFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -package codechicken.nei.filter; - -import java.util.LinkedList; -import java.util.List; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.NEIClientConfig; -import codechicken.nei.api.ItemFilter; - -public class AnyMultiItemFilter implements ItemFilter { - - public final List filters; - - public AnyMultiItemFilter(List filters) { - this.filters = filters; - } - - public AnyMultiItemFilter() { - this(new LinkedList<>()); - } - - @Override - public boolean matches(ItemStack item) { - for (ItemFilter filter : filters) try { - if (filter != null && filter.matches(item)) return true; - } catch (Exception e) { - NEIClientConfig.logger - .error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e); - } - - return false; - } -} diff --git a/src/main/java/codechicken/nei/filter/AnyMultiRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnyMultiRecipeFilter.java new file mode 100644 index 00000000..c64f19da --- /dev/null +++ b/src/main/java/codechicken/nei/filter/AnyMultiRecipeFilter.java @@ -0,0 +1,40 @@ +package codechicken.nei.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import codechicken.nei.NEIClientConfig; +import codechicken.nei.api.IRecipeFilter; +import codechicken.nei.recipe.IRecipeHandler; + +public class AnyMultiRecipeFilter implements IRecipeFilter { + + public final List filters; + + public AnyMultiRecipeFilter(List filters) { + this.filters = filters; + } + + public AnyMultiRecipeFilter(IRecipeFilter filters) { + this(Arrays.asList(filters)); + } + + public AnyMultiRecipeFilter() { + this(new ArrayList<>()); + } + + @Override + public boolean matches(IRecipeHandler handler, int recipeIndex) { + for (IRecipeFilter filter : filters) { + try { + if (filter != null && filter.matches(handler, recipeIndex)) return true; + } catch (Exception e) { + NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e); + } + } + + return false; + } + +} diff --git a/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java index 63e21ad4..f3c4c31f 100644 --- a/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java +++ b/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java @@ -1,23 +1,44 @@ package codechicken.nei.filter; -import java.util.List; +import net.minecraft.item.ItemStack; import codechicken.nei.PositionedStack; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.ItemFilter; import codechicken.nei.recipe.IRecipeHandler; -public class AnyOthersRecipeFilter extends AnyRecipeFilter implements IRecipeFilter { +public class AnyOthersRecipeFilter implements IRecipeFilter { + + private final ItemFilter filter; public AnyOthersRecipeFilter(ItemFilter filter) { - super(filter); + this.filter = filter; } @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { + public boolean matches(IRecipeHandler handler, int recipeIndex) { - return anyMatch(others); + for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) { + if (match(pStack)) { + return true; + } + } + + return false; + } + + protected boolean match(PositionedStack pStack) { + if (pStack == null) { + return false; + } + + for (ItemStack stack : pStack.items) { + if (filter.matches(stack)) { + return true; + } + } + + return false; } } diff --git a/src/main/java/codechicken/nei/filter/AnyRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnyRecipeFilter.java deleted file mode 100644 index 9fb521aa..00000000 --- a/src/main/java/codechicken/nei/filter/AnyRecipeFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.ItemFilter; - -public abstract class AnyRecipeFilter { - - private final ItemFilter filter; - - protected AnyRecipeFilter(ItemFilter filter) { - this.filter = filter; - } - - protected boolean anyMatch(List items) { - for (PositionedStack pStack : items) { - if (anyMatch(pStack)) { - return true; - } - } - return false; - } - - protected boolean anyMatch(PositionedStack pStack) { - if (pStack == null) { - return false; - } - - for (ItemStack stack : pStack.items) { - if (filter.matches(stack)) { - return true; - } - } - - return false; - } - -} diff --git a/src/main/java/codechicken/nei/filter/AnySmartResultRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnySmartResultRecipeFilter.java deleted file mode 100644 index b9f9c892..00000000 --- a/src/main/java/codechicken/nei/filter/AnySmartResultRecipeFilter.java +++ /dev/null @@ -1,27 +0,0 @@ -package codechicken.nei.filter; - -import java.util.List; - -import codechicken.nei.PositionedStack; -import codechicken.nei.api.IRecipeFilter; -import codechicken.nei.api.ItemFilter; -import codechicken.nei.recipe.IRecipeHandler; - -public class AnySmartResultRecipeFilter extends AnyRecipeFilter implements IRecipeFilter { - - public AnySmartResultRecipeFilter(ItemFilter filter) { - super(filter); - } - - @Override - public boolean matches(IRecipeHandler handler, List ingredients, PositionedStack result, - List others) { - if (result != null) { - return anyMatch(result); - } else { - // GT recipes use others as a result - return anyMatch(others); - } - } - -} diff --git a/src/main/java/codechicken/nei/filter/DoubleItemFilter.java b/src/main/java/codechicken/nei/filter/DoubleItemFilter.java deleted file mode 100644 index 28862cde..00000000 --- a/src/main/java/codechicken/nei/filter/DoubleItemFilter.java +++ /dev/null @@ -1,25 +0,0 @@ -package codechicken.nei.filter; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.SearchField; -import codechicken.nei.api.ItemFilter; - -public class DoubleItemFilter implements ItemFilter { - - private final ItemFilter filter; - private final PatternItemFilter patternFilter; - - public DoubleItemFilter(ItemFilter filter, String itemFilter) { - this.filter = filter; - this.patternFilter = new PatternItemFilter(SearchField.getPattern(itemFilter)); - } - - @Override - public boolean matches(ItemStack item) { - if (filter.matches(item)) { - return patternFilter.matches(item); - } - return false; - } -} diff --git a/src/main/java/codechicken/nei/filter/EverythingItemFilter.java b/src/main/java/codechicken/nei/filter/EverythingItemFilter.java deleted file mode 100644 index 23310873..00000000 --- a/src/main/java/codechicken/nei/filter/EverythingItemFilter.java +++ /dev/null @@ -1,13 +0,0 @@ -package codechicken.nei.filter; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.api.ItemFilter; - -public class EverythingItemFilter implements ItemFilter { - - @Override - public boolean matches(ItemStack item) { - return true; - } -} diff --git a/src/main/java/codechicken/nei/filter/NegatedItemFilter.java b/src/main/java/codechicken/nei/filter/NegatedItemFilter.java deleted file mode 100644 index 5ab036c4..00000000 --- a/src/main/java/codechicken/nei/filter/NegatedItemFilter.java +++ /dev/null @@ -1,19 +0,0 @@ -package codechicken.nei.filter; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.api.ItemFilter; - -public class NegatedItemFilter implements ItemFilter { - - private final ItemFilter filter; - - public NegatedItemFilter(ItemFilter filter) { - this.filter = filter; - } - - @Override - public boolean matches(ItemStack item) { - return this.filter == null || !this.filter.matches(item); - } -} diff --git a/src/main/java/codechicken/nei/filter/NothingItemFilter.java b/src/main/java/codechicken/nei/filter/NothingItemFilter.java deleted file mode 100644 index 77372730..00000000 --- a/src/main/java/codechicken/nei/filter/NothingItemFilter.java +++ /dev/null @@ -1,13 +0,0 @@ -package codechicken.nei.filter; - -import net.minecraft.item.ItemStack; - -import codechicken.nei.api.ItemFilter; - -public class NothingItemFilter implements ItemFilter { - - @Override - public boolean matches(ItemStack item) { - return false; - } -} diff --git a/src/main/java/codechicken/nei/filter/PatternItemFilter.java b/src/main/java/codechicken/nei/filter/PatternItemFilter.java deleted file mode 100644 index 20f3c1fc..00000000 --- a/src/main/java/codechicken/nei/filter/PatternItemFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package codechicken.nei.filter; - -import java.util.regex.Pattern; - -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; - -import codechicken.nei.api.ItemFilter; - -public class PatternItemFilter implements ItemFilter { - - public final Pattern pattern; - - public PatternItemFilter(Pattern pattern) { - this.pattern = pattern; - } - - @Override - public boolean matches(ItemStack item) { - String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName()); - - if (displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find()) { - return true; - } - - if (item.hasDisplayName()) { - displayName = EnumChatFormatting - .getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item)); - - return displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find(); - } - - return false; - } -} diff --git a/src/main/java/codechicken/nei/filter/RecipeFilter.java b/src/main/java/codechicken/nei/filter/RecipeFilter.java new file mode 100644 index 00000000..5e7af833 --- /dev/null +++ b/src/main/java/codechicken/nei/filter/RecipeFilter.java @@ -0,0 +1,89 @@ +package codechicken.nei.filter; + +import java.util.List; + +import net.minecraft.item.ItemStack; + +import codechicken.nei.PositionedStack; +import codechicken.nei.api.IRecipeFilter; +import codechicken.nei.api.ItemFilter; +import codechicken.nei.recipe.IRecipeHandler; + +public class RecipeFilter implements IRecipeFilter { + + public enum FilterContext { + + ANY, + INPUT, + OUTPUT; + + public static FilterContext fromChar(char c) { + switch (c) { + case '<': + return INPUT; + case '>': + return OUTPUT; + default: + return ANY; + } + } + } + + private final ItemFilter filter; + private final FilterContext context; + private final boolean anyMatch; + + public RecipeFilter(FilterContext context, boolean anyMatch, ItemFilter filter) { + this.context = context; + this.anyMatch = anyMatch; + this.filter = filter; + } + + @Override + public boolean matches(IRecipeHandler handler, int recipeIndex) { + + if ((this.context == FilterContext.ANY || this.context == FilterContext.INPUT) + && matchPositionedStack(handler.getIngredientStacks(recipeIndex), this.anyMatch)) { + return this.anyMatch; + } + + if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) { + final PositionedStack result = handler.getResultStack(recipeIndex); + + if (result != null && matchPositionedStack(result) == this.anyMatch) { + return this.anyMatch; + } + + if (matchPositionedStack(handler.getOtherStacks(recipeIndex), this.anyMatch)) { + return this.anyMatch; + } + + } + + return !this.anyMatch; + } + + private boolean matchPositionedStack(List items, boolean dir) { + + for (PositionedStack pStack : items) { + if (matchPositionedStack(pStack) == dir) { + return true; + } + } + + return false; + } + + private boolean matchPositionedStack(PositionedStack pStack) { + if (pStack == null) return false; + + for (ItemStack stack : pStack.items) { + if (this.filter.matches(stack)) { + return true; + } + } + + return false; + } + +} diff --git a/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java b/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java index d5078cc2..61f0298f 100644 --- a/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/SearchRecipeHandler.java @@ -37,12 +37,8 @@ class SearchRecipeHandler { } } - protected static boolean mathRecipe(IRecipeHandler handler, int recipe, IRecipeFilter filter) { - return filter.matches( - handler, - handler.getIngredientStacks(recipe), - handler.getResultStack(recipe), - handler.getOtherStacks(recipe)); + protected static boolean mathRecipe(IRecipeHandler handler, int recipeIndex, IRecipeFilter filter) { + return filter.matches(handler, recipeIndex); } public boolean searchingAvailable() { diff --git a/src/main/java/codechicken/nei/search/ItemFilterVisitor.java b/src/main/java/codechicken/nei/search/ItemFilterVisitor.java index ea6c82e8..2e7b0a47 100644 --- a/src/main/java/codechicken/nei/search/ItemFilterVisitor.java +++ b/src/main/java/codechicken/nei/search/ItemFilterVisitor.java @@ -9,13 +9,13 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.RuleNode; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.NegatedItemFilter; +import codechicken.nei.ItemList.NothingItemFilter; import codechicken.nei.NEIClientConfig; import codechicken.nei.SearchTokenParser; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AllMultiItemFilter; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.NegatedItemFilter; -import codechicken.nei.filter.NothingItemFilter; public class ItemFilterVisitor extends AbstractSearchExpressionVisitor { diff --git a/src/main/java/codechicken/nei/search/RecipeFilterVisitor.java b/src/main/java/codechicken/nei/search/RecipeFilterVisitor.java index e32d78a2..2841100b 100644 --- a/src/main/java/codechicken/nei/search/RecipeFilterVisitor.java +++ b/src/main/java/codechicken/nei/search/RecipeFilterVisitor.java @@ -4,18 +4,15 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Function; +import codechicken.nei.ItemList.EverythingItemFilter; import codechicken.nei.SearchTokenParser; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AllIngredientsRecipeFilter; import codechicken.nei.filter.AllMultiRecipeFilter; import codechicken.nei.filter.AllOthersRecipeFilter; -import codechicken.nei.filter.AllSmartResultRecipeFilter; -import codechicken.nei.filter.AnyIngredientsRecipeFilter; -import codechicken.nei.filter.AnyItemRecipeFilter; import codechicken.nei.filter.AnyOthersRecipeFilter; -import codechicken.nei.filter.AnySmartResultRecipeFilter; -import codechicken.nei.filter.EverythingItemFilter; +import codechicken.nei.filter.RecipeFilter; +import codechicken.nei.filter.RecipeFilter.FilterContext; public class RecipeFilterVisitor extends AbstractSearchExpressionVisitor { @@ -40,7 +37,7 @@ public class RecipeFilterVisitor extends AbstractSearchExpressionVisitor filters = new ArrayList<>(); + + for (ISearchParserProvider _provider : parser.getProviders()) { + if (_provider.getSearchMode() == SearchMode.ALWAYS) { + AllMultiItemFilter filter = generateFilters(_provider); + + if (!filter.filters.isEmpty()) { + filters.add(filter); + } + } + } + + return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters); + } else { + return generateFilters(provider); + } + } + + private AllMultiItemFilter generateFilters(ISearchParserProvider provider) { + final AllMultiItemFilter filters = new AllMultiItemFilter(); + + for (String work : this.words) { + final ItemFilter filter = provider.getFilter(work); + + if (filter != null) { + filters.filters.add(filter); + } + } + + return filters; + } +} diff --git a/src/main/java/codechicken/nei/util/ItemStackFilterParser.java b/src/main/java/codechicken/nei/util/ItemStackFilterParser.java index db12c8b1..cdb569ed 100644 --- a/src/main/java/codechicken/nei/util/ItemStackFilterParser.java +++ b/src/main/java/codechicken/nei/util/ItemStackFilterParser.java @@ -14,11 +14,11 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraftforge.oredict.OreDictionary; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.NegatedItemFilter; import codechicken.nei.NEIServerUtils; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AllMultiItemFilter; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.NegatedItemFilter; import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry; import cpw.mods.fml.common.registry.GameData; diff --git a/src/test/java/codechicken/nei/SearchTokenParserTest.java b/src/test/java/codechicken/nei/SearchTokenParserTest.java index 47dd1c7e..792cf92a 100644 --- a/src/test/java/codechicken/nei/SearchTokenParserTest.java +++ b/src/test/java/codechicken/nei/SearchTokenParserTest.java @@ -25,11 +25,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.FieldSource; import org.mockito.MockedStatic; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.ItemList.AnyMultiItemFilter; +import codechicken.nei.ItemList.PatternItemFilter; import codechicken.nei.SearchField.SearchParserProvider; import codechicken.nei.api.ItemFilter; -import codechicken.nei.filter.AllMultiItemFilter; -import codechicken.nei.filter.AnyMultiItemFilter; -import codechicken.nei.filter.PatternItemFilter; import codechicken.nei.search.IdentifierFilter; @DisplayName("Search expression")