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