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