diff options
| author | shedaniel <daniel@shedaniel.me> | 2022-07-27 23:25:27 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2022-08-26 10:52:27 +0900 |
| commit | 05069aa62b09f02a8cd6e526ec58a30347a56500 (patch) | |
| tree | acb90d01f0a06de7c6b540eefeeee8259016f8ac /runtime-engine/search/src | |
| parent | 685861c91bbb8a8a882da51381d392f1310d061d (diff) | |
| download | RoughlyEnoughItems-05069aa62b09f02a8cd6e526ec58a30347a56500.tar.gz RoughlyEnoughItems-05069aa62b09f02a8cd6e526ec58a30347a56500.tar.bz2 RoughlyEnoughItems-05069aa62b09f02a8cd6e526ec58a30347a56500.zip | |
WIP Module
Diffstat (limited to 'runtime-engine/search/src')
23 files changed, 2215 insertions, 0 deletions
diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java new file mode 100644 index 000000000..94c259b8c --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java @@ -0,0 +1,27 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search; + +public record IntRange(int min, int max) { +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java new file mode 100644 index 000000000..894153ddc --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java @@ -0,0 +1,129 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search; + +import com.google.common.base.Suppliers; +import me.shedaniel.rei.api.client.search.SearchFilter; +import me.shedaniel.rei.api.client.search.SearchProvider; +import me.shedaniel.rei.api.client.search.method.InputMethod; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.impl.client.search.argument.AlternativeArgument; +import me.shedaniel.rei.impl.client.search.argument.Argument; +import me.shedaniel.rei.impl.client.search.argument.CompoundArgument; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentType; +import me.shedaniel.rei.impl.client.util.CrashReportUtils; +import me.shedaniel.rei.impl.common.InternalLogger; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class SearchProviderImpl implements SearchProvider { + @Override + public void startReload() { + } + + @Override + public SearchFilter createFilter(String filter, InputMethod<?> inputMethod) { + return new SearchFilterImpl(filter, inputMethod); + } + + @Override + public void clearCache() { + Argument.SEARCH_CACHE.clear(); + } + + @Override + public boolean hasCache() { + return !Argument.SEARCH_CACHE.isEmpty(); + } + + public static class SearchFilterImpl implements SearchFilter { + private final String filter; + private final InputMethod<?> inputMethod; + private final Supplier<List<CompoundArgument>> arguments; + private final Supplier<List<ArgumentType<?, ?>>> argumentTypes; + + public SearchFilterImpl(String filter, InputMethod<?> inputMethod) { + this.filter = filter; + this.inputMethod = inputMethod; + this.arguments = Suppliers.memoize(() -> Argument.bakeArguments(filter)); + this.argumentTypes = Suppliers.memoize(() -> this.arguments.get().stream() + .flatMap(CompoundArgument::stream) + .flatMap(AlternativeArgument::stream) + .map(Argument::getArgument) + .distinct() + .collect(Collectors.toList())); + InternalLogger.getInstance().debug("Created search filter with %s using %s", filter, inputMethod.getName().getString()); + } + + @Override + public boolean test(EntryStack<?> stack) { + try { + return Argument.matches(stack, arguments.get(), inputMethod); + } catch (Throwable throwable) { + CrashReport report = CrashReportUtils.essential(throwable, "Testing entry with search filter"); + CrashReportCategory category = report.addCategory("Search entry details"); + try { + stack.fillCrashReport(report, category); + } catch (Throwable throwable1) { + category.setDetailError("Filling Report", throwable1); + } + throw CrashReportUtils.throwReport(report); + } + } + + @Override + public void prepareFilter(Collection<EntryStack<?>> stacks) { + Argument.prepareFilter(stacks, argumentTypes.get()); + } + + @Override + public void processDecoration(ParseDecorationSink sink) { + Argument.bakeArguments(filter, sink); + } + + @Override + public String getFilter() { + return filter; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SearchFilterImpl that = (SearchFilterImpl) o; + return Objects.equals(filter, that.filter); + } + + @Override + public int hashCode() { + return Objects.hash(filter); + } + } +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java new file mode 100644 index 000000000..01b169f6b --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java @@ -0,0 +1,74 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import com.google.common.collect.ForwardingList; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@ApiStatus.Internal +public class AlternativeArgument extends ForwardingList<Argument<?, ?>> { + static final AlternativeArgument EMPTY = new AlternativeArgument(Collections.emptyList()); + + private final List<Argument<?, ?>> arguments; + + public AlternativeArgument(List<Argument<?, ?>> arguments) { + this.arguments = arguments; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + protected List<Argument<?, ?>> delegate() { + return arguments; + } + + public static class Builder { + private List<Argument<?, ?>> arguments; + + public <T, R> Builder add(Argument<T, R> argument) { + if (arguments == null) { + this.arguments = new ArrayList<>(); + } + + arguments.add(argument); + return this; + } + + public boolean isEmpty() { + return arguments == null; + } + + public AlternativeArgument build() { + if (arguments == null) return AlternativeArgument.EMPTY; + if (arguments.size() == 1) return new AlternativeArgument(Collections.singletonList(arguments.get(0))); + return new AlternativeArgument(arguments); + } + } +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java new file mode 100644 index 000000000..8724af7d1 --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java @@ -0,0 +1,360 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; +import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.gui.config.SearchMode; +import me.shedaniel.rei.api.client.search.SearchFilter; +import me.shedaniel.rei.api.client.search.method.CharacterUnpackingInputMethod; +import me.shedaniel.rei.api.client.search.method.InputMethod; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.api.common.util.EntryStacks; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentType; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentTypesRegistry; +import me.shedaniel.rei.impl.client.search.result.ArgumentApplicableResult; +import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@ApiStatus.Internal +@Environment(EnvType.CLIENT) +public class Argument<T, R> { + public static final Short2ObjectMap<Long2ObjectMap<Object>> SEARCH_CACHE = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); + private static final Object NO_CACHE = new Object(); + private static final AtomicReference<String> lastLanguage = new AtomicReference<>(); + private ArgumentType<T, R> argumentType; + private String text; + private T filterData; + private boolean regular; + private final int start; + private final int end; + private static final Pattern SPLIT_PATTERN = Pattern.compile("(?:\"([^\"]*)\")|([^\\s]+)"); + + public Argument(ArgumentType<T, R> argumentType, String text, boolean regular, int start, int end, boolean lowercase) { + this.argumentType = argumentType; + this.text = lowercase ? text.toLowerCase(Locale.ROOT) : text; + this.regular = regular; + this.filterData = null; + this.start = start; + this.end = end; + } + + public int start() { + return start; + } + + public int end() { + return end; + } + + public static List<CompoundArgument> bakeArguments(String filter) { + return bakeArguments(filter, null); + } + + public static List<CompoundArgument> bakeArguments(String filter, @Nullable SearchFilter.ParseDecorationSink sink) { + List<CompoundArgument> compoundArguments = Lists.newArrayList(); + int tokenStartIndex = 0; + + for (String token : StringUtils.splitByWholeSeparatorPreserveAllTokens(filter, "|")) { + Matcher terms = SPLIT_PATTERN.matcher(token); + CompoundArgument.Builder builder = CompoundArgument.builder(); + while (terms.find()) { + AlternativeArgument.Builder alternativeBuilder = AlternativeArgument.builder(); + + for (ArgumentType<?, ?> type : ArgumentTypesRegistry.ARGUMENT_TYPE_LIST) { + applyArgument(type, filter, terms, tokenStartIndex, alternativeBuilder, true, sink); + if (!alternativeBuilder.isEmpty()) { + break; + } + } + + if (alternativeBuilder.isEmpty()) { + for (ArgumentType<?, ?> type : ArgumentTypesRegistry.ARGUMENT_TYPE_LIST) { + applyArgument(type, filter, terms, tokenStartIndex, alternativeBuilder, false, sink); + } + } + + builder.add(alternativeBuilder); + } + compoundArguments.add(builder.build()); + tokenStartIndex += 1 + token.length(); + if (sink != null && tokenStartIndex - 1 < filter.length()) { + sink.addSplitter(tokenStartIndex - 1); + } + } + prepareSearchFilter(compoundArguments); + return compoundArguments; + } + + private static void prepareSearchFilter(List<CompoundArgument> compoundArguments) { + for (CompoundArgument arguments : compoundArguments) { + for (AlternativeArgument alternativeArgument : arguments) { + for (Argument<?, ?> argument : alternativeArgument) { + //noinspection RedundantCast + ((Argument<Object, Object>) argument).filterData = argument.argumentType.prepareSearchFilter(argument.getText()); + } + } + } + } + + private static void applyArgument(ArgumentType<?, ?> type, String filter, Matcher terms, int tokenStartIndex, AlternativeArgument.Builder alternativeBuilder, boolean forceGrammar, @Nullable SearchFilter.ParseDecorationSink sink) { + String term = MoreObjects.firstNonNull(terms.group(1), terms.group(2)); + if (type.getSearchMode() == SearchMode.NEVER) return; + ArgumentApplicableResult result = type.checkApplicable(term, forceGrammar); + + if (result.isApplicable()) { + int group = terms.group(1) != null ? 1 : 2; + Argument<?, ?> argument = new Argument<>(type, result.getText(), !result.isInverted(), + terms.start(group) + tokenStartIndex, terms.end(group) + tokenStartIndex, !result.shouldPreserveCasing()); + alternativeBuilder.add(argument); + if (sink != null) { + if (group == 1) { + sink.addQuote(terms.start() + tokenStartIndex); + if (terms.end() - 1 + tokenStartIndex < filter.length()) { + sink.addQuote(terms.end() - 1 + tokenStartIndex); + } + } + sink.addPart(IntIntPair.of(argument.start(), argument.end()), argument.argumentType.getHighlightedStyle(), result.isUsingGrammar(), + CollectionUtils.map(result.grammarRanges(), range -> IntIntPair.of(range.min(), range.max())), terms.start() + tokenStartIndex); + } + } + } + + public static boolean matches(EntryStack<?> stack, List<CompoundArgument> compoundArguments, InputMethod<?> inputMethod) { + if (compoundArguments.isEmpty()) return true; + String newLanguage = Minecraft.getInstance().options.languageCode; + if (!Objects.equals(lastLanguage.getAndSet(newLanguage), newLanguage)) { + SEARCH_CACHE.clear(); + } + + a: + for (CompoundArgument arguments : compoundArguments) { + for (AlternativeArgument argument : arguments) { + if (!matches(stack, argument, inputMethod)) { + continue a; + } + } + + return true; + } + + return false; + } + + private static <T> boolean matches(EntryStack<?> stack, AlternativeArgument alternativeArgument, InputMethod<T> inputMethod) { + if (alternativeArgument.isEmpty()) return true; + long hashExact = EntryStacks.hashExact(stack); + ResultSinkImpl<T> sink = new ResultSinkImpl<>(inputMethod); + + for (Argument<?, ?> argument : alternativeArgument) { + sink.filters = inputMethod.expendFilter(argument.getText()); + if (matches(argument.getArgument(), stack, hashExact, argument.filterData, sink) == argument.isRegular()) { + return true; + } + } + + return false; + } + + private static Long2ObjectMap<Object> getSearchCache(ArgumentType<?, ?> argumentType) { + short argumentIndex = (short) argumentType.getIndex(); + Long2ObjectMap<Object> map = SEARCH_CACHE.get(argumentIndex); + if (map == null) { + SEARCH_CACHE.put(argumentIndex, map = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>())); + } + return map; + } + + private static <T, R, B> boolean matches(ArgumentType<T, B> argumentType, EntryStack<?> stack, long hashExact, R filterData, ResultSinkImpl<?> sink) { + Long2ObjectMap<Object> map = getSearchCache(argumentType); + Object value = map.get(hashExact); + if (value == null) { + value = argumentType.cacheData(stack); + map.put(hashExact, value == null ? NO_CACHE : value); + } + sink.matches = false; + argumentType.matches(value == NO_CACHE ? null : (B) value, stack, (T) filterData, sink); + return sink.matches; + } + + private static class ResultSinkImpl<T> implements ArgumentType.ResultSink { + private final InputMethod<T> inputMethod; + private boolean matches; + private Iterable<T> filters; + + public ResultSinkImpl(InputMethod<T> inputMethod) { + this.inputMethod = inputMethod; + } + + @Override + public boolean testTrue() { + return matches = true; + } + + @Override + public boolean testString(String text) { + if (matches) return true; + if (inputMethod instanceof CharacterUnpackingInputMethod im) { + for (T filter : filters) { + if (InputMethodMatcher.contains(im, IntList.of(text.codePoints().toArray()), (IntList) filter)) { + return matches = true; + } + } + } else { + for (T filter : filters) { + if (inputMethod.contains(text, filter)) { + return matches = true; + } + } + } + return false; + } + } + + public static Long prepareStart = null; + public static Collection<EntryStack<?>> prepareStacks = null; + public static IntIntPair prepareStage = null; + public static IntIntPair[] currentStages = null; + + public static void prepareFilter(Collection<EntryStack<?>> stacks, Collection<ArgumentType<?, ?>> argumentTypes) { + if (prepareStage != null || currentStages != null) return; + try { + prepareStart = Util.getEpochMillis(); + prepareStacks = stacks; + prepareStage = new IntIntMutablePair(0, argumentTypes.size()); + currentStages = new IntIntPair[argumentTypes.size()]; + List<HashedEntryStackWrapper> hashedStacks = CollectionUtils.map(stacks, HashedEntryStackWrapper::new); + int searchPartitionSize = ConfigObject.getInstance().getAsyncSearchPartitionSize(); + boolean async = ConfigObject.getInstance().shouldAsyncSearch() && stacks.size() > searchPartitionSize * 4; + List<CompletableFuture<Long2ObjectMap<Object>>> futures = Lists.newArrayList(); + List<Pair<ArgumentType<?, ?>, CompletableFuture<Long2ObjectMap<Object>>>> pairs = Lists.newArrayList(); + + for (ArgumentType<?, ?> argumentType : argumentTypes) { + prepareStage.first(prepareStage.firstInt() + 1); + Long2ObjectMap<Object> map = getSearchCache(argumentType); + IntIntPair currentStage = currentStages[prepareStage.firstInt() - 1] = new IntIntMutablePair(0, hashedStacks.size()); + + if (async) { + for (Collection<HashedEntryStackWrapper> partitionStacks : CollectionUtils.partition(hashedStacks, searchPartitionSize)) { + CompletableFuture<Long2ObjectMap<Object>> future = CompletableFuture.supplyAsync(() -> { + Long2ObjectMap<Object> out = new Long2ObjectArrayMap<>(searchPartitionSize + 1); + for (HashedEntryStackWrapper stack : partitionStacks) { + if (map.get(stack.hashExact()) == null) { + Object data = argumentType.cacheData(stack.unwrap()); + + if (data != null) { + out.put(stack.hashExact(), data); + } + } + } + return out; + }).whenComplete((objectLong2ObjectMap, throwable) -> { + currentStage.first(currentStage.firstInt() + partitionStacks.size()); + }); + futures.add(future); + pairs.add(Pair.of(argumentType, future)); + } + } else { + for (HashedEntryStackWrapper stack : hashedStacks) { + currentStage.first(currentStage.firstInt() + 1); + + if (map.get(stack.hashExact()) == null) { + Object data = argumentType.cacheData(stack.unwrap()); + + if (data != null) { + map.put(stack.hashExact(), data); + } + } + } + } + } + + if (async) { + try { + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(30, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + for (Pair<ArgumentType<?, ?>, CompletableFuture<Long2ObjectMap<Object>>> pair : pairs) { + Long2ObjectMap<Object> now = pair.second().getNow(null); + if (now != null) getSearchCache(pair.left()).putAll(now); + } + } + } finally { + prepareStart = null; + prepareStacks = null; + prepareStage = null; + currentStages = null; + } + } + + public ArgumentType<?, ?> getArgument() { + return argumentType; + } + + public String getText() { + return text; + } + + public boolean isRegular() { + return regular; + } + + @Override + public String |
