1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.model.metaschema.impl;
7   
8   import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory;
9   import gov.nist.secauto.metaschema.core.model.IAssemblyInstanceAbsolute;
10  import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
11  import gov.nist.secauto.metaschema.core.model.IContainerModelSupport;
12  import gov.nist.secauto.metaschema.core.model.IFieldInstanceAbsolute;
13  import gov.nist.secauto.metaschema.core.model.IModelInstanceAbsolute;
14  import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute;
15  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
16  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelChoiceGroup;
18  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedAssembly;
19  import gov.nist.secauto.metaschema.databind.model.metaschema.binding.AssemblyReference;
20  import gov.nist.secauto.metaschema.databind.model.metaschema.binding.FieldReference;
21  import gov.nist.secauto.metaschema.databind.model.metaschema.binding.InlineDefineAssembly;
22  import gov.nist.secauto.metaschema.databind.model.metaschema.binding.InlineDefineField;
23  import gov.nist.secauto.metaschema.databind.model.metaschema.binding.AssemblyModel.Choice;
24  
25  import java.util.LinkedHashMap;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.concurrent.atomic.AtomicInteger;
30  
31  import javax.xml.namespace.QName;
32  
33  import edu.umd.cs.findbugs.annotations.NonNull;
34  import edu.umd.cs.findbugs.annotations.Nullable;
35  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
36  
37  class ChoiceModelContainerSupport
38      extends AbstractBindingModelContainerSupport {
39    @NonNull
40    private final List<IModelInstanceAbsolute> modelInstances;
41    @NonNull
42    private final Map<QName, INamedModelInstanceAbsolute> namedModelInstances;
43    @NonNull
44    private final Map<QName, IFieldInstanceAbsolute> fieldInstances;
45    @NonNull
46    private final Map<QName, IAssemblyInstanceAbsolute> assemblyInstances;
47  
48    @SuppressWarnings("PMD.ShortMethodName")
49    public static IContainerModelSupport<
50        IModelInstanceAbsolute,
51        INamedModelInstanceAbsolute,
52        IFieldInstanceAbsolute,
53        IAssemblyInstanceAbsolute> of(
54            @Nullable Choice binding,
55            @NonNull IBoundInstanceModelGroupedAssembly bindingInstance,
56            @NonNull IChoiceInstance parent,
57            @NonNull INodeItemFactory nodeItemFactory) {
58      List<Object> instances;
59      return binding == null || (instances = binding.getChoices()) == null || instances.isEmpty()
60          ? IContainerModelSupport.empty()
61          : new ChoiceModelContainerSupport(
62              binding,
63              bindingInstance,
64              parent,
65              nodeItemFactory);
66    }
67  
68    /**
69     * Construct a new assembly model container.
70     *
71     * @param binding
72     *          the choice model object bound to a Java class
73     * @param bindingInstance
74     *          the Metaschema module instance for the bound model object
75     * @param parent
76     *          the assembly definition containing this container
77     * @param nodeItemFactory
78     *          the node item factory used to generate child nodes
79     */
80    @SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseConcurrentHashMap", "PMD.PrematureDeclaration" })
81    @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
82    public ChoiceModelContainerSupport(
83        @NonNull Choice binding,
84        @NonNull IBoundInstanceModelGroupedAssembly bindingInstance,
85        @NonNull IChoiceInstance parent,
86        @NonNull INodeItemFactory nodeItemFactory) {
87  
88      // create temporary collections to store the child binding objects
89      final List<IModelInstanceAbsolute> modelInstances = new LinkedList<>();
90      final Map<QName, INamedModelInstanceAbsolute> namedModelInstances = new LinkedHashMap<>();
91      final Map<QName, IFieldInstanceAbsolute> fieldInstances = new LinkedHashMap<>();
92      final Map<QName, IAssemblyInstanceAbsolute> assemblyInstances = new LinkedHashMap<>();
93  
94      // create counters to track child positions
95      AtomicInteger assemblyReferencePosition = new AtomicInteger();
96      AtomicInteger assemblyInlineDefinitionPosition = new AtomicInteger();
97      AtomicInteger fieldReferencePosition = new AtomicInteger();
98      AtomicInteger fieldInlineDefinitionPosition = new AtomicInteger();
99  
100     // TODO: make "instances" a constant
101     IBoundInstanceModelChoiceGroup instance = ObjectUtils.requireNonNull(
102         bindingInstance.getDefinition().getChoiceGroupInstanceByName("choices"));
103 
104     ObjectUtils.notNull(binding.getChoices()).forEach(obj -> {
105       assert obj != null;
106       IBoundInstanceModelGroupedAssembly objInstance
107           = (IBoundInstanceModelGroupedAssembly) instance.getItemInstance(obj);
108 
109       if (obj instanceof AssemblyReference) {
110         addInstance(
111             newInstance(
112                 (AssemblyReference) obj,
113                 objInstance,
114                 assemblyReferencePosition.getAndIncrement(),
115                 parent),
116             modelInstances,
117             namedModelInstances,
118             assemblyInstances);
119       } else if (obj instanceof InlineDefineAssembly) {
120         IAssemblyInstanceAbsolute assembly = new InstanceModelAssemblyInline(
121             (InlineDefineAssembly) obj,
122             objInstance,
123             assemblyInlineDefinitionPosition.getAndIncrement(),
124             parent,
125             nodeItemFactory);
126         addInstance(assembly, modelInstances, namedModelInstances, assemblyInstances);
127       } else if (obj instanceof FieldReference) {
128         IFieldInstanceAbsolute field = newInstance(
129             (FieldReference) obj,
130             objInstance,
131             fieldReferencePosition.getAndIncrement(),
132             parent);
133         addInstance(field, modelInstances, namedModelInstances, fieldInstances);
134       } else if (obj instanceof InlineDefineField) {
135         IFieldInstanceAbsolute field = new InstanceModelFieldInline(
136             (InlineDefineField) obj,
137             objInstance,
138             fieldInlineDefinitionPosition.getAndIncrement(),
139             parent);
140         addInstance(field, modelInstances, namedModelInstances, fieldInstances);
141       } else {
142         throw new UnsupportedOperationException(String.format("Unknown model instance class: %s", obj.getClass()));
143       }
144     });
145 
146     this.modelInstances = modelInstances.isEmpty()
147         ? CollectionUtil.emptyList()
148         : CollectionUtil.unmodifiableList(modelInstances);
149     this.namedModelInstances = namedModelInstances.isEmpty()
150         ? CollectionUtil.emptyMap()
151         : CollectionUtil.unmodifiableMap(namedModelInstances);
152     this.fieldInstances = fieldInstances.isEmpty()
153         ? CollectionUtil.emptyMap()
154         : CollectionUtil.unmodifiableMap(fieldInstances);
155     this.assemblyInstances = assemblyInstances.isEmpty()
156         ? CollectionUtil.emptyMap()
157         : CollectionUtil.unmodifiableMap(assemblyInstances);
158   }
159 
160   @Override
161   public List<IModelInstanceAbsolute> getModelInstances() {
162     return modelInstances;
163   }
164 
165   @Override
166   public Map<QName, INamedModelInstanceAbsolute> getNamedModelInstanceMap() {
167     return namedModelInstances;
168   }
169 
170   @Override
171   public Map<QName, IFieldInstanceAbsolute> getFieldInstanceMap() {
172     return fieldInstances;
173   }
174 
175   @Override
176   public Map<QName, IAssemblyInstanceAbsolute> getAssemblyInstanceMap() {
177     return assemblyInstances;
178   }
179 }