IFeatureComplexItemValueHandler.java

/*
 * SPDX-FileCopyrightText: none
 * SPDX-License-Identifier: CC0-1.0
 */

package gov.nist.secauto.metaschema.databind.model.info;

import gov.nist.secauto.metaschema.core.model.IBoundObject;
import gov.nist.secauto.metaschema.core.model.IMetaschemaData;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import gov.nist.secauto.metaschema.databind.io.BindingException;
import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex;
import gov.nist.secauto.metaschema.databind.model.IBoundProperty;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.function.Supplier;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

public interface IFeatureComplexItemValueHandler extends IItemValueHandler<IBoundObject> {
  /**
   * Get the Metaschema definition representing the bound complex data.
   *
   * @return the definition
   */
  @NonNull
  IBoundDefinitionModelComplex getDefinition();

  // /**
  // * Get the name of the JSON key, if a JSON key is configured.
  // *
  // * @return the name of the JSON key flag if configured, or {@code null}
  // * otherwise
  // */
  // @Nullable
  // String getJsonKeyFlagName();

  /**
   * Get the mapping of JSON property names to property bindings.
   *
   * @return the mapping
   */
  // REFACTOR: move JSON-specific methods to a binding cache implementation
  @NonNull
  Map<String, IBoundProperty<?>> getJsonProperties();

  // REFACTOR: flatten implementations?
  @Override
  @NonNull
  IBoundObject deepCopyItem(
      @NonNull IBoundObject item,
      @Nullable IBoundObject parentInstance) throws BindingException;

  /**
   * The class this binding is to.
   *
   * @return the bound class
   */
  @NonNull
  Class<? extends IBoundObject> getBoundClass();

  /**
   * Gets a new instance of the bound class.
   *
   * @param <CLASS>
   *          the type of the bound class
   * @param supplier
   *          the metaschema data generator used to capture parse information
   *          (i.e., location)
   * @return a Java object for the class
   * @throws RuntimeException
   *           if the instance cannot be created due to a binding error
   */
  @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes")
  @NonNull
  default <CLASS extends IBoundObject> CLASS newInstance(@Nullable Supplier<IMetaschemaData> supplier) {
    Class<?> clazz = getBoundClass();
    try {
      CLASS retval;
      if (supplier != null) {
        @SuppressWarnings("unchecked")
        Constructor<CLASS> constructor
            = (Constructor<CLASS>) clazz.getDeclaredConstructor(IMetaschemaData.class);
        retval = constructor.newInstance(supplier.get());
      } else {
        @SuppressWarnings("unchecked")
        Constructor<CLASS> constructor
            = (Constructor<CLASS>) clazz.getDeclaredConstructor();
        retval = constructor.newInstance();
      }
      return ObjectUtils.notNull(retval);
    } catch (NoSuchMethodException ex) {
      String msg = String.format("Class '%s' does not have a required no-arg constructor.", clazz.getName());
      throw new RuntimeException(msg, ex);
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
      throw new RuntimeException(ex);
    }
  }

  void callBeforeDeserialize(
      @NonNull IBoundObject targetObject,
      @Nullable IBoundObject parentObject) throws BindingException;

  void callAfterDeserialize(
      @NonNull IBoundObject targetObject,
      @Nullable IBoundObject parentObject) throws BindingException;
}