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.datatype.IDataTypeAdapter; 009import gov.nist.secauto.metaschema.core.model.IBoundObject; 010import gov.nist.secauto.metaschema.core.util.ObjectUtils; 011import gov.nist.secauto.metaschema.databind.model.info.IItemReadHandler; 012import gov.nist.secauto.metaschema.databind.model.info.IItemWriteHandler; 013 014import java.io.IOException; 015import java.util.Collection; 016import java.util.Map; 017import java.util.function.Function; 018import java.util.function.Predicate; 019import java.util.stream.Collectors; 020import java.util.stream.Stream; 021 022import javax.xml.namespace.QName; 023 024import edu.umd.cs.findbugs.annotations.NonNull; 025import edu.umd.cs.findbugs.annotations.Nullable; 026 027/** 028 * Represents a field definition bound to a Java class. 029 * <p> 030 * This definition is considered "complex", since it is bound to a Java class. 031 */ 032public interface IBoundDefinitionModelFieldComplex 033 extends IBoundDefinitionModelField<IBoundObject>, IBoundDefinitionModelComplex { 034 035 // Complex Field Definition Features 036 // ================================= 037 038 @Override 039 @NonNull 040 default IBoundDefinitionModelFieldComplex getDefinition() { 041 return this; 042 } 043 044 @Override 045 default Object getDefaultValue() { 046 Object retval = null; 047 IBoundDefinitionModelFieldComplex definition = getDefinition(); 048 IBoundFieldValue fieldValue = definition.getFieldValue(); 049 050 Object fieldValueDefault = fieldValue.getDefaultValue(); 051 if (fieldValueDefault != null) { 052 retval = definition.newInstance(null); 053 fieldValue.setValue(retval, fieldValueDefault); 054 055 // since the field value is non-null, populate the flags 056 for (IBoundInstanceFlag flag : definition.getFlagInstances()) { 057 Object flagDefault = flag.getResolvedDefaultValue(); 058 if (flagDefault != null) { 059 flag.setValue(retval, flagDefault); 060 } 061 } 062 } 063 return retval; 064 } 065 066 /** 067 * Get the bound field value associated with this field. 068 * 069 * @return the field's value binding 070 */ 071 @NonNull 072 IBoundFieldValue getFieldValue(); 073 074 @Override 075 @NonNull 076 default Object getFieldValue(@NonNull Object item) { 077 return ObjectUtils.requireNonNull(getFieldValue().getValue(item)); 078 } 079 080 @Override 081 @NonNull 082 default String getJsonValueKeyName() { 083 return getFieldValue().getJsonValueKeyName(); 084 } 085 086 @Override 087 @NonNull 088 default IDataTypeAdapter<?> getJavaTypeAdapter() { 089 return getFieldValue().getJavaTypeAdapter(); 090 } 091 092 @SuppressWarnings("PMD.NullAssignment") 093 @Override 094 @NonNull 095 default Map<String, IBoundProperty<?>> getJsonProperties(@Nullable Predicate<IBoundInstanceFlag> flagFilter) { 096 Predicate<IBoundInstanceFlag> actualFlagFilter = flagFilter; 097 098 IBoundFieldValue fieldValue = getFieldValue(); 099 IBoundInstanceFlag jsonValueKey = getDefinition().getJsonValueKeyFlagInstance(); 100 if (jsonValueKey != null) { 101 Predicate<IBoundInstanceFlag> jsonValueKeyFilter = flag -> !flag.equals(jsonValueKey); 102 actualFlagFilter = actualFlagFilter == null ? jsonValueKeyFilter : actualFlagFilter.and(jsonValueKeyFilter); 103 // ensure the field value is omitted too! 104 fieldValue = null; 105 } 106 107 Stream<? extends IBoundInstanceFlag> flagStream = getFlagInstances().stream(); 108 if (actualFlagFilter != null) { 109 flagStream = flagStream.filter(actualFlagFilter); 110 } 111 112 if (fieldValue != null) { 113 // determine if we use the field value or not 114 Collection<? extends IBoundInstanceFlag> flagInstances = flagStream 115 .collect(Collectors.toList()); 116 117 if (flagInstances.isEmpty()) { 118 // no relevant flags, so this field should expect a scalar value 119 fieldValue = null; 120 } 121 flagStream = flagInstances.stream(); 122 } 123 124 Stream<? extends IBoundProperty<?>> resultStream = fieldValue == null 125 ? flagStream 126 : Stream.concat(flagStream, Stream.of(getFieldValue())); 127 128 return ObjectUtils.notNull(resultStream 129 .collect(Collectors.toUnmodifiableMap(IBoundProperty::getJsonName, Function.identity()))); 130 } 131 132 @Override 133 @NonNull 134 default IBoundObject readItem(IBoundObject parent, IItemReadHandler handler) throws IOException { 135 return handler.readItemField(parent, this); 136 } 137 138 @Override 139 default void writeItem(IBoundObject item, IItemWriteHandler handler) throws IOException { 140 handler.writeItemField(item, this); 141 } 142 143 @Override 144 default boolean canHandleXmlQName(QName qname) { 145 // not handled, since not root 146 return false; 147 } 148}