/**
  * INTERNAL Used by SQLCall.appendModify(..) If the field should be passed to
  * customModifyInDatabaseCall, return true, otherwise false. Methods
  * shouldCustomModifyInDatabaseCall and customModifyInDatabaseCall should be kept in sync:
  * shouldCustomModifyInDatabaseCall should return true if and only if the field is handled by
  * customModifyInDatabaseCall.
  */
 public boolean shouldUseCustomModifyForCall(DatabaseField field) {
   Class type = field.getType();
   if ((type != null) && isOracle9Specific(type)) {
     return true;
   }
   return super.shouldUseCustomModifyForCall(field);
 }
 /**
  * INTERNAL: Used by SQLCall.translate(..) The binding *must* be performed (NCHAR, NSTRING,
  * NCLOB). In these special cases the method returns a wrapper object which knows whether it
  * should be bound or appended and knows how to do that.
  */
 public Object getCustomModifyValueForCall(
     Call call, Object value, DatabaseField field, boolean shouldBind) {
   Class type = field.getType();
   if ((type != null) && isOracle9Specific(type)) {
     if (value == null) {
       return null;
     }
     if (NCHAR.equals(type) || NSTRING.equals(type)) {
       return new NTypeBindCallCustomParameter(value);
     } else if (NCLOB.equals(type)) {
       value = convertToDatabaseType(value);
       if (shouldUseLocatorForLOBWrite()) {
         if (lobValueExceedsLimit(value)) {
           ((DatabaseCall) call).addContext(field, value);
           value = new String(" ");
         }
       }
       return new NTypeBindCallCustomParameter(value);
     } else if (XMLTYPE.equals(type)) {
       return getXMLTypeFactory().createXMLTypeBindCallCustomParameter(value);
     }
   }
   return super.getCustomModifyValueForCall(call, value, field, shouldBind);
 }
  /**
   * INTERNAL: Build and return the nested rows from the specified field value. This method allows
   * the field value to be an ARRAY containing other structures such as arrays or Struct, or direct
   * values.
   */
  public static Object buildContainerFromArray(
      Array fieldValue, ObjectRelationalDatabaseField arrayField, AbstractSession session)
      throws DatabaseException {
    if (arrayField.getType() == null) {
      return fieldValue;
    }
    Object[] objects = null;
    try {
      objects = (Object[]) fieldValue.getArray();
    } catch (java.sql.SQLException ex) {
      throw DatabaseException.sqlException(ex, session, false);
    }
    if (objects == null) {
      return null;
    }

    boolean isNestedStructure = false;
    ObjectRelationalDataTypeDescriptor ord = null;
    DatabaseField nestedType = null;
    if (arrayField != null) {
      nestedType = arrayField.getNestedTypeField();
      if ((nestedType != null) && nestedType.getSqlType() == Types.STRUCT) {
        ClassDescriptor descriptor = session.getDescriptor(nestedType.getType());
        if ((descriptor != null) && (descriptor.isObjectRelationalDataTypeDescriptor())) {
          // this is used to convert non-null objects passed through stored procedures and custom
          // SQL to structs
          ord = (ObjectRelationalDataTypeDescriptor) descriptor;
        }
      } else if ((nestedType != null) && (nestedType instanceof ObjectRelationalDatabaseField)) {
        isNestedStructure = true;
      }
    }
    // handle ARRAY conversions
    ReadObjectQuery query = new ReadObjectQuery();
    query.setSession(session);
    ContainerPolicy cp = ContainerPolicy.buildPolicyFor(arrayField.getType());
    Object container = cp.containerInstance(objects.length);
    for (int i = 0; i < objects.length; i++) {
      Object arrayValue = objects[i];
      if (arrayValue == null) {
        return null;
      }
      if (ord != null) {
        AbstractRecord nestedRow = ord.buildRowFromStructure((Struct) arrayValue);
        ClassDescriptor descriptor = ord;
        if (descriptor.hasInheritance()) {
          Class newElementClass =
              descriptor.getInheritancePolicy().classFromRow(nestedRow, session);
          if (!descriptor.getJavaClass().equals(newElementClass)) {
            descriptor = session.getDescriptor(newElementClass);
            if (descriptor == null) {
              descriptor = ord;
            }
          }
        }
        arrayValue = descriptor.getObjectBuilder().buildNewInstance();
        descriptor
            .getObjectBuilder()
            .buildAttributesIntoObject(arrayValue, nestedRow, query, null, false);
      } else if (isNestedStructure && (arrayValue instanceof Array)) {
        arrayValue =
            buildContainerFromArray(
                (Array) arrayValue, (ObjectRelationalDatabaseField) nestedType, session);
      }

      cp.addInto(arrayValue, container, session);
    }
    return container;
  }