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 this.javaField = javaField;
122 this.annotation = annotation;
123 this.collectionInfo = ObjectUtils.notNull(Lazy.lazy(() -> IModelInstanceCollectionInfo.of(this)));
124 this.groupAs = groupAs;
125 this.javaTypeAdapter = ModelUtil.getDataTypeAdapter(
126 annotation.typeAdapter(),
127 parent.getBindingContext());
128 this.defaultValue = ModelUtil.resolveDefaultValue(annotation.defaultValue(), this.javaTypeAdapter);
129
130 IModule module = getContainingModule();
131 ISource source = module.getSource();
132
133 this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> {
134 IValueConstrained retval = new ValueConstraintSet(source);
135 ValueConstraints valueAnnotation = annotation.valueConstraints();
136 ConstraintSupport.parse(valueAnnotation, module.getSource(), retval);
137 return retval;
138 }));
139 this.properties = ObjectUtils.notNull(
140 Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
141 Arrays.stream(annotation.properties())
142 .map(ModelUtil::toPropertyEntry)
143 .collect(
144 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
145 }
146
147
148
149
150
151 @Override
152 public IBindingContext getBindingContext() {
153 return getContainingDefinition().getBindingContext();
154 }
155
156 @Override
157 public IBoundModule getContainingModule() {
158 return getContainingDefinition().getContainingModule();
159 }
160
161 @Override
162 public Field getField() {
163 return javaField;
164 }
165
166
167
168
169
170
171 @NonNull
172 public BoundField getAnnotation() {
173 return annotation;
174 }
175
176 @SuppressWarnings("null")
177 @Override
178 public IModelInstanceCollectionInfo<Object> getCollectionInfo() {
179 return collectionInfo.get();
180 }
181
182 @SuppressWarnings("null")
183 @Override
184 @NonNull
185 public IValueConstrained getConstraintSupport() {
186 return constraints.get();
187 }
188
189 @Override
190 public IDataTypeAdapter<?> getJavaTypeAdapter() {
191 return javaTypeAdapter;
192 }
193
194 @Override
195 public Object getDefaultValue() {
196 return defaultValue;
197 }
198
199 @Override
200 public IGroupAs getGroupAs() {
201 return groupAs;
202 }
203
204 @Override
205 public String getFormalName() {
206 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
207 }
208
209 @Override
210 public MarkupLine getDescription() {
211 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
212 }
213
214 @Override
215 public Integer getUseIndex() {
216 int value = getAnnotation().useIndex();
217 return value == Integer.MIN_VALUE ? null : value;
218 }
219
220 @Override
221 public boolean isInXmlWrapped() {
222 return getAnnotation().inXmlWrapped();
223 }
224
225 @Override
226 public int getMinOccurs() {
227 return getAnnotation().minOccurs();
228 }
229
230 @Override
231 public int getMaxOccurs() {
232 return getAnnotation().maxOccurs();
233 }
234
235 @Override
236 public Map<Key, Set<String>> getProperties() {
237 return ObjectUtils.notNull(properties.get());
238 }
239
240 @Override
241 public MarkupMultiline getRemarks() {
242 return ModelUtil.resolveToMarkupMultiline(getAnnotation().remarks());
243 }
244
245 @Override
246 public String getName() {
247
248
249 return ObjectUtils.notNull(
250 Optional.ofNullable(ModelUtil.resolveNoneOrValue(getAnnotation().useName())).orElse(getField().getName()));
251 }
252
253
254
255
256 }