001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package dev.metaschema.databind.model.info; 007 008import java.lang.reflect.Constructor; 009import java.lang.reflect.InvocationTargetException; 010import java.util.Map; 011import java.util.function.Supplier; 012 013import dev.metaschema.core.model.IBoundObject; 014import dev.metaschema.core.model.IMetaschemaData; 015import dev.metaschema.core.util.ObjectUtils; 016import dev.metaschema.databind.io.BindingException; 017import dev.metaschema.databind.model.IBoundDefinitionModelComplex; 018import dev.metaschema.databind.model.IBoundProperty; 019import edu.umd.cs.findbugs.annotations.NonNull; 020import edu.umd.cs.findbugs.annotations.Nullable; 021 022/** 023 * A feature interface for handling complex item values during binding 024 * operations. 025 * <p> 026 * Complex items are bound to Java classes and can contain flags and other 027 * nested model content. 028 */ 029public interface IFeatureComplexItemValueHandler extends IItemValueHandler<IBoundObject> { 030 /** 031 * Get the Metaschema definition representing the bound complex data. 032 * 033 * @return the definition 034 */ 035 @NonNull 036 IBoundDefinitionModelComplex getDefinition(); 037 038 // /** 039 // * Get the name of the JSON key, if a JSON key is configured. 040 // * 041 // * @return the name of the JSON key flag if configured, or {@code null} 042 // * otherwise 043 // */ 044 // @Nullable 045 // String getJsonKeyFlagName(); 046 047 /** 048 * Get the mapping of JSON property names to property bindings. 049 * 050 * @return the mapping 051 */ 052 // REFACTOR: move JSON-specific methods to a binding cache implementation 053 @NonNull 054 Map<String, IBoundProperty<?>> getJsonProperties(); 055 056 // REFACTOR: flatten implementations? 057 @Override 058 @NonNull 059 IBoundObject deepCopyItem( 060 @NonNull IBoundObject item, 061 @Nullable IBoundObject parentInstance) throws BindingException; 062 063 /** 064 * The class this binding is to. 065 * 066 * @return the bound class 067 */ 068 @NonNull 069 Class<? extends IBoundObject> getBoundClass(); 070 071 /** 072 * Gets a new instance of the bound class. 073 * 074 * @param <CLASS> 075 * the type of the bound class 076 * @param supplier 077 * the metaschema data generator used to capture parse information 078 * (i.e., location) 079 * @return a Java object for the class 080 * @throws RuntimeException 081 * if the instance cannot be created due to a binding error 082 */ 083 @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") 084 @NonNull 085 default <CLASS extends IBoundObject> CLASS newInstance(@Nullable Supplier<IMetaschemaData> supplier) { 086 Class<?> clazz = getBoundClass(); 087 try { 088 CLASS retval; 089 if (supplier != null) { 090 @SuppressWarnings("unchecked") 091 Constructor<CLASS> constructor 092 = (Constructor<CLASS>) clazz.getDeclaredConstructor(IMetaschemaData.class); 093 retval = constructor.newInstance(supplier.get()); 094 } else { 095 @SuppressWarnings("unchecked") 096 Constructor<CLASS> constructor 097 = (Constructor<CLASS>) clazz.getDeclaredConstructor(); 098 retval = constructor.newInstance(); 099 } 100 return ObjectUtils.notNull(retval); 101 } catch (NoSuchMethodException ex) { 102 String msg = String.format("Class '%s' does not have a required no-arg constructor.", clazz.getName()); 103 throw new RuntimeException(msg, ex); 104 } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { 105 throw new RuntimeException(ex); 106 } 107 } 108 109 /** 110 * Invoke the before-deserialize lifecycle callback on the target object. 111 * 112 * @param targetObject 113 * the object being deserialized 114 * @param parentObject 115 * the parent object containing the target, or {@code null} if there is 116 * no parent 117 * @throws BindingException 118 * if an error occurs during the callback 119 */ 120 void callBeforeDeserialize( 121 @NonNull IBoundObject targetObject, 122 @Nullable IBoundObject parentObject) throws BindingException; 123 124 /** 125 * Invoke the after-deserialize lifecycle callback on the target object. 126 * 127 * @param targetObject 128 * the object that was deserialized 129 * @param parentObject 130 * the parent object containing the target, or {@code null} if there is 131 * no parent 132 * @throws BindingException 133 * if an error occurs during the callback 134 */ 135 void callAfterDeserialize( 136 @NonNull IBoundObject targetObject, 137 @Nullable IBoundObject parentObject) throws BindingException; 138}