1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model.impl;
7
8 import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
9 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
10 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
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.model.ISource;
14 import gov.nist.secauto.metaschema.core.model.constraint.AssemblyConstraintSet;
15 import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
16 import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained;
17 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
18 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
19 import gov.nist.secauto.metaschema.databind.IBindingContext;
20 import gov.nist.secauto.metaschema.databind.io.BindingException;
21 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelFieldComplex;
22 import gov.nist.secauto.metaschema.databind.model.IBoundFieldValue;
23 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
24 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
25 import gov.nist.secauto.metaschema.databind.model.IBoundProperty;
26 import gov.nist.secauto.metaschema.databind.model.annotations.BoundFieldValue;
27 import gov.nist.secauto.metaschema.databind.model.annotations.Ignore;
28 import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaField;
29 import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
30 import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
31
32 import java.lang.reflect.Field;
33 import java.util.Arrays;
34 import java.util.LinkedHashMap;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.function.Predicate;
38 import java.util.stream.Collectors;
39
40 import edu.umd.cs.findbugs.annotations.NonNull;
41 import edu.umd.cs.findbugs.annotations.Nullable;
42 import nl.talsmasoftware.lazy4j.Lazy;
43
44
45
46
47 @SuppressWarnings("PMD.CouplingBetweenObjects")
48 public final class DefinitionField
49 extends AbstractBoundDefinitionModelComplex<MetaschemaField>
50 implements IBoundDefinitionModelFieldComplex {
51 @NonNull
52 private final FieldValue fieldValue;
53 @Nullable
54 private IBoundInstanceFlag jsonValueKeyFlagInstance;
55 @NonNull
56 private final Lazy<FlagContainerSupport> flagContainer;
57 @NonNull
58 private final Lazy<IValueConstrained> constraints;
59 @NonNull
60 private final Lazy<Map<String, IBoundProperty<?>>> jsonProperties;
61 @NonNull
62 private final Lazy<Map<IAttributable.Key, Set<String>>> properties;
63
64
65
66
67
68
69
70
71 @Nullable
72 private static Field getFieldValueField(Class<?> clazz) {
73 Field[] fields = clazz.getDeclaredFields();
74
75 Field retval = null;
76 for (Field field : fields) {
77 if (!field.isAnnotationPresent(BoundFieldValue.class) || field.isAnnotationPresent(Ignore.class)) {
78
79 continue;
80 }
81 retval = field;
82 }
83
84 if (retval == null) {
85 Class<?> superClass = clazz.getSuperclass();
86 if (superClass != null) {
87
88 retval = getFieldValueField(superClass);
89 }
90 }
91 return retval;
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 @NonNull
109 public static DefinitionField newInstance(
110 @NonNull Class<? extends IBoundObject> clazz,
111 @NonNull MetaschemaField annotation,
112 @NonNull IBoundModule module,
113 @NonNull IBindingContext bindingContext) {
114 return new DefinitionField(clazz, annotation, module, bindingContext);
115 }
116
117 private DefinitionField(
118 @NonNull Class<? extends IBoundObject> clazz,
119 @NonNull MetaschemaField annotation,
120 @NonNull IBoundModule module,
121 @NonNull IBindingContext bindingContext) {
122 super(clazz, annotation, module, bindingContext);
123 Field field = getFieldValueField(getBoundClass());
124 if (field == null) {
125 throw new IllegalArgumentException(
126 String.format("Class '%s' is missing the '%s' annotation on one of its fields.",
127 clazz.getName(),
128 BoundFieldValue.class.getName()));
129 }
130 FieldSupport.bindField(field);
131 this.fieldValue = new FieldValue(field, BoundFieldValue.class, bindingContext);
132 this.flagContainer = ObjectUtils.notNull(Lazy.lazy(() -> new FlagContainerSupport(this, this::handleFlagInstance)));
133
134 ISource source = module.getSource();
135
136 this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> {
137 IModelConstrained retval = new AssemblyConstraintSet(source);
138 ValueConstraints valueAnnotation = getAnnotation().valueConstraints();
139 ConstraintSupport.parse(valueAnnotation, source, retval);
140 return retval;
141 }));
142 this.jsonProperties = ObjectUtils.notNull(Lazy.lazy(() -> {
143 IBoundInstanceFlag jsonValueKey = getJsonValueKeyFlagInstance();
144 Predicate<IBoundInstanceFlag> flagFilter = jsonValueKey == null ? null : flag -> !flag.equals(jsonValueKey);
145 return getJsonProperties(flagFilter);
146 }));
147 this.properties = ObjectUtils.notNull(
148 Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
149 Arrays.stream(annotation.properties())
150 .map(ModelUtil::toPropertyEntry)
151 .collect(
152 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
153 }
154
155
156
157
158
159
160
161 protected void handleFlagInstance(@NonNull IBoundInstanceFlag instance) {
162 if (instance.isJsonValueKey()) {
163 this.jsonValueKeyFlagInstance = instance;
164 }
165 }
166
167 @Override
168 @NonNull
169 public FieldValue getFieldValue() {
170 return fieldValue;
171 }
172
173 @Override
174 public IBoundInstanceFlag getJsonValueKeyFlagInstance() {
175
176 getFlagContainer();
177 return jsonValueKeyFlagInstance;
178 }
179
180 @Override
181 protected void deepCopyItemInternal(IBoundObject fromObject, IBoundObject toObject) throws BindingException {
182
183 super.deepCopyItemInternal(fromObject, toObject);
184
185 getFieldValue().deepCopy(fromObject, toObject);
186 }
187
188
189
190
191
192 @Override
193 @SuppressWarnings("null")
194 @NonNull
195 public FlagContainerSupport getFlagContainer() {
196 return flagContainer.get();
197 }
198
199 @Override
200 @NonNull
201 public IValueConstrained getConstraintSupport() {
202 return ObjectUtils.notNull(constraints.get());
203 }
204
205 @Override
206 public Map<String, IBoundProperty<?>> getJsonProperties() {
207 return ObjectUtils.notNull(jsonProperties.get());
208 }
209
210 @Override
211 @Nullable
212 public String getFormalName() {
213 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
214 }
215
216 @Override
217 @Nullable
218 public MarkupLine getDescription() {
219 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
220 }
221
222 @Override
223 @NonNull
224 public String getName() {
225 return getAnnotation().name();
226 }
227
228 @Override
229 public Map<Key, Set<String>> getProperties() {
230 return ObjectUtils.notNull(properties.get());
231 }
232
233 @Override
234 @Nullable
235 public Integer getIndex() {
236 return ModelUtil.resolveDefaultInteger(getAnnotation().index());
237 }
238
239 @Override
240 @Nullable
241 public MarkupMultiline getRemarks() {
242 return ModelUtil.resolveToMarkupMultiline(getAnnotation().description());
243 }
244
245
246
247
248 protected class FieldValue
249 implements IBoundFieldValue {
250 @NonNull
251 private final Field javaField;
252 @NonNull
253 private final BoundFieldValue annotation;
254 @NonNull
255 private final IDataTypeAdapter<?> javaTypeAdapter;
256 @Nullable
257 private final Object defaultValue;
258
259
260
261
262
263
264
265
266
267
268
269 protected FieldValue(
270 @NonNull Field javaField,
271 @NonNull Class<BoundFieldValue> annotationClass,
272 @NonNull IBindingContext bindingContext) {
273 FieldSupport.bindField(javaField);
274 this.javaField = javaField;
275 this.annotation = ModelUtil.getAnnotation(javaField, annotationClass);
276 this.javaTypeAdapter = ModelUtil.getDataTypeAdapter(
277 this.annotation.typeAdapter(),
278 bindingContext);
279 this.defaultValue = ModelUtil.resolveDefaultValue(this.annotation.defaultValue(), this.javaTypeAdapter);
280 }
281
282
283
284
285
286
287 @Override
288 @NonNull
289 public Field getField() {
290 return javaField;
291 }
292
293
294
295
296
297
298 @NonNull
299 public BoundFieldValue getAnnotation() {
300 return annotation;
301 }
302
303 @Override
304 public IBoundDefinitionModelFieldComplex getParentFieldDefinition() {
305 return DefinitionField.this;
306 }
307
308 @Override
309 public String getJsonValueKeyName() {
310 String name = ModelUtil.resolveNoneOrValue(getAnnotation().valueKeyName());
311 return name == null ? getJavaTypeAdapter().getDefaultJsonValueKey() : name;
312 }
313
314 @Override
315 public String getJsonValueKeyFlagName() {
316 return ModelUtil.resolveNoneOrValue(getAnnotation().valueKeyName());
317 }
318
319 @Override
320 public Object getDefaultValue() {
321 return defaultValue;
322 }
323
324 @Override
325 public IDataTypeAdapter<?> getJavaTypeAdapter() {
326 return javaTypeAdapter;
327 }
328
329 @Override
330 public Object getEffectiveDefaultValue() {
331 return getDefaultValue();
332 }
333
334 @Override
335 public String getJsonName() {
336 return getEffectiveJsonValueKeyName();
337 }
338 }
339
340
341
342 }