Skip to content

Commit 042979a

Browse files
committed
feat(key): Add KeyFormat
1 parent b52e823 commit 042979a

5 files changed

Lines changed: 479 additions & 28 deletions

File tree

key/src/main/java/net/kyori/adventure/key/Key.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,8 @@ static Key key(@KeyPattern final String string) {
100100
* @throws InvalidKeyException if the namespace or value contains an invalid character
101101
* @since 4.0.0
102102
*/
103-
@SuppressWarnings("PatternValidation") // impossible to validate since the character is variable
104103
static Key key(final String string, final char character) {
105-
Objects.requireNonNull(string, "string");
106-
final int index = string.indexOf(character);
107-
final String namespace = index >= 1 ? string.substring(0, index) : MINECRAFT_NAMESPACE;
108-
final String value = index >= 0 ? string.substring(index + 1) : string;
109-
return key(namespace, value);
104+
return KeyFormatImpl.parseKey(Objects.requireNonNull(string, "string"), character, MINECRAFT_NAMESPACE);
110105
}
111106

112107
/**
@@ -156,13 +151,7 @@ static Comparator<? super Key> comparator() {
156151
* @since 4.12.0
157152
*/
158153
static boolean parseable(final @Nullable String string) {
159-
if (string == null) {
160-
return false;
161-
}
162-
final int index = string.indexOf(DEFAULT_SEPARATOR);
163-
final String namespace = index >= 1 ? string.substring(0, index) : MINECRAFT_NAMESPACE;
164-
final String value = index >= 0 ? string.substring(index + 1) : string;
165-
return parseableNamespace(namespace) && parseableValue(value);
154+
return KeyFormat.minecraft().parseable(string);
166155
}
167156

168157
/**
@@ -268,7 +257,9 @@ static boolean allowedInValue(final char character) {
268257
* @return the string representation
269258
* @since 4.0.0
270259
*/
271-
String asString();
260+
default String asString() {
261+
return KeyFormat.minecraft().asString(this);
262+
}
272263

273264
/**
274265
* Returns the string representation of this key in minimal form.
@@ -279,10 +270,7 @@ static boolean allowedInValue(final char character) {
279270
* @since 4.15.0
280271
*/
281272
default String asMinimalString() {
282-
if (this.namespace().equals(MINECRAFT_NAMESPACE)) {
283-
return this.value();
284-
}
285-
return this.asString();
273+
return KeyFormat.minecraft().asMinimalString(this);
286274
}
287275

288276
@Override
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
* This file is part of adventure, licensed under the MIT License.
3+
*
4+
* Copyright (c) 2017-2025 KyoriPowered
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package net.kyori.adventure.key;
25+
26+
import org.jetbrains.annotations.Contract;
27+
import org.jetbrains.annotations.Nullable;
28+
29+
import static java.util.Objects.requireNonNull;
30+
31+
/**
32+
* A factory to create {@link Key}s using configured defaults.
33+
*
34+
* <p>The configuration options include:</p>
35+
* <dl>
36+
* <dt>namespace</dt>
37+
* <dd>The namespace to be used as fallback, when no other namespace is specified</dd>
38+
* <dt>separator</dt>
39+
* <dd>The character separating namespace and value</dd>
40+
* </dl>
41+
*
42+
* <p>Example usage of this class:</p>
43+
* <pre>{@code
44+
* KeyFormat format = keyFormat("adventure", ':');
45+
* Key key = format.key("translations");
46+
* format.asString(key); // -> "adventure:translations"
47+
* }</pre>
48+
*
49+
* @see Key#key(String)
50+
* @since 5.1.0
51+
*/
52+
public interface KeyFormat extends Namespaced {
53+
/**
54+
* Gets the default minecraft key format instance.
55+
*
56+
* <p>This uses {@link Key#MINECRAFT_NAMESPACE} and {@link Key#DEFAULT_SEPARATOR}.</p>
57+
*
58+
* @return the instance
59+
* @since 5.1.0
60+
*/
61+
static KeyFormat minecraft() {
62+
return KeyFormatImpl.MINECRAFT_INSTANCE;
63+
}
64+
65+
/**
66+
* Creates a key format with a fallback namespace from a {@link Namespaced} instance.
67+
*
68+
* @param namespaced the namespaced instance to get the namespace from
69+
* @return the key format
70+
* @throws IllegalArgumentException if the namespace contains an invalid character
71+
* @see #namespace(String)
72+
* @since 5.1.0
73+
*/
74+
@SuppressWarnings("PatternValidation") // The namespace is tested later.
75+
static KeyFormat namespace(final Namespaced namespaced) {
76+
return namespace(requireNonNull(namespaced, "namespaced").namespace());
77+
}
78+
79+
/**
80+
* Creates a key format with a fallback {@code namespace} and the {@link Key#DEFAULT_SEPARATOR}.
81+
*
82+
* @param namespace the namespace to be used as fallback in {@link #parse(String)}
83+
* @return the key format
84+
* @throws IllegalArgumentException if the namespace contains an invalid character
85+
* @since 5.1.0
86+
*/
87+
static KeyFormat namespace(@KeyPattern.Namespace final String namespace) {
88+
return keyFormat(namespace, Key.DEFAULT_SEPARATOR);
89+
}
90+
91+
/**
92+
* Creates a key format.
93+
*
94+
* @param namespaced the namespaced instance to get the namespace from
95+
* @param separator the separator to bse used when parsing keys
96+
* @return the key format
97+
* @throws IllegalArgumentException if the namespace contains an invalid character
98+
* @see #keyFormat(String, char)
99+
* @since 5.1.0
100+
*/
101+
@SuppressWarnings("PatternValidation") // The namespace is tested later.
102+
static KeyFormat keyFormat(final Namespaced namespaced, final char separator) {
103+
return keyFormat(requireNonNull(namespaced, "namespaced").namespace(), separator);
104+
}
105+
106+
/**
107+
* Creates a key format.
108+
*
109+
* @param namespace the namespace to be used as fallback in {@link #parse(String)}
110+
* @param separator the separator to be used when parsing keys using {@link #parse(String)}
111+
* @return the key format
112+
* @throws IllegalArgumentException if the namespace contains an invalid character
113+
* @since 5.1.0
114+
*/
115+
static KeyFormat keyFormat(@KeyPattern.Namespace final String namespace, final char separator) {
116+
return keyFormat()
117+
.namespace(namespace)
118+
.separator(separator)
119+
.build();
120+
}
121+
122+
/**
123+
* Creates a key format builder.
124+
*
125+
* @return the builder
126+
* @since 5.1.0
127+
*/
128+
static KeyFormat.Builder keyFormat() {
129+
return new KeyFormatImpl.BuilderImpl();
130+
}
131+
132+
/**
133+
* Gets the separator of this key format.
134+
*
135+
* @return the separator
136+
* @since 5.1.0
137+
*/
138+
char separator();
139+
140+
/**
141+
* Gets the fallback namespace of this key format.
142+
*
143+
* @return the namespace
144+
* @since 5.1.0
145+
*/
146+
@KeyPattern.Namespace
147+
@Override
148+
String namespace();
149+
150+
/**
151+
* Creates a key from this format.
152+
*
153+
* <p>This will create a key with {@link #namespace()} and {@code value}.</p>
154+
*
155+
* @param value the value of the key
156+
* @return the key
157+
* @throws InvalidKeyException if the namespace or value contains an invalid character
158+
* @since 5.1.0
159+
*/
160+
Key key(@KeyPattern.Value final String value);
161+
162+
/**
163+
* Creates a key from this format.
164+
*
165+
* <p>This will parse {@code string} as a key, using {@link #separator()} as a separator between namespace and value.</p>
166+
*
167+
* <p>The namespace is optional. If you do not provide one (for example, if you provide just {@code player} or {@code separator + "player"}
168+
* as the string) then {@link #namespace()} will be used as a namespace and {@code string} will be used as the value,
169+
* removing the separator if necessary.</p>
170+
*
171+
* @param string the string
172+
* @return the key
173+
* @throws InvalidKeyException if the namespace or value contains an invalid character
174+
* @since 5.1.0
175+
*/
176+
Key parse(final String string);
177+
178+
/**
179+
* Checks if {@code string} can be parsed into a {@link Key} using this formats {@link #separator()}.
180+
* If no namespace is specified in {@code string}, the fallback namespace {@link #namespace()} is used.
181+
*
182+
* @param string the string
183+
* @return {@code true} if {@code string} can be parsed into a key using this format, {@code false} otherwise
184+
* @since 5.1.0
185+
*/
186+
boolean parseable(final @Nullable String string);
187+
188+
/**
189+
* Turns a {@link Key} into its string representation using the configuration of this format.
190+
*
191+
* @param key the key
192+
* @return the string representation of the key
193+
* @since 5.1.0
194+
*/
195+
String asString(final Key key);
196+
197+
/**
198+
* Turns a {@link Key} into its string representation in minimal form using the configuration of this format.
199+
*
200+
* <p>If the {@link Key#namespace()} of the key is {@link #namespace()}, only the {@link Key#value()} will be returned.</p>
201+
*
202+
* @param key the key
203+
* @return the minimal string representation of the key
204+
* @since 5.1.0
205+
*/
206+
String asMinimalString(final Key key);
207+
208+
/**
209+
* A builder for a key format.
210+
*
211+
* @since 5.1.0
212+
*/
213+
interface Builder {
214+
/**
215+
* Sets the fallback namespace of this builder from a {@link Namespaced} instance.
216+
*
217+
* @param namespaced the namespaced instance
218+
* @return this builder
219+
* @see #namespace(String)
220+
* @since 5.1.0
221+
*/
222+
@Contract("_ -> this")
223+
@SuppressWarnings("PatternValidation") // The namespace is tested later.
224+
default Builder namespace(final Namespaced namespaced) {
225+
return this.namespace(requireNonNull(namespaced, "namespaced").namespace());
226+
}
227+
228+
/**
229+
* Sets the fallback namespace of this builder.
230+
*
231+
* <p>This namespace is used in {@link #parse(String)} when the input string doesn't specify a namespace.</p>
232+
*
233+
* <p>Defaults to {@link Key#MINECRAFT_NAMESPACE}</p>
234+
*
235+
* @param namespace the namespace
236+
* @return this builder
237+
* @throws IllegalArgumentException if the namespace contains an invalid character
238+
* @since 5.1.0
239+
*/
240+
@Contract("_ -> this")
241+
Builder namespace(@KeyPattern.Namespace final String namespace);
242+
243+
/**
244+
* Sets the separator of this builder.
245+
*
246+
* <p>The separator is used to separate namespace and value in {@link #parse(String)}.</p>
247+
*
248+
* <p>Defaults to {@link Key#DEFAULT_SEPARATOR}</p>
249+
*
250+
* @param separator the separator
251+
* @return this builder
252+
* @since 5.1.0
253+
*/
254+
@Contract("_ -> this")
255+
Builder separator(final char separator);
256+
257+
/**
258+
* Builds the key format.
259+
*
260+
* @return the key format
261+
* @since 5.1.0
262+
*/
263+
@Contract(value = "-> new", pure = true)
264+
KeyFormat build();
265+
}
266+
}

0 commit comments

Comments
 (0)