package net.minecraft.core; import com.mojang.datafixers.util.Either; import java.util.Collection; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; import net.minecraft.core.component.DataComponentMap; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.tags.TagKey; import org.jspecify.annotations.Nullable; public sealed interface Holder permits Holder.Direct, Holder.Reference { T value(); boolean isBound(); boolean areComponentsBound(); boolean is(Identifier key); boolean is(ResourceKey key); boolean is(Predicate> predicate); boolean is(TagKey tag); @Deprecated boolean is(Holder holder); Stream> tags(); DataComponentMap components(); Either, T> unwrap(); Optional> unwrapKey(); Holder.Kind kind(); boolean canSerializeIn(HolderOwner registry); default String getRegisteredName() { return (String)this.unwrapKey().map(key -> key.identifier().toString()).orElse("[unregistered]"); } static Holder direct(final T value) { return new Holder.Direct<>(value, DataComponentMap.EMPTY); } static Holder direct(final T value, final DataComponentMap components) { return new Holder.Direct<>(value, components); } public record Direct(T value, DataComponentMap components) implements Holder { @Override public boolean isBound() { return true; } @Override public boolean areComponentsBound() { return true; } @Override public boolean is(final Identifier key) { return false; } @Override public boolean is(final ResourceKey key) { return false; } @Override public boolean is(final TagKey tag) { return false; } @Override public boolean is(final Holder holder) { return this.value.equals(holder.value()); } @Override public boolean is(final Predicate> predicate) { return false; } @Override public Either, T> unwrap() { return Either.right(this.value); } @Override public Optional> unwrapKey() { return Optional.empty(); } @Override public Holder.Kind kind() { return Holder.Kind.DIRECT; } public String toString() { return "Direct{" + this.value + "}"; } @Override public boolean canSerializeIn(final HolderOwner registry) { return true; } @Override public Stream> tags() { return Stream.of(); } } public static enum Kind { REFERENCE, DIRECT; } public static non-sealed class Reference implements Holder { private final HolderOwner owner; @Nullable private Set> tags; @Nullable private DataComponentMap components; private final Holder.Reference.Type type; @Nullable private ResourceKey key; @Nullable private T value; protected Reference(final Holder.Reference.Type type, final HolderOwner owner, @Nullable final ResourceKey key, @Nullable final T value) { this.owner = owner; this.type = type; this.key = key; this.value = value; } public static Holder.Reference createStandAlone(final HolderOwner owner, final ResourceKey key) { return new Holder.Reference<>(Holder.Reference.Type.STAND_ALONE, owner, key, null); } @Deprecated public static Holder.Reference createIntrusive(final HolderOwner owner, @Nullable final T value) { return new Holder.Reference<>(Holder.Reference.Type.INTRUSIVE, owner, null, value); } public ResourceKey key() { if (this.key == null) { throw new IllegalStateException("Trying to access unbound value '" + this.value + "' from registry " + this.owner); } else { return this.key; } } @Override public T value() { if (this.value == null) { throw new IllegalStateException("Trying to access unbound value '" + this.key + "' from registry " + this.owner); } else { return this.value; } } @Override public boolean is(final Identifier key) { return this.key().identifier().equals(key); } @Override public boolean is(final ResourceKey key) { return this.key() == key; } private Set> boundTags() { if (this.tags == null) { throw new IllegalStateException("Tags not bound"); } else { return this.tags; } } @Override public boolean is(final TagKey tag) { return this.boundTags().contains(tag); } @Override public boolean is(final Holder holder) { return holder.is(this.key()); } @Override public boolean is(final Predicate> predicate) { return predicate.test(this.key()); } @Override public boolean canSerializeIn(final HolderOwner context) { return this.owner.canSerializeIn(context); } @Override public Either, T> unwrap() { return Either.left(this.key()); } @Override public Optional> unwrapKey() { return Optional.of(this.key()); } @Override public Holder.Kind kind() { return Holder.Kind.REFERENCE; } @Override public boolean isBound() { return this.key != null && this.value != null; } @Override public boolean areComponentsBound() { return this.components != null; } void bindKey(final ResourceKey key) { if (this.key != null && key != this.key) { throw new IllegalStateException("Can't change holder key: existing=" + this.key + ", new=" + key); } else { this.key = key; } } protected void bindValue(final T value) { if (this.type == Holder.Reference.Type.INTRUSIVE && this.value != value) { throw new IllegalStateException("Can't change holder " + this.key + " value: existing=" + this.value + ", new=" + value); } else { this.value = value; } } void bindTags(final Collection> tags) { this.tags = Set.copyOf(tags); } public void bindComponents(final DataComponentMap components) { this.components = components; } @Override public Stream> tags() { return this.boundTags().stream(); } @Override public DataComponentMap components() { return (DataComponentMap)Objects.requireNonNull(this.components, "Components not bound yet"); } public String toString() { return "Reference{" + this.key + "=" + this.value + "}"; } protected static enum Type { STAND_ALONE, INTRUSIVE; } } }