private static void addDefaultsAndExtensions(
     Map<String, Set<Descriptors.FieldDescriptor>> e,
     Map<String, Object> defaultValueMap,
     Descriptors.Descriptor d) {
   for (Descriptors.FieldDescriptor fieldDescriptor : d.getExtensions()) {
     String containingType = fieldDescriptor.getContainingType().getFullName();
     Set<Descriptors.FieldDescriptor> fieldDescriptors = e.get(containingType);
     if (fieldDescriptors == null) {
       fieldDescriptors = new LinkedHashSet<>();
       e.put(containingType, fieldDescriptors);
     }
     fieldDescriptors.add(fieldDescriptor);
     if (fieldDescriptor.hasDefaultValue()) {
       defaultValueMap.put(
           fieldDescriptor.getContainingType().getFullName() + "." + fieldDescriptor.getName(),
           fieldDescriptor.getDefaultValue());
     }
   }
   for (Descriptors.FieldDescriptor fieldDescriptor : d.getFields()) {
     if (fieldDescriptor.hasDefaultValue()) {
       defaultValueMap.put(
           d.getFullName() + "." + fieldDescriptor.getName(), fieldDescriptor.getDefaultValue());
     }
   }
   for (Descriptors.Descriptor nestedType : d.getNestedTypes()) {
     addDefaultsAndExtensions(e, defaultValueMap, nestedType);
   }
 }
  /**
   * Serializes a field path in a record to a protobuf message using the specified descriptor.
   *
   * @param record Record with the field to serialize
   * @param field The field to serialize
   * @param fieldPath The field path of the specified field
   * @param desc Protobuf descriptor
   * @param messageTypeToExtensionMap Protobuf extension map
   * @param defaultValueMap Protobuf default field values
   * @return serialized message
   * @throws DataGeneratorException
   */
  private static DynamicMessage sdcFieldToProtobufMsg(
      Record record,
      Field field,
      String fieldPath,
      Descriptors.Descriptor desc,
      Map<String, Set<Descriptors.FieldDescriptor>> messageTypeToExtensionMap,
      Map<String, Object> defaultValueMap)
      throws DataGeneratorException {
    if (field == null) {
      return null;
    }

    // compute all fields to look for including extensions
    DynamicMessage.Builder builder = DynamicMessage.newBuilder(desc);
    List<Descriptors.FieldDescriptor> fields = new ArrayList<>();
    fields.addAll(desc.getFields());
    if (messageTypeToExtensionMap.containsKey(desc.getFullName())) {
      fields.addAll(messageTypeToExtensionMap.get(desc.getFullName()));
    }

    // root field is always a Map in a record representing protobuf data
    Map<String, Field> valueAsMap = field.getValueAsMap();

    for (Descriptors.FieldDescriptor f : fields) {
      Field mapField = valueAsMap.get(f.getName());
      // Repeated field
      if (f.isMapField()) {
        handleMapField(
            record, mapField, fieldPath, messageTypeToExtensionMap, defaultValueMap, f, builder);
      } else if (f.isRepeated()) {
        handleRepeatedField(
            record, mapField, fieldPath, messageTypeToExtensionMap, defaultValueMap, f, builder);
      } else {
        // non repeated field
        handleNonRepeatedField(
            record,
            valueAsMap,
            fieldPath,
            messageTypeToExtensionMap,
            defaultValueMap,
            desc,
            f,
            builder);
      }
    }

    // if record has unknown fields for this field path, handle it
    try {
      handleUnknownFields(record, fieldPath, builder);
    } catch (IOException e) {
      throw new DataGeneratorException(Errors.PROTOBUF_05, e.toString(), e);
    }

    return builder.build();
  }
  /**
   * Converts a protobuf message to an SDC Record Field.
   *
   * @param record SDC Record to add field to
   * @param fieldPath location in record where to insert field.
   * @param descriptor protobuf descriptor instance
   * @param messageTypeToExtensionMap protobuf extensions map
   * @param message message to decode and insert into the specified field path
   * @return new Field instance representing the decoded message
   * @throws DataParserException
   */
  public static Field protobufToSdcField(
      Record record,
      String fieldPath,
      Descriptors.Descriptor descriptor,
      Map<String, Set<Descriptors.FieldDescriptor>> messageTypeToExtensionMap,
      Object message)
      throws DataParserException {
    Map<String, Field> sdcRecordMapFieldValue = new HashMap<>();

    // get all the expected fields from the proto file
    Map<String, Descriptors.FieldDescriptor> protobufFields = new LinkedHashMap<>();
    for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) {
      protobufFields.put(fieldDescriptor.getName(), fieldDescriptor);
    }

    // get all fields in the read message
    Map<Descriptors.FieldDescriptor, Object> values = ((DynamicMessage) message).getAllFields();

    // for every field present in the proto definition create an sdc field.
    for (Descriptors.FieldDescriptor fieldDescriptor : protobufFields.values()) {
      Object value = values.get(fieldDescriptor);
      sdcRecordMapFieldValue.put(
          fieldDescriptor.getName(),
          createField(record, fieldPath, fieldDescriptor, messageTypeToExtensionMap, value));
    }

    // handle applicable extensions for this message type
    if (messageTypeToExtensionMap.containsKey(descriptor.getFullName())) {
      for (Descriptors.FieldDescriptor fieldDescriptor :
          messageTypeToExtensionMap.get(descriptor.getFullName())) {
        if (values.containsKey(fieldDescriptor)) {
          Object value = values.get(fieldDescriptor);
          sdcRecordMapFieldValue.put(
              fieldDescriptor.getName(),
              createField(record, fieldPath, fieldDescriptor, messageTypeToExtensionMap, value));
        }
      }
    }

    // handle unknown fields
    // unknown fields can go into the record header
    UnknownFieldSet unknownFields = ((DynamicMessage) message).getUnknownFields();
    if (!unknownFields.asMap().isEmpty()) {
      ByteArrayOutputStream bOut = new ByteArrayOutputStream();
      try {
        unknownFields.writeDelimitedTo(bOut);
        bOut.flush();
        bOut.close();
      } catch (IOException e) {
        throw new DataParserException(Errors.PROTOBUF_10, e.toString(), e);
      }
      String path = fieldPath.isEmpty() ? FORWARD_SLASH : fieldPath;
      byte[] bytes = org.apache.commons.codec.binary.Base64.encodeBase64(bOut.toByteArray());
      record
          .getHeader()
          .setAttribute(
              PROTOBUF_UNKNOWN_FIELDS_PREFIX + path, new String(bytes, StandardCharsets.UTF_8));
    }

    return Field.create(sdcRecordMapFieldValue);
  }