1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.databind.model;
7   
8   import java.lang.reflect.Field;
9   
10  import dev.metaschema.core.datatype.IDataTypeAdapter;
11  import dev.metaschema.core.model.IBoundObject;
12  import dev.metaschema.core.model.IFieldInstanceAbsolute;
13  import dev.metaschema.core.qname.IEnhancedQName;
14  import dev.metaschema.core.util.ObjectUtils;
15  import dev.metaschema.databind.IBindingContext;
16  import dev.metaschema.databind.model.impl.DefinitionField;
17  import dev.metaschema.databind.model.impl.InstanceModelFieldComplex;
18  import dev.metaschema.databind.model.impl.InstanceModelFieldScalar;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  
21  /**
22   * Represents a field instance bound to a Java field.
23   * <p>
24   * This interface handles both scalar (simple type) and complex (class-bound)
25   * field instances.
26   *
27   * @param <ITEM>
28   *          the Java type for associated bound objects
29   */
30  public interface IBoundInstanceModelField<ITEM> extends IBoundInstanceModelNamed<ITEM>, IFieldInstanceAbsolute {
31  
32    @Override
33    IBoundDefinitionModelField<ITEM> getDefinition();
34  
35    /**
36     * Create a new bound field instance.
37     *
38     * @param field
39     *          the Java field the instance is bound to
40     * @param containingDefinition
41     *          the definition containing the instance
42     * @return the new instance
43     */
44    @NonNull
45    static IBoundInstanceModelField<?> newInstance(
46        @NonNull Field field,
47        @NonNull IBoundDefinitionModelAssembly containingDefinition) {
48      Class<?> itemType = IBoundInstanceModel.getItemType(field);
49  
50      IBoundInstanceModelField<?> retval;
51      if (IBoundObject.class.isAssignableFrom(itemType)) {
52        IBindingContext bindingContext = containingDefinition.getBindingContext();
53        IBoundDefinitionModel<?> definition = bindingContext.getBoundDefinitionForClass(
54            ObjectUtils.notNull(itemType.asSubclass(IBoundObject.class)));
55        if (definition == null) {
56          throw new IllegalStateException(String.format(
57              "The field '%s' on class '%s' is not bound to a Metaschema field",
58              field.toString(),
59              field.getDeclaringClass().getName()));
60        }
61        retval = InstanceModelFieldComplex.newInstance(field, (DefinitionField) definition, containingDefinition);
62      } else {
63  
64        retval = InstanceModelFieldScalar.newInstance(field, containingDefinition);
65      }
66      return retval;
67    }
68  
69    @Override
70    default boolean canHandleXmlQName(IEnhancedQName qname) {
71      boolean retval;
72      if (isEffectiveValueWrappedInXml()) {
73        retval = qname.equals(getQName());
74      } else {
75        IDataTypeAdapter<?> adapter = getDefinition().getJavaTypeAdapter();
76        // we are to parse the data type
77        retval = adapter.canHandleQName(qname);
78      }
79      return retval;
80    }
81  }