View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.schemagen;
28  
29  import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
30  import gov.nist.secauto.metaschema.core.model.IAssemblyInstance;
31  import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
32  import gov.nist.secauto.metaschema.core.model.IDefinition;
33  import gov.nist.secauto.metaschema.core.model.IFieldInstance;
34  import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
35  import gov.nist.secauto.metaschema.core.model.IFlagInstance;
36  import gov.nist.secauto.metaschema.core.model.IModule;
37  import gov.nist.secauto.metaschema.core.model.INamedInstance;
38  import gov.nist.secauto.metaschema.core.model.ModelWalker;
39  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
40  
41  import java.util.Collection;
42  import java.util.Map;
43  import java.util.concurrent.ConcurrentHashMap;
44  import java.util.concurrent.atomic.AtomicBoolean;
45  import java.util.concurrent.atomic.AtomicInteger;
46  
47  import edu.umd.cs.findbugs.annotations.NonNull;
48  
49  public class ModuleIndex {
50    private final Map<IDefinition, DefinitionEntry> index = new ConcurrentHashMap<>();
51  
52    @NonNull
53    public static ModuleIndex indexDefinitions(@NonNull IModule module) {
54      Collection<? extends IAssemblyDefinition> definitions = module.getExportedRootAssemblyDefinitions();
55      ModuleIndex index = new ModuleIndex();
56      if (!definitions.isEmpty()) {
57        IndexVisitor visitor = new IndexVisitor(index);
58        for (IAssemblyDefinition definition : definitions) {
59          assert definition != null;
60  
61          // add the root definition to the index
62          index.getEntry(definition).incrementReferenceCount();
63  
64          // walk the definition
65          visitor.walk(ObjectUtils.requireNonNull(definition));
66        }
67      }
68      return index;
69    }
70  
71    public boolean hasEntry(@NonNull IDefinition definition) {
72      return index.containsKey(definition);
73    }
74  
75    @NonNull
76    public DefinitionEntry getEntry(@NonNull IDefinition definition) {
77      return ObjectUtils.notNull(index.computeIfAbsent(
78          definition,
79          k -> new ModuleIndex.DefinitionEntry(ObjectUtils.notNull(k))));
80    }
81  
82    @NonNull
83    public Collection<DefinitionEntry> getDefinitions() {
84      return ObjectUtils.notNull(index.values());
85    }
86  
87    private static class IndexVisitor
88        extends ModelWalker<ModuleIndex> {
89      private final ModuleIndex index;
90  
91      public IndexVisitor(@NonNull ModuleIndex index) {
92        this.index = index;
93      }
94  
95      @Override
96      protected ModuleIndex getDefaultData() {
97        return index;
98      }
99  
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 }