1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model.impl;
7
8 import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
9 import gov.nist.secauto.metaschema.core.model.IContainerModelAssemblySupport;
10 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
11 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
12 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
13 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModel;
14 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelAssembly;
15 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelChoiceGroup;
16 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelField;
17 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelNamed;
18 import gov.nist.secauto.metaschema.databind.model.annotations.BoundAssembly;
19 import gov.nist.secauto.metaschema.databind.model.annotations.BoundChoiceGroup;
20 import gov.nist.secauto.metaschema.databind.model.annotations.BoundField;
21 import gov.nist.secauto.metaschema.databind.model.annotations.Ignore;
22
23 import java.lang.reflect.Field;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
32
33 import javax.xml.namespace.QName;
34
35 import edu.umd.cs.findbugs.annotations.NonNull;
36 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
37
38 class AssemblyModelContainerSupport
39 implements IContainerModelAssemblySupport<
40 IBoundInstanceModel<?>,
41 IBoundInstanceModelNamed<?>,
42 IBoundInstanceModelField<?>,
43 IBoundInstanceModelAssembly,
44 IChoiceInstance,
45 IBoundInstanceModelChoiceGroup> {
46 @NonNull
47 private final List<IBoundInstanceModel<?>> modelInstances;
48 @NonNull
49 private final Map<QName, IBoundInstanceModelNamed<?>> namedModelInstances;
50 @NonNull
51 private final Map<QName, IBoundInstanceModelField<?>> fieldInstances;
52 @NonNull
53 private final Map<QName, IBoundInstanceModelAssembly> assemblyInstances;
54 @NonNull
55 private final Map<String, IBoundInstanceModelChoiceGroup> choiceGroupInstances;
56
57 @SuppressWarnings("PMD.UseConcurrentHashMap")
58 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
59 public AssemblyModelContainerSupport(
60 @NonNull DefinitionAssembly containingDefinition) {
61 this.modelInstances = CollectionUtil.unmodifiableList(ObjectUtils.notNull(
62 getModelInstanceStream(containingDefinition, containingDefinition.getBoundClass())
63 .collect(Collectors.toUnmodifiableList())));
64
65 Map<QName, IBoundInstanceModelNamed<?>> namedModelInstances = new LinkedHashMap<>();
66 Map<QName, IBoundInstanceModelField<?>> fieldInstances = new LinkedHashMap<>();
67 Map<QName, IBoundInstanceModelAssembly> assemblyInstances = new LinkedHashMap<>();
68 Map<String, IBoundInstanceModelChoiceGroup> choiceGroupInstances = new LinkedHashMap<>();
69 for (IBoundInstanceModel<?> instance : this.modelInstances) {
70 if (instance instanceof IBoundInstanceModelNamed) {
71 IBoundInstanceModelNamed<?> named = (IBoundInstanceModelNamed<?>) instance;
72 QName key = named.getXmlQName();
73 namedModelInstances.put(key, named);
74
75 if (instance instanceof IBoundInstanceModelField) {
76 fieldInstances.put(key, (IBoundInstanceModelField<?>) named);
77 } else if (instance instanceof IBoundInstanceModelAssembly) {
78 assemblyInstances.put(key, (IBoundInstanceModelAssembly) named);
79 }
80 } else if (instance instanceof IBoundInstanceModelChoiceGroup) {
81 IBoundInstanceModelChoiceGroup choiceGroup = (IBoundInstanceModelChoiceGroup) instance;
82 String key = ObjectUtils.requireNonNull(choiceGroup.getGroupAsName());
83 choiceGroupInstances.put(key, choiceGroup);
84 }
85 }
86
87 this.namedModelInstances = namedModelInstances.isEmpty()
88 ? CollectionUtil.emptyMap()
89 : CollectionUtil.unmodifiableMap(namedModelInstances);
90 this.fieldInstances = fieldInstances.isEmpty()
91 ? CollectionUtil.emptyMap()
92 : CollectionUtil.unmodifiableMap(fieldInstances);
93 this.assemblyInstances = assemblyInstances.isEmpty()
94 ? CollectionUtil.emptyMap()
95 : CollectionUtil.unmodifiableMap(assemblyInstances);
96 this.choiceGroupInstances = choiceGroupInstances.isEmpty()
97 ? CollectionUtil.emptyMap()
98 : CollectionUtil.unmodifiableMap(choiceGroupInstances);
99 }
100
101 protected static IBoundInstanceModel<?> newBoundModelInstance(
102 @NonNull Field field,
103 @NonNull IBoundDefinitionModelAssembly definition) {
104 IBoundInstanceModel<?> retval = null;
105 if (field.isAnnotationPresent(BoundAssembly.class)) {
106 retval = IBoundInstanceModelAssembly.newInstance(field, definition);
107 } else if (field.isAnnotationPresent(BoundField.class)) {
108 retval = IBoundInstanceModelField.newInstance(field, definition);
109 } else if (field.isAnnotationPresent(BoundChoiceGroup.class)) {
110 retval = IBoundInstanceModelChoiceGroup.newInstance(field, definition);
111 }
112 return retval;
113 }
114
115 @NonNull
116 protected static Stream<IBoundInstanceModel<?>> getModelInstanceStream(
117 @NonNull IBoundDefinitionModelAssembly definition,
118 @NonNull Class<?> clazz) {
119
120 Stream<IBoundInstanceModel<?>> superInstances;
121 Class<?> superClass = clazz.getSuperclass();
122 if (superClass == null) {
123 superInstances = Stream.empty();
124 } else {
125
126 superInstances = getModelInstanceStream(definition, superClass);
127 }
128
129 return ObjectUtils.notNull(Stream.concat(superInstances, Arrays.stream(clazz.getDeclaredFields())
130
131 .filter(field -> !field.isAnnotationPresent(Ignore.class))
132
133 .filter(field -> field.isAnnotationPresent(BoundField.class)
134 || field.isAnnotationPresent(BoundAssembly.class)
135 || field.isAnnotationPresent(BoundChoiceGroup.class))
136 .map(field -> {
137 assert field != null;
138
139 IBoundInstanceModel<?> retval = newBoundModelInstance(field, definition);
140 if (retval == null) {
141 throw new IllegalStateException(
142 String.format("The field '%s' on class '%s' is not bound", field.getName(), clazz.getName()));
143 }
144 return retval;
145 })
146 .filter(Objects::nonNull)));
147 }
148
149 @Override
150 public Collection<IBoundInstanceModel<?>> getModelInstances() {
151 return modelInstances;
152 }
153
154 @Override
155 public Map<QName, IBoundInstanceModelNamed<?>> getNamedModelInstanceMap() {
156 return namedModelInstances;
157 }
158
159 @Override
160 public Map<QName, IBoundInstanceModelField<?>> getFieldInstanceMap() {
161 return fieldInstances;
162 }
163
164 @Override
165 public Map<QName, IBoundInstanceModelAssembly> getAssemblyInstanceMap() {
166 return assemblyInstances;
167 }
168
169 @Override
170 public List<IChoiceInstance> getChoiceInstances() {
171
172 return CollectionUtil.emptyList();
173 }
174
175 @Override
176 public Map<String, IBoundInstanceModelChoiceGroup> getChoiceGroupInstanceMap() {
177 return choiceGroupInstances;
178 }
179 }