1
2
3
4
5
6 package dev.metaschema.databind.model.impl;
7
8 import java.lang.reflect.Field;
9 import java.util.Arrays;
10 import java.util.LinkedHashMap;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.function.Predicate;
14 import java.util.stream.Collectors;
15
16 import dev.metaschema.core.datatype.markup.MarkupLine;
17 import dev.metaschema.core.datatype.markup.MarkupMultiline;
18 import dev.metaschema.core.model.AbstractAssemblyInstance;
19 import dev.metaschema.core.model.IAttributable;
20 import dev.metaschema.core.model.IBoundObject;
21 import dev.metaschema.core.util.CollectionUtil;
22 import dev.metaschema.core.util.ObjectUtils;
23 import dev.metaschema.databind.model.IBoundDefinitionModelAssembly;
24 import dev.metaschema.databind.model.IBoundInstanceFlag;
25 import dev.metaschema.databind.model.IBoundInstanceModelAssembly;
26 import dev.metaschema.databind.model.IBoundModule;
27 import dev.metaschema.databind.model.IBoundProperty;
28 import dev.metaschema.databind.model.IGroupAs;
29 import dev.metaschema.databind.model.annotations.BoundAssembly;
30 import dev.metaschema.databind.model.annotations.GroupAs;
31 import dev.metaschema.databind.model.annotations.ModelUtil;
32 import dev.metaschema.databind.model.info.IModelInstanceCollectionInfo;
33 import edu.umd.cs.findbugs.annotations.NonNull;
34 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
35 import nl.talsmasoftware.lazy4j.Lazy;
36
37
38
39
40
41
42
43
44
45
46
47 public final class InstanceModelAssemblyComplex
48 extends AbstractAssemblyInstance<
49 IBoundDefinitionModelAssembly,
50 IBoundDefinitionModelAssembly,
51 IBoundInstanceModelAssembly,
52 IBoundDefinitionModelAssembly>
53 implements IBoundInstanceModelAssembly, IFeatureInstanceModelGroupAs {
54 @NonNull
55 private final Field javaField;
56 @NonNull
57 private final BoundAssembly annotation;
58 @NonNull
59 private final Lazy<IModelInstanceCollectionInfo<IBoundObject>> collectionInfo;
60 @NonNull
61 private final IBoundDefinitionModelAssembly definition;
62 @NonNull
63 private final IGroupAs groupAs;
64 @NonNull
65 private final Lazy<Map<String, IBoundProperty<?>>> jsonProperties;
66 @NonNull
67 private final Lazy<Map<IAttributable.Key, Set<String>>> properties;
68
69
70
71
72
73
74
75
76
77
78
79
80
81 @NonNull
82 public static InstanceModelAssemblyComplex newInstance(
83 @NonNull Field javaField,
84 @NonNull IBoundDefinitionModelAssembly definition,
85 @NonNull IBoundDefinitionModelAssembly containingDefinition) {
86 BoundAssembly annotation = ModelUtil.getAnnotation(javaField, BoundAssembly.class);
87 IGroupAs groupAs = ModelUtil.resolveDefaultGroupAs(
88 annotation.groupAs(),
89 containingDefinition.getContainingModule());
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 containingDefinition.getBoundClass().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 containingDefinition.getBoundClass().getName(),
104 GroupAs.class.getName()));
105 }
106 return new InstanceModelAssemblyComplex(javaField, annotation, groupAs, definition, containingDefinition);
107 }
108
109 @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
110 private InstanceModelAssemblyComplex(
111 @NonNull Field javaField,
112 @NonNull BoundAssembly annotation,
113 @NonNull IGroupAs groupAs,
114 @NonNull IBoundDefinitionModelAssembly definition,
115 @NonNull IBoundDefinitionModelAssembly containingDefinition) {
116 super(containingDefinition);
117 FieldSupport.bindField(javaField);
118 this.javaField = javaField;
119 this.annotation = annotation;
120 this.groupAs = groupAs;
121 this.collectionInfo = ObjectUtils.notNull(Lazy.of(() -> IModelInstanceCollectionInfo.of(this)));
122 this.definition = definition;
123 this.jsonProperties = ObjectUtils.notNull(Lazy.of(() -> {
124 IBoundInstanceFlag jsonKey = getEffectiveJsonKey();
125 Predicate<IBoundInstanceFlag> flagFilter = jsonKey == null ? null : flag -> !jsonKey.equals(flag);
126 return getDefinition().getJsonProperties(flagFilter);
127 }));
128 this.properties = ObjectUtils.notNull(
129 Lazy.of(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
130 Arrays.stream(annotation.properties())
131 .map(ModelUtil::toPropertyEntry)
132 .collect(
133 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
134 }
135
136
137
138
139
140 @Override
141 public Field getField() {
142 return javaField;
143 }
144
145
146
147
148
149
150 @NonNull
151 public BoundAssembly getAnnotation() {
152 return annotation;
153 }
154
155 @SuppressWarnings("null")
156 @Override
157 public IModelInstanceCollectionInfo<IBoundObject> getCollectionInfo() {
158 return collectionInfo.get();
159 }
160
161 @Override
162 public Map<String, IBoundProperty<?>> getJsonProperties() {
163 return ObjectUtils.notNull(jsonProperties.get());
164 }
165
166 @Override
167 public IBoundDefinitionModelAssembly getDefinition() {
168 return definition;
169 }
170
171 @Override
172 public IBoundModule getContainingModule() {
173 return getContainingDefinition().getContainingModule();
174 }
175
176 @Override
177 public IGroupAs getGroupAs() {
178 return groupAs;
179 }
180
181 @Override
182 public String getFormalName() {
183 return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
184 }
185
186 @Override
187 public MarkupLine getDescription() {
188 return ModelUtil.resolveToMarkupLine(getAnnotation().description());
189 }
190
191 @Override
192 public String getUseName() {
193 return ModelUtil.resolveNoneOrValue(getAnnotation().useName());
194 }
195
196 @Override
197 public Integer getUseIndex() {
198 int value = getAnnotation().useIndex();
199 return value == Integer.MIN_VALUE ? null : value;
200 }
201
202 @Override
203 public int getMinOccurs() {
204 return getAnnotation().minOccurs();
205 }
206
207 @Override
208 public int getMaxOccurs() {
209 return getAnnotation().maxOccurs();
210 }
211
212 @Override
213 public Map<Key, Set<String>> getProperties() {
214 return ObjectUtils.notNull(properties.get());
215 }
216
217 @Override
218 public MarkupMultiline getRemarks() {
219 return ModelUtil.resolveToMarkupMultiline(getAnnotation().remarks());
220 }
221 }