1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model.impl;
7
8 import gov.nist.secauto.metaschema.core.model.AbstractChoiceGroupInstance;
9 import gov.nist.secauto.metaschema.core.model.DefaultChoiceGroupModelBuilder;
10 import gov.nist.secauto.metaschema.core.model.IBoundObject;
11 import gov.nist.secauto.metaschema.core.model.IContainerModelSupport;
12 import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
13 import gov.nist.secauto.metaschema.core.util.CustomCollectors;
14 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
15 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
16 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
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.IBoundInstanceModelGroupedField;
20 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelGroupedNamed;
21 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
22 import gov.nist.secauto.metaschema.databind.model.IGroupAs;
23 import gov.nist.secauto.metaschema.databind.model.annotations.BoundChoiceGroup;
24 import gov.nist.secauto.metaschema.databind.model.annotations.BoundGroupedAssembly;
25 import gov.nist.secauto.metaschema.databind.model.annotations.BoundGroupedField;
26 import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
27 import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
28 import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
29
30 import java.lang.reflect.Field;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.Map;
34 import java.util.stream.Collectors;
35
36 import edu.umd.cs.findbugs.annotations.NonNull;
37 import edu.umd.cs.findbugs.annotations.Nullable;
38 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39 import nl.talsmasoftware.lazy4j.Lazy;
40
41
42
43
44 @SuppressWarnings("PMD.CouplingBetweenObjects")
45 public final class InstanceModelChoiceGroup
46 extends AbstractChoiceGroupInstance<
47 IBoundDefinitionModelAssembly,
48 IBoundInstanceModelGroupedNamed,
49 IBoundInstanceModelGroupedField,
50 IBoundInstanceModelGroupedAssembly>
51 implements IBoundInstanceModelChoiceGroup, IFeatureInstanceModelGroupAs {
52 @NonNull
53 private final Field javaField;
54 @NonNull
55 private final BoundChoiceGroup annotation;
56 @NonNull
57 private final Lazy<IModelInstanceCollectionInfo<IBoundObject>> collectionInfo;
58 @NonNull
59 private final IGroupAs groupAs;
60 @NonNull
61 private final Lazy<IContainerModelSupport<
62 IBoundInstanceModelGroupedNamed,
63 IBoundInstanceModelGroupedNamed,
64 IBoundInstanceModelGroupedField,
65 IBoundInstanceModelGroupedAssembly>> modelContainer;
66 @NonNull
67 private final Lazy<Map<Class<?>, IBoundInstanceModelGroupedNamed>> classToInstanceMap;
68 @NonNull
69 private final Lazy<Map<IEnhancedQName, IBoundInstanceModelGroupedNamed>> qnameToInstanceMap;
70 @NonNull
71 private final Lazy<Map<String, IBoundInstanceModelGroupedNamed>> discriminatorToInstanceMap;
72
73
74
75
76
77
78
79
80
81
82 @NonNull
83 public static InstanceModelChoiceGroup newInstance(
84 @NonNull Field javaField,
85 @NonNull IBoundDefinitionModelAssembly parent) {
86 BoundChoiceGroup annotation = ModelUtil.getAnnotation(javaField, BoundChoiceGroup.class);
87 IGroupAs groupAs = ModelUtil.resolveDefaultGroupAs(annotation.groupAs(), parent.getContainingModule());
88 if (annotation.maxOccurs() == -1 || annotation.maxOccurs() > 1) {
89 if (IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
90 throw new IllegalStateException(String.format("Field '%s' on class '%s' is missing the '%s' annotation.",
91 javaField.getName(),
92 parent.getBoundClass().getName(),
93 GroupAs.class.getName()));
94 }
95 } else if (!IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
96
97 throw new IllegalStateException(
98 String.format(
99 "Field '%s' on class '%s' has the '%s' annotation, but maxOccurs=1. A groupAs must not be specfied.",
100 javaField.getName(),
101 parent.getBoundClass().getName(),
102 GroupAs.class.getName()));
103 }
104 return new InstanceModelChoiceGroup(javaField, annotation, groupAs, parent);
105 }
106
107 @NonNull
108 private static IContainerModelSupport<
109 IBoundInstanceModelGroupedNamed,
110 IBoundInstanceModelGroupedNamed,
111 IBoundInstanceModelGroupedField,
112 IBoundInstanceModelGroupedAssembly> newContainerModel(
113 @NonNull BoundGroupedAssembly[] assemblies,
114 @NonNull BoundGroupedField[] fields,
115 @NonNull IBoundInstanceModelChoiceGroup container) {
116 DefaultChoiceGroupModelBuilder<
117 IBoundInstanceModelGroupedNamed,
118 IBoundInstanceModelGroupedField,
119 IBoundInstanceModelGroupedAssembly> builder = new DefaultChoiceGroupModelBuilder<>();
120
121 Arrays.stream(assemblies)
122 .map(instance -> {
123 assert instance != null;
124 return IBoundInstanceModelGroupedAssembly.newInstance(instance, container);
125 }).forEachOrdered(builder::append);
126 Arrays.stream(fields)
127 .map(instance -> {
128 assert instance != null;
129 return IBoundInstanceModelGroupedField.newInstance(instance, container);
130 }).forEachOrdered(builder::append);
131
132 return builder.buildChoiceGroup();
133 }
134
135 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
136 private InstanceModelChoiceGroup(
137 @NonNull Field javaField,
138 @NonNull BoundChoiceGroup annotation,
139 @NonNull IGroupAs groupAs,
140 @NonNull IBoundDefinitionModelAssembly parent) {
141 super(parent);
142 FieldSupport.bindField(javaField);
143 this.javaField = javaField;
144 this.annotation = annotation;
145 this.groupAs = groupAs;
146 this.collectionInfo = ObjectUtils.notNull(Lazy.lazy(() -> IModelInstanceCollectionInfo.of(this)));
147 this.modelContainer = ObjectUtils.notNull(Lazy.lazy(() -> newContainerModel(
148 this.annotation.assemblies(),
149 this.annotation.fields(),
150 this)));
151 this.classToInstanceMap = ObjectUtils.notNull(Lazy.lazy(() -> Collections.unmodifiableMap(
152 getNamedModelInstances().stream()
153 .map(instance -> instance)
154 .collect(Collectors.toMap(
155 item -> item.getDefinition().getBoundClass(),
156 CustomCollectors.identity())))));
157 this.qnameToInstanceMap = ObjectUtils.notNull(Lazy.lazy(() -> Collections.unmodifiableMap(
158 getNamedModelInstances().stream()
159 .collect(Collectors.toMap(
160 IBoundInstanceModelGroupedNamed::getQName,
161 CustomCollectors.identity())))));
162 this.discriminatorToInstanceMap = ObjectUtils.notNull(Lazy.lazy(() -> Collections.unmodifiableMap(
163 getNamedModelInstances().stream()
164 .collect(Collectors.toMap(
165 IBoundInstanceModelGroupedNamed::getEffectiveDisciminatorValue,
166 CustomCollectors.identity())))));
167 }
168
169
170
171
172
173 @Override
174 public Field getField() {
175 return javaField;
176 }
177
178
179
180
181
182
183 @NonNull
184 public BoundChoiceGroup getAnnotation() {
185 return annotation;
186 }
187
188 @SuppressWarnings("null")
189 @Override
190 public IModelInstanceCollectionInfo<IBoundObject> getCollectionInfo() {
191 return collectionInfo.get();
192 }
193
194
195
196
197
198
199
200 @SuppressWarnings("null")
201 @NonNull
202 protected Map<IEnhancedQName, IBoundInstanceModelGroupedNamed> getQNameToInstanceMap() {
203 return qnameToInstanceMap.get();
204 }
205
206
207
208
209
210
211 @SuppressWarnings("null")
212 @NonNull
213 protected Map<Class<?>, IBoundInstanceModelGroupedNamed> getClassToInstanceMap() {
214 return classToInstanceMap.get();
215 }
216
217
218
219
220
221
222
223 @SuppressWarnings("null")
224 @NonNull
225 protected Map<String, IBoundInstanceModelGroupedNamed> getDiscriminatorToInstanceMap() {
226 return discriminatorToInstanceMap.get();
227 }
228
229 @Override
230 @Nullable
231 public IBoundInstanceModelGroupedNamed getGroupedModelInstance(@NonNull Class<?> clazz) {
232 return getClassToInstanceMap().get(clazz);
233 }
234
235 @Override
236 @Nullable
237 public IBoundInstanceModelGroupedNamed getGroupedModelInstance(@NonNull IEnhancedQName name) {
238 return getQNameToInstanceMap().get(name);
239 }
240
241 @Override
242 public IBoundInstanceModelGroupedNamed getGroupedModelInstance(String discriminator) {
243 return getDiscriminatorToInstanceMap().get(discriminator);
244 }
245
246 @Override
247 public IGroupAs getGroupAs() {
248 return groupAs;
249 }
250
251 @SuppressWarnings("null")
252 @Override
253 public IContainerModelSupport<
254 IBoundInstanceModelGroupedNamed,
255 IBoundInstanceModelGroupedNamed,
256 IBoundInstanceModelGroupedField,
257 IBoundInstanceModelGroupedAssembly> getModelContainer() {
258 return modelContainer.get();
259 }
260
261 @Override
262 public IBoundDefinitionModelAssembly getOwningDefinition() {
263 return getParentContainer();
264 }
265
266 @Override
267 public IBoundModule getContainingModule() {
268 return getOwningDefinition().getContainingModule();
269 }
270
271 @Override
272 public int getMinOccurs() {
273 return getAnnotation().minOccurs();
274 }
275
276 @Override
277 public int getMaxOccurs() {
278 return getAnnotation().maxOccurs();
279 }
280
281 @Override
282 public String getJsonDiscriminatorProperty() {
283 return getAnnotation().discriminator();
284 }
285
286 @Override
287 public String getJsonKeyFlagInstanceName() {
288 return getAnnotation().jsonKey();
289 }
290
291 @Override
292 public IBoundInstanceFlag getItemJsonKey(Object item) {
293 String jsonKeyFlagName = getJsonKeyFlagInstanceName();
294 IBoundInstanceFlag retval = null;
295
296 if (jsonKeyFlagName != null) {
297 Class<?> clazz = item.getClass();
298
299 IBoundInstanceModelGroupedNamed itemInstance = getClassToInstanceMap().get(clazz);
300 String namespace = itemInstance.getQName().getNamespace();
301 retval = itemInstance.getDefinition().getFlagInstanceByName(IEnhancedQName.of(namespace, jsonKeyFlagName)
302 .getIndexPosition());
303 }
304 return retval;
305 }
306 }