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