/**
   * Constructor, taking the meta data for the field, and the table it is mapped to.
   *
   * @param mmd MetaData for the field.
   * @param table Table definition
   */
  public RDBMSStoreData(AbstractMemberMetaData mmd, Table table) {
    super(mmd.getFullFieldName(), mmd, SCO_TYPE, null);

    if (table == null) {
      throw new NullPointerException("table should not be null");
    }
    this.table = table;
    this.tableName = table.toString();
    this.tableOwner = true;
    this.tableIdentifier = table.getIdentifier();

    String interfaceName =
        (table.getStoreManager().getMetaDataManager().isPersistentInterface(mmd.getType().getName())
            ? mmd.getType().getName()
            : null);
    if (interfaceName != null) {
      this.interfaceName = interfaceName;
    }
  }
  /**
   * Constructor.
   *
   * @param enhancer ClassEnhancer
   * @param fmd MetaData for the field we are generating for
   */
  public JdoSetViaCheck(ClassEnhancer enhancer, AbstractMemberMetaData fmd) {
    super(
        enhancer,
        enhancer.getNamer().getSetMethodPrefixMethodName() + fmd.getName(),
        (fmd.isPublic() ? Opcodes.ACC_PUBLIC : 0)
            | (fmd.isProtected() ? Opcodes.ACC_PROTECTED : 0)
            | (fmd.isPrivate() ? Opcodes.ACC_PRIVATE : 0)
            | Opcodes.ACC_STATIC,
        null,
        null,
        null);

    // Set the arg types/names
    argTypes = new Class[] {getClassEnhancer().getClassBeingEnhanced(), fmd.getType()};
    argNames = new String[] {"objPC", "val"};

    this.fmd = fmd;
  }
  /* (non-Javadoc)
   * @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchObjectField(int)
   */
  @Override
  public Object fetchObjectField(int fieldNumber) {
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
    RelationType relationType = mmd.getRelationType(clr);
    EmbeddedMetaData embmd = mmds.get(0).getEmbeddedMetaData();
    if (mmds.size() == 1
        && embmd != null
        && embmd.getOwnerMember() != null
        && embmd.getOwnerMember().equals(mmd.getName())) {
      // Special case of this being a link back to the owner. TODO Repeat this for nested and their
      // owners
      ObjectProvider[] ownerOps = ec.getOwnersForEmbeddedObjectProvider(op);
      return (ownerOps != null && ownerOps.length > 0 ? ownerOps[0].getObject() : null);
    }

    if (relationType != RelationType.NONE
        && MetaDataUtils.getInstance()
            .isMemberEmbedded(ec.getMetaDataManager(), clr, mmd, relationType, null)) {
      // Embedded field
      if (RelationType.isRelationSingleValued(relationType)) {
        // TODO Cater for null value detection

        List<AbstractMemberMetaData> embMmds = new ArrayList<AbstractMemberMetaData>(mmds);
        embMmds.add(mmd);

        AbstractClassMetaData embCmd =
            ec.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
        ObjectProvider embOP =
            ec.getNucleusContext()
                .getObjectProviderFactory()
                .newForEmbedded(ec, embCmd, op, fieldNumber);
        FieldManager ffm = new FetchEmbeddedFieldManager(embOP, result, embMmds, table);
        embOP.replaceFields(embCmd.getAllMemberPositions(), ffm);
        return embOP.getObject();
      }
    }

    return fetchNonEmbeddedObjectField(mmd, relationType, clr);
  }
  /** Method to add the contents of the class method. */
  public void execute() {
    visitor.visitCode();

    String fieldTypeDesc = Type.getDescriptor(fmd.getType());

    Label startLabel = new Label();
    visitor.visitLabel(startLabel);

    // "if (objPC.jdoFlags != 0 && objPC.jdoStateManager != null)"
    visitor.visitVarInsn(Opcodes.ALOAD, 0);
    visitor.visitFieldInsn(
        Opcodes.GETFIELD,
        getClassEnhancer().getASMClassName(),
        getNamer().getFlagsFieldName(),
        "B");
    Label l1 = new Label();
    visitor.visitJumpInsn(Opcodes.IFEQ, l1);
    visitor.visitVarInsn(Opcodes.ALOAD, 0);
    visitor.visitFieldInsn(
        Opcodes.GETFIELD,
        getClassEnhancer().getASMClassName(),
        getNamer().getStateManagerFieldName(),
        "L" + getNamer().getStateManagerAsmClassName() + ";");
    visitor.visitJumpInsn(Opcodes.IFNULL, l1);

    // "objPC.jdoStateManager.setYYYField(objPC, 8, objPC.ZZZ, val);"
    visitor.visitVarInsn(Opcodes.ALOAD, 0);
    visitor.visitFieldInsn(
        Opcodes.GETFIELD,
        getClassEnhancer().getASMClassName(),
        getNamer().getStateManagerFieldName(),
        "L" + getNamer().getStateManagerAsmClassName() + ";");
    visitor.visitVarInsn(Opcodes.ALOAD, 0);
    EnhanceUtils.addBIPUSHToMethod(visitor, fmd.getFieldId());
    if (enhancer.getClassMetaData().getPersistenceCapableSuperclass() != null) {
      visitor.visitFieldInsn(
          Opcodes.GETSTATIC,
          getClassEnhancer().getASMClassName(),
          getNamer().getInheritedFieldCountFieldName(),
          "I");
      visitor.visitInsn(Opcodes.IADD);
    }
    visitor.visitVarInsn(Opcodes.ALOAD, 0);
    visitor.visitFieldInsn(
        Opcodes.GETFIELD, getClassEnhancer().getASMClassName(), fmd.getName(), fieldTypeDesc);
    EnhanceUtils.addLoadForType(visitor, fmd.getType(), 1);
    String jdoMethodName = "set" + EnhanceUtils.getTypeNameForJDOMethod(fmd.getType()) + "Field";
    String argTypeDesc = fieldTypeDesc;
    if (jdoMethodName.equals("setObjectField")) {
      argTypeDesc = EnhanceUtils.CD_Object;
    }
    visitor.visitMethodInsn(
        Opcodes.INVOKEINTERFACE,
        getNamer().getStateManagerAsmClassName(),
        jdoMethodName,
        "(L" + getNamer().getPersistableAsmClassName() + ";I" + argTypeDesc + argTypeDesc + ")V");
    Label l3 = new Label();
    visitor.visitJumpInsn(Opcodes.GOTO, l3);

    // "objPC.text = val;"
    visitor.visitLabel(l1);
    if (JavaUtils.useStackMapFrames()) {
      visitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
    }

    visitor.visitVarInsn(Opcodes.ALOAD, 0);
    EnhanceUtils.addLoadForType(visitor, fmd.getType(), 1);
    visitor.visitFieldInsn(
        Opcodes.PUTFIELD, getClassEnhancer().getASMClassName(), fmd.getName(), fieldTypeDesc);

    if (enhancer.getClassMetaData().isDetachable()) {
      // "if (objPC.jdoIsDetached() == true)  ((BitSet) objPC.jdoDetachedState[3]).set(8);"
      visitor.visitVarInsn(Opcodes.ALOAD, 0);
      visitor.visitMethodInsn(
          Opcodes.INVOKEVIRTUAL,
          getClassEnhancer().getASMClassName(),
          getNamer().getIsDetachedMethodName(),
          "()Z");
      visitor.visitJumpInsn(Opcodes.IFEQ, l3);
      visitor.visitVarInsn(Opcodes.ALOAD, 0);
      visitor.visitFieldInsn(
          Opcodes.GETFIELD,
          getClassEnhancer().getASMClassName(),
          getNamer().getDetachedStateFieldName(),
          "[Ljava/lang/Object;");
      visitor.visitInsn(Opcodes.ICONST_3);
      visitor.visitInsn(Opcodes.AALOAD);
      visitor.visitTypeInsn(Opcodes.CHECKCAST, "java/util/BitSet");
      EnhanceUtils.addBIPUSHToMethod(visitor, fmd.getFieldId());
      if (enhancer.getClassMetaData().getPersistenceCapableSuperclass() != null) {
        visitor.visitFieldInsn(
            Opcodes.GETSTATIC,
            getClassEnhancer().getASMClassName(),
            getNamer().getInheritedFieldCountFieldName(),
            "I");
        visitor.visitInsn(Opcodes.IADD);
      }
      visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/BitSet", "set", "(I)V");
    }

    visitor.visitLabel(l3);
    if (JavaUtils.useStackMapFrames()) {
      visitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
    }
    visitor.visitInsn(Opcodes.RETURN);

    Label endLabel = new Label();
    visitor.visitLabel(endLabel);
    visitor.visitLocalVariable(
        argNames[0], getClassEnhancer().getClassDescriptor(), null, startLabel, endLabel, 0);
    visitor.visitLocalVariable(argNames[1], fieldTypeDesc, null, startLabel, endLabel, 1);
    visitor.visitMaxs(5, 2);

    visitor.visitEnd();
  }
  /**
   * Accessor for the expected foreign keys for this table.
   *
   * @param clr The ClassLoaderResolver
   * @return The expected foreign keys.
   */
  public List getExpectedForeignKeys(ClassLoaderResolver clr) {
    assertIsInitialized();

    boolean autoMode = false;
    if (storeMgr
        .getStringProperty(RDBMSPropertyNames.PROPERTY_RDBMS_CONSTRAINT_CREATE_MODE)
        .equals("DataNucleus")) {
      autoMode = true;
    }

    ArrayList foreignKeys = new ArrayList();
    try {
      // FK from join table to owner table
      DatastoreClass referencedTable = storeMgr.getDatastoreClass(ownerType, clr);
      if (referencedTable != null) {
        // Take <foreign-key> from <join>
        ForeignKeyMetaData fkmd = null;
        if (mmd.getJoinMetaData() != null) {
          fkmd = mmd.getJoinMetaData().getForeignKeyMetaData();
        }
        if (fkmd != null || autoMode) {
          ForeignKey fk = new ForeignKey(ownerMapping, dba, referencedTable, true);
          fk.setForMetaData(fkmd);
          foreignKeys.add(fk);
        }
      }

      if (!isSerialisedValuePC()) {
        if (isEmbeddedValuePC()) {
          // Add any FKs for the fields of the (embedded) value
          EmbeddedValuePCMapping embMapping = (EmbeddedValuePCMapping) valueMapping;
          for (int i = 0; i < embMapping.getNumberOfJavaTypeMappings(); i++) {
            JavaTypeMapping embFieldMapping = embMapping.getJavaTypeMapping(i);
            AbstractMemberMetaData embFmd = embFieldMapping.getMemberMetaData();
            if (ClassUtils.isReferenceType(embFmd.getType())
                && embFieldMapping instanceof ReferenceMapping) {
              // Field is a reference type, so add a FK to the table of the PC for each PC
              // implementation
              Collection fks =
                  TableUtils.getForeignKeysForReferenceField(
                      embFieldMapping, embFmd, autoMode, storeMgr, clr);
              foreignKeys.addAll(fks);
            } else if (storeMgr
                        .getNucleusContext()
                        .getMetaDataManager()
                        .getMetaDataForClass(embFmd.getType(), clr)
                    != null
                && embFieldMapping.getNumberOfDatastoreMappings() > 0
                && embFieldMapping instanceof PersistableMapping) {
              // Field is for a PC class with the FK at this side, so add a FK to the table of this
              // PC
              ForeignKey fk =
                  TableUtils.getForeignKeyForPCField(
                      embFieldMapping, embFmd, autoMode, storeMgr, clr);
              if (fk != null) {
                foreignKeys.add(fk);
              }
            }
          }
        } else if (mmd.getMap().valueIsPersistent()) {
          // FK from join table to value table
          referencedTable = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr);
          if (referencedTable != null) {
            // Take <foreign-key> from <value>
            ForeignKeyMetaData fkmd = null;
            if (mmd.getValueMetaData() != null) {
              fkmd = mmd.getValueMetaData().getForeignKeyMetaData();
            }
            if (fkmd != null || autoMode) {
              ForeignKey fk = new ForeignKey(valueMapping, dba, referencedTable, true);
              fk.setForMetaData(fkmd);
              foreignKeys.add(fk);
            }
          }
        }
      }

      if (!isSerialisedKeyPC()) {
        if (isEmbeddedKeyPC()) {
          // Add any FKs for the fields of the (embedded) key
          EmbeddedKeyPCMapping embMapping = (EmbeddedKeyPCMapping) keyMapping;
          for (int i = 0; i < embMapping.getNumberOfJavaTypeMappings(); i++) {
            JavaTypeMapping embFieldMapping = embMapping.getJavaTypeMapping(i);
            AbstractMemberMetaData embFmd = embFieldMapping.getMemberMetaData();
            if (ClassUtils.isReferenceType(embFmd.getType())
                && embFieldMapping instanceof ReferenceMapping) {
              // Field is a reference type, so add a FK to the table of the PC for each PC
              // implementation
              Collection fks =
                  TableUtils.getForeignKeysForReferenceField(
                      embFieldMapping, embFmd, autoMode, storeMgr, clr);
              foreignKeys.addAll(fks);
            } else if (storeMgr
                        .getNucleusContext()
                        .getMetaDataManager()
                        .getMetaDataForClass(embFmd.getType(), clr)
                    != null
                && embFieldMapping.getNumberOfDatastoreMappings() > 0
                && embFieldMapping instanceof PersistableMapping) {
              // Field is for a PC class with the FK at this side, so add a FK to the table of this
              // PC
              ForeignKey fk =
                  TableUtils.getForeignKeyForPCField(
                      embFieldMapping, embFmd, autoMode, storeMgr, clr);
              if (fk != null) {
                foreignKeys.add(fk);
              }
            }
          }
        } else if (mmd.getMap().keyIsPersistent()) {
          // FK from join table to key table
          referencedTable = storeMgr.getDatastoreClass(mmd.getMap().getKeyType(), clr);
          if (referencedTable != null) {
            // Take <foreign-key> from <key>
            ForeignKeyMetaData fkmd = null;
            if (mmd.getKeyMetaData() != null) {
              fkmd = mmd.getKeyMetaData().getForeignKeyMetaData();
            }
            if (fkmd != null || autoMode) {
              ForeignKey fk = new ForeignKey(keyMapping, dba, referencedTable, true);
              fk.setForMetaData(fkmd);
              foreignKeys.add(fk);
            }
          }
        }
      }
    } catch (NoTableManagedException e) {
      // expected when no table exists
    }
    return foreignKeys;
  }
  /** Method to add the contents of the class method. */
  public void execute() {
    visitor.visitCode();

    Label startLabel = new Label();
    visitor.visitLabel(startLabel);

    ClassMetaData cmd = enhancer.getClassMetaData();
    if (cmd.getIdentityType() == IdentityType.APPLICATION) {
      // application identity
      if (!cmd.isInstantiable()) {
        // Application identity but mapped-superclass with no PK defined, so throw exception
        visitor.visitTypeInsn(
            Opcodes.NEW, getClassEnhancer().getNamer().getFatalInternalExceptionAsmClassName());
        visitor.visitInsn(Opcodes.DUP);
        visitor.visitLdcInsn("This class has no identity");
        visitor.visitMethodInsn(
            Opcodes.INVOKESPECIAL,
            getClassEnhancer().getNamer().getFatalInternalExceptionAsmClassName(),
            "<init>",
            "(Ljava/lang/String;)V");
        visitor.visitInsn(Opcodes.ATHROW);
        Label endLabel = new Label();
        visitor.visitLabel(endLabel);
        visitor.visitLocalVariable(
            "this", getClassEnhancer().getClassDescriptor(), null, startLabel, endLabel, 0);
        visitor.visitMaxs(3, 1);
      } else {
        String objectIdClass = cmd.getObjectidClass();
        int[] pkFieldNums = cmd.getPKMemberPositions();
        if (enhancer
            .getMetaDataManager()
            .getApiAdapter()
            .isSingleFieldIdentityClass(objectIdClass)) {
          // SingleFieldIdentity
          String ACN_objectIdClass = objectIdClass.replace('.', '/');
          AbstractMemberMetaData fmd =
              cmd.getMetaDataForManagedMemberAtAbsolutePosition(pkFieldNums[0]);

          visitor.visitTypeInsn(Opcodes.NEW, ACN_objectIdClass);
          visitor.visitInsn(Opcodes.DUP);
          visitor.visitVarInsn(Opcodes.ALOAD, 0);
          visitor.visitMethodInsn(
              Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
          visitor.visitVarInsn(Opcodes.ALOAD, 0);
          if (fmd instanceof PropertyMetaData) {
            // Persistent property so use jdoGetXXX()
            visitor.visitMethodInsn(
                Opcodes.INVOKEVIRTUAL,
                getClassEnhancer().getASMClassName(),
                getNamer().getGetMethodPrefixMethodName() + fmd.getName(),
                "()" + Type.getDescriptor(fmd.getType()));
          } else {
            // Persistent field so use xxx
            visitor.visitFieldInsn(
                Opcodes.GETFIELD,
                getClassEnhancer().getASMClassName(),
                fmd.getName(),
                Type.getDescriptor(fmd.getType()));
          }
          Class primitiveType = ClassUtils.getPrimitiveTypeForType(fmd.getType());
          if (primitiveType != null) {
            // Using object wrapper of primitive so use wrapper constructor
            visitor.visitMethodInsn(
                Opcodes.INVOKESPECIAL,
                ACN_objectIdClass,
                "<init>",
                "(Ljava/lang/Class;" + Type.getDescriptor(fmd.getType()) + ")V");
          } else {
            visitor.visitMethodInsn(
                Opcodes.INVOKESPECIAL,
                ACN_objectIdClass,
                "<init>",
                "(Ljava/lang/Class;"
                    + getNamer().getTypeDescriptorForSingleFieldIdentityGetKey(objectIdClass)
                    + ")V");
          }

          visitor.visitInsn(Opcodes.ARETURN);

          Label endLabel = new Label();
          visitor.visitLabel(endLabel);
          visitor.visitLocalVariable(
              "this", getClassEnhancer().getClassDescriptor(), null, startLabel, endLabel, 0);
          visitor.visitMaxs(4, 1);
        } else {
          // User-provided app identity, and compound identity
          String ACN_objectIdClass = objectIdClass.replace('.', '/');

          visitor.visitTypeInsn(Opcodes.NEW, ACN_objectIdClass);
          visitor.visitInsn(Opcodes.DUP);
          visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, ACN_objectIdClass, "<init>", "()V");
          visitor.visitInsn(Opcodes.ARETURN);

          Label endLabel = new Label();
          visitor.visitLabel(endLabel);
          visitor.visitLocalVariable(
              "this", getClassEnhancer().getClassDescriptor(), null, startLabel, endLabel, 0);
          visitor.visitMaxs(2, 1);
        }
      }
    } else {
      // datastore/nondurable identity
      visitor.visitInsn(Opcodes.ACONST_NULL);
      visitor.visitInsn(Opcodes.ARETURN);

      Label endLabel = new Label();
      visitor.visitLabel(endLabel);
      visitor.visitLocalVariable(
          "this", getClassEnhancer().getClassDescriptor(), null, startLabel, endLabel, 0);
      visitor.visitMaxs(1, 1);
    }

    visitor.visitEnd();
  }