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