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 this.fieldValue = new FieldValue(field, BoundFieldValue.class, bindingContext);
131 this.flagContainer = ObjectUtils.notNull(Lazy.lazy(() -> new FlagContainerSupport(this, this::handleFlagInstance)));
132
133 ISource source = module.getSource();
134
135 this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> {
136 IModelConstrained retval = new AssemblyConstraintSet(source);
137 ValueConstraints valueAnnotation = getAnnotation().valueConstraints();
138 ConstraintSupport.parse(valueAnnotation, source, retval);
139 return retval;
140 }));
141 this.jsonProperties = ObjectUtils.notNull(Lazy.lazy(() -> {
142 IBoundInstanceFlag jsonValueKey = getJsonValueKeyFlagInstance();
143 Predicate<IBoundInstanceFlag> flagFilter = jsonValueKey == null ? null : flag -> !flag.equals(jsonValueKey);
144 return getJsonProperties(flagFilter);
145 }));
146 this.properties = ObjectUtils.notNull(
147 Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
148 Arrays.stream(annotation.properties())
149 .map(ModelUtil::toPropertyEntry)
150 .collect(
151 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
152 }
153
154
155
156
157
158
159
160 protected void handleFlagInstance(@NonNull IBoundInstanceFlag instance) {
161 if (instance.isJsonValueKey()) {
162 this.jsonValueKeyFlagInstance = instance;
163 }
164 }
165
166 @Override
167 @NonNull
168 public FieldValue getFieldValue() {
169 return fieldValue;
170 }
171
172 @Override
173 public IBoundInstanceFlag getJsonValueKeyFlagInstance() {
174
175 getFlagContainer();
176 return jsonValueKeyFlagInstance;
177 }
178
179 @Override
180 protected void deepCopyItemInternal(IBoundObject fromObject, IBoundObject toObject) throws BindingException {
181
182 super.deepCopyItemInternal(fromObject, toObject);
183
184 getFieldValue().deepCopy(fromObject, toObject);
185 }
186
187
188
189
190
191 @Override
192 @SuppressWarnings("null")
193 @NonNull
194 public FlagContainerSupport getFlagContainer() {
195 return flagContainer.get();
196 }
197
198 @Override
199 @NonNull
200 public IValueConstrained getConstraintSupport() {
201 return ObjectUtils.notNull(constraints.get());
202 }
203
204 @Override
205 public Map<String, IBoundProperty<?>> getJsonProperties() {
206 return ObjectUtils.notNull(jsonProperties.get());
207 }
208
209 @Override
210 @Nullable
211 public String getFormalName() {
212 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
213 }
214
215 @Override
216 @Nullable
217 public MarkupLine getDescription() {
218 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
219 }
220
221 @Override
222 @NonNull
223 public String getName() {
224 return getAnnotation().name();
225 }
226
227 @Override
228 public Map<Key, Set<String>> getProperties() {
229 return ObjectUtils.notNull(properties.get());
230 }
231
232 @Override
233 @Nullable
234 public Integer getIndex() {
235 return ModelUtil.resolveDefaultInteger(getAnnotation().index());
236 }
237
238 @Override
239 @Nullable
240 public MarkupMultiline getRemarks() {
241 return ModelUtil.resolveToMarkupMultiline(getAnnotation().description());
242 }
243
244
245
246
247 protected class FieldValue
248 implements IBoundFieldValue {
249 @NonNull
250 private final Field javaField;
251 @NonNull
252 private final BoundFieldValue annotation;
253 @NonNull
254 private final IDataTypeAdapter<?> javaTypeAdapter;
255 @Nullable
256 private final Object defaultValue;
257
258
259
260
261
262
263
264
265
266
267
268 protected FieldValue(
269 @NonNull Field javaField,
270 @NonNull Class<BoundFieldValue> annotationClass,
271 @NonNull IBindingContext bindingContext) {
272 this.javaField = javaField;
273 this.annotation = ModelUtil.getAnnotation(javaField, annotationClass);
274 this.javaTypeAdapter = ModelUtil.getDataTypeAdapter(
275 this.annotation.typeAdapter(),
276 bindingContext);
277 this.defaultValue = ModelUtil.resolveDefaultValue(this.annotation.defaultValue(), this.javaTypeAdapter);
278 }
279
280
281
282
283
284
285 @Override
286 @NonNull
287 public Field getField() {
288 return javaField;
289 }
290
291
292
293
294
295
296 @NonNull
297 public BoundFieldValue getAnnotation() {
298 return annotation;
299 }
300
301 @Override
302 public IBoundDefinitionModelFieldComplex getParentFieldDefinition() {
303 return DefinitionField.this;
304 }
305
306 @Override
307 public String getJsonValueKeyName() {
308 String name = ModelUtil.resolveNoneOrValue(getAnnotation().valueKeyName());
309 return name == null ? getJavaTypeAdapter().getDefaultJsonValueKey() : name;
310 }
311
312 @Override
313 public String getJsonValueKeyFlagName() {
314 return ModelUtil.resolveNoneOrValue(getAnnotation().valueKeyName());
315 }
316
317 @Override
318 public Object getDefaultValue() {
319 return defaultValue;
320 }
321
322 @Override
323 public IDataTypeAdapter<?> getJavaTypeAdapter() {
324 return javaTypeAdapter;
325 }
326
327 @Override
328 public Object getEffectiveDefaultValue() {
329 return getDefaultValue();
330 }
331
332 @Override
333 public String getJsonName() {
334 return getEffectiveJsonValueKeyName();
335 }
336 }
337
338
339
340 }