1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
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.AbstractInlineFlagDefinition;
12  import gov.nist.secauto.metaschema.core.model.IAttributable;
13  import gov.nist.secauto.metaschema.core.model.IBoundObject;
14  import gov.nist.secauto.metaschema.core.model.IModule;
15  import gov.nist.secauto.metaschema.core.model.ISource;
16  import gov.nist.secauto.metaschema.core.model.constraint.IValueConstrained;
17  import gov.nist.secauto.metaschema.core.model.constraint.ValueConstraintSet;
18  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
19  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
20  import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionFlag;
21  import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModel;
22  import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag;
23  import gov.nist.secauto.metaschema.databind.model.IBoundModule;
24  import gov.nist.secauto.metaschema.databind.model.annotations.BoundFlag;
25  import gov.nist.secauto.metaschema.databind.model.annotations.JsonFieldValueKeyFlag;
26  import gov.nist.secauto.metaschema.databind.model.annotations.JsonKey;
27  import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil;
28  import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
29  
30  import java.lang.reflect.Field;
31  import java.util.Arrays;
32  import java.util.LinkedHashMap;
33  import java.util.Map;
34  import java.util.Optional;
35  import java.util.Set;
36  import java.util.stream.Collectors;
37  
38  import edu.umd.cs.findbugs.annotations.NonNull;
39  import edu.umd.cs.findbugs.annotations.Nullable;
40  import nl.talsmasoftware.lazy4j.Lazy;
41  
42  /**
43   * Implements a Metaschema module inline flag instance/definition bound to a
44   * Java field.
45   */
46  public class InstanceFlagInline
47      extends AbstractInlineFlagDefinition<IBoundDefinitionModel<IBoundObject>, IBoundDefinitionFlag, IBoundInstanceFlag>
48      implements IBoundInstanceFlag {
49    @NonNull
50    private final Field javaField;
51    @NonNull
52    private final BoundFlag annotation;
53    @NonNull
54    private final IDataTypeAdapter<?> javaTypeAdapter;
55    @Nullable
56    private final Object defaultValue;
57    @NonNull
58    private final Lazy<IValueConstrained> constraints;
59    @NonNull
60    private final Lazy<Map<IAttributable.Key, Set<String>>> properties;
61  
62    /**
63     * Construct a new inline field instance/definition.
64     *
65     * @param javaField
66     *          the Java field bound to this instance
67     * @param parent
68     *          the definition containing this instance
69     */
70    public InstanceFlagInline(
71        @NonNull Field javaField,
72        @NonNull IBoundDefinitionModel<IBoundObject> parent) {
73      super(parent);
74      this.javaField = javaField;
75      this.annotation = ModelUtil.getAnnotation(javaField, BoundFlag.class);
76      Class<? extends IDataTypeAdapter<?>> adapterClass = ObjectUtils.notNull(getAnnotation().typeAdapter());
77      this.javaTypeAdapter = ModelUtil.getDataTypeAdapter(
78          adapterClass,
79          parent.getBindingContext());
80      this.defaultValue = ModelUtil.resolveDefaultValue(getAnnotation().defaultValue(), this.javaTypeAdapter);
81  
82      IModule module = parent.getContainingModule();
83      ISource source = module.getSource();
84  
85      this.constraints = ObjectUtils.notNull(Lazy.lazy(() -> {
86        IValueConstrained retval = new ValueConstraintSet(source);
87        ValueConstraints valueAnnotation = getAnnotation().valueConstraints();
88        ConstraintSupport.parse(valueAnnotation, source, retval);
89        return retval;
90      }));
91      this.properties = ObjectUtils.notNull(
92          Lazy.lazy(() -> CollectionUtil.unmodifiableMap(ObjectUtils.notNull(
93              Arrays.stream(annotation.properties())
94                  .map(ModelUtil::toPropertyEntry)
95                  .collect(
96                      Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new))))));
97    }
98  
99    /**
100    * Get the bound Java field.
101    *
102    * @return the bound Java field
103    */
104   @Override
105   @NonNull
106   public Field getField() {
107     return javaField;
108   }
109 
110   /**
111    * Get the binding Java annotation.
112    *
113    * @return the binding Java annotation
114    */
115   @NonNull
116   private BoundFlag getAnnotation() {
117     return annotation;
118   }
119 
120   @Override
121   public IBoundModule getContainingModule() {
122     return getContainingDefinition().getContainingModule();
123   }
124 
125   // ------------------------------------------
126   // - Start annotation driven code - CPD-OFF -
127   // ------------------------------------------
128 
129   @SuppressWarnings("null")
130   @Override
131   @NonNull
132   public IValueConstrained getConstraintSupport() {
133     return constraints.get();
134   }
135 
136   @Override
137   public boolean isRequired() {
138     return getAnnotation().required();
139   }
140 
141   @Override
142   public Object getDefaultValue() {
143     return defaultValue;
144   }
145 
146   @Override
147   public boolean isJsonKey() {
148     return getField().isAnnotationPresent(JsonKey.class);
149   }
150 
151   @Override
152   public boolean isJsonValueKey() {
153     return getField().isAnnotationPresent(JsonFieldValueKeyFlag.class);
154   }
155 
156   @Override
157   public IDataTypeAdapter<?> getJavaTypeAdapter() {
158     return javaTypeAdapter;
159   }
160 
161   @Override
162   @Nullable
163   public String getFormalName() {
164     return ModelUtil.resolveNoneOrValue(getAnnotation().formalName());
165   }
166 
167   @Override
168   @Nullable
169   public MarkupLine getDescription() {
170     return ModelUtil.resolveToMarkupLine(getAnnotation().description());
171   }
172 
173   @Override
174   public String getName() {
175     return ObjectUtils.notNull(
176         Optional.ofNullable(ModelUtil.resolveNoneOrValue(getAnnotation().name())).orElse(getField().getName()));
177   }
178 
179   @Override
180   public Integer getUseIndex() {
181     return getAnnotation().useIndex();
182   }
183 
184   @Override
185   public Map<Key, Set<String>> getProperties() {
186     return ObjectUtils.notNull(properties.get());
187   }
188 
189   @Override
190   @Nullable
191   public MarkupMultiline getRemarks() {
192     return ModelUtil.resolveToMarkupMultiline(getAnnotation().remarks());
193   }
194 
195   // ----------------------------------------
196   // - End annotation driven code - CPD-OFF -
197   // ----------------------------------------
198 }