diff --git a/feeds.api.php b/feeds.api.php
index c139da1..89188f2 100644
--- a/feeds.api.php
+++ b/feeds.api.php
@@ -283,8 +283,8 @@ function hook_feeds_processor_targets_alter(&$targets, $entity_type, $bundle) {
 
       // Specify both summary_callback and form_callback to add a per mapping
       // configuration form.
-      'summary_callback' => 'my_module_summary_callback',
-      'form_callback' => 'my_module_form_callback',
+      'summary_callback' => array('my_module_summary_callback'),
+      'form_callback' => array('my_module_form_callback'),
     );
     $targets['my_node_field2'] = array(
       'name' => t('My Second custom node field'),
diff --git a/feeds.module b/feeds.module
index 419e071..4418e9c 100644
--- a/feeds.module
+++ b/feeds.module
@@ -35,6 +35,7 @@ function feeds_hook_info() {
     'feeds_after_save',
     'feeds_after_import',
     'feeds_after_clear',
+    'feeds_processor_targets',
     'feeds_processor_targets_alter',
     'feeds_parser_sources_alter',
   );
diff --git a/feeds_ui/feeds_ui.admin.inc b/feeds_ui/feeds_ui.admin.inc
index 45b423c..5856ea6 100644
--- a/feeds_ui/feeds_ui.admin.inc
+++ b/feeds_ui/feeds_ui.admin.inc
@@ -633,12 +633,10 @@ function feeds_ui_mapping_settings_form($form, $form_state, $i, $mapping, $targe
   }
 
   if ($form_state['mapping_settings_edit'] === $i) {
+    $settings_form = array();
     // Build the form.
-    if (isset($target['form_callback'])) {
-      $settings_form = call_user_func($target['form_callback'], $mapping, $target, $form, $form_state);
-    }
-    else {
-      $settings_form = array();
+    foreach ((array) $target['form_callback'] as $callback) {
+      $settings_form += $callback($mapping, $target, array(), $form_state);
     }
 
     // Merge in the optional unique form.
@@ -662,13 +660,13 @@ function feeds_ui_mapping_settings_form($form, $form_state, $i, $mapping, $targe
     );
   }
   else {
+    $summary = '';
     // Build the summary.
-    if (isset($target['summary_callback'])) {
-      $summary = call_user_func($target['summary_callback'], $mapping, $target, $form, $form_state);
-    }
-    else {
-      $summary = '';
+    $summary = array();
+    foreach ((array) $target['summary_callback'] as $callback) {
+      $summary[] = $callback($mapping, $target, $form, $form_state);
     }
+    $summary = implode('<br />', $summary);
 
     // Append the optional unique summary.
     if ($optional_unique_summary = feeds_ui_mapping_settings_optional_unique_summary($mapping, $target, $form, $form_state)) {
diff --git a/mappers/date.inc b/mappers/date.inc
index 061962d..165db5a 100644
--- a/mappers/date.inc
+++ b/mappers/date.inc
@@ -6,13 +6,12 @@
  */
 
 /**
- * Implements hook_feeds_processor_targets_alter().
- *
- * @see FeedsNodeProcessor::getMappingTargets().
+ * Implements hook_feeds_processor_targets().
  *
  * @todo Only provides "end date" target if field allows it.
  */
-function date_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function date_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
     $info = field_info_field($name);
     if (in_array($info['type'], array('date', 'datestamp', 'datetime'))) {
@@ -30,12 +29,13 @@ function date_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam
       );
     }
   }
+  return $targets;
 }
 
 /**
  * Callback for setting target values.
  */
-function date_feeds_set_target($source, $entity, $target, array $values) {
+function date_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
   list($field_name, $sub_field) = explode(':', $target, 2);
 
   $delta = 0;
diff --git a/mappers/file.inc b/mappers/file.inc
index e9cbc26..d761ba6 100644
--- a/mappers/file.inc
+++ b/mappers/file.inc
@@ -7,11 +7,10 @@
  */
 
 /**
- * Implements hook_feeds_processor_targets_alter().
- *
- * @see FeedsNodeProcessor::getMappingTargets()
+ * Implements hook_feeds_processor_targets().
  */
-function file_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function file_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
     $info = field_info_field($name);
 
@@ -39,16 +38,13 @@ function file_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam
       }
     }
   }
+  return $targets;
 }
 
 /**
- * Callback for mapping. Here is where the actual mapping happens.
- *
- * When the callback is invoked, $target contains the name of the field the
- * user has decided to map to and $value contains the value of the feed item
- * element the user has picked as a source.
+ * Callback for mapping file fields.
  */
-function file_feeds_set_target($source, $entity, $target, array $values) {
+function file_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
   // Add default of uri for backwards compatibility.
   list($field_name, $sub_field) = explode(':', $target . ':uri');
   $info = field_info_field($field_name);
diff --git a/mappers/link.inc b/mappers/link.inc
index 90b5268..4d3d0e9 100644
--- a/mappers/link.inc
+++ b/mappers/link.inc
@@ -6,11 +6,10 @@
  */
 
 /**
- * Implements hook_feeds_processor_targets_alter().
- *
- * @see FeedsProcessor::getMappingTargets()
+ * Implements hook_feeds_processor_targets().
  */
-function link_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function link_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
     $info = field_info_field($name);
     if ($info['type'] == 'link_field') {
@@ -32,16 +31,13 @@ function link_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam
       }
     }
   }
+  return $targets;
 }
 
 /**
- * Callback for mapping. Here is where the actual mapping happens.
- *
- * When the callback is invoked, $target contains the name of the field the
- * user has decided to map to and $value contains the value of the feed item
- * element the user has picked as a source.
+ * Callback for mapping link fields.
  */
-function link_feeds_set_target($source, $entity, $target, array $values) {
+function link_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
   list($field_name, $column) = explode(':', $target);
 
   $field = isset($entity->$field_name) ? $entity->$field_name : array('und' => array());
diff --git a/mappers/number.inc b/mappers/number.inc
index 338d569..d2c2d76 100644
--- a/mappers/number.inc
+++ b/mappers/number.inc
@@ -6,11 +6,10 @@
  */
 
 /**
- * Implements hook_feeds_processor_targets_alter().
- *
- * @see FeedsProcessor::getMappingTargets()
+ * Implements hook_feeds_processor_targets().
  */
-function number_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function number_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   $numeric_types = array(
     'list_integer',
     'list_float',
@@ -30,14 +29,13 @@ function number_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_n
       );
     }
   }
+  return $targets;
 }
 
 /**
- * Callback for mapping numerics.
- *
- * Ensure that $value is a numeric to avoid database errors.
+ * Callback for mapping numeric fields.
  */
-function number_feeds_set_target($source, $entity, $target, array $values) {
+function number_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
   // Iterate over all values.
   $field = isset($entity->$target) ? $entity->$target : array('und' => array());
 
diff --git a/mappers/path.inc b/mappers/path.inc
index cd39fb1..8d86391 100644
--- a/mappers/path.inc
+++ b/mappers/path.inc
@@ -6,11 +6,11 @@
  */
 
 /**
- * Implements hook_feeds_processor_targets_alter().
- *
- * @see FeedsNodeProcessor::getMappingTargets().
+ * Implements hook_feeds_processor_targets().
  */
-function path_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function path_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
+
   switch ($entity_type) {
     case 'node':
     case 'taxonomy_term':
@@ -19,11 +19,12 @@ function path_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam
         'name' => t('Path alias'),
         'description' => t('URL path alias of the node.'),
         'callback' => 'path_feeds_set_target',
-        'summary_callback' => 'path_feeds_summary_callback',
-        'form_callback' => 'path_feeds_form_callback',
+        'summary_callback' => array('path_feeds_summary_callback'),
+        'form_callback' => array('path_feeds_form_callback'),
       );
       break;
   }
+  return $targets;
 }
 
 /**
@@ -73,7 +74,7 @@ function path_feeds_set_target($source, $entity, $target, array $values, $mappin
  *   Associative array of the mapping settings.
  * @param $target
  *   Array of target settings, as defined by the processor or
- *   hook_feeds_processor_targets_alter().
+ *   hook_feeds_processor_targets().
  * @param $form
  *   The whole mapping form.
  * @param $form_state
diff --git a/mappers/profile.inc b/mappers/profile.inc
index 00fdf3a..20f4843 100644
--- a/mappers/profile.inc
+++ b/mappers/profile.inc
@@ -6,10 +6,10 @@
  */
 
 /**
- * Implements hook_feeds_processor_target_alter().
+ * Implements hook_feeds_processor_targets().
  */
-function profile_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
-
+function profile_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   if ($entity_type != 'user') {
     return;
   }
@@ -25,6 +25,7 @@ function profile_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_
       );
     }
   }
+  return $targets;
 }
 
 /**
diff --git a/mappers/taxonomy.inc b/mappers/taxonomy.inc
index 088a558..742249f 100644
--- a/mappers/taxonomy.inc
+++ b/mappers/taxonomy.inc
@@ -55,9 +55,10 @@ function taxonomy_feeds_get_source(FeedsSource $source, FeedsParserResult $resul
 }
 
 /**
- * Implements hook_feeds_processor_targets_alter().
+ * Implements hook_feeds_processor_targets().
  */
-function taxonomy_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function taxonomy_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
     $info = field_info_field($name);
     if ($info['type'] == 'taxonomy_term_reference') {
@@ -65,8 +66,9 @@ function taxonomy_feeds_processor_targets_alter(&$targets, $entity_type, $bundle
         'name' => check_plain($instance['label']),
         'callback' => 'taxonomy_feeds_set_target',
         'description' => t('The @label field of the entity.', array('@label' => $instance['label'])),
-        'summary_callback' => 'taxonomy_feeds_summary_callback',
-        'form_callback' => 'taxonomy_feeds_form_callback',
+        'summary_callback' => array('taxonomy_feeds_summary_callback'),
+        'form_callback' => array('taxonomy_feeds_form_callback'),
+        'preprocess_callbacks' => array('taxonomy_feeds_preprocess_callback'),
       );
     }
   }
@@ -75,20 +77,24 @@ function taxonomy_feeds_processor_targets_alter(&$targets, $entity_type, $bundle
     $targets['tid']['description'] = t('The tid of the taxonomy term. NOTE: use this feature with care, node ids are usually assigned by Drupal.');
     unset($targets['vocabulary']);
   }
+  return $targets;
 }
 
 /**
- * Callback for mapping. Here is where the actual mapping happens.
- *
- * @todo Do not create new terms for non-autotag fields.
+ * Callback for processing the mapping array.
  */
-function taxonomy_feeds_set_target($source, $entity, $target, array $terms, $mapping = array()) {
+function taxonomy_feeds_preprocess_callback(FeedsSource $source, $entity, $target, array $terms, array &$mapping) {
   // Add in default values.
   $mapping += array(
     'term_search' => FEEDS_TAXONOMY_SEARCH_TERM_NAME,
     'autocreate' => FALSE,
   );
+}
 
+/**
+ * Callback for mapping taxonomy fields.
+ */
+function taxonomy_feeds_set_target(FeedsSource $source, $entity, $target, array $terms, array $mapping) {
   $info = field_info_field($target);
 
   $cache = &drupal_static(__FUNCTION__);
@@ -244,7 +250,7 @@ function taxonomy_feeds_term_lookup_term_by_guid($guid) {
  *   Associative array of the mapping settings.
  * @param array $target
  *   Array of target settings, as defined by the processor or
- *   hook_feeds_processor_targets_alter().
+ *   hook_feeds_processor_targets().
  * @param array $form
  *   The whole mapping form.
  * @param array $form_state
diff --git a/mappers/text.inc b/mappers/text.inc
index 235aea3..21ee8f9 100644
--- a/mappers/text.inc
+++ b/mappers/text.inc
@@ -6,11 +6,10 @@
  */
 
 /**
- * Implements hook_feeds_processor_targets_alter().
- *
- * @see FeedsProcessor::getMappingTargets()
+ * Implements hook_feeds_processor_targets().
  */
-function text_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+function text_feeds_processor_targets($entity_type, $bundle_name) {
+  $targets = array();
   $text_types = array(
     'list_text',
     'text',
@@ -38,16 +37,17 @@ function text_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam
     }
 
     if (!empty($instance['settings']['text_processing'])) {
-      $targets[$name]['summary_callback'] = 'text_feeds_summary_callback';
-      $targets[$name]['form_callback'] = 'text_feeds_form_callback';
+      $targets[$name]['summary_callback'] = array('text_feeds_summary_callback');
+      $targets[$name]['form_callback'] = array('text_feeds_form_callback');
     }
   }
+  return $targets;
 }
 
 /**
  * Callback for mapping text fields.
  */
-function text_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping = array()) {
+function text_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) {
   list($field_name, $column) = explode(':', $target . ':value');
 
   if ($column === 'value' && isset($source->importer->processor->config['input_format'])) {
@@ -88,7 +88,7 @@ function text_feeds_set_target(FeedsSource $source, $entity, $target, array $val
  *
  * Displays which text format will be used for the text field target.
  *
- * @see text_feeds_processor_targets_alter()
+ * @see text_feeds_processor_targets()
  * @see text_feeds_form_callback()
  */
 function text_feeds_summary_callback(array $mapping, $target, array $form, array $form_state) {
@@ -110,7 +110,7 @@ function text_feeds_summary_callback(array $mapping, $target, array $form, array
  *
  * Allows to select a text format for the text field target.
  *
- * @see text_feeds_processor_targets_alter()
+ * @see text_feeds_processor_targets()
  * @see text_feeds_summary_callback()
  */
 function text_feeds_form_callback(array $mapping, $target, array $form, array $form_state) {
diff --git a/plugins/FeedsEntityProcessor.inc b/plugins/FeedsEntityProcessor.inc
index 5125c94..4e48fe9 100644
--- a/plugins/FeedsEntityProcessor.inc
+++ b/plugins/FeedsEntityProcessor.inc
@@ -260,10 +260,7 @@ class FeedsEntityProcessor extends FeedsProcessor {
       unset($targets[$entity_info['bundle keys']['bundle']]);
     }
 
-    // Let other modules expose mapping targets.
-    self::loadMappers();
-    $type = $this->entityType();
-    drupal_alter('feeds_processor_targets', $targets, $type, $info['bundle']);
+    $this->getHookTargets($targets);
 
     return $targets;
   }
diff --git a/plugins/FeedsNodeProcessor.inc b/plugins/FeedsNodeProcessor.inc
index d3d3bb4..1f8e584 100644
--- a/plugins/FeedsNodeProcessor.inc
+++ b/plugins/FeedsNodeProcessor.inc
@@ -339,11 +339,7 @@ class FeedsNodeProcessor extends FeedsProcessor {
       );
     }
 
-    // Let other modules expose mapping targets.
-    self::loadMappers();
-    $entity_type = $this->entityType();
-    $bundle = $this->bundle();
-    drupal_alter('feeds_processor_targets', $targets, $entity_type, $bundle);
+    $this->getHookTargets($targets);
 
     return $targets;
   }
diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc
index 1c56f62..05180ea 100755
--- a/plugins/FeedsProcessor.inc
+++ b/plugins/FeedsProcessor.inc
@@ -669,6 +669,10 @@ abstract class FeedsProcessor extends FeedsPlugin {
           $value = array($value);
         }
 
+        foreach ($targets[$this->id][$mapping['target']]['preprocess_callbacks'] as $preprocess) {
+          $preprocess($source, $target_item, $mapping['target'], $value, $mapping);
+        }
+
         $callback($source, $target_item, $mapping['target'], $value, $mapping);
       }
       else {
@@ -820,6 +824,28 @@ abstract class FeedsProcessor extends FeedsPlugin {
   }
 
   /**
+   * Allows other modules to expose targets.
+   *
+   * @param array &$targets
+   *   The existing target array.
+   */
+  protected function getHookTargets(array &$targets) {
+    self::loadMappers();
+    $type = $this->entityType();
+    $bundle = $this->bundle();
+    $targets += module_invoke_all('feeds_processor_targets', $type, $bundle);
+    foreach ($targets as $target => $info) {
+      $targets[$target] += array(
+        'summary_callback' => array(),
+        'form_callback' => array(),
+        'preprocess_callbacks' => array(),
+        'unique_callbacks' => array(),
+      );
+    }
+    drupal_alter('feeds_processor_targets', $targets, $type, $bundle);
+  }
+
+  /**
    * Set a concrete target element. Invoked from FeedsProcessor::map().
    *
    * @ingroup mappingapi
diff --git a/plugins/FeedsTermProcessor.inc b/plugins/FeedsTermProcessor.inc
index 9b0024e..d4670d4 100644
--- a/plugins/FeedsTermProcessor.inc
+++ b/plugins/FeedsTermProcessor.inc
@@ -177,21 +177,13 @@ class FeedsTermProcessor extends FeedsProcessor {
       'description' => array(
         'name' => t('Term description'),
         'description' => t('Description of the taxonomy term.'),
-        'summary_callback' => 'text_feeds_summary_callback',
-        'form_callback' => 'text_feeds_form_callback',
+        'summary_callback' => array('text_feeds_summary_callback'),
+        'form_callback' => array('text_feeds_form_callback'),
       ),
     );
 
-    // Let implementers of hook_feeds_term_processor_targets() add their targets.
-    try {
-      self::loadMappers();
-      $entity_type = $this->entityType();
-      $bundle = $this->bundle();
-      drupal_alter('feeds_processor_targets', $targets, $entity_type, $bundle);
-    }
-    catch (Exception $e) {
-      // Do nothing.
-    }
+    $this->getHookTargets($targets);
+
     return $targets;
   }
 
diff --git a/plugins/FeedsUserProcessor.inc b/plugins/FeedsUserProcessor.inc
index d01db43..02607f2 100644
--- a/plugins/FeedsUserProcessor.inc
+++ b/plugins/FeedsUserProcessor.inc
@@ -193,11 +193,7 @@ class FeedsUserProcessor extends FeedsProcessor {
        );
     }
 
-    // Let other modules expose mapping targets.
-    self::loadMappers();
-    $entity_type = $this->entityType();
-    $bundle = $this->bundle();
-    drupal_alter('feeds_processor_targets', $targets, $entity_type, $bundle);
+    $this->getHookTargets($targets);
 
     return $targets;
   }
diff --git a/tests/feeds_tests.module b/tests/feeds_tests.module
index fe25704..5ce8961 100644
--- a/tests/feeds_tests.module
+++ b/tests/feeds_tests.module
@@ -102,17 +102,17 @@ function feeds_tests_files_remote() {
 }
 
 /**
- * Implements hook_feeds_processor_targets_alter().
+ * Implements hook_feeds_processor_targets().
  */
-function feeds_tests_feeds_processor_targets_alter(&$targets, $entity_type, $bundle) {
+function feeds_tests_feeds_processor_targets($entity_type, $bundle) {
+  $targets = array();
   $targets['test_target'] = array(
     'name' => t('Test Target'),
     'description' => t('This is a test target.'),
     'callback' => 'feeds_tests_mapper_set_target',
-    'summary_callback' => 'feeds_tests_mapper_summary',
-    'form_callback' => 'feeds_tests_mapper_form',
+    'summary_callback' => array('feeds_tests_mapper_summary'),
+    'form_callback' => array('feeds_tests_mapper_form'),
   );
-
   $targets['test_unique_target'] = array(
     'name' => t('Test unique target'),
     'description' => t('This is a unique test target.'),
@@ -120,6 +120,7 @@ function feeds_tests_feeds_processor_targets_alter(&$targets, $entity_type, $bun
     'optional_unique' => TRUE,
     'unique_callbacks' => array('feeds_tests_mapper_unique'),
   );
+  return $targets;
 }
 
 /**
@@ -217,7 +218,7 @@ function feeds_tests_mapper_form($mapping, $target, $form, $form_state) {
 /**
  * Callback for unique_callbacks for test_target mapper.
  *
- * @see feeds_tests_feeds_processor_targets_alter()
+ * @see feeds_tests_feeds_processor_targets()
  */
 function feeds_tests_mapper_unique(FeedsSource $source, $entity_type, $bundle, $target, array $values) {
   $query = new EntityFieldQuery();
