AbstractNormalizedMapper.java
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.spring.security.core.mapping;
import static java.util.Objects.nonNull;
import static java.util.Objects.requireNonNullElse;
import static org.springframework.util.ObjectUtils.isEmpty;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jspecify.annotations.NonNull;
import org.springframework.util.Assert;
/**
* The abstract normalized mapper.
*
* @param <T> the type parameter
* @author Christian Bremer
*/
public abstract class AbstractNormalizedMapper<T> {
/**
* The default values.
*/
private final List<String> defaultValues;
/**
* The value mapping.
*/
private final Map<String, String> valueMapping;
/**
* The value prefix.
*/
private final String valuePrefix;
/**
* The value case transformation.
*/
private final CaseTransformation valueCaseTransformation;
/**
* The value string replacements.
*/
private final Map<String, String> valueStringReplacements;
/**
* Instantiates a new normalized mapper.
*
* @param defaultValues the default values
* @param valueMapping the value mapping
* @param valuePrefix the value prefix
* @param valueCaseTransformation the value case transformation
* @param valueStringReplacements the value string replacements
*/
protected AbstractNormalizedMapper(
List<String> defaultValues,
Map<String, String> valueMapping,
String valuePrefix,
CaseTransformation valueCaseTransformation,
Map<String, String> valueStringReplacements) {
this.defaultValues = defaultValues;
this.valueMapping = valueMapping;
this.valuePrefix = requireNonNullElse(valuePrefix, getDefaultPrefix());
this.valueCaseTransformation = valueCaseTransformation;
this.valueStringReplacements = valueStringReplacements;
}
/**
* Map collection.
*
* @param sourceCollection the source collection
* @return the target collection
*/
@NonNull
protected Collection<T> map(
@NonNull Collection<? extends T> sourceCollection) {
Stream<T> defaultTargets = defaultValues.stream()
.filter(target -> !isEmpty(target))
.map(this::createTarget);
Stream<T> givenTargets = Stream.ofNullable(sourceCollection)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.map(this::getStringValue)
.filter(target -> !isEmpty(target))
.map(this::normalize);
return Stream.concat(defaultTargets, givenTargets)
.collect(Collectors.toSet());
}
/**
* Normalize source.
*
* @param source the source
* @return the target
*/
@NonNull
protected T normalize(@NonNull String source) {
Assert.hasText(source, "Source must be present.");
Optional<T> mappedValue = Stream.ofNullable(valueMapping)
.flatMap(m -> m.entrySet().stream())
.filter(e -> source.equalsIgnoreCase(e.getKey()))
.findFirst()
.map(Entry::getValue)
.map(this::createTarget);
if (mappedValue.isPresent()) {
return mappedValue.get();
}
String value = source;
if (nonNull(valueCaseTransformation)) {
value = switch (valueCaseTransformation) {
case TO_LOWER_CASE -> value.toLowerCase();
case TO_UPPER_CASE -> value.toUpperCase();
case NONE -> value;
};
}
if (!isEmpty(valueStringReplacements)) {
for (Entry<String, String> replacement : valueStringReplacements.entrySet()) {
value = value.replaceAll(replacement.getKey(), replacement.getValue());
}
}
if (!isEmpty(valuePrefix) && !value.startsWith(valuePrefix)) {
value = valuePrefix + value;
}
return createTarget(value);
}
/**
* Gets string value.
*
* @param value the value
* @return the string value
*/
@NonNull
protected abstract String getStringValue(@NonNull T value);
/**
* Create target t.
*
* @param mappedValue the mapped value
* @return the t
*/
@NonNull
protected abstract T createTarget(@NonNull String mappedValue);
/**
* Gets default prefix.
*
* @return the default prefix
*/
@NonNull
protected abstract String getDefaultPrefix();
}