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") Constructor<CLASS> constructor
085            = (Constructor<CLASS>) clazz.getDeclaredConstructor(IMetaschemaData.class);
086        retval = constructor.newInstance(supplier.get());
087      } else {
088        @SuppressWarnings("unchecked") Constructor<CLASS> constructor
089            = (Constructor<CLASS>) clazz.getDeclaredConstructor();
090        retval = constructor.newInstance();
091      }
092      return ObjectUtils.notNull(retval);
093    } catch (NoSuchMethodException ex) {
094      String msg = String.format("Class '%s' does not have a required no-arg constructor.", clazz.getName());
095      throw new RuntimeException(msg, ex);
096    } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
097      throw new RuntimeException(ex);
098    }
099  }
100
101  void callBeforeDeserialize(
102      @NonNull IBoundObject targetObject,
103      @Nullable IBoundObject parentObject) throws BindingException;
104
105  void callAfterDeserialize(
106      @NonNull IBoundObject targetObject,
107      @Nullable IBoundObject parentObject) throws BindingException;
108}