1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.model;
7   
8   import gov.nist.secauto.metaschema.core.model.IModelInstanceAbsolute;
9   import gov.nist.secauto.metaschema.core.util.ObjectUtils;
10  import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
11  
12  import java.lang.reflect.Field;
13  import java.lang.reflect.ParameterizedType;
14  import java.lang.reflect.Type;
15  import java.util.Collection;
16  import java.util.List;
17  import java.util.Map;
18  
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  import edu.umd.cs.findbugs.annotations.Nullable;
21  
22  /**
23   * Represents an assembly or field instance bound to Java data.
24   *
25   * @param <ITEM>
26   *          the Java type for associated bound objects
27   */
28  public interface IBoundInstanceModel<ITEM>
29      extends IBoundInstance<ITEM>, IModelInstanceAbsolute {
30    /**
31     * Get the collection Java item type for the Java field associated with this
32     * instance.
33     *
34     * @param field
35     *          the Java field for the instance
36     * @return the Java item type
37     */
38    @NonNull
39    static Class<?> getItemType(@NonNull Field field) {
40      Type fieldType = field.getGenericType();
41      Class<?> rawType = ObjectUtils.notNull(
42          (Class<?>) (fieldType instanceof ParameterizedType ? ((ParameterizedType) fieldType).getRawType() : fieldType));
43  
44      Class<?> itemType;
45      if (Map.class.isAssignableFrom(rawType)) {
46        // this is a Map so the second generic type is the value
47        itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[1]);
48      } else if (List.class.isAssignableFrom(rawType)) {
49        // this is a List so there is only a single generic type
50        itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0]);
51      } else {
52        // non-collection
53        itemType = rawType;
54      }
55      return itemType;
56    }
57  
58    /**
59     * Get the collection Java item type for the Java field associated with this
60     * instance.
61     *
62     * @param <TYPE>
63     *          the item's expected Java type
64     * @param field
65     *          the Java field for the instance
66     * @param expectedItemType
67     *          the item's expected Java type, which may be a super type of the
68     *          item's type
69     * @return the Java item type
70     */
71    @NonNull
72    static <TYPE> Class<? extends TYPE> getItemType(@NonNull Field field, @NonNull Class<TYPE> expectedItemType) {
73      Type fieldType = field.getGenericType();
74      Class<?> rawType = ObjectUtils.notNull(
75          (Class<?>) (fieldType instanceof ParameterizedType ? ((ParameterizedType) fieldType).getRawType() : fieldType));
76  
77      Class<?> itemType;
78      if (Map.class.isAssignableFrom(rawType)) {
79        // this is a Map so the second generic type is the value
80        itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[1]);
81      } else if (List.class.isAssignableFrom(rawType)) {
82        // this is a List so there is only a single generic type
83        itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0]);
84      } else {
85        // non-collection
86        itemType = rawType;
87      }
88      return ObjectUtils.notNull(itemType.asSubclass(expectedItemType));
89    }
90  
91    @Override
92    @NonNull
93    IBoundDefinitionModelAssembly getContainingDefinition();
94  
95    @Override
96    default Object getResolvedDefaultValue() {
97      return getMaxOccurs() == 1 ? getEffectiveDefaultValue() : getCollectionInfo().emptyValue();
98    }
99  
100   /**
101    * Get the item values associated with the provided value.
102    *
103    * @param value
104    *          the value which may be a singleton or a collection
105    * @return the ordered collection of values
106    */
107   @Override
108   @NonNull
109   default Collection<? extends Object> getItemValues(Object value) {
110     return getCollectionInfo().getItemsFromValue(value);
111   }
112 
113   /**
114    * Get the Java binding information for the collection type supported by this
115    * instance.
116    *
117    * @return the collection Java binding information
118    */
119   @NonNull
120   IModelInstanceCollectionInfo<ITEM> getCollectionInfo();
121 
122   /**
123    * Get the JSON key flag for the provided item.
124    *
125    * @param item
126    *          the item to get the JSON key flag for
127    * @return the JSON key flag
128    */
129   @Nullable
130   IBoundInstanceFlag getItemJsonKey(@NonNull Object item);
131 }