package net.minecraft.core; import com.mojang.datafixers.util.Either; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.Spliterator; import java.util.function.Function; import java.util.stream.Stream; import net.minecraft.Util; import net.minecraft.tags.TagKey; import net.minecraft.util.RandomSource; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.VisibleForTesting; public interface HolderSet extends Iterable> { Stream> stream(); int size(); boolean isBound(); Either, List>> unwrap(); Optional> getRandomElement(RandomSource random); Holder get(int index); boolean contains(Holder holder); boolean canSerializeIn(HolderOwner owner); Optional> unwrapKey(); @Deprecated @VisibleForTesting static HolderSet.Named emptyNamed(HolderOwner owner, TagKey key) { return new HolderSet.Named(owner, key) { @Override protected List> contents() { throw new UnsupportedOperationException("Tag " + this.key() + " can't be dereferenced during construction"); } }; } static HolderSet empty() { return (HolderSet)HolderSet.Direct.EMPTY; } @SafeVarargs static HolderSet.Direct direct(Holder... contents) { return new HolderSet.Direct<>(List.of(contents)); } static HolderSet.Direct direct(List> contents) { return new HolderSet.Direct<>(List.copyOf(contents)); } @SafeVarargs static HolderSet.Direct direct(Function> holderFactory, E... values) { return direct(Stream.of(values).map(holderFactory).toList()); } static HolderSet.Direct direct(Function> holderFactory, Collection values) { return direct(values.stream().map(holderFactory).toList()); } public static final class Direct extends HolderSet.ListBacked { static final HolderSet.Direct EMPTY = new HolderSet.Direct(List.of()); private final List> contents; @Nullable private Set> contentsSet; Direct(List> contents) { this.contents = contents; } @Override protected List> contents() { return this.contents; } @Override public boolean isBound() { return true; } @Override public Either, List>> unwrap() { return Either.right(this.contents); } @Override public Optional> unwrapKey() { return Optional.empty(); } @Override public boolean contains(Holder holder) { if (this.contentsSet == null) { this.contentsSet = Set.copyOf(this.contents); } return this.contentsSet.contains(holder); } public String toString() { return "DirectSet[" + this.contents + "]"; } public boolean equals(Object object) { return this == object ? true : object instanceof HolderSet.Direct direct && this.contents.equals(direct.contents); } public int hashCode() { return this.contents.hashCode(); } } public abstract static class ListBacked implements HolderSet { protected abstract List> contents(); @Override public int size() { return this.contents().size(); } public Spliterator> spliterator() { return this.contents().spliterator(); } public Iterator> iterator() { return this.contents().iterator(); } @Override public Stream> stream() { return this.contents().stream(); } @Override public Optional> getRandomElement(RandomSource random) { return Util.getRandomSafe(this.contents(), random); } @Override public Holder get(int index) { return (Holder)this.contents().get(index); } @Override public boolean canSerializeIn(HolderOwner owner) { return true; } } public static class Named extends HolderSet.ListBacked { private final HolderOwner owner; private final TagKey key; @Nullable private List> contents; Named(HolderOwner owner, TagKey key) { this.owner = owner; this.key = key; } void bind(List> contents) { this.contents = List.copyOf(contents); } public TagKey key() { return this.key; } @Override protected List> contents() { if (this.contents == null) { throw new IllegalStateException("Trying to access unbound tag '" + this.key + "' from registry " + this.owner); } else { return this.contents; } } @Override public boolean isBound() { return this.contents != null; } @Override public Either, List>> unwrap() { return Either.left(this.key); } @Override public Optional> unwrapKey() { return Optional.of(this.key); } @Override public boolean contains(Holder holder) { return holder.is(this.key); } public String toString() { return "NamedSet(" + this.key + ")[" + this.contents + "]"; } @Override public boolean canSerializeIn(HolderOwner owner) { return this.owner.canSerializeIn(owner); } } }