A lightweight, extensible Java placeholder engine with nesting, recursion, and multiple syntax support.
- Multiple placeholder syntaxes (
BRACKET,PAPI) - Recursive placeholder resolution
- Nested placeholders (placeholders inside names and parameters)
- Parameterized placeholders
- Resolver chaining (fallback resolvers)
- Context-aware resolution
- Integration with PlaceholderAPI via
PapiResolver
<repository>
<id>bdev-repo-releases</id>
<name>BDev repository</name>
<url>https://repo.by1337.space/releases</url>
</repository>
<dependency>
<groupId>dev.by1337.plc</groupId>
<artifactId>BPlaceholder</artifactId>
<version>1.1</version>
</dependency>{key}
{key:value}
{outer:{inner:42}}
- Placeholder name: any characters except
:and whitespace - Parameters separated by
:
%key%
%key_param%
%outer_%inner%%
- Placeholder name: any characters except
_and whitespace - Parameters separated by
_ - Compatible with PlaceholderAPI-style placeholders
var placeholders = Placeholders.create()
.of("test", "ok");
String result = placeholders.replace("{test}", null);
// result = "ok"Placeholders can be nested inside other placeholder names:
var v = Placeholders.create()
.of("test_1337", "ok")
.of("test", "1337");
v.replace("{test_{test}}", null);
// → "ok"var v = Placeholders.<String>create()
.withParams("test", params -> params);
v.setPlaceholders("{test:1337}", null);
// → "1337"Nested parameters are also supported:
var v = Placeholders.<String>create()
.withParams("outer", p -> "OUT-" + p)
.withParams("inner", p -> "IN-" + p);
v.setPlaceholders("{outer:{inner:42}}", null);
// → "OUT-IN-42"Placeholders may resolve to other placeholders. Resolution continues until a final value is produced.
var v = Placeholders.<String>create()
.format(PlaceholderSyntax.PAPI)
.of("a", "%b%")
.of("b", "%c%")
.of("c", "final");
v.setPlaceholders("%a%", null);
// → "final"Resolvers can be combined:
var v = Placeholders.<String>create()
.withParams("test", params -> params)
.and(
Placeholders.<String>create()
.format(PlaceholderSyntax.PAPI)
.of("ok+777", "yes")
);
v.setPlaceholders("%ok+{test:777}%", null);
// → "yes"Resolution order:
- First resolver that supports the placeholder
- Fallback to the next resolver
- If unresolved — original placeholder is preserved
If a placeholder cannot be resolved, it is left unchanged:
var v = Placeholders.create();
v.setPlaceholders("{missing}", null);
// → "{missing}"Empty values are allowed:
var v = Placeholders.create().of("empty", "");
v.setPlaceholders("{empty}", null);
// → ""Use PapiResolver to delegate resolution to PlaceholderAPI:
var v = Placeholders.<OfflinePlayer>create()
.of("test_1337", "ok")
.and(PapiResolver.INSTANCE);
v.setPlaceholders("%player_name%", player);You can also map contexts:
PapiResolver.INSTANCE.map(myContext -> offlinePlayer);| Type | Description |
|---|---|
PlaceholderProcessor |
Core engine that parses and resolves placeholders |
Placeholders |
Placeholder registry and resolver implementation |
PlaceholderResolver |
Resolves placeholders by key, params, and syntax |
PlaceholderApplier |
Bound resolver function (String -> String) |
PlaceholderSyntax |
Placeholder syntax (BRACKET, PAPI) |
PapiResolver |
Adapter for PlaceholderAPI |
- Predictable resolution order
- Syntax-tolerant parsing
- Safe recursion handling
- Clean, composable API