MappingConfigLoader.java
package io.github.pojotools.flat2pojo.core.config;
import io.github.pojotools.flat2pojo.core.config.MappingConfig.ListRule;
import io.github.pojotools.flat2pojo.core.config.MappingConfig.OrderBy;
import io.github.pojotools.flat2pojo.core.util.PathOps;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class MappingConfigLoader {
private MappingConfigLoader() {}
public static MappingConfig fromYaml(final String yaml) {
return YamlConfigParser.parse(yaml);
}
public static void validateHierarchy(final MappingConfig config) {
final HierarchyValidator validator = new HierarchyValidator(config);
validator.validate();
}
private static class HierarchyValidator {
private final String separator;
private final Map<String, Integer> declarationOrder;
private final Set<String> listPaths;
private final List<ListRule> listRules;
HierarchyValidator(MappingConfig cfg) {
this.separator = cfg.separator();
this.listRules = cfg.lists();
this.declarationOrder = buildDeclarationOrderMap();
this.listPaths = buildListPathsSet();
}
private Map<String, Integer> buildDeclarationOrderMap() {
Map<String, Integer> order = new HashMap<>();
for (int i = 0; i < listRules.size(); i++) {
order.put(listRules.get(i).path(), i);
}
return order;
}
private Set<String> buildListPathsSet() {
Set<String> paths = new HashSet<>();
for (ListRule rule : listRules) {
paths.add(rule.path());
}
return paths;
}
void validate() {
for (ListRule rule : listRules) {
validateSingleListRule(rule);
}
}
private void validateSingleListRule(ListRule rule) {
validateParentChildOrder(rule);
validateKeyPathsAreRelative(rule);
validateOrderByPathsAreRelative(rule);
// validateImpliedParentLists is no longer needed with relative paths
}
private void validateKeyPathsAreRelative(ListRule rule) {
String listPath = rule.path();
String listPathPrefix = listPath + separator;
for (String keyPath : rule.keyPaths()) {
// keyPath must be relative (not start with listPath prefix)
if (keyPath.startsWith(listPathPrefix)) {
throw new ValidationException(
"keyPath '"
+ keyPath
+ "' in list rule '"
+ listPath
+ "' must be relative, not absolute. Use '"
+ keyPath.substring(listPathPrefix.length())
+ "' instead.");
}
}
}
private void validateOrderByPathsAreRelative(ListRule rule) {
String listPath = rule.path();
String listPathPrefix = listPath + separator;
for (OrderBy orderBy : rule.orderBy()) {
String orderPath = orderBy.path();
// orderBy path must be relative (not start with listPath prefix)
if (orderPath.startsWith(listPathPrefix)) {
throw new ValidationException(
"orderBy path '"
+ orderPath
+ "' in list rule '"
+ listPath
+ "' must be relative, not absolute. Use '"
+ orderPath.substring(listPathPrefix.length())
+ "' instead.");
}
}
}
private void validateParentChildOrder(ListRule rule) {
String path = rule.path();
String nearestAncestor = findNearestListAncestor(path);
if (nearestAncestor != null
&& declarationOrder.get(nearestAncestor) > declarationOrder.get(path)) {
throw new ValidationException(
"List '" + path + "' must be declared after its parent list '" + nearestAncestor + "'");
}
}
private String findNearestListAncestor(String path) {
return PathOps.findParentPath(path, listPaths, separator);
}
}
}