protected function processComponentData(&$component_data, &$component_data_info) {
    // Work over each property.
    foreach ($component_data_info as $property_name => &$property_info) {
      // Set defaults for properties that don't have a value yet.
      $this->setComponentDataPropertyDefault($property_name, $property_info, $component_data);

      // Set values from a preset.
      // We do this after defaults, so preset properties can have a default
      // value. Note this means that presets can only affect properties that
      // come after them in the property info order.
      if (isset($property_info['presets'])) {
        $this->setPresetValues($property_name, $component_data_info, $component_data);
      }

      // Allow each property to apply its processing callback. Note that this
      // may set or alter other properties in the component data array, and may
      // also make changes to the property info.
      $this->applyComponentDataPropertyProcessing($property_name, $property_info, $component_data);
    }
    // Clear the loop reference, otherwise PHP does Bad Things.
    unset($property_info);

    // Recurse into compound properties.
    // We do this last to allow the parent property to have default and
    // processing applied to the child data as a whole.
    // (TODO: test this!)
    foreach ($component_data_info as $property_name => $property_info) {
      // Only work with compound properties.
      if ($property_info['format'] != 'compound') {
        continue;
      }

      // Don't work with component child properties, as the generator will
      // handle this.
      if (isset($property_info['component_type'])) {
        continue;
      }

      if (!isset($component_data[$property_name])) {
        // Skip if no data for this property.
        continue;
      }

      foreach ($component_data[$property_name] as $delta => &$item_data) {
        $this->processComponentData($item_data, $property_info['properties']);
      }
    }
  }