Merge branch '6.2.x'

This commit is contained in:
Stéphane Nicoll
2024-12-05 17:01:50 +01:00
2 changed files with 102 additions and 38 deletions

View File

@@ -247,7 +247,7 @@ final class PlaceholderParser {
if (!parts.isEmpty()) {
Part current = parts.removeLast();
if (current instanceof TextPart textPart) {
parts.add(new TextPart(textPart.text + text));
parts.add(new TextPart(textPart.text() + text));
}
else {
parts.add(current);
@@ -420,51 +420,42 @@ final class PlaceholderParser {
/**
* A {@link Part} implementation that does not contain a valid placeholder.
* @param text the raw (and resolved) text
* A base {@link Part} implementation.
*/
record TextPart(String text) implements Part {
abstract static class AbstractPart implements Part {
private final String text;
protected AbstractPart(String text) {
this.text = text;
}
@Override
public String resolve(PartResolutionContext resolutionContext) {
public String text() {
return this.text;
}
}
/**
* A {@link Part} implementation that represents a single placeholder with
* a hard-coded fallback.
* @param text the raw text
* @param key the key of the placeholder
* @param fallback the fallback to use, if any
*/
record SimplePlaceholderPart(String text, String key, @Nullable String fallback) implements Part {
@Override
public String resolve(PartResolutionContext resolutionContext) {
String resolvedValue = resolveToText(resolutionContext, this.key);
if (resolvedValue != null) {
return resolvedValue;
}
else if (this.fallback != null) {
return this.fallback;
}
return resolutionContext.handleUnresolvablePlaceholder(this.key, this.text);
}
/**
* Resolve the placeholder with the given {@code key}. If the result of such
* resolution return other placeholders, those are resolved as well until the
* resolution no longer contains any placeholders.
* @param resolutionContext the resolution context to use
* @param key the initial placeholder
* @return the full resolution of the given {@code key} or {@code null} if
* the placeholder has no value to begin with
*/
@Nullable
private String resolveToText(PartResolutionContext resolutionContext, String text) {
String resolvedValue = resolutionContext.resolvePlaceholder(text);
protected String resolveRecursively(PartResolutionContext resolutionContext, String key) {
String resolvedValue = resolutionContext.resolvePlaceholder(key);
if (resolvedValue != null) {
resolutionContext.flagPlaceholderAsVisited(text);
resolutionContext.flagPlaceholderAsVisited(key);
// Let's check if we need to recursively resolve that value
List<Part> nestedParts = resolutionContext.parse(resolvedValue);
String value = toText(nestedParts);
if (!isTextOnly(nestedParts)) {
value = new ParsedValue(resolvedValue, nestedParts).resolve(resolutionContext);
}
resolutionContext.removePlaceholder(text);
resolutionContext.removePlaceholder(key);
return value;
}
// Not found
@@ -483,26 +474,97 @@ final class PlaceholderParser {
}
/**
* A {@link Part} implementation that does not contain a valid placeholder.
*/
static class TextPart extends AbstractPart {
/**
* Create a new instance.
* @param text the raw (and resolved) text
*/
public TextPart(String text) {
super(text);
}
@Override
public String resolve(PartResolutionContext resolutionContext) {
return text();
}
}
/**
* A {@link Part} implementation that represents a single placeholder with
* a hard-coded fallback.
*/
static class SimplePlaceholderPart extends AbstractPart {
private final String key;
@Nullable
private final String fallback;
/**
* Create a new instance.
* @param text the raw text
* @param key the key of the placeholder
* @param fallback the fallback to use, if any
*/
public SimplePlaceholderPart(String text,String key, @Nullable String fallback) {
super(text);
this.key = key;
this.fallback = fallback;
}
@Override
public String resolve(PartResolutionContext resolutionContext) {
String value = resolveRecursively(resolutionContext, this.key);
if (value != null) {
return value;
}
else if (this.fallback != null) {
return this.fallback;
}
return resolutionContext.handleUnresolvablePlaceholder(this.key, text());
}
}
/**
* A {@link Part} implementation that represents a single placeholder
* containing nested placeholders.
* @param text the raw text of the root placeholder
* @param keyParts the parts of the key
* @param defaultParts the parts of the fallback, if any
*/
record NestedPlaceholderPart(String text, List<Part> keyParts, @Nullable List<Part> defaultParts) implements Part {
static class NestedPlaceholderPart extends AbstractPart {
private final List<Part> keyParts;
@Nullable
private final List<Part> defaultParts;
/**
* Create a new instance.
* @param text the raw text of the root placeholder
* @param keyParts the parts of the key
* @param defaultParts the parts of the fallback, if any
*/
NestedPlaceholderPart(String text, List<Part> keyParts, @Nullable List<Part> defaultParts) {
super(text);
this.keyParts = keyParts;
this.defaultParts = defaultParts;
}
@Override
public String resolve(PartResolutionContext resolutionContext) {
String resolvedKey = Part.resolveAll(this.keyParts, resolutionContext);
String value = resolutionContext.resolvePlaceholder(resolvedKey);
String value = resolveRecursively(resolutionContext, resolvedKey);
if (value != null) {
return value;
}
else if (this.defaultParts != null) {
return Part.resolveAll(this.defaultParts, resolutionContext);
}
return resolutionContext.handleUnresolvablePlaceholder(resolvedKey, this.text);
return resolutionContext.handleUnresolvablePlaceholder(resolvedKey, text());
}
}

View File

@@ -210,6 +210,8 @@ class PlaceholderParserTests {
static Stream<Arguments> nestedPlaceholders() {
return Stream.of(
Arguments.of("${p6}", "v1:v2:def"),
Arguments.of("${p6:not-used}", "v1:v2:def"),
Arguments.of("${p6:${invalid}}", "v1:v2:def"),
Arguments.of("${invalid:${p1}:${p2}}", "v1:v2"),
Arguments.of("${invalid:${p3}}", "v1:v2"),
Arguments.of("${invalid:${p4}}", "v1:v2"),