1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.model.impl;
7
8 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
9 import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
10 import gov.nist.secauto.metaschema.core.model.AbstractFieldInstance;
11 import gov.nist.secauto.metaschema.core.model.IBoundObject;
12 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
13 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly;
14 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelFieldComplex;
15 import gov.nist.secauto.metaschema.databind.model.IBoundFieldValue;
16 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
17 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldComplex;
18 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
19 import gov.nist.secauto.metaschema.databind.model.IBoundProperty;
20 import gov.nist.secauto.metaschema.databind.model.IGroupAs;
21 import gov.nist.secauto.metaschema.databind.model.annotations.BoundField;
22 import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
23 import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
24 import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
25
26 import java.lang.reflect.Field;
27 import java.util.Map;
28 import java.util.function.Predicate;
29
30 import edu.umd.cs.findbugs.annotations.NonNull;
31 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
32 import nl.talsmasoftware.lazy4j.Lazy;
33
34
35
36
37
38 public final class InstanceModelFieldComplex
39 extends AbstractFieldInstance<
40 IBoundDefinitionModelAssembly,
41 IBoundDefinitionModelFieldComplex,
42 IBoundInstanceModelFieldComplex,
43 IBoundDefinitionModelAssembly>
44 implements IBoundInstanceModelFieldComplex, IFeatureInstanceModelGroupAs<IBoundObject> {
45 @NonNull
46 private final Field javaField;
47 @NonNull
48 private final BoundField annotation;
49 @NonNull
50 private final Lazy<IModelInstanceCollectionInfo<IBoundObject>> collectionInfo;
51 @NonNull
52 private final IGroupAs groupAs;
53 @NonNull
54 private final DefinitionField definition;
55 @NonNull
56 private final Lazy<Object> defaultValue;
57 @NonNull
58 private final Lazy<Map<String, IBoundProperty<?>>> jsonProperties;
59
60 @NonNull
61 public static InstanceModelFieldComplex newInstance(
62 @NonNull Field javaField,
63 @NonNull DefinitionField definition,
64 @NonNull IBoundDefinitionModelAssembly parent) {
65 BoundField annotation = ModelUtil.getAnnotation(javaField, BoundField.class);
66 if (!annotation.inXmlWrapped()) {
67 if (definition.hasChildren()) {
68 throw new IllegalStateException(
69 String.format("Field '%s' on class '%s' is requested to be unwrapped, but it has flags preventing this.",
70 javaField.getName(),
71 parent.getBoundClass().getName()));
72 }
73 if (!definition.getJavaTypeAdapter().isUnrappedValueAllowedInXml()) {
74 throw new IllegalStateException(
75 String.format(
76 "Field '%s' on class '%s' is requested to be unwrapped, but its data type '%s' does not allow this.",
77 javaField.getName(),
78 parent.getBoundClass().getName(),
79 definition.getJavaTypeAdapter().getPreferredName()));
80 }
81 }
82
83 IGroupAs groupAs = ModelUtil.resolveDefaultGroupAs(
84 annotation.groupAs(),
85 parent.getContainingModule());
86 if (annotation.maxOccurs() == -1 || annotation.maxOccurs() > 1) {
87 if (IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
88 throw new IllegalStateException(String.format("Field '%s' on class '%s' is missing the '%s' annotation.",
89 javaField.getName(),
90 javaField.getDeclaringClass().getName(),
91 GroupAs.class.getName()));
92 }
93 } else if (!IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
94
95 throw new IllegalStateException(
96 String.format(
97 "Field '%s' on class '%s' has the '%s' annotation, but maxOccurs=1. A groupAs must not be specfied.",
98 javaField.getName(),
99 javaField.getDeclaringClass().getName(),
100 GroupAs.class.getName()));
101 }
102 return new InstanceModelFieldComplex(javaField, annotation, groupAs, definition, parent);
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
117 private InstanceModelFieldComplex(
118 @NonNull Field javaField,
119 @NonNull BoundField annotation,
120 @NonNull IGroupAs groupAs,
121 @NonNull DefinitionField definition,
122 @NonNull IBoundDefinitionModelAssembly parent) {
123 super(parent);
124 this.javaField = javaField;
125 this.annotation = annotation;
126 this.collectionInfo = ObjectUtils.notNull(Lazy.lazy(() -> IModelInstanceCollectionInfo.of(this)));
127 this.groupAs = groupAs;
128 this.definition = definition;
129
130 this.defaultValue = ObjectUtils.notNull(Lazy.lazy(() -> {
131 Object retval = null;
132 if (getMaxOccurs() == 1) {
133 IBoundFieldValue fieldValue = definition.getFieldValue();
134
135 Object fieldValueDefault = fieldValue.getDefaultValue();
136 if (fieldValueDefault != null) {
137 retval = newInstance(null);
138 fieldValue.setValue(retval, fieldValueDefault);
139
140 for (IBoundInstanceFlag flag : definition.getFlagInstances()) {
141 Object flagDefault = flag.getResolvedDefaultValue();
142 if (flagDefault != null) {
143 flag.setValue(retval, flagDefault);
144 }
145 }
146 }
147 }
148 return retval;
149 }));
150 this.jsonProperties = ObjectUtils.notNull(Lazy.lazy(() -> {
151 Predicate<IBoundInstanceFlag> flagFilter = null;
152 IBoundInstanceFlag jsonKey = getEffectiveJsonKey();
153 if (jsonKey != null) {
154 flagFilter = flag -> !jsonKey.equals(flag);
155 }
156 return getDefinition().getJsonProperties(flagFilter);
157 }));
158 }
159
160
161
162
163
164 @Override
165 public Field getField() {
166 return javaField;
167 }
168
169
170
171
172
173
174 @NonNull
175 public BoundField getAnnotation() {
176 return annotation;
177 }
178
179 @SuppressWarnings("null")
180 @Override
181 public IModelInstanceCollectionInfo<IBoundObject> getCollectionInfo() {
182 return collectionInfo.get();
183 }
184
185 @Override
186 public DefinitionField getDefinition() {
187 return definition;
188 }
189
190 @Override
191 public IBoundModule getContainingModule() {
192 return getContainingDefinition().getContainingModule();
193 }
194
195 @Override
196 public Object getDefaultValue() {
197 return defaultValue.get();
198 }
199
200 @Override
201 public Map<String, IBoundProperty<?>> getJsonProperties() {
202 return ObjectUtils.notNull(jsonProperties.get());
203 }
204
205 @Override
206 public IGroupAs getGroupAs() {
207 return groupAs;
208 }
209
210 @Override
211 public String getFormalName() {
212 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
213 }
214
215 @Override
216 public MarkupLine getDescription() {
217 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
218 }
219
220 @Override
221 public String getUseName() {
222 return ModelUtil.resolveNoneOrValue(getAnnotation().useName());
223 }
224
225 @Override
226 public Integer getUseIndex() {
227 int value = getAnnotation().useIndex();
228 return value == Integer.MIN_VALUE ? null : value;
229 }
230
231 @Override
232 public boolean isInXmlWrapped() {
233 return getAnnotation().inXmlWrapped();
234 }
235
236 @Override
237 public int getMinOccurs() {
238 return getAnnotation().minOccurs();
239 }
240
241 @Override
242 public int getMaxOccurs() {
243 return getAnnotation().maxOccurs();
244 }
245
246 @Override
247 public MarkupMultiline getRemarks() {
248 return ModelUtil.resolveToMarkupMultiline(getAnnotation().remarks());
249 }
250 }