package net.minecraft.resources;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import io.netty.buffer.ByteBuf;
import java.util.function.UnaryOperator;
import net.minecraft.ResourceLocationException;
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import org.jetbrains.annotations.Nullable;
/**
* An immutable location of a resource, in terms of a path and namespace.
*
* This is used as an identifier for a resource, usually for those housed in a {@link net.minecraft.core.Registry}, such as blocks and items.
*
* {@code minecraft} is always taken as the default namespace for a resource location when none is explicitly stated. When using this for registering objects, this namespace should only be used for resources added by Minecraft itself.
*
* Generally, and by the implementation of {@link #toString()}, the string representation of this class is expressed in the form {@code namespace:path}. The colon is also used as the default separator for parsing strings as a {@code ResourceLocation}.
* @see net.minecraft.resources.ResourceKey
*/
public final class ResourceLocation implements Comparable {
public static final Codec CODEC = Codec.STRING.comapFlatMap(ResourceLocation::read, ResourceLocation::toString).stable();
public static final StreamCodec STREAM_CODEC = ByteBufCodecs.STRING_UTF8.map(ResourceLocation::parse, ResourceLocation::toString);
public static final SimpleCommandExceptionType ERROR_INVALID = new SimpleCommandExceptionType(Component.translatable("argument.id.invalid"));
public static final char NAMESPACE_SEPARATOR = ':';
public static final String DEFAULT_NAMESPACE = "minecraft";
public static final String REALMS_NAMESPACE = "realms";
private final String namespace;
private final String path;
private ResourceLocation(String namespace, String path) {
assert isValidNamespace(namespace);
assert isValidPath(path);
this.namespace = namespace;
this.path = path;
}
private static ResourceLocation createUntrusted(String namespace, String path) {
return new ResourceLocation(assertValidNamespace(namespace, path), assertValidPath(namespace, path));
}
public static ResourceLocation fromNamespaceAndPath(String namespace, String path) {
return createUntrusted(namespace, path);
}
public static ResourceLocation parse(String location) {
return bySeparator(location, ':');
}
public static ResourceLocation withDefaultNamespace(String location) {
return new ResourceLocation("minecraft", assertValidPath("minecraft", location));
}
/**
* Attempts to parse the specified {@code location} as a {@code ResourceLocation} by splitting it into a
* namespace and path by a colon.
*
* If no colon is present in the {@code location}, the namespace defaults to {@code minecraft}, taking the {@code location} as the path.
* @return the parsed resource location; otherwise {@code null} if there is a non {@code [a-z0-9_.-]} character in the decomposed namespace or a non {@code [a-z0-9/._-]} character in the decomposed path
* @see #of(String, char)
*
* @param location the location string to try to parse as a {@code ResourceLocation}
*/
@Nullable
public static ResourceLocation tryParse(String location) {
return tryBySeparator(location, ':');
}
@Nullable
public static ResourceLocation tryBuild(String namespace, String path) {
return isValidNamespace(namespace) && isValidPath(path) ? new ResourceLocation(namespace, path) : null;
}
public static ResourceLocation bySeparator(String location, char seperator) {
int i = location.indexOf(seperator);
if (i >= 0) {
String string = location.substring(i + 1);
if (i != 0) {
String string2 = location.substring(0, i);
return createUntrusted(string2, string);
} else {
return withDefaultNamespace(string);
}
} else {
return withDefaultNamespace(location);
}
}
@Nullable
public static ResourceLocation tryBySeparator(String location, char seperator) {
int i = location.indexOf(seperator);
if (i >= 0) {
String string = location.substring(i + 1);
if (!isValidPath(string)) {
return null;
} else if (i != 0) {
String string2 = location.substring(0, i);
return isValidNamespace(string2) ? new ResourceLocation(string2, string) : null;
} else {
return new ResourceLocation("minecraft", string);
}
} else {
return isValidPath(location) ? new ResourceLocation("minecraft", location) : null;
}
}
public static DataResult read(String location) {
try {
return DataResult.success(parse(location));
} catch (ResourceLocationException var2) {
return DataResult.error(() -> "Not a valid resource location: " + location + " " + var2.getMessage());
}
}
public String getPath() {
return this.path;
}
public String getNamespace() {
return this.namespace;
}
public ResourceLocation withPath(String path) {
return new ResourceLocation(this.namespace, assertValidPath(this.namespace, path));
}
public ResourceLocation withPath(UnaryOperator pathOperator) {
return this.withPath((String)pathOperator.apply(this.path));
}
public ResourceLocation withPrefix(String pathPrefix) {
return this.withPath(pathPrefix + this.path);
}
public ResourceLocation withSuffix(String pathSuffix) {
return this.withPath(this.path + pathSuffix);
}
public String toString() {
return this.namespace + ":" + this.path;
}
public boolean equals(Object object) {
if (this == object) {
return true;
} else {
return !(object instanceof ResourceLocation resourceLocation)
? false
: this.namespace.equals(resourceLocation.namespace) && this.path.equals(resourceLocation.path);
}
}
public int hashCode() {
return 31 * this.namespace.hashCode() + this.path.hashCode();
}
public int compareTo(ResourceLocation other) {
int i = this.path.compareTo(other.path);
if (i == 0) {
i = this.namespace.compareTo(other.namespace);
}
return i;
}
public String toDebugFileName() {
return this.toString().replace('/', '_').replace(':', '_');
}
public String toLanguageKey() {
return this.namespace + "." + this.path;
}
public String toShortLanguageKey() {
return this.namespace.equals("minecraft") ? this.path : this.toLanguageKey();
}
public String toLanguageKey(String type) {
return type + "." + this.toLanguageKey();
}
public String toLanguageKey(String type, String key) {
return type + "." + this.toLanguageKey() + "." + key;
}
private static String readGreedy(StringReader reader) {
int i = reader.getCursor();
while (reader.canRead() && isAllowedInResourceLocation(reader.peek())) {
reader.skip();
}
return reader.getString().substring(i, reader.getCursor());
}
public static ResourceLocation read(StringReader reader) throws CommandSyntaxException {
int i = reader.getCursor();
String string = readGreedy(reader);
try {
return parse(string);
} catch (ResourceLocationException var4) {
reader.setCursor(i);
throw ERROR_INVALID.createWithContext(reader);
}
}
public static ResourceLocation readNonEmpty(StringReader reader) throws CommandSyntaxException {
int i = reader.getCursor();
String string = readGreedy(reader);
if (string.isEmpty()) {
throw ERROR_INVALID.createWithContext(reader);
} else {
try {
return parse(string);
} catch (ResourceLocationException var4) {
reader.setCursor(i);
throw ERROR_INVALID.createWithContext(reader);
}
}
}
public static boolean isAllowedInResourceLocation(char character) {
return character >= '0' && character <= '9'
|| character >= 'a' && character <= 'z'
|| character == '_'
|| character == ':'
|| character == '/'
|| character == '.'
|| character == '-';
}
/**
* @return {@code true} if the specified {@code path} is valid: consists only of {@code [a-z0-9/._-]} characters
*/
public static boolean isValidPath(String path) {
for (int i = 0; i < path.length(); i++) {
if (!validPathChar(path.charAt(i))) {
return false;
}
}
return true;
}
/**
* @return {@code true} if the specified {@code namespace} is valid: consists only of {@code [a-z0-9_.-]} characters
*/
public static boolean isValidNamespace(String namespace) {
for (int i = 0; i < namespace.length(); i++) {
if (!validNamespaceChar(namespace.charAt(i))) {
return false;
}
}
return true;
}
private static String assertValidNamespace(String namespace, String path) {
if (!isValidNamespace(namespace)) {
throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + namespace + ":" + path);
} else {
return namespace;
}
}
public static boolean validPathChar(char pathChar) {
return pathChar == '_' || pathChar == '-' || pathChar >= 'a' && pathChar <= 'z' || pathChar >= '0' && pathChar <= '9' || pathChar == '/' || pathChar == '.';
}
private static boolean validNamespaceChar(char namespaceChar) {
return namespaceChar == '_'
|| namespaceChar == '-'
|| namespaceChar >= 'a' && namespaceChar <= 'z'
|| namespaceChar >= '0' && namespaceChar <= '9'
|| namespaceChar == '.';
}
private static String assertValidPath(String namespace, String path) {
if (!isValidPath(path)) {
throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + path);
} else {
return path;
}
}
}