protected void enhanceAttributesAccess(
      CtClass managedCtClass,
      IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) {
    final ConstPool constPool = managedCtClass.getClassFile().getConstPool();

    for (Object oMethod : managedCtClass.getClassFile().getMethods()) {
      final MethodInfo methodInfo = (MethodInfo) oMethod;
      final String methodName = methodInfo.getName();

      // skip methods added by enhancement and abstract methods (methods without any code)
      if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) {
        continue;
      }

      try {
        final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
        while (itr.hasNext()) {
          final int index = itr.next();
          final int op = itr.byteAt(index);
          if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) {
            continue;
          }
          final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1));
          final PersistentAttributeAccessMethods attributeMethods =
              attributeDescriptorMap.get(fieldName);

          // its not a field we have enhanced for interception, so skip it
          if (attributeMethods == null) {
            continue;
          }
          // System.out.printf( "Transforming access to field [%s] from method [%s]%n", fieldName,
          // methodName );
          log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName);

          if (op == Opcode.GETFIELD) {
            final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getReader());
            itr.writeByte(Opcode.INVOKESPECIAL, index);
            itr.write16bit(methodIndex, index + 1);
          } else {
            final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getWriter());
            itr.writeByte(Opcode.INVOKESPECIAL, index);
            itr.write16bit(methodIndex, index + 1);
          }
        }
        methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo));
      } catch (BadBytecode bb) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new EnhancementException(msg, bb);
      }
    }
  }
  protected void enhanceAttributesAccess(Map<String, CtField> fieldsMap, CtClass managedCtClass)
      throws Exception {
    final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
    final ClassPool classPool = managedCtClass.getClassPool();

    for (Object oMethod : managedCtClass.getClassFile().getMethods()) {
      final MethodInfo methodInfo = (MethodInfo) oMethod;
      final String methodName = methodInfo.getName();

      // skip methods added by enhancement, and abstract methods (methods without any code)
      if (methodName.startsWith(DROOLS_PREFIX) || methodInfo.getCodeAttribute() == null) {
        continue;
      }

      try {
        final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
        while (itr.hasNext()) {
          final int index = itr.next();
          final int op = itr.byteAt(index);
          if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) {
            continue;
          }

          final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1));
          CtField ctField = fieldsMap.get(fieldName);
          if (ctField == null) {
            continue;
          }

          // if we are in constructors, only need to intercept assignment statement for Reactive
          // Collection/List/... (regardless they may be final)
          if (methodInfo.isConstructor() && !(isCtFieldACollection(ctField))) {
            continue;
          }

          if (op == Opcode.PUTFIELD) {
            // addMethod is a safe add, if constant already present it return the existing value
            // without adding.
            final int methodIndex = addMethod(constPool, writeMethods.get(fieldName));
            itr.writeByte(Opcode.INVOKEVIRTUAL, index);
            itr.write16bit(methodIndex, index + 1);
          }
        }
        methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo));
      } catch (BadBytecode bb) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new Exception(msg, bb);
      }
    }
  }
  /**
   * Replace access to fields of entities (for example, entity.field) with a call to the enhanced
   * getter / setter (in this example, entity.$$_hibernate_read_field()). It's assumed that the
   * target entity is enhanced as well.
   *
   * @param managedCtClass Class to enhance
   */
  public void enhanceFieldAccess(CtClass managedCtClass) {
    final ConstPool constPool = managedCtClass.getClassFile().getConstPool();

    for (Object oMethod : managedCtClass.getClassFile().getMethods()) {
      final MethodInfo methodInfo = (MethodInfo) oMethod;
      final String methodName = methodInfo.getName();

      // skip methods added by enhancement and abstract methods (methods without any code)
      if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) {
        continue;
      }

      try {
        final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
        while (itr.hasNext()) {
          int index = itr.next();
          int op = itr.byteAt(index);
          if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) {
            continue;
          }
          String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1));
          String fieldClassName =
              constPool.getClassInfo(constPool.getFieldrefClass(itr.u16bitAt(index + 1)));
          CtClass targetCtClass = this.classPool.getCtClass(fieldClassName);

          if (!enhancementContext.isEntityClass(targetCtClass)
              && !enhancementContext.isCompositeClass(targetCtClass)) {
            continue;
          }
          if (targetCtClass == managedCtClass
              || !enhancementContext.isPersistentField(targetCtClass.getField(fieldName))
              || "this$0".equals(fieldName)) {
            continue;
          }

          log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName);

          if (op == Opcode.GETFIELD) {
            int fieldReaderMethodIndex =
                constPool.addMethodrefInfo(
                    constPool.addClassInfo(fieldClassName),
                    EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName,
                    "()" + constPool.getFieldrefType(itr.u16bitAt(index + 1)));
            itr.writeByte(Opcode.INVOKEVIRTUAL, index);
            itr.write16bit(fieldReaderMethodIndex, index + 1);
          } else {
            int fieldWriterMethodIndex =
                constPool.addMethodrefInfo(
                    constPool.addClassInfo(fieldClassName),
                    EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName,
                    "(" + constPool.getFieldrefType(itr.u16bitAt(index + 1)) + ")V");
            itr.writeByte(Opcode.INVOKEVIRTUAL, index);
            itr.write16bit(fieldWriterMethodIndex, index + 1);
          }
        }
        methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo));
      } catch (BadBytecode bb) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new EnhancementException(msg, bb);
      } catch (NotFoundException nfe) {
        final String msg =
            String.format(
                "Unable to perform field access transformation in method [%s]", methodName);
        throw new EnhancementException(msg, nfe);
      }
    }
  }