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