1
2
3
4
5
6 package dev.metaschema.databind.codegen.typeinfo;
7
8 import com.squareup.javapoet.AnnotationSpec;
9 import com.squareup.javapoet.ClassName;
10 import com.squareup.javapoet.FieldSpec;
11 import com.squareup.javapoet.MethodSpec;
12 import com.squareup.javapoet.ParameterizedTypeName;
13 import com.squareup.javapoet.TypeName;
14 import com.squareup.javapoet.TypeSpec;
15 import com.squareup.javapoet.WildcardTypeName;
16
17 import java.util.Collection;
18 import java.util.LinkedHashMap;
19 import java.util.LinkedHashSet;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24
25 import dev.metaschema.core.model.IAssemblyDefinition;
26 import dev.metaschema.core.model.IChoiceGroupInstance;
27 import dev.metaschema.core.model.IGroupable;
28 import dev.metaschema.core.model.IModelDefinition;
29 import dev.metaschema.core.model.INamedModelInstanceGrouped;
30 import dev.metaschema.core.model.JsonGroupAsBehavior;
31 import dev.metaschema.core.util.ObjectUtils;
32 import dev.metaschema.databind.codegen.config.IBindingConfiguration;
33 import dev.metaschema.databind.codegen.config.IChoiceGroupBindingConfiguration;
34 import dev.metaschema.databind.codegen.config.IDefinitionBindingConfiguration;
35 import dev.metaschema.databind.codegen.typeinfo.def.IAssemblyDefinitionTypeInfo;
36 import dev.metaschema.databind.model.annotations.BoundChoiceGroup;
37 import edu.umd.cs.findbugs.annotations.NonNull;
38 import edu.umd.cs.findbugs.annotations.Nullable;
39
40 public class ChoiceGroupTypeInfoImpl
41 extends AbstractModelInstanceTypeInfo<IChoiceGroupInstance>
42 implements IChoiceGroupTypeInfo {
43
44
45
46
47
48
49
50
51
52
53 public ChoiceGroupTypeInfoImpl(
54 @NonNull IChoiceGroupInstance instance,
55 @NonNull IAssemblyDefinitionTypeInfo parent) {
56 super(instance, parent);
57 }
58
59 @Override
60 public TypeName getJavaItemType() {
61 return getParentTypeInfo().getTypeResolver().getClassName(getInstance());
62 }
63
64
65
66
67
68
69
70
71
72
73
74
75
76 @NonNull
77 @Override
78 public TypeName getJavaFieldType() {
79 TypeName item = getJavaItemType();
80
81 @NonNull
82 TypeName retval;
83 IChoiceGroupInstance instance = getInstance();
84 int maxOccurrence = instance.getMaxOccurs();
85 if (maxOccurrence == -1 || maxOccurrence > 1) {
86
87 TypeName collectionItemType = item;
88 IAssemblyDefinition parent = instance.getContainingDefinition();
89 ITypeResolver resolver = getParentTypeInfo().getTypeResolver();
90 IBindingConfiguration bindingConfig = resolver.getBindingConfiguration();
91 IDefinitionBindingConfiguration defConfig = bindingConfig.getBindingConfigurationForDefinition(parent);
92 if (defConfig != null) {
93 IChoiceGroupBindingConfiguration choiceConfig = defConfig.getChoiceGroupBindings()
94 .get(instance.getGroupAsName());
95 if (choiceConfig != null && choiceConfig.getItemTypeName() != null && choiceConfig.isUseWildcard()) {
96
97 collectionItemType = WildcardTypeName.subtypeOf(item);
98 }
99 }
100
101 if (JsonGroupAsBehavior.KEYED.equals(instance.getJsonGroupAsBehavior())) {
102 retval = ObjectUtils.notNull(
103 ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), collectionItemType));
104 } else {
105 retval = ObjectUtils.notNull(ParameterizedTypeName.get(ClassName.get(List.class), collectionItemType));
106 }
107 } else {
108 retval = item;
109 }
110 return retval;
111 }
112
113 @Override
114 protected AnnotationSpec.Builder newBindingAnnotation() {
115 return ObjectUtils.notNull(AnnotationSpec.builder(BoundChoiceGroup.class));
116 }
117
118 @SuppressWarnings({ "PMD.UseConcurrentHashMap", "PMD.NPathComplexity", "PMD.CyclomaticComplexity" })
119 @Override
120 public Set<IModelDefinition> buildBindingAnnotation(
121 TypeSpec.Builder typeBuilder,
122 FieldSpec.Builder fieldBuilder,
123 AnnotationSpec.Builder annotation) {
124 IChoiceGroupInstance choiceGroup = getInstance();
125
126 String discriminator = choiceGroup.getJsonDiscriminatorProperty();
127 if (!IChoiceGroupInstance.DEFAULT_JSON_DISCRIMINATOR_PROPERTY_NAME.equals(discriminator)) {
128 annotation.addMember("discriminator", "$S", discriminator);
129 }
130
131 int minOccurs = choiceGroup.getMinOccurs();
132 if (minOccurs != IGroupable.DEFAULT_GROUP_AS_MIN_OCCURS) {
133 annotation.addMember("minOccurs", "$L", minOccurs);
134 }
135
136 int maxOccurs = choiceGroup.getMaxOccurs();
137 if (maxOccurs != IGroupable.DEFAULT_GROUP_AS_MAX_OCCURS) {
138 annotation.addMember("maxOccurs", "$L", maxOccurs);
139 }
140
141 if (maxOccurs == -1 || maxOccurs > 1) {
142
143 annotation.addMember("groupAs", "$L", generateGroupAsAnnotation().build());
144 }
145
146 String jsonKeyName = choiceGroup.getJsonKeyFlagInstanceName();
147 if (jsonKeyName != null) {
148 annotation.addMember("jsonKey", "$S", jsonKeyName);
149 }
150
151 Set<IModelDefinition> retval = new LinkedHashSet<>();
152
153 IAssemblyDefinitionTypeInfo parentTypeInfo = getParentTypeInfo();
154 ITypeResolver typeResolver = parentTypeInfo.getTypeResolver();
155
156 Map<ClassName, List<INamedModelInstanceGrouped>> referencedDefinitions = new LinkedHashMap<>();
157 Collection<? extends INamedModelInstanceGrouped> modelInstances = getInstance().getNamedModelInstances();
158 for (INamedModelInstanceGrouped modelInstance : modelInstances) {
159 ClassName className = typeResolver.getClassName(modelInstance.getDefinition());
160 List<INamedModelInstanceGrouped> instances = referencedDefinitions.get(className);
161 if (instances == null) {
162 instances = new LinkedList<>();
163 referencedDefinitions.put(className, instances);
164 }
165 instances.add(modelInstance);
166 }
167
168 for (INamedModelInstanceGrouped modelInstance : modelInstances) {
169 assert modelInstance != null;
170 IGroupedNamedModelInstanceTypeInfo instanceTypeInfo = typeResolver.getTypeInfo(modelInstance, this);
171
172 ClassName className = typeResolver.getClassName(modelInstance.getDefinition());
173 retval.addAll(instanceTypeInfo.generateMemberAnnotation(
174 annotation,
175 typeBuilder,
176 referencedDefinitions.get(className).size() > 1));
177 }
178 return retval;
179 }
180
181
182
183
184
185
186
187 @Nullable
188 private IChoiceGroupBindingConfiguration getChoiceGroupBindingConfiguration() {
189 IChoiceGroupInstance instance = getInstance();
190 IAssemblyDefinition parent = instance.getContainingDefinition();
191 ITypeResolver resolver = getParentTypeInfo().getTypeResolver();
192 IBindingConfiguration bindingConfig = resolver.getBindingConfiguration();
193 IDefinitionBindingConfiguration defConfig = bindingConfig.getBindingConfigurationForDefinition(parent);
194 if (defConfig != null) {
195 return defConfig.getChoiceGroupBindings().get(instance.getGroupAsName());
196 }
197 return null;
198 }
199
200 @Override
201 public void buildGetterJavadoc(@NonNull MethodSpec.Builder builder) {
202 IChoiceGroupInstance instance = getInstance();
203 String groupAsName = instance.getGroupAsName();
204
205 builder.addJavadoc("Get the {@code $L} choice group items.\n", groupAsName);
206
207
208 IChoiceGroupBindingConfiguration choiceConfig = getChoiceGroupBindingConfiguration();
209 if (choiceConfig != null) {
210 String itemTypeName = choiceConfig.getItemTypeName();
211 if (itemTypeName != null) {
212 builder.addJavadoc("\n");
213 builder.addJavadoc("<p>\n");
214 String simpleTypeName = itemTypeName.substring(itemTypeName.lastIndexOf('.') + 1);
215 if (choiceConfig.isUseWildcard()) {
216 builder.addJavadoc("Items in this collection implement {@link $L}.\n", simpleTypeName);
217 } else {
218 builder.addJavadoc("Items in this collection are of type {@link $L}.\n", simpleTypeName);
219 }
220 }
221 }
222
223 builder.addJavadoc("\n");
224 builder.addJavadoc("@return the $L items\n", groupAsName);
225 }
226
227
228
229
230
231
232
233
234 @Override
235 public void buildSetterJavadoc(@NonNull MethodSpec.Builder builder, @NonNull String paramName) {
236 IChoiceGroupInstance instance = getInstance();
237 String groupAsName = instance.getGroupAsName();
238
239 builder.addJavadoc("Set the {@code $L} choice group items.\n", groupAsName);
240
241
242 IChoiceGroupBindingConfiguration choiceConfig = getChoiceGroupBindingConfiguration();
243 if (choiceConfig != null) {
244 String itemTypeName = choiceConfig.getItemTypeName();
245 if (itemTypeName != null) {
246 builder.addJavadoc("\n");
247 builder.addJavadoc("<p>\n");
248 String simpleTypeName = itemTypeName.substring(itemTypeName.lastIndexOf('.') + 1);
249 if (choiceConfig.isUseWildcard()) {
250 builder.addJavadoc("Items in this collection must implement {@link $L}.\n", simpleTypeName);
251 } else {
252 builder.addJavadoc("Items in this collection must be of type {@link $L}.\n", simpleTypeName);
253 }
254 }
255 }
256
257 builder.addJavadoc("\n");
258 builder.addJavadoc("@param $L\n", paramName);
259 builder.addJavadoc(" the $L items to set\n", groupAsName);
260 }
261
262 }