1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
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.TypeName;
12  import com.squareup.javapoet.TypeSpec;
13  
14  import java.util.Set;
15  
16  import dev.metaschema.core.datatype.IDataTypeAdapter;
17  import dev.metaschema.core.datatype.markup.MarkupLine;
18  import dev.metaschema.core.datatype.markup.MarkupMultiline;
19  import dev.metaschema.core.model.IFieldDefinition;
20  import dev.metaschema.core.model.IFlagDefinition;
21  import dev.metaschema.core.model.IFlagInstance;
22  import dev.metaschema.core.model.IModelDefinition;
23  import dev.metaschema.core.util.CollectionUtil;
24  import dev.metaschema.core.util.ObjectUtils;
25  import dev.metaschema.databind.codegen.impl.AnnotationGenerator;
26  import dev.metaschema.databind.codegen.typeinfo.def.IDefinitionTypeInfo;
27  import dev.metaschema.databind.model.annotations.BoundFlag;
28  import dev.metaschema.databind.model.annotations.JsonFieldValueKeyFlag;
29  import dev.metaschema.databind.model.annotations.JsonKey;
30  import edu.umd.cs.findbugs.annotations.NonNull;
31  
32  public class FlagInstanceTypeInfoImpl
33      extends AbstractInstanceTypeInfo<IFlagInstance, IDefinitionTypeInfo>
34      implements IFlagInstanceTypeInfo {
35    /**
36     * Constructs a new type information object for a flag instance.
37     *
38     * @param instance
39     *          the flag instance
40     * @param parentDefinition
41     *          the type information for the parent definition containing this
42     *          instance
43     */
44    public FlagInstanceTypeInfoImpl(@NonNull IFlagInstance instance, @NonNull IDefinitionTypeInfo parentDefinition) {
45      super(instance, parentDefinition);
46    }
47  
48    @Override
49    public String getBaseName() {
50      return getInstance().getEffectiveName();
51    }
52  
53    @Override
54    public boolean isRequired() {
55      return getInstance().isRequired();
56    }
57  
58    @Override
59    public TypeName getJavaFieldType() {
60      return ObjectUtils.notNull(ClassName.get(getInstance().getDefinition().getJavaTypeAdapter().getJavaClass()));
61    }
62  
63    @Override
64    public Set<IModelDefinition> buildField(
65        TypeSpec.Builder typeBuilder,
66        FieldSpec.Builder fieldBuilder) {
67      super.buildField(typeBuilder, fieldBuilder);
68  
69      IFlagInstance instance = getInstance();
70  
71      fieldBuilder.addAnnotation(buildBoundFlagAnnotation(instance).build());
72  
73      IModelDefinition parent = instance.getContainingDefinition();
74      IFlagInstance jsonKey = parent.getJsonKey();
75      if (instance.equals(jsonKey)) {
76        fieldBuilder.addAnnotation(JsonKey.class);
77      }
78  
79      if (parent instanceof IFieldDefinition) {
80        IFieldDefinition parentField = (IFieldDefinition) parent;
81  
82        if (parentField.hasJsonValueKeyFlagInstance() && instance.equals(parentField.getJsonValueKeyFlagInstance())) {
83          fieldBuilder.addAnnotation(JsonFieldValueKeyFlag.class);
84        }
85      }
86      return CollectionUtil.emptySet();
87    }
88  
89    private static AnnotationSpec.Builder buildBoundFlagAnnotation(@NonNull IFlagInstance instance) {
90      AnnotationSpec.Builder annotation = AnnotationSpec.builder(BoundFlag.class);
91  
92      String formalName = instance.getEffectiveFormalName();
93      if (formalName != null) {
94        annotation.addMember("formalName", "$S", formalName);
95      }
96  
97      MarkupLine description = instance.getEffectiveDescription();
98      if (description != null) {
99        annotation.addMember("description", "$S", description.toMarkdown());
100     }
101 
102     annotation.addMember("name", "$S", instance.getEffectiveName());
103 
104     Integer index = instance.getEffectiveIndex();
105     if (index != null) {
106       annotation.addMember("useIndex", "$L", index);
107     }
108 
109     // TODO: handle flag namespace as a prefix
110 
111     IFlagDefinition definition = instance.getDefinition();
112 
113     IDataTypeAdapter<?> valueDataType = definition.getJavaTypeAdapter();
114     Object defaultValue = instance.getEffectiveDefaultValue();
115     if (defaultValue != null) {
116       annotation.addMember("defaultValue", "$S", valueDataType.asString(defaultValue));
117     }
118 
119     if (instance.isRequired()) {
120       annotation.addMember("required", "$L", true);
121     }
122     annotation.addMember("typeAdapter", "$T.class", valueDataType.getClass());
123 
124     MarkupMultiline remarks = instance.getRemarks();
125     if (remarks != null) {
126       annotation.addMember("remarks", "$S", remarks.toMarkdown());
127     }
128 
129     AnnotationGenerator.buildValueConstraints(annotation, definition);
130 
131     return annotation;
132   }
133 }