001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.metaschema.schemagen; 007 008import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; 009import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; 010import gov.nist.secauto.metaschema.core.model.IChoiceInstance; 011import gov.nist.secauto.metaschema.core.model.IDefinition; 012import gov.nist.secauto.metaschema.core.model.IFieldDefinition; 013import gov.nist.secauto.metaschema.core.model.IFieldInstance; 014import gov.nist.secauto.metaschema.core.model.IFlagDefinition; 015import gov.nist.secauto.metaschema.core.model.IFlagInstance; 016import gov.nist.secauto.metaschema.core.model.IModule; 017import gov.nist.secauto.metaschema.core.model.INamedInstance; 018import gov.nist.secauto.metaschema.core.model.INamedModelInstance; 019import gov.nist.secauto.metaschema.core.model.INamedModelInstanceGrouped; 020import gov.nist.secauto.metaschema.core.model.ModelWalker; 021import gov.nist.secauto.metaschema.core.util.ObjectUtils; 022import gov.nist.secauto.metaschema.databind.codegen.JavaGenerator; 023 024import org.apache.logging.log4j.LogManager; 025import org.apache.logging.log4j.Logger; 026 027import java.util.Collection; 028import java.util.HashSet; 029import java.util.LinkedHashMap; 030import java.util.Map; 031import java.util.Set; 032import java.util.concurrent.atomic.AtomicBoolean; 033 034import edu.umd.cs.findbugs.annotations.NonNull; 035 036public class ModuleIndex { 037 private static final Logger LOGGER = LogManager.getLogger(JavaGenerator.class); 038 039 private final Map<IDefinition, DefinitionEntry> index = new LinkedHashMap<>();// new ConcurrentHashMap<>(); 040 041 @NonNull 042 public static ModuleIndex indexDefinitions(@NonNull IModule module, @NonNull IInlineStrategy inlineStrategy) { 043 Collection<? extends IAssemblyDefinition> definitions = module.getExportedRootAssemblyDefinitions(); 044 ModuleIndex index = new ModuleIndex(); 045 if (!definitions.isEmpty()) { 046 IndexVisitor visitor = new IndexVisitor(index, inlineStrategy); 047 for (IAssemblyDefinition definition : definitions) { 048 assert definition != null; 049 050 // // add the root definition to the index 051 // index.getEntry(definition).incrementReferenceCount(); 052 053 // walk the definition 054 visitor.walk(ObjectUtils.requireNonNull(definition)); 055 } 056 } 057 return index; 058 } 059 060 public boolean hasEntry(@NonNull IDefinition definition) { 061 return index.containsKey(definition); 062 } 063 064 @NonNull 065 public DefinitionEntry getEntry(@NonNull IDefinition definition) { 066 return ObjectUtils.notNull(index.computeIfAbsent( 067 definition, 068 k -> new ModuleIndex.DefinitionEntry(ObjectUtils.notNull(k)))); 069 } 070 071 @NonNull 072 public Collection<DefinitionEntry> getDefinitions() { 073 return ObjectUtils.notNull(index.values()); 074 } 075 076 private static class IndexVisitor 077 extends ModelWalker<ModuleIndex> { 078 @NonNull 079 private final IInlineStrategy inlineStrategy; 080 @NonNull 081 private final ModuleIndex index; 082 083 public IndexVisitor(@NonNull ModuleIndex index, @NonNull IInlineStrategy inlineStrategy) { 084 this.index = index; 085 this.inlineStrategy = inlineStrategy; 086 } 087 088 @Override 089 protected ModuleIndex getDefaultData() { 090 return index; 091 } 092 093 @Override 094 protected boolean visit(IFlagInstance instance, ModuleIndex index) { 095 handleInstance(instance); 096 return true; 097 } 098 099 @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 // @Override 117 // protected boolean visit(IAssemblyDefinition def, ModuleIndex data) { 118 // // only walk if the definition hasn't already been visited 119 // return !index.hasEntry(def); 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 * Updates the index entry for the definition associated with the reference. 147 * 148 * @param instance 149 * the instance to process 150 */ 151 @NonNull 152 private DefinitionEntry handleInstance(INamedInstance instance) { 153 IDefinition definition = instance.getDefinition(); 154 // check if this will be a new entry, which needs to be called before getEntry, 155 // which will create it 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(); // false 185 private final AtomicBoolean visited = new AtomicBoolean(); // false 186 private final AtomicBoolean usedAsChoice = new AtomicBoolean(); // false 187 private final AtomicBoolean choiceSibling = new AtomicBoolean(); // false 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}