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}