/**
   * INTERNAL: This methods clones all the fields and ensures that each collection refers to the
   * same clones.
   */
  @Override
  public Object clone() {
    VariableOneToOneMapping clone = (VariableOneToOneMapping) super.clone();
    Map setOfKeys = new HashMap(getSourceToTargetQueryKeyNames().size());
    Map sourceToTarget = new HashMap(getSourceToTargetQueryKeyNames().size());
    Vector foreignKeys =
        org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(
            getForeignKeyFields().size());

    if (getTypeField() != null) {
      clone.setTypeField((DatabaseField) this.getTypeField().clone());
    }

    for (Iterator enumtr = getSourceToTargetQueryKeyNames().keySet().iterator();
        enumtr.hasNext(); ) {
      // Clone the SourceKeyFields
      DatabaseField field = (DatabaseField) enumtr.next();
      DatabaseField clonedField = (DatabaseField) field.clone();
      setOfKeys.put(field, clonedField);
      // on the next line I'm cloning the query key names
      sourceToTarget.put(clonedField, getSourceToTargetQueryKeyNames().get(field));
    }

    for (Enumeration enumtr = getForeignKeyFields().elements(); enumtr.hasMoreElements(); ) {
      DatabaseField field = (DatabaseField) enumtr.nextElement();
      foreignKeys.addElement(setOfKeys.get(field));
    }
    clone.setSourceToTargetQueryKeyFields(sourceToTarget);
    clone.setForeignKeyFields(foreignKeys);
    clone.setTypeIndicatorTranslation(new HashMap(this.getTypeIndicatorTranslation()));
    return clone;
  }
  /**
   * INTERNAL: Return the value of the field from the row or a value holder on the query to obtain
   * the object. Check for batch + aggregation reading.
   */
  @Override
  public Object valueFromRow(
      AbstractRecord row,
      JoinedAttributeManager joinManager,
      ObjectBuildingQuery sourceQuery,
      AbstractSession executionSession)
      throws DatabaseException {
    // If any field in the foreign key is null then it means there are no referenced objects
    for (Enumeration enumeration = getFields().elements(); enumeration.hasMoreElements(); ) {
      DatabaseField field = (DatabaseField) enumeration.nextElement();
      if (row.get(field) == null) {
        return getIndirectionPolicy().nullValueFromRow();
      }
    }

    if (getTypeField() != null) {
      // If the query used batched reading, return a special value holder,
      // or retrieve the object from the query property.
      if (sourceQuery.isReadAllQuery()
          && (((ReadAllQuery) sourceQuery).isAttributeBatchRead(getDescriptor(), getAttributeName())
              || shouldUseBatchReading())) {
        return batchedValueFromRow(row, ((ReadAllQuery) sourceQuery));
      }

      // If the field is empty we cannot load the object because we do not know what class it will
      // be
      if (row.get(getTypeField()) == null) {
        return getIndirectionPolicy().nullValueFromRow();
      }
      Class implementerClass =
          (Class) getImplementorForType(row.get(getTypeField()), executionSession);
      ReadObjectQuery query = (ReadObjectQuery) getSelectionQuery().clone();
      query.setReferenceClass(implementerClass);
      query.setSelectionCriteria(getSelectionCriteria());
      query.setDescriptor(null); // Must set to null so the right descriptor is used

      if (sourceQuery.isObjectLevelReadQuery()
          && (sourceQuery.shouldCascadeAllParts()
              || (sourceQuery.shouldCascadePrivateParts() && isPrivateOwned())
              || (sourceQuery.shouldCascadeByMapping() && this.cascadeRefresh))) {
        query.setShouldRefreshIdentityMapResult(sourceQuery.shouldRefreshIdentityMapResult());
        query.setCascadePolicy(sourceQuery.getCascadePolicy());
        query.setShouldMaintainCache(sourceQuery.shouldMaintainCache());
        // For flashback.
        if (((ObjectLevelReadQuery) sourceQuery).hasAsOfClause()) {
          query.setAsOfClause(((ObjectLevelReadQuery) sourceQuery).getAsOfClause());
        }

        // CR #4365 - used to prevent infinit recursion on refresh object cascade all
        query.setQueryId(sourceQuery.getQueryId());
      }

      return getIndirectionPolicy().valueFromQuery(query, row, executionSession);
    } else {
      return super.valueFromRow(row, joinManager, sourceQuery, executionSession);
    }
  }
  /**
   * PUBLIC: Return the foreign key field names associated with the mapping. These are only the
   * source fields that are writable.
   */
  public Vector getForeignKeyFieldNames() {
    Vector fieldNames = new Vector(getForeignKeyFields().size());
    for (Enumeration fieldsEnum = getForeignKeyFields().elements();
        fieldsEnum.hasMoreElements(); ) {
      fieldNames.addElement(((DatabaseField) fieldsEnum.nextElement()).getQualifiedName());
    }

    return fieldNames;
  }
 /** PUBLIC: Set a collection of the source to target query key/field associations. */
 public void setSourceToTargetQueryKeyFieldAssociations(
     Vector sourceToTargetQueryKeyFieldAssociations) {
   setSourceToTargetQueryKeyFields(
       new HashMap(sourceToTargetQueryKeyFieldAssociations.size() + 1));
   for (Enumeration associationsEnum = sourceToTargetQueryKeyFieldAssociations.elements();
       associationsEnum.hasMoreElements(); ) {
     Association association = (Association) associationsEnum.nextElement();
     Object sourceField = new DatabaseField((String) association.getKey());
     String targetQueryKey = (String) association.getValue();
     getSourceToTargetQueryKeyNames().put(sourceField, targetQueryKey);
   }
 }
  /**
   * PUBLIC: Return the foreign key field names associated with the mapping. These are only the
   * source fields that are writable.
   */
  public void setForeignKeyFieldNames(Vector fieldNames) {
    Vector fields =
        org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(
            fieldNames.size());
    for (Enumeration fieldNamesEnum = fieldNames.elements(); fieldNamesEnum.hasMoreElements(); ) {
      fields.addElement(new DatabaseField((String) fieldNamesEnum.nextElement()));
    }

    setForeignKeyFields(fields);
    if (!fields.isEmpty()) {
      setIsForeignKeyRelationship(true);
    }
  }
 /** INTERNAL: Get a value from the object and set that in the respective field of the row. */
 protected void writeFromNullObjectIntoRow(AbstractRecord record) {
   if (isReadOnly()) {
     return;
   }
   if (isForeignKeyRelationship()) {
     Enumeration foreignKeys = getForeignKeyFields().elements();
     while (foreignKeys.hasMoreElements()) {
       record.put((DatabaseField) foreignKeys.nextElement(), null);
     }
   }
   if (getTypeField() != null) {
     record.put(getTypeField(), null);
   }
 }
 /** PUBLIC: Set the class indicator associations. */
 public void setClassIndicatorAssociations(Vector classIndicatorAssociations) {
   setTypeIndicatorNameTranslation(new HashMap(classIndicatorAssociations.size() + 1));
   setTypeIndicatorTranslation(new HashMap((classIndicatorAssociations.size() * 2) + 1));
   for (Enumeration associationsEnum = classIndicatorAssociations.elements();
       associationsEnum.hasMoreElements(); ) {
     Association association = (Association) associationsEnum.nextElement();
     Object classValue = association.getKey();
     if (classValue instanceof Class) {
       // 904 projects will be a class type.
       addClassIndicator((Class) association.getKey(), association.getValue());
     } else {
       addClassNameIndicator((String) association.getKey(), association.getValue());
     }
   }
 }
  /** INTERNAL: Get a value from the object and set that in the respective field of the row. */
  @Override
  public void writeFromObjectIntoRowForWhereClause(
      ObjectLevelModifyQuery query, AbstractRecord record) {
    if (isReadOnly()) {
      return;
    }
    Object object;
    if (query.isDeleteObjectQuery()) {
      object = query.getObject();
    } else {
      object = query.getBackupClone();
    }
    Object referenceObject = getRealAttributeValueFromObject(object, query.getSession());

    if (referenceObject == null) {
      writeFromNullObjectIntoRow(record);
    } else {
      if (isForeignKeyRelationship()) {
        Enumeration sourceFields = getForeignKeyFields().elements();
        ClassDescriptor descriptor = query.getSession().getDescriptor(referenceObject.getClass());
        while (sourceFields.hasMoreElements()) {
          DatabaseField sourceKey = (DatabaseField) sourceFields.nextElement();
          String targetQueryKey = (String) getSourceToTargetQueryKeyNames().get(sourceKey);
          DatabaseField targetKeyField =
              descriptor.getObjectBuilder().getFieldForQueryKeyName(targetQueryKey);
          if (targetKeyField == null) {
            throw DescriptorException.variableOneToOneMappingIsNotDefinedProperly(
                this, descriptor, targetQueryKey);
          }
          Object referenceValue =
              descriptor
                  .getObjectBuilder()
                  .extractValueFromObjectForField(
                      referenceObject, targetKeyField, query.getSession());
          record.put(sourceKey, referenceValue);
        }
      }
      if (getTypeField() != null) {
        record.put(getTypeField(), getTypeForImplementor(referenceObject.getClass()));
      }
    }
  }
  /**
   * INTERNAL: Get a value from the object and set that in the respective field of the row. If the
   * mapping id target foreign key, you must only write the type into the roe, the rest will be
   * updated when the object itself is written
   */
  @Override
  public void writeFromObjectIntoRowWithChangeRecord(
      ChangeRecord changeRecord, AbstractRecord record, AbstractSession session) {
    if (isReadOnly()) {
      return;
    }

    ObjectChangeSet changeSet =
        (ObjectChangeSet) ((ObjectReferenceChangeRecord) changeRecord).getNewValue();
    if (changeSet == null) {
      writeFromNullObjectIntoRow(record);
    } else {
      Object referenceObject = changeSet.getUnitOfWorkClone();
      if (isForeignKeyRelationship()) {
        Enumeration sourceFields = getForeignKeyFields().elements();
        ClassDescriptor descriptor = session.getDescriptor(referenceObject.getClass());
        while (sourceFields.hasMoreElements()) {
          DatabaseField sourceKey = (DatabaseField) sourceFields.nextElement();
          String targetQueryKey = (String) getSourceToTargetQueryKeyNames().get(sourceKey);
          DatabaseField targetKeyField =
              descriptor.getObjectBuilder().getFieldForQueryKeyName(targetQueryKey);
          if (targetKeyField == null) {
            throw DescriptorException.variableOneToOneMappingIsNotDefinedProperly(
                this, descriptor, targetQueryKey);
          }
          Object referenceValue =
              descriptor
                  .getObjectBuilder()
                  .extractValueFromObjectForField(referenceObject, targetKeyField, session);
          record.put(sourceKey, referenceValue);
        }
      }
      if (getTypeField() != null) {
        record.put(getTypeField(), getTypeForImplementor(referenceObject.getClass()));
      }
    }
  }
  /**
   * INTERNAL: Return the value for in memory comparison. This is only valid for valueable
   * expressions.
   */
  public Object valueFromObject(
      Object object,
      AbstractSession session,
      AbstractRecord translationRow,
      int valueHolderPolicy,
      boolean isObjectUnregistered) {
    // The expression may be across a relationship, in which case it must be traversed.
    if ((!getBaseExpression().isExpressionBuilder())
        && getBaseExpression().isQueryKeyExpression()) {
      object =
          getBaseExpression()
              .valueFromObject(
                  object, session, translationRow, valueHolderPolicy, isObjectUnregistered);

      // toDo: Null means the join filters out the row, returning null is not correct if an inner
      // join,
      // outer/inner joins need to be fixed to filter correctly.
      if (object == null) {
        return null;
      }

      // If from an anyof the object will be a collection of values,
      // A new vector must union the object values and the values extracted from it.
      if (object instanceof Vector) {
        Vector comparisonVector = new Vector(((Vector) object).size() + 2);
        for (Enumeration valuesToIterate = ((Vector) object).elements();
            valuesToIterate.hasMoreElements(); ) {
          Object vectorObject = valuesToIterate.nextElement();
          if (vectorObject == null) {
            comparisonVector.addElement(vectorObject);
          } else {
            Object valueOrValues =
                valuesFromCollection(
                    vectorObject, session, valueHolderPolicy, isObjectUnregistered);

            // If a collection of values were extracted union them.
            if (valueOrValues instanceof Vector) {
              for (Enumeration nestedValuesToIterate = ((Vector) valueOrValues).elements();
                  nestedValuesToIterate.hasMoreElements(); ) {
                comparisonVector.addElement(nestedValuesToIterate.nextElement());
              }
            } else {
              comparisonVector.addElement(valueOrValues);
            }
          }
        }
        return comparisonVector;
      }
    }
    return valuesFromCollection(object, session, valueHolderPolicy, isObjectUnregistered);
  }