1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model.impl;
7
8 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
9 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
10 import gov.nist.secauto.metaschema.core.model.AbstractFieldInstance;
11 import gov.nist.secauto.metaschema.core.model.IAttributable;
12 import gov.nist.secauto.metaschema.core.model.IBoundObject;
13 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
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.IBoundDefinitionModelFieldComplex;
17 import gov.nist.secauto.metaschema.databind.model.IBoundFieldValue;
18 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
19 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldComplex;
20 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
21 import gov.nist.secauto.metaschema.databind.model.IBoundProperty;
22 import gov.nist.secauto.metaschema.databind.model.IGroupAs;
23 import gov.nist.secauto.metaschema.databind.model.annotations.BoundField;
24 import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
25 import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
26 import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
27
28 import java.lang.reflect.Field;
29 import java.util.Arrays;
30 import java.util.LinkedHashMap;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.function.Predicate;
34 import java.util.stream.Collectors;
35
36 import edu.umd.cs.findbugs.annotations.NonNull;
37 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
38 import nl.talsmasoftware.lazy4j.Lazy;
39
40
41
42
43
44 public final class InstanceModelFieldComplex
45 extends AbstractFieldInstance<
46 IBoundDefinitionModelAssembly,
47 IBoundDefinitionModelFieldComplex,
48 IBoundInstanceModelFieldComplex,
49 IBoundDefinitionModelAssembly>
50 implements IBoundInstanceModelFieldComplex, IFeatureInstanceModelGroupAs {
51 @NonNull
52 private final Field javaField;
53 @NonNull
54 private final BoundField annotation;
55 @NonNull
56 private final Lazy<IModelInstanceCollectionInfo<IBoundObject>> collectionInfo;
57 @NonNull
58 private final IGroupAs groupAs;
59 @NonNull
60 private final DefinitionField definition;
61 @NonNull
62 private final Lazy<Object> defaultValue;
63 @NonNull
64 private final Lazy<Map<String, IBoundProperty<?>>> jsonProperties;
65 @NonNull
66 private final Lazy<Map<IAttributable.Key, Set<String>>> properties;
67
68
69
70
71
72
73
74
75
76
77
78
79 @NonNull
80 public static InstanceModelFieldComplex newInstance(
81 @NonNull Field javaField,
82 @NonNull DefinitionField definition,
83 @NonNull IBoundDefinitionModelAssembly parent) {
84 BoundField annotation = ModelUtil.getAnnotation(javaField, BoundField.class);
85 if (!annotation.inXmlWrapped()) {
86 if (definition.hasChildren()) {
87 throw new IllegalStateException(
88 String.format("Field '%s' on class '%s' is requested to be unwrapped, but it has flags preventing this.",
89 javaField.getName(),
90 parent.getBoundClass().getName()));
91 }
92 if (!definition.getJavaTypeAdapter().isUnrappedValueAllowedInXml()) {
93 throw new IllegalStateException(
94 String.format(
95 "Field '%s' on class '%s' is requested to be unwrapped, but its data type '%s' does not allow this.",
96 javaField.getName(),
97 parent.getBoundClass().getName(),
98 definition.getJavaTypeAdapter().getPreferredName()));
99 }
100 }
101
102 IGroupAs groupAs = ModelUtil.resolveDefaultGroupAs(
103 annotation.groupAs(),
104 parent.getContainingModule());
105 if (annotation.maxOccurs() == -1 || annotation.maxOccurs() > 1) {
106 if (IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
107 throw new IllegalStateException(String.format("Field '%s' on class '%s' is missing the '%s' annotation.",
108 javaField.getName(),
109 javaField.getDeclaringClass().getName(),
110 GroupAs.class.getName()));
111 }
112 } else if (!IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
113
114 throw new IllegalStateException(
115 String.format(
116 "Field '%s' on class '%s' has the '%s' annotation, but maxOccurs=1. A groupAs must not be specfied.",
117 javaField.getName(),
118 javaField.getDeclaringClass().getName(),
119 GroupAs.class.getName()));
120 }
121 return new InstanceModelFieldComplex(javaField, annotation, groupAs, definition, parent);
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
136 private InstanceModelFieldComplex(
137 @NonNull Field javaField,
138 @NonNull BoundField annotation,
139 @NonNull IGroupAs groupAs,
140 @NonNull DefinitionField definition,
141 @NonNull IBoundDefinitionModelAssembly parent) {
142 super(parent);
143 FieldSupport.bindField(javaField);
144 this.javaField = javaField;
145 this.annotation = annotation;
146 this.collectionInfo = ObjectUtils.notNull(Lazy.lazy(() -> IModelInstanceCollectionInfo.of(this)));
147 this.groupAs = groupAs;
148 this.definition = definition;
149 this.defaultValue = ObjectUtils.notNull(Lazy.lazy(() -> {
150 Object retval = null;
151 if (getMaxOccurs() == 1) {
152 IBoundFieldValue fieldValue = definition.getFieldValue();
153
154 Object fieldValueDefault = fieldValue.getDefaultValue();
155 if (fieldValueDefault != null) {
156 retval = newInstance(null);
157 fieldValue.setValue(retval, fieldValueDefault);
158
159 for (IBoundInstanceFlag flag : definition.getFlagInstances()) {
160 assert flag != null;
161
162 Object flagDefault = flag.getResolvedDefaultValue();
163 if (flagDefault != null) {
164 flag.setValue(retval, flagDefault);
165 }
166 }
167 }
168 }
169 return retval;
170 }));
171 this.jsonProperties = ObjectUtils.notNull(Lazy.lazy(() -> {
172 Predicate<IBoundInstanceFlag> flagFilter = null;
173 IBoundInstanceFlag jsonKey = getEffectiveJsonKey();
174 if (jsonKey != null) {
175 flagFilter = flag -> !jsonKey.equals(flag);
176 }
177 return definition.getJsonProperties(flagFilter);
178 }));
179 this.properties = ObjectUtils.notNull(
180 Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
181 Arrays.stream(annotation.properties())
182 .map(ModelUtil::toPropertyEntry)
183 .collect(
184 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
185 }
186
187
188
189
190
191 @Override
192 public Field getField() {
193 return javaField;
194 }
195
196
197
198
199
200
201 @NonNull
202 public BoundField getAnnotation() {
203 return annotation;
204 }
205
206 @SuppressWarnings("null")
207 @Override
208 public IModelInstanceCollectionInfo<IBoundObject> getCollectionInfo() {
209 return collectionInfo.get();
210 }
211
212 @Override
213 public DefinitionField getDefinition() {
214 return definition;
215 }
216
217 @Override
218 public IBoundModule getContainingModule() {
219 return getContainingDefinition().getContainingModule();
220 }
221
222 @Override
223 public Object getDefaultValue() {
224 return defaultValue.get();
225 }
226
227 @Override
228 public Map<String, IBoundProperty<?>> getJsonProperties() {
229 return ObjectUtils.notNull(jsonProperties.get());
230 }
231
232 @Override
233 public IGroupAs getGroupAs() {
234 return groupAs;
235 }
236
237 @Override
238 public String getFormalName() {
239 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
240 }
241
242 @Override
243 public MarkupLine getDescription() {
244 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
245 }
246
247 @Override
248 public String getUseName() {
249 return ModelUtil.resolveNoneOrValue(getAnnotation().useName());
250 }
251
252 @Override
253 public Integer getUseIndex() {
254 int value = getAnnotation().useIndex();
255 return value == Integer.MIN_VALUE ? null : value;
256 }
257
258 @Override
259 public boolean isInXmlWrapped() {
260 return getAnnotation().inXmlWrapped();
261 }
262
263 @Override
264 public int getMinOccurs() {
265 return getAnnotation().minOccurs();
266 }
267
268 @Override
269 public int getMaxOccurs() {
270 return getAnnotation().maxOccurs();
271 }
272
273 @Override
274 public Map<Key, Set<String>> getProperties() {
275 return ObjectUtils.notNull(properties.get());
276 }
277
278 @Override
279 public MarkupMultiline getRemarks() {
280 return ModelUtil.resolveToMarkupMultiline(getAnnotation().remarks());
281 }
282 }