1
2
3
4
5
6 package dev.metaschema.core.model.constraint;
7
8 import org.apache.logging.log4j.LogManager;
9 import org.apache.logging.log4j.Logger;
10
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.stream.Collectors;
18
19 import javax.xml.namespace.QName;
20
21 import dev.metaschema.core.metapath.DynamicContext;
22 import dev.metaschema.core.metapath.IMetapathExpression;
23 import dev.metaschema.core.metapath.item.IItem;
24 import dev.metaschema.core.metapath.item.ISequence;
25 import dev.metaschema.core.metapath.item.node.IDefinitionNodeItem;
26 import dev.metaschema.core.metapath.item.node.IModuleNodeItem;
27 import dev.metaschema.core.model.IDefinition;
28 import dev.metaschema.core.model.IModelElementVisitor;
29 import dev.metaschema.core.model.ISource;
30 import dev.metaschema.core.qname.IEnhancedQName;
31 import dev.metaschema.core.util.CollectionUtil;
32 import dev.metaschema.core.util.ObjectUtils;
33 import edu.umd.cs.findbugs.annotations.NonNull;
34
35
36
37
38
39 public class ScopedConstraintSet implements IConstraintSet {
40 private static final Logger LOGGER = LogManager.getLogger(ScopedConstraintSet.class);
41 @NonNull
42 private final ISource source;
43 @NonNull
44 private final Set<IConstraintSet> importedConstraintSets;
45 @NonNull
46 private final Map<IEnhancedQName, List<IScopedContraints>> scopedContraints;
47 @NonNull
48 private final Set<IDefinition> previouslyTargetedDefinitions = new HashSet<>();
49
50
51
52
53
54
55
56
57
58
59
60 @SuppressWarnings("null")
61 public ScopedConstraintSet(
62 @NonNull ISource source,
63 @NonNull List<IScopedContraints> scopedContraints,
64 @NonNull Set<IConstraintSet> importedConstraintSets) {
65 this.source = source;
66 this.scopedContraints = scopedContraints.stream()
67 .collect(
68 Collectors.collectingAndThen(
69 Collectors.groupingBy(
70 scope -> IEnhancedQName.of(scope.getModuleNamespace().toString(), scope.getModuleShortName()),
71 Collectors.toUnmodifiableList()),
72 Collections::unmodifiableMap));
73 this.importedConstraintSets = CollectionUtil.unmodifiableSet(importedConstraintSets);
74 }
75
76
77
78
79
80
81 @Override
82 public ISource getSource() {
83 return source;
84 }
85
86
87
88
89
90
91
92 @NonNull
93 public Map<IEnhancedQName, List<IScopedContraints>> getScopedContraints() {
94 return scopedContraints;
95 }
96
97 @Override
98 public Set<IConstraintSet> getImportedConstraintSets() {
99 return importedConstraintSets;
100 }
101
102 @Override
103 public void applyConstraintsForModule(
104 IModuleNodeItem moduleItem,
105 IModelElementVisitor<ITargetedConstraints, Void> visitor) {
106 IEnhancedQName qname = moduleItem.getModule().getQName();
107 List<IScopedContraints> scopes = getScopedContraints().getOrDefault(qname, CollectionUtil.emptyList());
108
109 @SuppressWarnings("PMD.UseConcurrentHashMap")
110 Map<IDefinition, Set<ITargetedConstraints>> definitionConstraints = new HashMap<>();
111
112 DynamicContext dynamicContext = new DynamicContext(getSource().getStaticContext());
113
114 for (IScopedContraints scoped : scopes) {
115 for (ITargetedConstraints targeted : scoped.getTargetedContraints()) {
116 for (IMetapathExpression metapath : targeted.getTargets()) {
117 ISequence<? extends IDefinitionNodeItem<?, ?>> items = ISequence.of(ObjectUtils.notNull(
118 metapath.evaluate(moduleItem, dynamicContext).stream()
119 .filter(item -> filterNonDefinitionItem(item, metapath))
120 .map(item -> (IDefinitionNodeItem<?, ?>) item)))
121 .reusable();
122 assert items != null;
123
124 Set<IDefinition> targetedDefinitions = items.stream()
125 .map(IDefinitionNodeItem::getDefinition)
126 .filter(definition -> !previouslyTargetedDefinitions.contains(definition))
127 .collect(Collectors.toUnmodifiableSet());
128
129 targetedDefinitions.forEach(definition -> {
130 definitionConstraints.compute(definition, (key, value) -> {
131 Set<ITargetedConstraints> targets = value == null ? new HashSet<>() : value;
132 targets.add(targeted);
133 return targets;
134 });
135 });
136 }
137 }
138 }
139
140 for (Map.Entry<IDefinition, Set<ITargetedConstraints>> entry : definitionConstraints.entrySet()) {
141 IDefinition definition = entry.getKey();
142 for (ITargetedConstraints constraints : entry.getValue()) {
143 definition.accept(visitor, constraints);
144 }
145 }
146 previouslyTargetedDefinitions.addAll(ObjectUtils.notNull(definitionConstraints.keySet()));
147 }
148
149 private static boolean filterNonDefinitionItem(IItem item, @NonNull IMetapathExpression metapath) {
150 boolean retval = item instanceof IDefinitionNodeItem;
151 if (!retval) {
152 LOGGER.atError().log(
153 "Found non-definition item '{}' while applying external constraints using target expression '{}'.",
154 item.toString(),
155 metapath.getPath());
156 }
157 return retval;
158 }
159 }