CommaSeparatedSetConverter.java
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright 2026 Nikolas Charalambidis
*/
package dev.nichar.facepalm.configurator;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.ConfigurationListener;
import org.codehaus.plexus.component.configurator.converters.AbstractConfigurationConverter;
import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.configuration.PlexusConfiguration;
/**
* Custom Plexus converter for transforming comma-separated strings into {@link java.util.Set} objects.
* Resolves Maven expressions and trims whitespace to ensure clean configuration injection.
*/
public class CommaSeparatedSetConverter extends AbstractConfigurationConverter {
@Override
public boolean canConvert(final Class<?> type) {
return Set.class.isAssignableFrom(type);
}
@Override
public Object fromConfiguration(final ConverterLookup converterLookup,
final PlexusConfiguration configuration,
final Class<?> type,
final Class<?> baseType,
final ClassLoader classLoader,
final ExpressionEvaluator expressionEvaluator,
final ConfigurationListener listener) throws ComponentConfigurationException {
final Object evaluatedValue;
try {
// Resolve Maven expressions like "${project.build.directory}".
evaluatedValue = expressionEvaluator.evaluate(configuration.getValue());
} catch (Exception e) {
throw new ComponentConfigurationException("Failed to evaluate configuration expression", e);
}
final var value = evaluatedValue != null ? evaluatedValue.toString() : null;
// Split comma-delimited strings into unique, trimmed elements.
if (value != null && !value.trim().isEmpty()) {
return Arrays.stream(value.split(","))
// Removes all Unicode whitespace (cleaner than trim()).
.map(String::strip)
// Discards strings that are empty OR only contain whitespace.
.filter(Predicate.not(String::isBlank))
// Aggregates the cleaned elements into a LinkedHashSet to maintain the original insertion order while
// ensuring uniqueness.
.collect(Collectors.toCollection(LinkedHashSet::new));
}
// Fallback: process traditional nested XML configuration tags.
if (configuration.getChildCount() > 0) {
final var set = new LinkedHashSet<>();
for (final var child : configuration.getChildren()) {
try {
final var evaluatedChild = expressionEvaluator.evaluate(child.getValue());
if (evaluatedChild != null) {
set.add(evaluatedChild.toString());
}
} catch (Exception e) {
throw new ComponentConfigurationException("Failed to evaluate child configuration expression", e);
}
}
return set;
}
// Default to an empty set if no configuration is provided.
return new LinkedHashSet<>();
}
}