001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.databind.model;
007
008import gov.nist.secauto.metaschema.core.model.IModelInstanceAbsolute;
009import gov.nist.secauto.metaschema.core.util.ObjectUtils;
010import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
011
012import java.lang.reflect.Field;
013import java.lang.reflect.ParameterizedType;
014import java.lang.reflect.Type;
015import java.util.Collection;
016import java.util.List;
017import java.util.Map;
018
019import edu.umd.cs.findbugs.annotations.NonNull;
020import edu.umd.cs.findbugs.annotations.Nullable;
021
022/**
023 * Represents an assembly or field instance bound to Java data.
024 *
025 * @param <ITEM>
026 *          the Java type for associated bound objects
027 */
028public interface IBoundInstanceModel<ITEM>
029    extends IBoundInstance<ITEM>, IModelInstanceAbsolute {
030  /**
031   * Get the collection Java item type for the Java field associated with this
032   * instance.
033   *
034   * @param field
035   *          the Java field for the instance
036   * @return the Java item type
037   */
038  @NonNull
039  static Class<?> getItemType(@NonNull Field field) {
040    Type fieldType = field.getGenericType();
041    Class<?> rawType = ObjectUtils.notNull(
042        (Class<?>) (fieldType instanceof ParameterizedType ? ((ParameterizedType) fieldType).getRawType() : fieldType));
043
044    Class<?> itemType;
045    if (Map.class.isAssignableFrom(rawType)) {
046      // this is a Map so the second generic type is the value
047      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[1]);
048    } else if (List.class.isAssignableFrom(rawType)) {
049      // this is a List so there is only a single generic type
050      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0]);
051    } else {
052      // non-collection
053      itemType = rawType;
054    }
055    return itemType;
056  }
057
058  /**
059   * Get the collection Java item type for the Java field associated with this
060   * instance.
061   *
062   * @param <TYPE>
063   *          the item's expected Java type
064   * @param field
065   *          the Java field for the instance
066   * @param expectedItemType
067   *          the item's expected Java type, which may be a super type of the
068   *          item's type
069   * @return the Java item type
070   */
071  @NonNull
072  static <TYPE> Class<? extends TYPE> getItemType(@NonNull Field field, @NonNull Class<TYPE> expectedItemType) {
073    Type fieldType = field.getGenericType();
074    Class<?> rawType = ObjectUtils.notNull(
075        (Class<?>) (fieldType instanceof ParameterizedType ? ((ParameterizedType) fieldType).getRawType() : fieldType));
076
077    Class<?> itemType;
078    if (Map.class.isAssignableFrom(rawType)) {
079      // this is a Map so the second generic type is the value
080      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[1]);
081    } else if (List.class.isAssignableFrom(rawType)) {
082      // this is a List so there is only a single generic type
083      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0]);
084    } else {
085      // non-collection
086      itemType = rawType;
087    }
088    return ObjectUtils.notNull(itemType.asSubclass(expectedItemType));
089  }
090
091  @Override
092  @NonNull
093  IBoundDefinitionModelAssembly getContainingDefinition();
094
095  @Override
096  default Object getResolvedDefaultValue() {
097    return getMaxOccurs() == 1 ? getEffectiveDefaultValue() : getCollectionInfo().emptyValue();
098  }
099
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}