protected function prepareComponentDataPropertyCreateOptions($property_name, &$property_info) {
    if (isset($property_info['options'])) {
      $options_callback = $property_info['options'];
      // We may have prepared this options array from a callback previously,
      // when preparing multiple child items of a compound property.
      if (!is_callable($options_callback) && is_array($options_callback)) {
        return;
      }

      if (is_callable($options_callback)) {
        // The 'options' attribute is a callback that supplies the options
        // array.
        $options = $options_callback($property_info);
      }
      elseif (is_string($options_callback) && strpos($options_callback, ':') !== FALSE) {
        // The 'options' attribute is in the form 'TASKNAME:METHOD', to call to
        // get the options array.
        list($task_name, $method) = explode(':', $options_callback);

        $task_handler = \DrupalCodeBuilder\Factory::getTask($task_name);

        $options = call_user_func([$task_handler, $method]);
      }
      else {
        // The 'options' attribute format is incorrect.
        throw new \Exception("Unable to prepare options for property $property_name.");
      }

      $property_info['options'] = $options;
    }

    // Extract options from a list of presets for the property.
    if (isset($property_info['presets'])) {
      $options = [];
      foreach ($property_info['presets'] as $key => $preset_info) {
        $options[$key] = $preset_info['label'];
      }

      $property_info['options'] = $options;
    }

    // Recurse child properties.
    if (isset($property_info['properties'])) {
      foreach ($property_info['properties'] as $child_property_name => &$child_property_info) {
        $this->prepareComponentDataPropertyCreateOptions($child_property_name, $child_property_info);
      }
    }
  }