1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.databind.model;
28
29 import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter;
30 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
31 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
32 import gov.nist.secauto.metaschema.core.model.IFlagContainerSupport;
33 import gov.nist.secauto.metaschema.core.model.IModule;
34 import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.InternalModelSource;
35 import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained;
36 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
37 import gov.nist.secauto.metaschema.databind.IBindingContext;
38 import gov.nist.secauto.metaschema.databind.io.BindingException;
39 import gov.nist.secauto.metaschema.databind.model.annotations.BoundField;
40 import gov.nist.secauto.metaschema.databind.model.annotations.Ignore;
41 import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaField;
42 import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaFieldValue;
43 import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
44
45 import java.util.Objects;
46
47 import edu.umd.cs.findbugs.annotations.NonNull;
48 import edu.umd.cs.findbugs.annotations.Nullable;
49 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
50 import nl.talsmasoftware.lazy4j.Lazy;
51
52 public class DefaultFieldClassBinding
53 extends AbstractClassBinding
54 implements IFieldClassBinding {
55
56 @NonNull
57 private final MetaschemaField metaschemaField;
58 private IBoundFieldValueInstance fieldValue;
59 private IBoundFlagInstance jsonValueKeyFlagInstance;
60 private final Lazy<ClassBindingFlagContainerSupport> flagContainer;
61 private final Lazy<IValueConstrained> constraints;
62
63
64
65
66
67
68
69
70
71
72
73 @NonNull
74 public static DefaultFieldClassBinding createInstance(
75 @NonNull Class<?> clazz,
76 @NonNull IBindingContext bindingContext) {
77 Objects.requireNonNull(clazz, "clazz");
78 if (!clazz.isAnnotationPresent(MetaschemaField.class)) {
79 throw new IllegalArgumentException(
80 String.format("Class '%s' is missing the '%s' annotation.",
81 clazz.getName(),
82 MetaschemaField.class.getName()));
83 }
84 return new DefaultFieldClassBinding(clazz, bindingContext);
85 }
86
87
88
89
90
91
92
93
94
95
96 protected DefaultFieldClassBinding(
97 @NonNull Class<?> clazz,
98 @NonNull IBindingContext bindingContext) {
99 super(clazz, bindingContext);
100 this.metaschemaField = ObjectUtils.notNull(clazz.getAnnotation(MetaschemaField.class));
101 this.flagContainer = Lazy.lazy(() -> new ClassBindingFlagContainerSupport(this, this::handleFlagInstance));
102 this.constraints = Lazy.lazy(() -> new ValueConstraintSupport(
103 clazz.getAnnotation(ValueConstraints.class),
104 InternalModelSource.instance()));
105 }
106
107 @SuppressWarnings("null")
108 @Override
109 public IFlagContainerSupport<IBoundFlagInstance> getFlagContainer() {
110 return flagContainer.get();
111 }
112
113 @SuppressWarnings("null")
114 @Override
115 public IValueConstrained getConstraintSupport() {
116 return constraints.get();
117 }
118
119 @NonNull
120 private MetaschemaField getMetaschemaFieldAnnotation() {
121 return metaschemaField;
122 }
123
124 @Override
125 public String getFormalName() {
126 return ModelUtil.resolveToString(getMetaschemaFieldAnnotation().formalName());
127 }
128
129 @Override
130 public MarkupLine getDescription() {
131 return ModelUtil.resolveToMarkupLine(getMetaschemaFieldAnnotation().description());
132 }
133
134 @Override
135 public @Nullable MarkupMultiline getRemarks() {
136 return ModelUtil.resolveToMarkupMultiline(getMetaschemaFieldAnnotation().description());
137 }
138
139 @Override
140 public String getName() {
141 return getMetaschemaFieldAnnotation().name();
142 }
143
144 @Override
145 public Object getDefaultValue() {
146 return getFieldValueInstance().getDefaultValue();
147 }
148
149
150
151
152
153
154
155
156 protected java.lang.reflect.Field getFieldValueField(Class<?> clazz) {
157 java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
158
159 java.lang.reflect.Field retval = null;
160
161 Class<?> superClass = clazz.getSuperclass();
162 if (superClass != null) {
163
164 retval = getFieldValueField(superClass);
165 }
166
167 if (retval == null) {
168 for (java.lang.reflect.Field field : fields) {
169 if (!field.isAnnotationPresent(MetaschemaFieldValue.class)) {
170
171 continue;
172 }
173
174 if (field.isAnnotationPresent(Ignore.class)) {
175
176 continue;
177 }
178 retval = field;
179 }
180 }
181 return retval;
182 }
183
184
185
186
187
188
189 protected IBoundFieldValueInstance initalizeFieldValueInstance() {
190 synchronized (this) {
191 if (this.fieldValue == null) {
192 java.lang.reflect.Field field = getFieldValueField(getBoundClass());
193 if (field == null) {
194 throw new IllegalArgumentException(
195 String.format("Class '%s' is missing the '%s' annotation on one of its fields.",
196 getBoundClass().getName(),
197 MetaschemaFieldValue.class.getName()));
198 }
199
200 this.fieldValue = new DefaultFieldValueProperty(this, field);
201 }
202 return this.fieldValue;
203 }
204 }
205
206 @Override
207 public boolean isInline() {
208 return false;
209 }
210
211 @Override
212 public IBoundFieldInstance getInlineInstance() {
213 return null;
214 }
215
216 @SuppressWarnings("null")
217 @Override
218 public IBoundFieldValueInstance getFieldValueInstance() {
219 return initalizeFieldValueInstance();
220 }
221
222 @Override
223 public Object getFieldValue(@NonNull Object item) {
224 return ObjectUtils.requireNonNull(getFieldValueInstance().getValue(item));
225 }
226
227 protected void handleFlagInstance(IBoundFlagInstance instance) {
228 if (instance.isJsonValueKey()) {
229 this.jsonValueKeyFlagInstance = instance;
230 }
231 }
232
233 @Override
234 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "access is restricted using interface")
235 public IBoundFlagInstance getJsonValueKeyFlagInstance() {
236
237 flagContainer.get();
238 return jsonValueKeyFlagInstance;
239 }
240
241 @Override
242 public String getJsonValueKeyName() {
243 return getFieldValueInstance().getJsonValueKeyName();
244 }
245
246 @Override
247 public IDataTypeAdapter<?> getJavaTypeAdapter() {
248 return getFieldValueInstance().getJavaTypeAdapter();
249 }
250
251 @Override
252 protected void copyBoundObjectInternal(@NonNull Object fromInstance, @NonNull Object toInstance)
253 throws BindingException {
254 super.copyBoundObjectInternal(fromInstance, toInstance);
255
256 getFieldValueInstance().copyBoundObject(fromInstance, toInstance);
257 }
258
259 @Override
260 protected Class<? extends IModule> getModuleClass() {
261 return getMetaschemaFieldAnnotation().moduleClass();
262 }
263 }