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