001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.databind.model;
007
008import java.lang.reflect.Field;
009
010import dev.metaschema.core.datatype.IDataTypeAdapter;
011import dev.metaschema.core.model.IBoundObject;
012import dev.metaschema.core.model.IFieldInstanceAbsolute;
013import dev.metaschema.core.qname.IEnhancedQName;
014import dev.metaschema.core.util.ObjectUtils;
015import dev.metaschema.databind.IBindingContext;
016import dev.metaschema.databind.model.impl.DefinitionField;
017import dev.metaschema.databind.model.impl.InstanceModelFieldComplex;
018import dev.metaschema.databind.model.impl.InstanceModelFieldScalar;
019import edu.umd.cs.findbugs.annotations.NonNull;
020
021/**
022 * Represents a field instance bound to a Java field.
023 * <p>
024 * This interface handles both scalar (simple type) and complex (class-bound)
025 * field instances.
026 *
027 * @param <ITEM>
028 *          the Java type for associated bound objects
029 */
030public interface IBoundInstanceModelField<ITEM> extends IBoundInstanceModelNamed<ITEM>, IFieldInstanceAbsolute {
031
032  @Override
033  IBoundDefinitionModelField<ITEM> getDefinition();
034
035  /**
036   * Create a new bound field instance.
037   *
038   * @param field
039   *          the Java field the instance is bound to
040   * @param containingDefinition
041   *          the definition containing the instance
042   * @return the new instance
043   */
044  @NonNull
045  static IBoundInstanceModelField<?> newInstance(
046      @NonNull Field field,
047      @NonNull IBoundDefinitionModelAssembly containingDefinition) {
048    Class<?> itemType = IBoundInstanceModel.getItemType(field);
049
050    IBoundInstanceModelField<?> retval;
051    if (IBoundObject.class.isAssignableFrom(itemType)) {
052      IBindingContext bindingContext = containingDefinition.getBindingContext();
053      IBoundDefinitionModel<?> definition = bindingContext.getBoundDefinitionForClass(
054          ObjectUtils.notNull(itemType.asSubclass(IBoundObject.class)));
055      if (definition == null) {
056        throw new IllegalStateException(String.format(
057            "The field '%s' on class '%s' is not bound to a Metaschema field",
058            field.toString(),
059            field.getDeclaringClass().getName()));
060      }
061      retval = InstanceModelFieldComplex.newInstance(field, (DefinitionField) definition, containingDefinition);
062    } else {
063
064      retval = InstanceModelFieldScalar.newInstance(field, containingDefinition);
065    }
066    return retval;
067  }
068
069  @Override
070  default boolean canHandleXmlQName(IEnhancedQName qname) {
071    boolean retval;
072    if (isEffectiveValueWrappedInXml()) {
073      retval = qname.equals(getQName());
074    } else {
075      IDataTypeAdapter<?> adapter = getDefinition().getJavaTypeAdapter();
076      // we are to parse the data type
077      retval = adapter.canHandleQName(qname);
078    }
079    return retval;
080  }
081}