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; 022 023import java.util.Collection; 024import java.util.HashSet; 025import java.util.LinkedHashMap; 026import java.util.Map; 027import java.util.Set; 028import java.util.concurrent.atomic.AtomicBoolean; 029 030import edu.umd.cs.findbugs.annotations.NonNull; 031 032public class ModuleIndex { 033 private final Map<IDefinition, DefinitionEntry> index = new LinkedHashMap<>();// new ConcurrentHashMap<>(); 034 035 @NonNull 036 public static ModuleIndex indexDefinitions(@NonNull IModule module, @NonNull IInlineStrategy inlineStrategy) { 037 Collection<? extends IAssemblyDefinition> definitions = module.getExportedRootAssemblyDefinitions(); 038 ModuleIndex index = new ModuleIndex(); 039 if (!definitions.isEmpty()) { 040 IndexVisitor visitor = new IndexVisitor(index, inlineStrategy); 041 for (IAssemblyDefinition definition : definitions) { 042 assert definition != null; 043 044 // // add the root definition to the index 045 // index.getEntry(definition).incrementReferenceCount(); 046 047 // walk the definition 048 visitor.walk(ObjectUtils.requireNonNull(definition)); 049 } 050 } 051 return index; 052 } 053 054 public boolean hasEntry(@NonNull IDefinition definition) { 055 return index.containsKey(definition); 056 } 057 058 @NonNull 059 public DefinitionEntry getEntry(@NonNull IDefinition definition) { 060 return ObjectUtils.notNull(index.computeIfAbsent( 061 definition, 062 k -> new ModuleIndex.DefinitionEntry(ObjectUtils.notNull(k)))); 063 } 064 065 @NonNull 066 public Collection<DefinitionEntry> getDefinitions() { 067 return ObjectUtils.notNull(index.values()); 068 } 069 070 private static class IndexVisitor 071 extends ModelWalker<ModuleIndex> { 072 @NonNull 073 private final IInlineStrategy inlineStrategy; 074 @NonNull 075 private final ModuleIndex index; 076 077 public IndexVisitor(@NonNull ModuleIndex index, @NonNull IInlineStrategy inlineStrategy) { 078 this.index = index; 079 this.inlineStrategy = inlineStrategy; 080 } 081 082 @Override 083 protected ModuleIndex getDefaultData() { 084 return index; 085 } 086 087 @Override 088 protected boolean visit(IFlagInstance instance, ModuleIndex index) { 089 handleInstance(instance); 090 return true; 091 } 092 093 @Override 094 protected boolean visit(IFieldInstance instance, ModuleIndex index) { 095 handleInstance(instance); 096 return true; 097 } 098 099 @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 // @Override 111 // protected boolean visit(IAssemblyDefinition def, ModuleIndex data) { 112 // // only walk if the definition hasn't already been visited 113 // return !index.hasEntry(def); 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 * Updates the index entry for the definition associated with the reference. 141 * 142 * @param instance 143 * the instance to process 144 */ 145 @NonNull 146 private DefinitionEntry handleInstance(INamedInstance instance) { 147 IDefinition definition = instance.getDefinition(); 148 // check if this will be a new entry, which needs to be called before getEntry, 149 // which will create it 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(); // false 179 private final AtomicBoolean visited = new AtomicBoolean(); // false 180 private final AtomicBoolean usedAsChoice = new AtomicBoolean(); // false 181 private final AtomicBoolean choiceSibling = new AtomicBoolean(); // false 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}