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;
009import java.lang.reflect.Type;
010
011import edu.umd.cs.findbugs.annotations.NonNull;
012
013/**
014 * Provides access to a Java field that is bound to a Metaschema instance.
015 */
016@FunctionalInterface
017public interface IFeatureJavaField extends IValuedMutable {
018
019  /**
020   * Gets the bound Java field associated with this instance.
021   *
022   * @return the Java field
023   */
024  @NonNull
025  Field getField();
026
027  /**
028   * Get the actual Java type of the underlying bound object.
029   * <p>
030   * This may be the same as the what is returned by {@link #getItemType()}, or
031   * may be a Java collection class.
032   *
033   * @return the raw type of the bound object
034   */
035  @SuppressWarnings("null")
036  @NonNull
037  default Type getType() {
038    return getField().getGenericType();
039  }
040
041  /**
042   * Get the item type of the bound object. An item type is the primitive or
043   * specialized type that represents that data associated with this binding.
044   *
045   * @return the item type of the bound object
046   */
047  @NonNull
048  default Class<?> getItemType() {
049    return (Class<?>) getType();
050  }
051
052  @Override
053  default Object getValue(@NonNull Object parent) {
054    Field field = getField();
055    // boolean accessable = field.canAccess(parent);
056    // field.setAccessible(true);
057    Object retval;
058    try {
059      Object result = field.get(parent);
060      retval = result;
061    } catch (IllegalArgumentException | IllegalAccessException ex) {
062      throw new IllegalArgumentException(
063          String.format("Unable to get the value of field '%s' in class '%s'.", field.getName(),
064              field.getDeclaringClass().getName()),
065          ex);
066      // } finally {
067      // field.setAccessible(accessable);
068    }
069    return retval;
070  }
071
072  @Override
073  default void setValue(@NonNull Object parentObject, Object value) {
074    Field field = getField();
075    // boolean accessable = field.canAccess(parentObject);
076    // field.setAccessible(true);
077    try {
078      field.set(parentObject, value);
079    } catch (IllegalArgumentException | IllegalAccessException ex) {
080      throw new IllegalArgumentException(
081          String.format(
082              "Unable to set the value of field '%s' in class '%s'." +
083                  " Perhaps this is a data type adapter problem on the declared class?",
084              field.getName(),
085              field.getDeclaringClass().getName()),
086          ex);
087      // } finally {
088      // field.setAccessible(accessable);
089    }
090  }
091
092}