Skip to content

Commit bc75b98

Browse files
committed
feature(api): Make the maximum depth for component flattening configurable
1 parent 1b8f5e4 commit bc75b98

File tree

3 files changed

+46
-8
lines changed

3 files changed

+46
-8
lines changed

api/src/main/java/net/kyori/adventure/text/flattener/ComponentFlattener.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,14 @@ interface Builder extends AbstractBuilder<ComponentFlattener>, Buildable.Builder
123123
* @since 4.7.0
124124
*/
125125
@NotNull Builder unknownMapper(final @Nullable Function<Component, String> converter);
126+
127+
/**
128+
* Sets the maximum depth of the flattening.
129+
*
130+
* @param maximumDepth the maximum depth
131+
* @return this builder
132+
* @since 4.22.0
133+
*/
134+
@NotNull Builder maximumDepth(final int maximumDepth);
126135
}
127136
}

api/src/main/java/net/kyori/adventure/text/flattener/ComponentFlattenerImpl.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ final class ComponentFlattenerImpl implements ComponentFlattener {
6060
.mapper(TextComponent.class, TextComponent::content)
6161
.build();
6262

63-
private static final int MAX_DEPTH = 512;
64-
6563
private final InheritanceAwareMap<Component, Handler> flatteners;
6664
private final Function<Component, String> unknownHandler;
65+
private final int maximumDepth;
6766

68-
ComponentFlattenerImpl(final InheritanceAwareMap<Component, Handler> flatteners, final @Nullable Function<Component, String> unknownHandler) {
67+
ComponentFlattenerImpl(final InheritanceAwareMap<Component, Handler> flatteners, final @Nullable Function<Component, String> unknownHandler, final int maximumDepth) {
6968
this.flatteners = flatteners;
7069
this.unknownHandler = unknownHandler;
70+
this.maximumDepth = maximumDepth;
7171
}
7272

7373
@Override
@@ -79,8 +79,8 @@ private void flatten0(final @NotNull Component input, final @NotNull FlattenerLi
7979
requireNonNull(input, "input");
8080
requireNonNull(listener, "listener");
8181
if (input == Component.empty()) return;
82-
if (depth > MAX_DEPTH) {
83-
throw new IllegalStateException("Exceeded maximum depth of " + MAX_DEPTH + " while attempting to flatten components!");
82+
if (depth > this.maximumDepth) {
83+
throw new IllegalStateException("Exceeded maximum depth of " + this.maximumDepth + " while attempting to flatten components!");
8484
}
8585

8686
final @Nullable Handler flattener = this.flattener(input);
@@ -114,7 +114,7 @@ private void flatten0(final @NotNull Component input, final @NotNull FlattenerLi
114114

115115
@Override
116116
public ComponentFlattener.@NotNull Builder toBuilder() {
117-
return new BuilderImpl(this.flatteners, this.unknownHandler);
117+
return new BuilderImpl(this.flatteners, this.unknownHandler, this.maximumDepth);
118118
}
119119

120120
// A function that allows nesting other flatten operations
@@ -124,21 +124,26 @@ interface Handler {
124124
}
125125

126126
static final class BuilderImpl implements Builder {
127+
private static final int DEFAULT_MAX_DEPTH = 32;
128+
127129
private final InheritanceAwareMap.Builder<Component, Handler> flatteners;
128130
private @Nullable Function<Component, String> unknownHandler;
131+
private int maximumDepth;
129132

130133
BuilderImpl() {
131134
this.flatteners = InheritanceAwareMap.<Component, Handler>builder().strict(true);
135+
this.maximumDepth = DEFAULT_MAX_DEPTH;
132136
}
133137

134-
BuilderImpl(final InheritanceAwareMap<Component, Handler> flatteners, final @Nullable Function<Component, String> unknownHandler) {
138+
BuilderImpl(final InheritanceAwareMap<Component, Handler> flatteners, final @Nullable Function<Component, String> unknownHandler, final int maximumDepth) {
135139
this.flatteners = InheritanceAwareMap.builder(flatteners).strict(true);
136140
this.unknownHandler = unknownHandler;
141+
this.maximumDepth = maximumDepth;
137142
}
138143

139144
@Override
140145
public @NotNull ComponentFlattener build() {
141-
return new ComponentFlattenerImpl(this.flatteners.build(), this.unknownHandler);
146+
return new ComponentFlattenerImpl(this.flatteners.build(), this.unknownHandler, this.maximumDepth);
142147
}
143148

144149
@Override
@@ -160,5 +165,12 @@ static final class BuilderImpl implements Builder {
160165
this.unknownHandler = converter;
161166
return this;
162167
}
168+
169+
@Override
170+
public @NotNull Builder maximumDepth(int maximumDepth) {
171+
if (maximumDepth <= 0) throw new IllegalArgumentException("maxDepth must be greater than 0, was " + maximumDepth);
172+
this.maximumDepth = maximumDepth;
173+
return this;
174+
}
163175
}
164176
}

api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,21 @@ void testEarlyExit() {
288288
.assertPushesAndPops(3)
289289
.assertContents("Hello", "How are you?", "Not great");
290290
}
291+
292+
public static Component createNestedComponent(final int depth, final String finalText) {
293+
Component component = Component.text(finalText);
294+
295+
for (int i = 0; i < depth; i++) {
296+
component = Component.translatable("%1$s%1$s%1$s", component);
297+
}
298+
299+
return component;
300+
}
301+
302+
@Test
303+
void testGiantComponent() {
304+
final Component component = createNestedComponent(34, "only 34?!");
305+
final StringBuilder sb = new StringBuilder();
306+
ComponentFlattener.basic().flatten(component, sb::append);
307+
}
291308
}

0 commit comments

Comments
 (0)