001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.schemagen; 028 029import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; 030import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; 031import gov.nist.secauto.metaschema.core.model.IChoiceInstance; 032import gov.nist.secauto.metaschema.core.model.IDefinition; 033import gov.nist.secauto.metaschema.core.model.IFieldInstance; 034import gov.nist.secauto.metaschema.core.model.IFlagDefinition; 035import gov.nist.secauto.metaschema.core.model.IFlagInstance; 036import gov.nist.secauto.metaschema.core.model.IModule; 037import gov.nist.secauto.metaschema.core.model.INamedInstance; 038import gov.nist.secauto.metaschema.core.model.ModelWalker; 039import gov.nist.secauto.metaschema.core.util.ObjectUtils; 040 041import java.util.Collection; 042import java.util.Map; 043import java.util.concurrent.ConcurrentHashMap; 044import java.util.concurrent.atomic.AtomicBoolean; 045import java.util.concurrent.atomic.AtomicInteger; 046 047import edu.umd.cs.findbugs.annotations.NonNull; 048 049public class ModuleIndex { 050 private final Map<IDefinition, DefinitionEntry> index = new ConcurrentHashMap<>(); 051 052 @NonNull 053 public static ModuleIndex indexDefinitions(@NonNull IModule module) { 054 Collection<? extends IAssemblyDefinition> definitions = module.getExportedRootAssemblyDefinitions(); 055 ModuleIndex index = new ModuleIndex(); 056 if (!definitions.isEmpty()) { 057 IndexVisitor visitor = new IndexVisitor(index); 058 for (IAssemblyDefinition definition : definitions) { 059 assert definition != null; 060 061 // add the root definition to the index 062 index.getEntry(definition).incrementReferenceCount(); 063 064 // walk the definition 065 visitor.walk(ObjectUtils.requireNonNull(definition)); 066 } 067 } 068 return index; 069 } 070 071 public boolean hasEntry(@NonNull IDefinition definition) { 072 return index.containsKey(definition); 073 } 074 075 @NonNull 076 public DefinitionEntry getEntry(@NonNull IDefinition definition) { 077 return ObjectUtils.notNull(index.computeIfAbsent( 078 definition, 079 k -> new ModuleIndex.DefinitionEntry(ObjectUtils.notNull(k)))); 080 } 081 082 @NonNull 083 public Collection<DefinitionEntry> getDefinitions() { 084 return ObjectUtils.notNull(index.values()); 085 } 086 087 private static class IndexVisitor 088 extends ModelWalker<ModuleIndex> { 089 private final ModuleIndex index; 090 091 public IndexVisitor(@NonNull ModuleIndex index) { 092 this.index = index; 093 } 094 095 @Override 096 protected ModuleIndex getDefaultData() { 097 return index; 098 } 099 100 @Override 101 protected boolean visit(IFlagInstance instance, ModuleIndex data) { 102 return handleInstance(instance); 103 } 104 105 @Override 106 protected boolean visit(IFieldInstance instance, ModuleIndex data) { 107 return handleInstance(instance); 108 } 109 110 @Override 111 protected boolean visit(IAssemblyInstance instance, ModuleIndex data) { 112 return handleInstance(instance); 113 } 114 115 @Override 116 protected void visit(IFlagDefinition def, ModuleIndex data) { 117 // do nothing 118 } 119 // 120 // @Override 121 // protected boolean visit(IAssemblyDefinition def, ModuleIndex data) { 122 // // only walk if the definition hasn't already been visited 123 // return !index.hasEntry(def); 124 // } 125 126 /** 127 * Updates the index entry for the definition associated with the reference. 128 * 129 * @param instance 130 * the instance to process 131 * @return {@code true} if this is the first time handling the definition this 132 * instance references, or {@code false} otherwise 133 */ 134 private boolean handleInstance(INamedInstance instance) { 135 IDefinition definition = instance.getDefinition(); 136 // check if this will be a new entry, which needs to be called before getEntry, 137 // which will create it 138 final boolean exists = getDefaultData().hasEntry(definition); 139 DefinitionEntry entry = getDefaultData().getEntry(definition); 140 entry.incrementReferenceCount(); 141 142 if (isChoice(instance)) { 143 entry.markUsedAsChoice(); 144 } 145 146 if (isChoiceSibling(instance)) { 147 entry.markAsChoiceSibling(); 148 } 149 return !exists; 150 } 151 152 private static boolean isChoice(@NonNull INamedInstance instance) { 153 return instance.getParentContainer() instanceof IChoiceInstance; 154 } 155 156 private static boolean isChoiceSibling(@NonNull INamedInstance instance) { 157 IDefinition containingDefinition = instance.getContainingDefinition(); 158 return containingDefinition instanceof IAssemblyDefinition 159 && !((IAssemblyDefinition) containingDefinition).getChoiceInstances().isEmpty(); 160 } 161 } 162 163 public static class DefinitionEntry { 164 @NonNull 165 private final IDefinition definition; 166 private final AtomicInteger referenceCount = new AtomicInteger(); // 0 167 private final AtomicBoolean usedAsChoice = new AtomicBoolean(); // false 168 private final AtomicBoolean choiceSibling = new AtomicBoolean(); // false 169 170 public DefinitionEntry(@NonNull IDefinition definition) { 171 this.definition = definition; 172 } 173 174 public IDefinition getDefinition() { 175 return definition; 176 } 177 178 public boolean isReferenced() { 179 return getReferenceCount() > 0; 180 } 181 182 public int getReferenceCount() { 183 return referenceCount.get(); 184 } 185 186 public int incrementReferenceCount() { 187 return referenceCount.incrementAndGet(); 188 } 189 190 public int incrementReferenceCount(int increment) { 191 return referenceCount.addAndGet(increment); 192 } 193 194 public boolean isInline() { 195 return getDefinition().isInline(); 196 } 197 198 public void markUsedAsChoice() { 199 usedAsChoice.compareAndSet(false, true); 200 } 201 202 public boolean isUsedAsChoice() { 203 return usedAsChoice.get(); 204 } 205 206 public void markAsChoiceSibling() { 207 choiceSibling.compareAndSet(false, true); 208 } 209 210 public boolean isChoiceSibling() { 211 return choiceSibling.get(); 212 } 213 } 214}