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