2424
2525import org .checkerframework .checker .nullness .qual .Nullable ;
2626
27- public final class Key {
28-
29- private static final String MINECRAFT_NAMESPACE = "minecraft" ;
30- private static final int MINECRAFT_NAMESPACE_LENGTH = MINECRAFT_NAMESPACE . length ();
31- private final String original ;
32- private final String namespace ;
33- private final String path ;
27+ /**
28+ * Represents a Minecraft Identifier/ResourceLocation.
29+ * <p>
30+ * Internally split into {@link CustomKey} and {@link MinecraftKey} to reduce data held
31+ * and keep the runtime/allocation cost of {@link # original()} and {@link #toString()} minimal.
32+ */
33+ public abstract sealed class Key permits MinecraftKey , CustomKey {
3434
35- private Key (final String original , final String namespace , final String path ) {
36- if (!isValidNamespace (namespace )) {
37- throw new IllegalArgumentException ("Invalid namespace: " + namespace );
38- }
39- if (!isValidPath (path )) {
40- throw new IllegalArgumentException ("Invalid path: " + path );
41- }
42- this .original = original ; // assume this is correct; also saves whether the namespace was explitly set
43- this .namespace = namespace ;
44- this .path = path ;
45- }
35+ static final String MINECRAFT_NAMESPACE = "minecraft" ;
4636
4737 /**
4838 * Creates a new key with the given namespace and path.
@@ -52,7 +42,10 @@ private Key(final String original, final String namespace, final String path) {
5242 * @return a new key with the given namespace and path
5343 */
5444 public static Key of (final String namespace , final String path ) {
55- return new Key (namespace + ':' + path , namespace , path );
45+ if (namespace .equals (MINECRAFT_NAMESPACE ) || namespace .isEmpty ()) {
46+ return ofPath (path ); // ends up compacting it
47+ }
48+ return new CustomKey (namespace + ':' + path , namespace , path );
5649 }
5750
5851 /**
@@ -62,7 +55,7 @@ public static Key of(final String namespace, final String path) {
6255 * @return a new key with the given path and the default namespace
6356 */
6457 public static Key ofPath (final String path ) {
65- return new Key ( path , MINECRAFT_NAMESPACE , path );
58+ return new MinecraftKey . CompactMinecraftKey ( path );
6659 }
6760
6861 /**
@@ -77,9 +70,17 @@ public static Key of(final String identifier) {
7770 return ofPath (identifier );
7871 }
7972
80- final String namespace = separatorIndex == 0 ? MINECRAFT_NAMESPACE : identifier .substring (0 , separatorIndex );
8173 final String path = identifier .substring (separatorIndex + 1 );
82- return new Key (identifier , namespace , path );
74+ if (separatorIndex == 0 ) {
75+ return new MinecraftKey .ColonPrefixedMinecraftKey (identifier , path );
76+ }
77+
78+ final String namespace = identifier .substring (0 , separatorIndex );
79+ if (namespace .equals (MINECRAFT_NAMESPACE )) {
80+ return new MinecraftKey .FullMinecraftKey (identifier , path );
81+ }
82+
83+ return new CustomKey (identifier , namespace , path );
8384 }
8485
8586 /**
@@ -96,6 +97,38 @@ public static Key of(final String identifier) {
9697 }
9798 }
9899
100+ public abstract String namespace ();
101+
102+ public abstract String path ();
103+
104+ /**
105+ * Returns the unmodified original identifier, possbily without an explicit namespace.
106+ *
107+ * @return the original identifier, possibly without an explicit namespace
108+ */
109+ public abstract String original ();
110+
111+ /**
112+ * Returns the identifier in a minimized form. If the namespace is "minecraft", it will return just the path.
113+ *
114+ * @return the identifier in a minimized form
115+ */
116+ public abstract String minimized ();
117+
118+ public abstract boolean hasMinecraftNamespace ();
119+
120+ public Key withNamespace (final String namespace ) {
121+ return of (namespace , this .path ());
122+ }
123+
124+ public Key withPath (final String path ) {
125+ return of (this .namespace (), path );
126+ }
127+
128+ public final boolean equals (final String identifier ) {
129+ return this .equals (Key .of (identifier ));
130+ }
131+
99132 public static String stripNamespace (final String identifier ) {
100133 int index = identifier .indexOf (':' );
101134 if (index == -1 ) {
@@ -106,17 +139,15 @@ public static String stripNamespace(final String identifier) {
106139
107140 public static String namespace (final String identifier ) {
108141 final int index = identifier .indexOf (':' );
109- if (index == -1 ) {
110- return MINECRAFT_NAMESPACE ;
111- } else if (index == 0 ) {
142+ if (index == -1 || index == 0 ) {
112143 return MINECRAFT_NAMESPACE ;
113144 }
114145 return identifier .substring (0 , index );
115146 }
116147
117148 public static String stripMinecraftNamespace (final String identifier ) {
118149 if (identifier .startsWith ("minecraft:" )) {
119- return identifier .substring (MINECRAFT_NAMESPACE_LENGTH + 1 );
150+ return identifier .substring (MinecraftKey . MINECRAFT_NAMESPACE_LENGTH + 1 );
120151 } else if (!identifier .isEmpty () && identifier .charAt (0 ) == ':' ) {
121152 return identifier .substring (1 );
122153 }
@@ -140,98 +171,13 @@ public static String namespaced(final String identifier) {
140171 public static boolean isValid (final String identifier ) {
141172 final int separatorIndex = identifier .indexOf (':' );
142173 if (separatorIndex == -1 ) {
143- return isValidPath (identifier );
174+ return MinecraftKey . isValidPath (identifier );
144175 } else if (separatorIndex == 0 ) {
145- return isValidPath (identifier .substring (1 ));
176+ return MinecraftKey . isValidPath (identifier .substring (1 ));
146177 }
147178
148179 final String namespace = identifier .substring (0 , separatorIndex );
149180 final String path = identifier .substring (separatorIndex + 1 );
150- return isValidNamespace (namespace ) && isValidPath (path );
151- }
152-
153- public String namespace () {
154- return this .namespace ;
155- }
156-
157- public String path () {
158- return this .path ;
159- }
160-
161- /**
162- * Returns the unmodified original identifier, possbily without an explicit namespace.
163- *
164- * @return the original identifier, possibly without an explicit namespace
165- */
166- public String original () {
167- return this .original ;
168- }
169-
170- /**
171- * Returns the identifier in a minimized form. If the namespace is "minecraft", it will return just the path.
172- *
173- * @return the identifier in a minimized form
174- */
175- public String minimized () {
176- return this .hasMinecraftNamespace () ? this .path : this .toString ();
177- }
178-
179- public boolean hasMinecraftNamespace () {
180- return this .namespace .equals (MINECRAFT_NAMESPACE );
181- }
182-
183- public Key withNamespace (final String namespace ) {
184- return of (namespace , this .path );
185- }
186-
187- public Key withPath (final String path ) {
188- return of (this .namespace , path );
189- }
190-
191- @ Override
192- public final boolean equals (final Object o ) {
193- if (this == o ) return true ;
194- if (!(o instanceof final Key key )) return false ;
195- return this .namespace .equals (key .namespace ) && this .path .equals (key .path );
196- }
197-
198- public final boolean equals (final String identifier ) {
199- return this .equals (Key .of (identifier ));
200- }
201-
202- @ Override
203- public int hashCode () {
204- int result = this .namespace .hashCode ();
205- result = 31 * result + this .path .hashCode ();
206- return result ;
207- }
208-
209- @ Override
210- public String toString () {
211- return this .namespace + ':' + this .path ;
212- }
213-
214- private static boolean isValidNamespace (final String namespace ) {
215- //noinspection StringEquality - a quick way out
216- if (namespace == MINECRAFT_NAMESPACE ) {
217- return true ;
218- }
219- for (int i = 0 , length = namespace .length (); i < length ; i ++) {
220- final char c = namespace .charAt (i );
221- if (!(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.' )) {
222- return false ;
223- }
224- }
225- return true ;
226- }
227-
228- private static boolean isValidPath (final String path ) {
229- for (int i = 0 , length = path .length (); i < length ; i ++) {
230- final char c = path .charAt (i );
231- if (!(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.' || c == '/' )) {
232- return false ;
233- }
234- }
235- return true ;
181+ return MinecraftKey .isValidNamespace (namespace ) && MinecraftKey .isValidPath (path );
236182 }
237183}
0 commit comments