1
2
3
4
5
6 package gov.nist.secauto.metaschema.schemagen;
7
8 import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
9 import gov.nist.secauto.metaschema.core.model.IAssemblyInstance;
10 import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
11 import gov.nist.secauto.metaschema.core.model.IDefinition;
12 import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
13 import gov.nist.secauto.metaschema.core.model.IFieldInstance;
14 import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
15 import gov.nist.secauto.metaschema.core.model.IFlagInstance;
16 import gov.nist.secauto.metaschema.core.model.IModule;
17 import gov.nist.secauto.metaschema.core.model.INamedInstance;
18 import gov.nist.secauto.metaschema.core.model.INamedModelInstance;
19 import gov.nist.secauto.metaschema.core.model.INamedModelInstanceGrouped;
20 import gov.nist.secauto.metaschema.core.model.ModelWalker;
21 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
22
23 import java.util.Collection;
24 import java.util.HashSet;
25 import java.util.LinkedHashMap;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.atomic.AtomicBoolean;
29
30 import edu.umd.cs.findbugs.annotations.NonNull;
31
32 public class ModuleIndex {
33 private final Map<IDefinition, DefinitionEntry> index = new LinkedHashMap<>();
34
35 @NonNull
36 public static ModuleIndex indexDefinitions(@NonNull IModule module, @NonNull IInlineStrategy inlineStrategy) {
37 Collection<? extends IAssemblyDefinition> definitions = module.getExportedRootAssemblyDefinitions();
38 ModuleIndex index = new ModuleIndex();
39 if (!definitions.isEmpty()) {
40 IndexVisitor visitor = new IndexVisitor(index, inlineStrategy);
41 for (IAssemblyDefinition definition : definitions) {
42 assert definition != null;
43
44
45
46
47
48 visitor.walk(ObjectUtils.requireNonNull(definition));
49 }
50 }
51 return index;
52 }
53
54 public boolean hasEntry(@NonNull IDefinition definition) {
55 return index.containsKey(definition);
56 }
57
58 @NonNull
59 public DefinitionEntry getEntry(@NonNull IDefinition definition) {
60 return ObjectUtils.notNull(index.computeIfAbsent(
61 definition,
62 k -> new ModuleIndex.DefinitionEntry(ObjectUtils.notNull(k))));
63 }
64
65 @NonNull
66 public Collection<DefinitionEntry> getDefinitions() {
67 return ObjectUtils.notNull(index.values());
68 }
69
70 private static class IndexVisitor
71 extends ModelWalker<ModuleIndex> {
72 @NonNull
73 private final IInlineStrategy inlineStrategy;
74 @NonNull
75 private final ModuleIndex index;
76
77 public IndexVisitor(@NonNull ModuleIndex index, @NonNull IInlineStrategy inlineStrategy) {
78 this.index = index;
79 this.inlineStrategy = inlineStrategy;
80 }
81
82 @Override
83 protected ModuleIndex getDefaultData() {
84 return index;
85 }
86
87 @Override
88 protected boolean visit(IFlagInstance instance, ModuleIndex index) {
89 handleInstance(instance);
90 return true;
91 }
92
93 @Override
94 protected boolean visit(IFieldInstance instance, ModuleIndex index) {
95 handleInstance(instance);
96 return true;
97 }
98
99 @Override
100 protected boolean visit(IAssemblyInstance instance, ModuleIndex index) {
101 handleInstance(instance);
102 return true;
103 }
104
105 @Override
106 protected void visit(IFlagDefinition def, ModuleIndex data) {
107 handleDefinition(def);
108 }
109
110
111
112
113
114
115
116 @Override
117 protected boolean visit(IFieldDefinition def, ModuleIndex data) {
118 return handleDefinition(def);
119 }
120
121 @Override
122 protected boolean visit(IAssemblyDefinition def, ModuleIndex data) {
123 return handleDefinition(def);
124 }
125
126 private boolean handleDefinition(@NonNull IDefinition definition) {
127 DefinitionEntry entry = getDefaultData().getEntry(definition);
128 boolean visited = entry.isVisited();
129 if (!visited) {
130 entry.markVisited();
131
132 if (inlineStrategy.isInline(definition, index)) {
133 entry.markInline();
134 }
135 }
136 return !visited;
137 }
138
139
140
141
142
143
144
145 @NonNull
146 private DefinitionEntry handleInstance(INamedInstance instance) {
147 IDefinition definition = instance.getDefinition();
148
149
150 DefinitionEntry entry = getDefaultData().getEntry(definition);
151 entry.addReference(instance);
152
153 if (isChoice(instance)) {
154 entry.markUsedAsChoice();
155 }
156
157 if (isChoiceSibling(instance)) {
158 entry.markAsChoiceSibling();
159 }
160 return entry;
161 }
162
163 private static boolean isChoice(@NonNull INamedInstance instance) {
164 return instance.getParentContainer() instanceof IChoiceInstance;
165 }
166
167 private static boolean isChoiceSibling(@NonNull INamedInstance instance) {
168 IDefinition containingDefinition = instance.getContainingDefinition();
169 return containingDefinition instanceof IAssemblyDefinition
170 && !((IAssemblyDefinition) containingDefinition).getChoiceInstances().isEmpty();
171 }
172 }
173
174 public static class DefinitionEntry {
175 @NonNull
176 private final IDefinition definition;
177 private final Set<INamedInstance> references = new HashSet<>();
178 private final AtomicBoolean inline = new AtomicBoolean();
179 private final AtomicBoolean visited = new AtomicBoolean();
180 private final AtomicBoolean usedAsChoice = new AtomicBoolean();
181 private final AtomicBoolean choiceSibling = new AtomicBoolean();
182
183 public DefinitionEntry(@NonNull IDefinition definition) {
184 this.definition = definition;
185 }
186
187 @NonNull
188 public IDefinition getDefinition() {
189 return definition;
190 }
191
192 public boolean isRoot() {
193 return definition instanceof IAssemblyDefinition
194 && ((IAssemblyDefinition) definition).isRoot();
195 }
196
197 public boolean isReferenced() {
198 return !references.isEmpty()
199 || isRoot();
200 }
201
202 public Set<INamedInstance> getReferences() {
203 return references;
204 }
205
206 public boolean addReference(@NonNull INamedInstance reference) {
207 return references.add(reference);
208 }
209
210 public void markVisited() {
211 visited.compareAndSet(false, true);
212 }
213
214 public boolean isVisited() {
215 return visited.get();
216 }
217
218 public void markInline() {
219 inline.compareAndSet(false, true);
220 }
221
222 public boolean isInline() {
223 return inline.get();
224 }
225
226 public void markUsedAsChoice() {
227 usedAsChoice.compareAndSet(false, true);
228 }
229
230 public boolean isUsedAsChoice() {
231 return usedAsChoice.get();
232 }
233
234 public void markAsChoiceSibling() {
235 choiceSibling.compareAndSet(false, true);
236 }
237
238 public boolean isChoiceSibling() {
239 return choiceSibling.get();
240 }
241
242 public boolean isUsedAsJsonKey() {
243 return references.stream()
244 .anyMatch(ref -> ref instanceof INamedModelInstance
245 && ((INamedModelInstance) ref).hasJsonKey());
246 }
247
248 public boolean isUsedWithoutJsonKey() {
249 return definition instanceof IFlagDefinition
250 || references.isEmpty()
251 || references.stream()
252 .anyMatch(ref -> ref instanceof INamedModelInstance
253 && !((INamedModelInstance) ref).hasJsonKey());
254 }
255
256 public boolean isChoiceGroupMember() {
257 return references.stream()
258 .anyMatch(INamedModelInstanceGrouped.class::isInstance);
259 }
260 }
261 }