1
2
3
4
5
6 package dev.metaschema.oscal.lib.model.util;
7
8 import java.util.Collection;
9 import java.util.LinkedHashMap;
10 import java.util.LinkedList;
11 import java.util.List;
12 import java.util.Map;
13
14 import dev.metaschema.core.metapath.DynamicContext;
15 import dev.metaschema.core.metapath.StaticContext;
16 import dev.metaschema.core.metapath.item.ISequence;
17 import dev.metaschema.core.metapath.item.node.AbstractRecursionPreventingNodeItemVisitor;
18 import dev.metaschema.core.metapath.item.node.IAssemblyInstanceGroupedNodeItem;
19 import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
20 import dev.metaschema.core.metapath.item.node.IDefinitionNodeItem;
21 import dev.metaschema.core.metapath.item.node.IFieldNodeItem;
22 import dev.metaschema.core.metapath.item.node.IFlagNodeItem;
23 import dev.metaschema.core.metapath.item.node.IModuleNodeItem;
24 import dev.metaschema.core.metapath.item.node.INodeItemFactory;
25 import dev.metaschema.core.model.IModule;
26 import dev.metaschema.core.model.constraint.IAllowedValuesConstraint;
27 import dev.metaschema.core.model.constraint.ILet;
28 import edu.umd.cs.findbugs.annotations.NonNull;
29
30 public class AllowedValueCollectingNodeItemVisitor
31 extends AbstractRecursionPreventingNodeItemVisitor<DynamicContext, Void> {
32
33 private final Map<IDefinitionNodeItem<?, ?>, NodeItemRecord> nodeItemAnalysis = new LinkedHashMap<>();
34
35 public Collection<NodeItemRecord> getAllowedValueLocations() {
36 return nodeItemAnalysis.values();
37 }
38
39 public void visit(@NonNull IModule module) {
40 DynamicContext context = new DynamicContext(
41 StaticContext.builder()
42 .defaultModelNamespace(module.getXmlNamespace())
43 .build());
44 context.disablePredicateEvaluation();
45
46 visit(INodeItemFactory.instance().newModuleNodeItem(module), context);
47 }
48
49 public void visit(@NonNull IModuleNodeItem module, @NonNull DynamicContext context) {
50
51 visitMetaschema(module, context);
52 }
53
54 private void handleAllowedValuesAtLocation(
55 @NonNull IDefinitionNodeItem<?, ?> itemLocation,
56 @NonNull DynamicContext context) {
57 itemLocation.getDefinition().getAllowedValuesConstraints().stream()
58 .forEachOrdered(allowedValues -> {
59 ISequence<?> result = allowedValues.getTarget().evaluate(itemLocation, context);
60 result.stream().forEachOrdered(target -> {
61 assert target != null;
62 handleAllowedValues(allowedValues, itemLocation, (IDefinitionNodeItem<?, ?>) target);
63 });
64 });
65 }
66
67 private void handleAllowedValues(
68 @NonNull IAllowedValuesConstraint allowedValues,
69 @NonNull IDefinitionNodeItem<?, ?> location,
70 @NonNull IDefinitionNodeItem<?, ?> target) {
71 NodeItemRecord itemRecord = nodeItemAnalysis.get(target);
72 if (itemRecord == null) {
73 itemRecord = new NodeItemRecord(target);
74 nodeItemAnalysis.put(target, itemRecord);
75 }
76
77 AllowedValuesRecord allowedValuesRecord = new AllowedValuesRecord(allowedValues, location, target);
78 itemRecord.addAllowedValues(allowedValuesRecord);
79 }
80
81 @Override
82 public Void visitFlag(IFlagNodeItem item, DynamicContext context) {
83 assert context != null;
84 DynamicContext subContext = handleLetStatements(item, context);
85 handleAllowedValuesAtLocation(item, subContext);
86 return super.visitFlag(item, subContext);
87 }
88
89 @Override
90 public Void visitField(IFieldNodeItem item, DynamicContext context) {
91 assert context != null;
92 DynamicContext subContext = handleLetStatements(item, context);
93 handleAllowedValuesAtLocation(item, subContext);
94 return super.visitField(item, subContext);
95 }
96
97 @Override
98 public Void visitAssembly(IAssemblyNodeItem item, DynamicContext context) {
99 assert context != null;
100 DynamicContext subContext = handleLetStatements(item, context);
101 handleAllowedValuesAtLocation(item, subContext);
102 return super.visitAssembly(item, subContext);
103 }
104
105 private DynamicContext handleLetStatements(IDefinitionNodeItem<?, ?> item, DynamicContext context) {
106 assert context != null;
107 DynamicContext subContext = context;
108 for (ILet let : item.getDefinition().getLetExpressions().values()) {
109 ISequence<?> result = let.getValueExpression().evaluate(item,
110 subContext).reusable();
111 subContext = subContext.bindVariableValue(let.getName(), result);
112 }
113 return subContext;
114 }
115
116 @Override
117 public Void visitAssembly(IAssemblyInstanceGroupedNodeItem item, DynamicContext context) {
118 return visitAssembly((IAssemblyNodeItem) item, context);
119 }
120
121 @Override
122 protected Void defaultResult() {
123 return null;
124 }
125
126 public static final class NodeItemRecord {
127 @NonNull
128 private final IDefinitionNodeItem<?, ?> item;
129 @NonNull
130 private final List<AllowedValuesRecord> allowedValues = new LinkedList<>();
131
132 private NodeItemRecord(@NonNull IDefinitionNodeItem<?, ?> item) {
133 this.item = item;
134 }
135
136 @NonNull
137 public IDefinitionNodeItem<?, ?> getItem() {
138 return item;
139 }
140
141 @NonNull
142 public List<AllowedValuesRecord> getAllowedValues() {
143 return allowedValues;
144 }
145
146 public void addAllowedValues(@NonNull AllowedValuesRecord record) {
147 this.allowedValues.add(record);
148 }
149 }
150
151 public static final class AllowedValuesRecord {
152 @NonNull
153 private final IAllowedValuesConstraint allowedValues;
154 @NonNull
155 private final IDefinitionNodeItem<?, ?> location;
156 @NonNull
157 private final IDefinitionNodeItem<?, ?> target;
158
159 public AllowedValuesRecord(
160 @NonNull IAllowedValuesConstraint allowedValues,
161 @NonNull IDefinitionNodeItem<?, ?> location,
162 @NonNull IDefinitionNodeItem<?, ?> target) {
163 this.allowedValues = allowedValues;
164 this.location = location;
165 this.target = target;
166 }
167
168 @NonNull
169 public IAllowedValuesConstraint getAllowedValues() {
170 return allowedValues;
171 }
172
173 @NonNull
174 public IDefinitionNodeItem<?, ?> getLocation() {
175 return location;
176 }
177
178 @NonNull
179 public IDefinitionNodeItem<?, ?> getTarget() {
180 return target;
181 }
182 }
183 }