1
2
3
4
5
6 package dev.metaschema.core.metapath.item.node;
7
8 import org.apache.logging.log4j.LogManager;
9 import org.apache.logging.log4j.Logger;
10
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.LinkedHashMap;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Map;
17
18 import dev.metaschema.core.metapath.DynamicContext;
19 import dev.metaschema.core.metapath.MetapathException;
20 import dev.metaschema.core.metapath.StaticContext;
21 import dev.metaschema.core.metapath.item.ISequence;
22 import dev.metaschema.core.model.IModule;
23 import dev.metaschema.core.model.constraint.IAllowedValuesConstraint;
24 import dev.metaschema.core.model.constraint.ILet;
25 import edu.umd.cs.findbugs.annotations.NonNull;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class AllowedValueCollectingNodeItemVisitor
44 extends AbstractRecursionPreventingNodeItemVisitor<DynamicContext, Void> {
45 private static final Logger LOGGER = LogManager.getLogger(AllowedValueCollectingNodeItemVisitor.class);
46
47 @NonNull
48 private final Map<IDefinitionNodeItem<?, ?>, NodeItemRecord> nodeItemAnalysis = new LinkedHashMap<>();
49
50
51
52
53
54
55
56
57 @NonNull
58 public Collection<NodeItemRecord> getAllowedValueLocations() {
59 return nodeItemAnalysis.values();
60 }
61
62
63
64
65
66
67
68
69
70
71
72 public void visit(@NonNull IModule module) {
73 DynamicContext context = new DynamicContext(
74 StaticContext.builder()
75 .defaultModelNamespace(module.getXmlNamespace())
76 .build());
77
78 visit(INodeItemFactory.instance().newModuleNodeItem(module), context);
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public void visit(@NonNull IModuleNodeItem module, @NonNull DynamicContext context) {
100 context.disablePredicateEvaluation();
101 context.enableAtomizeNoDataAsEmpty();
102 visitMetaschema(module, context);
103 }
104
105 @SuppressWarnings("PMD.AvoidCatchingGenericException")
106 private void handleAllowedValuesAtLocation(
107 @NonNull IDefinitionNodeItem<?, ?> itemLocation,
108 @NonNull DynamicContext context) {
109 itemLocation.getDefinition().getAllowedValuesConstraints().stream()
110 .forEachOrdered(allowedValues -> {
111 try {
112 ISequence<?> result = allowedValues.getTarget().evaluate(itemLocation, context);
113 result.stream().forEachOrdered(target -> {
114 assert target != null;
115 handleAllowedValues(allowedValues, itemLocation, (IDefinitionNodeItem<?, ?>) target);
116 });
117 } catch (MetapathException ex) {
118 LOGGER.atWarn().log(
119 "Skipping allowed-values constraint target '{}' at '{}' because it cannot be evaluated"
120 + " against the module definition: {}",
121 allowedValues.getTarget().getPath(), itemLocation.getMetapath(), ex.getLocalizedMessage());
122 }
123 });
124 }
125
126 private void handleAllowedValues(
127 @NonNull IAllowedValuesConstraint allowedValues,
128 @NonNull IDefinitionNodeItem<?, ?> location,
129 @NonNull IDefinitionNodeItem<?, ?> target) {
130 NodeItemRecord itemRecord = nodeItemAnalysis.get(target);
131 if (itemRecord == null) {
132 itemRecord = new NodeItemRecord(target);
133 nodeItemAnalysis.put(target, itemRecord);
134 }
135
136 AllowedValuesRecord allowedValuesRecord = new AllowedValuesRecord(allowedValues, location, target);
137 itemRecord.addAllowedValues(allowedValuesRecord);
138 }
139
140 @Override
141 public Void visitFlag(IFlagNodeItem item, DynamicContext context) {
142 assert context != null;
143 DynamicContext subContext = handleLetStatements(item, context);
144 handleAllowedValuesAtLocation(item, subContext);
145 return super.visitFlag(item, subContext);
146 }
147
148 @Override
149 public Void visitField(IFieldNodeItem item, DynamicContext context) {
150 assert context != null;
151 DynamicContext subContext = handleLetStatements(item, context);
152 handleAllowedValuesAtLocation(item, subContext);
153 return super.visitField(item, subContext);
154 }
155
156 @Override
157 public Void visitAssembly(IAssemblyNodeItem item, DynamicContext context) {
158 assert context != null;
159 DynamicContext subContext = handleLetStatements(item, context);
160 handleAllowedValuesAtLocation(item, subContext);
161 return super.visitAssembly(item, subContext);
162 }
163
164 @SuppressWarnings("PMD.AssignmentInOperand")
165 private DynamicContext handleLetStatements(IDefinitionNodeItem<?, ?> item, DynamicContext context) {
166 assert context != null;
167 DynamicContext subContext = context;
168 for (ILet let : item.getDefinition().getLetExpressions().values()) {
169 try {
170 ISequence<?> result = let.getValueExpression().evaluate(item,
171 subContext).reusable();
172 subContext = subContext.bindVariableValue(let.getName(), result);
173 } catch (MetapathException ex) {
174
175
176
177
178 LOGGER.atWarn().log(
179 "Skipping let expression '${}' at '{}' because it cannot be evaluated against"
180 + " the module definition: {}",
181 let.getName(), item.getMetapath(), ex.getLocalizedMessage());
182 }
183 }
184 return subContext;
185 }
186
187 @Override
188 public Void visitAssembly(IAssemblyInstanceGroupedNodeItem item, DynamicContext context) {
189 return visitAssembly((IAssemblyNodeItem) item, context);
190 }
191
192 @Override
193 protected Void defaultResult() {
194 return null;
195 }
196
197
198
199
200
201 public static final class NodeItemRecord {
202 @NonNull
203 private final IDefinitionNodeItem<?, ?> item;
204 @NonNull
205 private final List<AllowedValuesRecord> allowedValues = new LinkedList<>();
206
207 private NodeItemRecord(@NonNull IDefinitionNodeItem<?, ?> item) {
208 this.item = item;
209 }
210
211
212
213
214
215
216
217 @NonNull
218 public IDefinitionNodeItem<?, ?> getItem() {
219 return item;
220 }
221
222
223
224
225
226
227 @NonNull
228 public List<AllowedValuesRecord> getAllowedValues() {
229 return Collections.unmodifiableList(allowedValues);
230 }
231
232
233
234
235
236
237
238 public void addAllowedValues(@NonNull AllowedValuesRecord record) {
239 this.allowedValues.add(record);
240 }
241 }
242
243
244
245
246
247 public static final class AllowedValuesRecord {
248 @NonNull
249 private final IAllowedValuesConstraint allowedValues;
250 @NonNull
251 private final IDefinitionNodeItem<?, ?> location;
252 @NonNull
253 private final IDefinitionNodeItem<?, ?> target;
254
255
256
257
258
259
260
261
262
263
264
265 public AllowedValuesRecord(
266 @NonNull IAllowedValuesConstraint allowedValues,
267 @NonNull IDefinitionNodeItem<?, ?> location,
268 @NonNull IDefinitionNodeItem<?, ?> target) {
269 this.allowedValues = allowedValues;
270 this.location = location;
271 this.target = target;
272 }
273
274
275
276
277
278
279 @NonNull
280 public IAllowedValuesConstraint getAllowedValues() {
281 return allowedValues;
282 }
283
284
285
286
287
288
289 @NonNull
290 public IDefinitionNodeItem<?, ?> getLocation() {
291 return location;
292 }
293
294
295
296
297
298
299 @NonNull
300 public IDefinitionNodeItem<?, ?> getTarget() {
301 return target;
302 }
303 }
304 }