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