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.AbstractInlineFieldDefinition;
12 import gov.nist.secauto.metaschema.core.model.IAttributable;
13 import gov.nist.secauto.metaschema.core.model.IModule;
14 import gov.nist.secauto.metaschema.core.model.ISource;
15 import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained;
16 import gov.nist.secauto.metaschema.core.model.constraint.ValueConstraintSet;
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.model.IBoundDefinitionModelAssembly;
21 import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelField;
22 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
23 import gov.nist.secauto.metaschema.databind.model.IBoundInstanceModelFieldScalar;
24 import gov.nist.secauto.metaschema.databind.model.IBoundModule;
25 import gov.nist.secauto.metaschema.databind.model.IGroupAs;
26 import gov.nist.secauto.metaschema.databind.model.annotations.BoundField;
27 import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
28 import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
29 import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
30 import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
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.Optional;
37 import java.util.Set;
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
48 public final class InstanceModelFieldScalar
49 extends AbstractInlineFieldDefinition<
50 IBoundDefinitionModelAssembly,
51 IBoundDefinitionModelField<Object>,
52 IBoundInstanceModelFieldScalar,
53 IBoundDefinitionModelAssembly,
54 IBoundInstanceFlag>
55 implements IBoundInstanceModelFieldScalar, IFeatureInstanceModelGroupAs {
56 @NonNull
57 private final Field javaField;
58 @NonNull
59 private final BoundField annotation;
60 @NonNull
61 private final Lazy<IModelInstanceCollectionInfo<Object>> collectionInfo;
62 @NonNull
63 private final IGroupAs groupAs;
64 @NonNull
65 private final IDataTypeAdapter<?> javaTypeAdapter;
66 @Nullable
67 private final Object defaultValue;
68 @NonNull
69 private final Lazy<IValueConstrained> constraints;
70 @NonNull
71 private final Lazy<Map<IAttributable.Key, Set<String>>> properties;
72
73
74
75
76
77
78
79
80
81
82 @NonNull
83 public static InstanceModelFieldScalar newInstance(
84 @NonNull Field javaField,
85 @NonNull IBoundDefinitionModelAssembly parent) {
86 BoundField annotation = ModelUtil.getAnnotation(javaField, BoundField.class);
87 IGroupAs groupAs = ModelUtil.resolveDefaultGroupAs(
88 annotation.groupAs(),
89 parent.getContainingModule());
90
91 if (annotation.maxOccurs() == -1 || annotation.maxOccurs() > 1) {
92 if (IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
93 throw new IllegalStateException(String.format("Field '%s' on class '%s' is missing the '%s' annotation.",
94 javaField.getName(),
95 javaField.getDeclaringClass().getName(),
96 GroupAs.class.getName()));
97 }
98 } else if (!IGroupAs.SINGLETON_GROUP_AS.equals(groupAs)) {
99
100 throw new IllegalStateException(
101 String.format(
102 "Field '%s' on class '%s' has the '%s' annotation, but maxOccurs=1. A groupAs must not be specfied.",
103 javaField.getName(),
104 javaField.getDeclaringClass().getName(),
105 GroupAs.class.getName()));
106 }
107
108 return new InstanceModelFieldScalar(
109 javaField,
110 annotation,
111 groupAs,
112 parent);
113 }
114
115 private InstanceModelFieldScalar(
116 @NonNull Field javaField,
117 @NonNull BoundField annotation,
118 @NonNull IGroupAs groupAs,
119 @NonNull IBoundDefinitionModelAssembly parent) {
120 super(parent);
121 FieldSupport.bindField(javaField);
122 this.javaField = javaField;
123 this.annotation = annotation;
124 this.collectionInfo = ObjectUtils.notNull(Lazy.lazy(() -> IModelInstanceCollectionInfo.of(this)));
125 this.groupAs = groupAs;
126 this.javaTypeAdapter = ModelUtil.getDataTypeAdapter(
127 annotation.typeAdapter(),
128 parent.getBindingContext());
129 this.defaultValue = ModelUtil.resolveDefaultValue(annotation.defaultValue(), this.javaTypeAdapter);
130
131 IModule module = getContainingModule();
132 ISource source = module.getSource();
133
134 this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> {
135 IValueConstrained retval = new ValueConstraintSet(source);
136 ValueConstraints valueAnnotation = annotation.valueConstraints();
137 ConstraintSupport.parse(valueAnnotation, module.getSource(), retval);
138 return retval;
139 }));
140 this.properties = ObjectUtils.notNull(
141 Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
142 Arrays.stream(annotation.properties())
143 .map(ModelUtil::toPropertyEntry)
144 .collect(
145 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
146 }
147
148
149
150
151
152 @Override
153 public IBindingContext getBindingContext() {
154 return getContainingDefinition().getBindingContext();
155 }
156
157 @Override
158 public IBoundModule getContainingModule() {
159 return getContainingDefinition().getContainingModule();
160 }
161
162 @Override
163 public Field getField() {
164 return javaField;
165 }
166
167
168
169
170
171
172 @NonNull
173 public BoundField getAnnotation() {
174 return annotation;
175 }
176
177 @SuppressWarnings("null")
178 @Override
179 public IModelInstanceCollectionInfo<Object> getCollectionInfo() {
180 return collectionInfo.get();
181 }
182
183 @SuppressWarnings("null")
184 @Override
185 @NonNull
186 public IValueConstrained getConstraintSupport() {
187 return constraints.get();
188 }
189
190 @Override
191 public IDataTypeAdapter<?> getJavaTypeAdapter() {
192 return javaTypeAdapter;
193 }
194
195 @Override
196 public Object getDefaultValue() {
197 return defaultValue;
198 }
199
200 @Override
201 public IGroupAs getGroupAs() {
202 return groupAs;
203 }
204
205 @Override
206 public String getFormalName() {
207 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
208 }
209
210 @Override
211 public MarkupLine getDescription() {
212 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
213 }
214
215 @Override
216 public Integer getUseIndex() {
217 int value = getAnnotation().useIndex();
218 return value == Integer.MIN_VALUE ? null : value;
219 }
220
221 @Override
222 public boolean isInXmlWrapped() {
223 return getAnnotation().inXmlWrapped();
224 }
225
226 @Override
227 public int getMinOccurs() {
228 return getAnnotation().minOccurs();
229 }
230
231 @Override
232 public int getMaxOccurs() {
233 return getAnnotation().maxOccurs();
234 }
235
236 @Override
237 public Map<Key, Set<String>> getProperties() {
238 return ObjectUtils.notNull(properties.get());
239 }
240
241 @Override
242 public MarkupMultiline getRemarks() {
243 return ModelUtil.resolveToMarkupMultiline(getAnnotation().remarks());
244 }
245
246 @Override
247 public String getName() {
248
249
250 return ObjectUtils.notNull(
251 Optional.ofNullable(ModelUtil.resolveNoneOrValue(getAnnotation().useName())).orElse(getField().getName()));
252 }
253
254
255
256
257 }