1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.databind.model.info;
7   
8   import java.lang.reflect.Constructor;
9   import java.lang.reflect.InvocationTargetException;
10  import java.util.Map;
11  import java.util.function.Supplier;
12  
13  import dev.metaschema.core.model.IBoundObject;
14  import dev.metaschema.core.model.IMetaschemaData;
15  import dev.metaschema.core.util.ObjectUtils;
16  import dev.metaschema.databind.io.BindingException;
17  import dev.metaschema.databind.model.IBoundDefinitionModelComplex;
18  import dev.metaschema.databind.model.IBoundProperty;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  import edu.umd.cs.findbugs.annotations.Nullable;
21  
22  /**
23   * A feature interface for handling complex item values during binding
24   * operations.
25   * <p>
26   * Complex items are bound to Java classes and can contain flags and other
27   * nested model content.
28   */
29  public interface IFeatureComplexItemValueHandler extends IItemValueHandler<IBoundObject> {
30    /**
31     * Get the Metaschema definition representing the bound complex data.
32     *
33     * @return the definition
34     */
35    @NonNull
36    IBoundDefinitionModelComplex getDefinition();
37  
38    // /**
39    // * Get the name of the JSON key, if a JSON key is configured.
40    // *
41    // * @return the name of the JSON key flag if configured, or {@code null}
42    // * otherwise
43    // */
44    // @Nullable
45    // String getJsonKeyFlagName();
46  
47    /**
48     * Get the mapping of JSON property names to property bindings.
49     *
50     * @return the mapping
51     */
52    // REFACTOR: move JSON-specific methods to a binding cache implementation
53    @NonNull
54    Map<String, IBoundProperty<?>> getJsonProperties();
55  
56    // REFACTOR: flatten implementations?
57    @Override
58    @NonNull
59    IBoundObject deepCopyItem(
60        @NonNull IBoundObject item,
61        @Nullable IBoundObject parentInstance) throws BindingException;
62  
63    /**
64     * The class this binding is to.
65     *
66     * @return the bound class
67     */
68    @NonNull
69    Class<? extends IBoundObject> getBoundClass();
70  
71    /**
72     * Gets a new instance of the bound class.
73     *
74     * @param <CLASS>
75     *          the type of the bound class
76     * @param supplier
77     *          the metaschema data generator used to capture parse information
78     *          (i.e., location)
79     * @return a Java object for the class
80     * @throws RuntimeException
81     *           if the instance cannot be created due to a binding error
82     */
83    @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes")
84    @NonNull
85    default <CLASS extends IBoundObject> CLASS newInstance(@Nullable Supplier<IMetaschemaData> supplier) {
86      Class<?> clazz = getBoundClass();
87      try {
88        CLASS retval;
89        if (supplier != null) {
90          @SuppressWarnings("unchecked")
91          Constructor<CLASS> constructor
92              = (Constructor<CLASS>) clazz.getDeclaredConstructor(IMetaschemaData.class);
93          retval = constructor.newInstance(supplier.get());
94        } else {
95          @SuppressWarnings("unchecked")
96          Constructor<CLASS> constructor
97              = (Constructor<CLASS>) clazz.getDeclaredConstructor();
98          retval = constructor.newInstance();
99        }
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 }