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.IBoundObject;
009import gov.nist.secauto.metaschema.core.model.IModelInstanceAbsolute;
010import gov.nist.secauto.metaschema.core.util.ObjectUtils;
011import gov.nist.secauto.metaschema.databind.io.BindingException;
012import gov.nist.secauto.metaschema.databind.model.info.IModelInstanceCollectionInfo;
013
014import java.lang.reflect.Field;
015import java.lang.reflect.ParameterizedType;
016import java.lang.reflect.Type;
017import java.util.Collection;
018import java.util.List;
019import java.util.Map;
020
021import edu.umd.cs.findbugs.annotations.NonNull;
022import edu.umd.cs.findbugs.annotations.Nullable;
023
024/**
025 * Represents an assembly or field instance bound to Java data.
026 *
027 * @param <ITEM>
028 *          the Java type for associated bound objects
029 */
030public interface IBoundInstanceModel<ITEM>
031    extends IBoundInstance<ITEM>, IModelInstanceAbsolute {
032  /**
033   * Get the collection Java item type for the Java field associated with this
034   * instance.
035   *
036   * @param field
037   *          the Java field for the instance
038   * @return the Java item type
039   */
040  @NonNull
041  static Class<?> getItemType(@NonNull Field field) {
042    Type fieldType = field.getGenericType();
043    Class<?> rawType = ObjectUtils.notNull(
044        (Class<?>) (fieldType instanceof ParameterizedType ? ((ParameterizedType) fieldType).getRawType() : fieldType));
045
046    Class<?> itemType;
047    if (Map.class.isAssignableFrom(rawType)) {
048      // this is a Map so the second generic type is the value
049      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[1]);
050    } else if (List.class.isAssignableFrom(rawType)) {
051      // this is a List so there is only a single generic type
052      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0]);
053    } else {
054      // non-collection
055      itemType = rawType;
056    }
057    return itemType;
058  }
059
060  /**
061   * Get the collection Java item type for the Java field associated with this
062   * instance.
063   *
064   * @param <TYPE>
065   *          the item's expected Java type
066   * @param field
067   *          the Java field for the instance
068   * @param expectedItemType
069   *          the item's expected Java type, which may be a super type of the
070   *          item's type
071   * @return the Java item type
072   */
073  @NonNull
074  static <TYPE> Class<? extends TYPE> getItemType(@NonNull Field field, @NonNull Class<TYPE> expectedItemType) {
075    Type fieldType = field.getGenericType();
076    Class<?> rawType = ObjectUtils.notNull(
077        (Class<?>) (fieldType instanceof ParameterizedType ? ((ParameterizedType) fieldType).getRawType() : fieldType));
078
079    Class<?> itemType;
080    if (Map.class.isAssignableFrom(rawType)) {
081      // this is a Map so the second generic type is the value
082      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[1]);
083    } else if (List.class.isAssignableFrom(rawType)) {
084      // this is a List so there is only a single generic type
085      itemType = ObjectUtils.notNull((Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0]);
086    } else {
087      // non-collection
088      itemType = rawType;
089    }
090    return ObjectUtils.notNull(itemType.asSubclass(expectedItemType));
091  }
092
093  @Override
094  @NonNull
095  IBoundDefinitionModelAssembly getContainingDefinition();
096
097  @Override
098  default Object getResolvedDefaultValue() {
099    return getMaxOccurs() == 1 ? getEffectiveDefaultValue() : getCollectionInfo().emptyValue();
100  }
101
102  /**
103   * Get the item values associated with the provided value.
104   *
105   * @param value
106   *          the value which may be a singleton or a collection
107   * @return the ordered collection of values
108   */
109  @Override
110  @NonNull
111  default Collection<? extends Object> getItemValues(Object value) {
112    return getCollectionInfo().getItemsFromValue(value);
113  }
114
115  /**
116   * Get the Java binding information for the collection type supported by this
117   * instance.
118   *
119   * @return the collection Java binding information
120   */
121  @NonNull
122  IModelInstanceCollectionInfo<ITEM> getCollectionInfo();
123
124  /**
125   * Get the JSON key flag for the provided item.
126   *
127   * @param item
128   *          the item to get the JSON key flag for
129   * @return the JSON key flag
130   */
131  @Nullable
132  IBoundInstanceFlag getItemJsonKey(@NonNull Object item);
133
134  @Override
135  default void deepCopy(@NonNull IBoundObject fromInstance, @NonNull IBoundObject toInstance) throws BindingException {
136    Object value = getValue(fromInstance);
137    if (value != null) {
138      value = getCollectionInfo().deepCopyItems(fromInstance, toInstance);
139    }
140    setValue(toInstance, value);
141  }
142}