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