Skip to content

Commit 92b183b

Browse files
committed
feat: Introduce concept of an option schema
This allows sourcing relevant values from outside sources
1 parent 0e02d45 commit 92b183b

File tree

8 files changed

+474
-61
lines changed

8 files changed

+474
-61
lines changed

src/main/java/net/kyori/option/Option.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of option, licensed under the MIT License.
33
*
4-
* Copyright (c) 2023 KyoriPowered
4+
* Copyright (c) 2023-2025 KyoriPowered
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -47,9 +47,11 @@ public interface Option<V> {
4747
* @param defaultValue the default value
4848
* @return the flag instance
4949
* @since 1.0.0
50+
* @deprecated For removal since 1.1.0, create new options within an {@link OptionSchema} instead
5051
*/
52+
@Deprecated
5153
static Option<Boolean> booleanOption(final String id, final boolean defaultValue) {
52-
return OptionImpl.option(id, Boolean.class, defaultValue);
54+
return OptionSchema.globalSchema().booleanOption(id, defaultValue);
5355
}
5456

5557
/**
@@ -63,9 +65,11 @@ static Option<Boolean> booleanOption(final String id, final boolean defaultValue
6365
* @param <E> the enum type
6466
* @return the flag instance
6567
* @since 1.0.0
68+
* @deprecated For removal since 1.1.0, create new options within an {@link OptionSchema} instead
6669
*/
70+
@Deprecated
6771
static <E extends Enum<E>> Option<E> enumOption(final String id, final Class<E> enumClazz, final E defaultValue) {
68-
return OptionImpl.option(id, enumClazz, defaultValue);
72+
return OptionSchema.globalSchema().enumOption(id, enumClazz, defaultValue);
6973
}
7074

7175
/**

src/main/java/net/kyori/option/OptionImpl.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of option, licensed under the MIT License.
33
*
4-
* Copyright (c) 2023 KyoriPowered
4+
* Copyright (c) 2023-2025 KyoriPowered
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -24,15 +24,10 @@
2424
package net.kyori.option;
2525

2626
import java.util.Objects;
27-
import java.util.Set;
28-
import java.util.concurrent.ConcurrentHashMap;
2927
import org.jetbrains.annotations.NotNull;
3028
import org.jetbrains.annotations.Nullable;
3129

32-
import static java.util.Objects.requireNonNull;
33-
3430
final class OptionImpl<V> implements Option<V> {
35-
private static final Set<String> KNOWN_KEYS = ConcurrentHashMap.newKeySet();
3631

3732
private final String id;
3833
private final Class<V> type;
@@ -44,18 +39,6 @@ final class OptionImpl<V> implements Option<V> {
4439
this.defaultValue = defaultValue;
4540
}
4641

47-
static <T> Option<T> option(final String id, final Class<T> type, final @Nullable T defaultValue) {
48-
if (!KNOWN_KEYS.add(id)) {
49-
throw new IllegalStateException("Key " + id + " has already been used. Option keys must be unique.");
50-
}
51-
52-
return new OptionImpl<>(
53-
requireNonNull(id, "id"),
54-
requireNonNull(type, "type"),
55-
defaultValue
56-
);
57-
}
58-
5942
@Override
6043
public @NotNull String id() {
6144
return this.id;
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* This file is part of option, licensed under the MIT License.
3+
*
4+
* Copyright (c) 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.option;
25+
26+
import java.util.Set;
27+
import org.jetbrains.annotations.ApiStatus;
28+
import org.jetbrains.annotations.NotNull;
29+
import org.jetbrains.annotations.Nullable;
30+
31+
import static java.util.Objects.requireNonNull;
32+
33+
/**
34+
* Represents a 'universe' of known options.
35+
*
36+
* @since 1.1.0
37+
*/
38+
@ApiStatus.NonExtendable
39+
public interface OptionSchema {
40+
/**
41+
* Retrieve the globally-shared option schema.
42+
*
43+
* <p>This mostly exists for backwards compatibility.</p>
44+
*
45+
* @return the global schema
46+
* @since 1.1.0
47+
*/
48+
static OptionSchema.@NotNull Mutable globalSchema() {
49+
return OptionSchemaImpl.Instances.GLOBAL;
50+
}
51+
52+
/**
53+
* Return a mutable schema that's a child of the specified schema.
54+
*
55+
* <p>This schema will inherit all options defined in the parent schema at the time the child schema is created.</p>
56+
*
57+
* @param schema the parent schema
58+
* @return the mutable child schema
59+
* @since 1.1.0
60+
*/
61+
static OptionSchema.@NotNull Mutable childSchema(final @NotNull OptionSchema schema) {
62+
final OptionSchemaImpl impl;
63+
if (schema instanceof OptionSchemaImpl.MutableImpl) {
64+
impl = (OptionSchemaImpl) ((Mutable) schema).frozenView();
65+
} else {
66+
impl = (OptionSchemaImpl) schema;
67+
}
68+
69+
return new OptionSchemaImpl(requireNonNull(impl, "impl")).new MutableImpl();
70+
}
71+
72+
/**
73+
* Create an empty schema inheriting from nothing but the contents
74+
* of the global schema at invocation time.
75+
*
76+
* @return a mutable schema
77+
* @since 1.1.0
78+
*/
79+
static OptionSchema.@NotNull Mutable emptySchema() {
80+
return childSchema(globalSchema());
81+
}
82+
83+
/**
84+
* Return all known options contained within this schema, and recursively through its parents.
85+
*
86+
* @return known options
87+
* @since 1.1.0
88+
*/
89+
@NotNull Set<Option<?>> knownOptions();
90+
91+
/**
92+
* Return whether the provided option is known within this scheam.
93+
*
94+
* @param option the option
95+
* @return whether the option is known
96+
* @since 1.1.0
97+
*/
98+
boolean has(final @NotNull Option<?> option);
99+
100+
/**
101+
* Create a builder for an unversioned option state containing only options within this schema.
102+
*
103+
* @return the builder
104+
* @since 1.1.0
105+
*/
106+
OptionState.@NotNull Builder stateBuilder();
107+
108+
/**
109+
* Create a builder for a versioned option state containing only values for options within this schema.
110+
*
111+
* @return the builder
112+
* @since 1.1.0
113+
*/
114+
OptionState.@NotNull VersionedBuilder versionedStateBuilder();
115+
116+
/**
117+
* Create an empty option state within this schema.
118+
*
119+
* @return the empty state
120+
* @since 1.1.0
121+
*/
122+
OptionState emptyState();
123+
124+
/**
125+
* A mutable view of an option schema that allows registering new options into the schema.
126+
*
127+
* @since 1.1.0
128+
*/
129+
@ApiStatus.NonExtendable
130+
interface Mutable extends OptionSchema {
131+
/**
132+
* Create an option with a string value type.
133+
*
134+
* <p>Flag keys must not be reused within a schema tree.</p>
135+
*
136+
* @param id the flag id
137+
* @param defaultValue the default value
138+
* @return the flag instance
139+
* @since 1.1.0
140+
*/
141+
@NotNull Option<String> stringOption(final @NotNull String id, final @Nullable String defaultValue);
142+
143+
/**
144+
* Create an option with a boolean value type.
145+
*
146+
* <p>Flag keys must not be reused within a schema tree.</p>
147+
*
148+
* @param id the flag id
149+
* @param defaultValue the default value
150+
* @return the flag instance
151+
* @since 1.1.0
152+
*/
153+
@NotNull Option<Boolean> booleanOption(final @NotNull String id, final boolean defaultValue);
154+
155+
/**
156+
* Create an option with an integer value type.
157+
*
158+
* <p>Flag keys must not be reused within a schema tree.</p>
159+
*
160+
* @param id the flag id
161+
* @param defaultValue the default value
162+
* @return the flag instance
163+
* @since 1.1.0
164+
*/
165+
@NotNull Option<Integer> intOption(final @NotNull String id, final int defaultValue);
166+
167+
/**
168+
* Create an option with a double value type.
169+
*
170+
* <p>Flag keys must not be reused within a schema tree.</p>
171+
*
172+
* @param id the flag id
173+
* @param defaultValue the default value
174+
* @return the flag instance
175+
* @since 1.1.0
176+
*/
177+
@NotNull Option<Double> doubleOption(final @NotNull String id, final double defaultValue);
178+
179+
/**
180+
* Create an option with an enum value type.
181+
*
182+
* <p>Flag keys must not be reused within a schema tree.</p>
183+
*
184+
* @param id the flag id
185+
* @param enumClazz the value type
186+
* @param defaultValue the default value
187+
* @param <E> the enum type
188+
* @return the flag instance
189+
* @since 1.1.0
190+
*/
191+
<E extends Enum<E>> @NotNull Option<E> enumOption(final @NotNull String id, final @NotNull Class<E> enumClazz, final @Nullable E defaultValue);
192+
193+
/**
194+
* Return a view of this schema which does not allow consumers to register new options.
195+
*
196+
* <p>This allows exposing known options within a schema without the risk of having values polluted.</p>
197+
*
198+
* @return the frozen view of this schema
199+
* @since 1.1.0
200+
*/
201+
@NotNull OptionSchema frozenView();
202+
}
203+
}

0 commit comments

Comments
 (0)