Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.160
diff -u -p -r1.160 module.inc
--- includes/module.inc	10 Oct 2009 12:12:15 -0000	1.160
+++ includes/module.inc	14 Oct 2009 20:33:54 -0000
@@ -361,6 +361,8 @@ function module_implements($hook, $sort 
   if ($reset) {
     $implementations = array();
     cache_set('module_implements', array());
+    drupal_static_reset('module_hook_info');
+    cache_clear_all('hook_info', 'cache');
     return;
   }
 
@@ -376,18 +378,25 @@ function module_implements($hook, $sort 
   }
 
   if (!isset($implementations[$hook])) {
+    $hook_info = module_hook_info();
     $implementations[$hook] = array();
     $list = module_list(FALSE, FALSE, $sort);
     foreach ($list as $module) {
-      if (module_hook($module, $hook)) {
-        $implementations[$hook][$module] = $module;
+      $include_file = FALSE;
+      if (module_hook($module, $hook) || (isset($hook_info[$hook]['group']) && $include_file = module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']) && module_hook($module, $hook))) {
+        $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
         // We added something to the cache, so write it when we are done.
         $implementations['#write_cache'] = TRUE;
       }
     }
   }
   else {
-    foreach ($implementations[$hook] as $module) {
+    foreach ($implementations[$hook] as $module => $group) {
+      // If this hook implementation is stored in a lazy-loaded file, so include
+      // that file first.
+      if ($group) {
+        module_load_include('inc', $module, "$module.$group");
+      }
       // It is possible that a module removed a hook implementation without the
       // implementations cache being rebuilt yet, so we check module_hook() on
       // each request to avoid undefined function errors.
@@ -400,13 +409,45 @@ function module_implements($hook, $sort 
     }
   }
 
-  // The explicit cast forces a copy to be made. This is needed because
-  // $implementations[$hook] is only a reference to an element of
-  // $implementations and if there are nested foreaches (due to nested node
-  // API calls, for example), they would both manipulate the same array's
-  // references, which causes some modules' hooks not to be called.
-  // See also http://www.zend.com/zend/art/ref-count.php.
-  return (array)$implementations[$hook];
+  return array_keys($implementations[$hook]);
+}
+
+/**
+ * Retrieve a list of what hooks are explicitly declared.
+ */
+function module_hook_info() {
+  $hook_info = &drupal_static(__FUNCTION__, array());
+
+  if (empty($hook_info)) {
+    $cache = cache_get('hook_info');
+    if ($cache === FALSE) {
+      // Rebuild the cache and save it.
+      // We can't use module_invoke_all() here or it would cause an infinite
+      // loop.
+      foreach (module_list() as $module) {
+        $function = $module . '_hook_info';
+        if (function_exists($function)) {
+          $result = $function();
+          if (isset($result) && is_array($result)) {
+            $hook_info = array_merge_recursive($hook_info, $result);
+          }
+        }
+      }
+      // We can't use drupal_alter() for the same reason as above.
+      foreach (module_list() as $module) {
+        $function = $module . '_hook_info_alter';
+        if (function_exists($function)) {
+          $function($hook_info);
+        }
+      }
+      cache_set('hook_info', $hook_info);
+    }
+    else {
+      $hook_info = $cache->data;
+    }
+  }
+
+  return $hook_info;
 }
 
 /**
Index: includes/token.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/token.inc,v
retrieving revision 1.5
diff -u -p -r1.5 token.inc
--- includes/token.inc	25 Aug 2009 02:50:08 -0000	1.5
+++ includes/token.inc	14 Oct 2009 20:05:58 -0000
@@ -153,7 +153,6 @@ function token_scan($text) {
 function token_generate($type, array $tokens, array $data = array(), array $options = array()) {
   $results = array();
   $options += array('sanitize' => TRUE);
-  _token_initialize();
 
   $result = module_invoke_all('tokens', $type, $tokens, $data, $options);
   foreach ($result as $original => $replacement) {
@@ -227,25 +226,8 @@ function token_find_with_prefix(array $t
 function token_info() {
   $data = &drupal_static(__FUNCTION__);
   if (!isset($data)) {
-    _token_initialize();
     $data = module_invoke_all('token_info');
     drupal_alter('token_info', $data);
   }
   return $data;
 }
-
-/**
- * Load modulename.tokens.inc for all enabled modules.
- */
-function _token_initialize() {
-  $initialized = &drupal_static(__FUNCTION__);
-  if (!$initialized) {
-    foreach (module_list() as $module) {
-      $filename = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$module.tokens.inc";
-      if (file_exists($filename)) {
-        include_once $filename;
-      }
-    }
-    $initialized = TRUE;
-  }
-}
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1145
diff -u -p -r1.1145 node.module
--- modules/node/node.module	11 Oct 2009 03:07:18 -0000	1.1145
+++ modules/node/node.module	14 Oct 2009 20:16:32 -0000
@@ -117,50 +117,6 @@ function node_help($path, $arg) {
 }
 
 /**
- * Implement hook_theme().
- */
-function node_theme() {
-  return array(
-    'node' => array(
-      'arguments' => array('elements' => NULL),
-      'template' => 'node',
-    ),
-    'node_list' => array(
-      'arguments' => array('items' => NULL, 'title' => NULL),
-    ),
-    'node_search_admin' => array(
-      'arguments' => array('form' => NULL),
-    ),
-    'node_filter_form' => array(
-      'arguments' => array('form' => NULL),
-      'file' => 'node.admin.inc',
-    ),
-    'node_filters' => array(
-      'arguments' => array('form' => NULL),
-      'file' => 'node.admin.inc',
-    ),
-    'node_add_list' => array(
-      'arguments' => array('content' => NULL),
-      'file' => 'node.pages.inc',
-    ),
-    'node_form' => array(
-      'arguments' => array('form' => NULL),
-      'file' => 'node.pages.inc',
-    ),
-    'node_preview' => array(
-      'arguments' => array('node' => NULL),
-      'file' => 'node.pages.inc',
-    ),
-    'node_log_message' => array(
-      'arguments' => array('log' => NULL),
-    ),
-    'node_admin_overview' => array(
-      'arguments' => array('name' => NULL, 'type' => NULL),
-    ),
-  );
-}
-
-/**
  * Implement hook_cron().
  */
 function node_cron() {
@@ -1688,189 +1644,6 @@ function _node_add_access() {
 }
 
 /**
- * Implement hook_menu().
- */
-function node_menu() {
-  $items['admin/content'] = array(
-    'title' => 'Content',
-    'description' => 'Find and manage content.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_admin_content'),
-    'access arguments' => array('administer nodes'),
-    'weight' => -10,
-    'file' => 'node.admin.inc',
-  );
-  $items['admin/content/node'] = array(
-    'title' => 'Content',
-    'description' => "View, edit, and delete your site's content.",
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -10,
-  );
-
-  $items['admin/reports/status/rebuild'] = array(
-    'title' => 'Rebuild permissions',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_configure_rebuild_confirm'),
-    // Any user than can potentially trigger a node_access_needs_rebuild(TRUE)
-    // has to be allowed access to the 'node access rebuild' confirm form.
-    'access arguments' => array('access administration pages'),
-    'type' => MENU_CALLBACK,
-    'file' => 'node.admin.inc',
-  );
-
-  $items['admin/structure/types'] = array(
-    'title' => 'Content types',
-    'description' => 'Manage posts by content type, including default status, front page promotion, comment settings, etc.',
-    'page callback' => 'node_overview_types',
-    'access arguments' => array('administer content types'),
-    'file' => 'content_types.inc',
-  );
-  $items['admin/structure/types/list'] = array(
-    'title' => 'List',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -10,
-  );
-  $items['admin/structure/types/add'] = array(
-    'title' => 'Add content type',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_type_form'),
-    'access arguments' => array('administer content types'),
-    'type' => MENU_LOCAL_ACTION,
-    'file' => 'content_types.inc',
-  );
-  $items['admin/structure/types/manage/%node_type'] = array(
-    'title' => 'Edit content type',
-    'title callback' => 'node_type_page_title',
-    'title arguments' => array(4),
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_type_form', 4),
-    'access arguments' => array('administer content types'),
-    'file' => 'content_types.inc',
-  );
-  $items['admin/structure/types/manage/%node_type/edit'] = array(
-    'title' => 'Edit',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  );
-  $items['admin/structure/types/manage/%node_type/delete'] = array(
-    'title' => 'Delete',
-    'page arguments' => array('node_type_delete_confirm', 4),
-    'access arguments' => array('administer content types'),
-    'type' => MENU_CALLBACK,
-    'file' => 'content_types.inc',
-  );
-
-  $items['node'] = array(
-    'title' => 'Content',
-    'page callback' => 'node_page_default',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  $items['node/add'] = array(
-    'title' => 'Add new content',
-    'page callback' => 'node_add_page',
-    'access callback' => '_node_add_access',
-    'weight' => 1,
-    'menu_name' => 'management',
-    'theme callback' => '_node_custom_theme',
-    'file' => 'node.pages.inc',
-  );
-  $items['rss.xml'] = array(
-    'title' => 'RSS feed',
-    'page callback' => 'node_feed',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  // @todo Remove this loop when we have a 'description callback' property.
-  // Reset internal static cache of _node_types_build(), forces to rebuild the
-  // node type information.
-  drupal_static_reset('_node_types_build');
-  foreach (node_type_get_types() as $type) {
-    $type_url_str = str_replace('_', '-', $type->type);
-    $items['node/add/' . $type_url_str] = array(
-      'title' => $type->name,
-      'title callback' => 'check_plain',
-      'page callback' => 'node_add',
-      'page arguments' => array(2),
-      'access callback' => 'node_access',
-      'access arguments' => array('create', $type->type),
-      'description' => $type->description,
-      'file' => 'node.pages.inc',
-    );
-  }
-  $items['node/%node'] = array(
-    'page callback' => 'node_page_view',
-    'page arguments' => array(1),
-    'access callback' => 'node_access',
-    'access arguments' => array('view', 1),
-    'type' => MENU_CALLBACK);
-  $items['node/%node/view'] = array(
-    'title' => 'View',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -10);
-  $items['node/%node/edit'] = array(
-    'title' => 'Edit',
-    'page callback' => 'node_page_edit',
-    'page arguments' => array(1),
-    'access callback' => 'node_access',
-    'access arguments' => array('update', 1),
-    'theme callback' => '_node_custom_theme',
-    'weight' => 1,
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'node.pages.inc',
-  );
-  $items['node/%node/delete'] = array(
-    'title' => 'Delete',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_delete_confirm', 1),
-    'access callback' => 'node_access',
-    'access arguments' => array('delete', 1),
-    'weight' => 1,
-    'type' => MENU_CALLBACK,
-    'file' => 'node.pages.inc',
-  );
-  $items['node/%node/revisions'] = array(
-    'title' => 'Revisions',
-    'page callback' => 'node_revision_overview',
-    'page arguments' => array(1),
-    'access callback' => '_node_revision_access',
-    'access arguments' => array(1),
-    'weight' => 2,
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'node.pages.inc',
-  );
-  $items['node/%node/revisions/%/view'] = array(
-    'title' => 'Revisions',
-    'load arguments' => array(3),
-    'page callback' => 'node_show',
-    'page arguments' => array(1, TRUE),
-    'access callback' => '_node_revision_access',
-    'access arguments' => array(1),
-    'type' => MENU_CALLBACK,
-  );
-  $items['node/%node/revisions/%/revert'] = array(
-    'title' => 'Revert to earlier revision',
-    'load arguments' => array(3),
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_revision_revert_confirm', 1),
-    'access callback' => '_node_revision_access',
-    'access arguments' => array(1, 'update'),
-    'type' => MENU_CALLBACK,
-    'file' => 'node.pages.inc',
-  );
-  $items['node/%node/revisions/%/delete'] = array(
-    'title' => 'Delete earlier revision',
-    'load arguments' => array(3),
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('node_revision_delete_confirm', 1),
-    'access callback' => '_node_revision_access',
-    'access arguments' => array(1, 'delete'),
-    'type' => MENU_CALLBACK,
-    'file' => 'node.pages.inc',
-  );
-  return $items;
-}
-
-/**
  * Title callback for a node type.
  */
 function node_type_page_title($type) {
Index: modules/node/node.registry.inc
===================================================================
RCS file: modules/node/node.registry.inc
diff -N modules/node/node.registry.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/node/node.registry.inc	14 Oct 2009 20:17:06 -0000
@@ -0,0 +1,235 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Hook implementations for the 'registry' group.
+ */
+
+/**
+ * Implement hook_menu().
+ */
+function node_menu() {
+  $items['admin/content'] = array(
+    'title' => 'Content',
+    'description' => 'Find and manage content.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_admin_content'),
+    'access arguments' => array('administer nodes'),
+    'weight' => -10,
+    'file' => 'node.admin.inc',
+  );
+  $items['admin/content/node'] = array(
+    'title' => 'Content',
+    'description' => "View, edit, and delete your site's content.",
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+
+  $items['admin/reports/status/rebuild'] = array(
+    'title' => 'Rebuild permissions',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_configure_rebuild_confirm'),
+    // Any user than can potentially trigger a node_access_needs_rebuild(TRUE)
+    // has to be allowed access to the 'node access rebuild' confirm form.
+    'access arguments' => array('access administration pages'),
+    'type' => MENU_CALLBACK,
+    'file' => 'node.admin.inc',
+  );
+
+  $items['admin/structure/types'] = array(
+    'title' => 'Content types',
+    'description' => 'Manage posts by content type, including default status, front page promotion, comment settings, etc.',
+    'page callback' => 'node_overview_types',
+    'access arguments' => array('administer content types'),
+    'file' => 'content_types.inc',
+  );
+  $items['admin/structure/types/list'] = array(
+    'title' => 'List',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/structure/types/add'] = array(
+    'title' => 'Add content type',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_type_form'),
+    'access arguments' => array('administer content types'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'content_types.inc',
+  );
+  $items['admin/structure/types/manage/%node_type'] = array(
+    'title' => 'Edit content type',
+    'title callback' => 'node_type_page_title',
+    'title arguments' => array(4),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_type_form', 4),
+    'access arguments' => array('administer content types'),
+    'file' => 'content_types.inc',
+  );
+  $items['admin/structure/types/manage/%node_type/edit'] = array(
+    'title' => 'Edit',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['admin/structure/types/manage/%node_type/delete'] = array(
+    'title' => 'Delete',
+    'page arguments' => array('node_type_delete_confirm', 4),
+    'access arguments' => array('administer content types'),
+    'type' => MENU_CALLBACK,
+    'file' => 'content_types.inc',
+  );
+
+  $items['node'] = array(
+    'title' => 'Content',
+    'page callback' => 'node_page_default',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/add'] = array(
+    'title' => 'Add new content',
+    'page callback' => 'node_add_page',
+    'access callback' => '_node_add_access',
+    'weight' => 1,
+    'menu_name' => 'management',
+    'theme callback' => '_node_custom_theme',
+    'file' => 'node.pages.inc',
+  );
+  $items['rss.xml'] = array(
+    'title' => 'RSS feed',
+    'page callback' => 'node_feed',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+  // @todo Remove this loop when we have a 'description callback' property.
+  // Reset internal static cache of _node_types_build(), forces to rebuild the
+  // node type information.
+  drupal_static_reset('_node_types_build');
+  foreach (node_type_get_types() as $type) {
+    $type_url_str = str_replace('_', '-', $type->type);
+    $items['node/add/' . $type_url_str] = array(
+      'title' => $type->name,
+      'title callback' => 'check_plain',
+      'page callback' => 'node_add',
+      'page arguments' => array(2),
+      'access callback' => 'node_access',
+      'access arguments' => array('create', $type->type),
+      'description' => $type->description,
+      'file' => 'node.pages.inc',
+    );
+  }
+  $items['node/%node'] = array(
+    'page callback' => 'node_page_view',
+    'page arguments' => array(1),
+    'access callback' => 'node_access',
+    'access arguments' => array('view', 1),
+    'type' => MENU_CALLBACK);
+  $items['node/%node/view'] = array(
+    'title' => 'View',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10);
+  $items['node/%node/edit'] = array(
+    'title' => 'Edit',
+    'page callback' => 'node_page_edit',
+    'page arguments' => array(1),
+    'access callback' => 'node_access',
+    'access arguments' => array('update', 1),
+    'theme callback' => '_node_custom_theme',
+    'weight' => 1,
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'node.pages.inc',
+  );
+  $items['node/%node/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_delete_confirm', 1),
+    'access callback' => 'node_access',
+    'access arguments' => array('delete', 1),
+    'weight' => 1,
+    'type' => MENU_CALLBACK,
+    'file' => 'node.pages.inc',
+  );
+  $items['node/%node/revisions'] = array(
+    'title' => 'Revisions',
+    'page callback' => 'node_revision_overview',
+    'page arguments' => array(1),
+    'access callback' => '_node_revision_access',
+    'access arguments' => array(1),
+    'weight' => 2,
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'node.pages.inc',
+  );
+  $items['node/%node/revisions/%/view'] = array(
+    'title' => 'Revisions',
+    'load arguments' => array(3),
+    'page callback' => 'node_show',
+    'page arguments' => array(1, TRUE),
+    'access callback' => '_node_revision_access',
+    'access arguments' => array(1),
+    'type' => MENU_CALLBACK,
+  );
+  $items['node/%node/revisions/%/revert'] = array(
+    'title' => 'Revert to earlier revision',
+    'load arguments' => array(3),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_revision_revert_confirm', 1),
+    'access callback' => '_node_revision_access',
+    'access arguments' => array(1, 'update'),
+    'type' => MENU_CALLBACK,
+    'file' => 'node.pages.inc',
+  );
+  $items['node/%node/revisions/%/delete'] = array(
+    'title' => 'Delete earlier revision',
+    'load arguments' => array(3),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('node_revision_delete_confirm', 1),
+    'access callback' => '_node_revision_access',
+    'access arguments' => array(1, 'delete'),
+    'type' => MENU_CALLBACK,
+    'file' => 'node.pages.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implement hook_theme().
+ */
+function node_theme() {
+  return array(
+    'node' => array(
+      'arguments' => array('elements' => NULL),
+      'template' => 'node',
+    ),
+    'node_list' => array(
+      'arguments' => array('items' => NULL, 'title' => NULL),
+    ),
+    'node_search_admin' => array(
+      'arguments' => array('form' => NULL),
+    ),
+    'node_filter_form' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'node.admin.inc',
+    ),
+    'node_filters' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'node.admin.inc',
+    ),
+    'node_add_list' => array(
+      'arguments' => array('content' => NULL),
+      'file' => 'node.pages.inc',
+    ),
+    'node_form' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'node.pages.inc',
+    ),
+    'node_preview' => array(
+      'arguments' => array('node' => NULL),
+      'file' => 'node.pages.inc',
+    ),
+    'node_log_message' => array(
+      'arguments' => array('log' => NULL),
+    ),
+    'node_admin_overview' => array(
+      'arguments' => array('name' => NULL, 'type' => NULL),
+    ),
+  );
+}
+
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.87
diff -u -p -r1.87 system.api.php
--- modules/system/system.api.php	14 Oct 2009 10:56:35 -0000	1.87
+++ modules/system/system.api.php	14 Oct 2009 20:15:37 -0000
@@ -12,6 +12,38 @@
  */
 
 /**
+ * Defines one or more hooks that are exposed by a module.
+ *
+ * Normally hooks do not need to be explicitly defined. However, by declaring a
+ * hook explicitly, a module may define a "group" for it. Modules that implement
+ * a hook may then place their implementation in either $module.module or in
+ * $module.$group.inc. If the hook is located in $module.$group.inc, then that
+ * file will be automatically loaded when needed.
+ * In general, hooks that are rarely invoked and/or are very large should be
+ * placed in a separate include file, while hooks that are very short or very
+ * frequently called should be left in the main module file so that they are
+ * always available.
+ *
+ * @return
+ *   An associative array whose keys are hook names and whose values are an
+ *   associative array containing:
+ *   - group: A string defining the group to which the hook belongs. The module
+ *     system will determine whether a file with the name $module.$group.inc
+ *     exists, and automatically load it when required.
+ *
+ * See system_hook_info() for all hook groups defined by Drupal core.
+ */
+function hook_hook_info() {
+  $hooks['menu'] = array(
+    'group' => 'registry',
+  );
+  $hooks['theme'] = array(
+    'group' => 'registry',
+  );
+  return $hooks;
+}
+
+/**
  * Inform the base system and the Field API about one or more entity types.
  *
  * Inform the system about one or more entity types (i.e., object types that
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.810
diff -u -p -r1.810 system.module
--- modules/system/system.module	13 Oct 2009 21:34:15 -0000	1.810
+++ modules/system/system.module	14 Oct 2009 20:14:49 -0000
@@ -271,6 +271,25 @@ function system_rdf_namespaces() {
 }
 
 /**
+ * Implement hook_hook_info().
+ */
+function system_hook_info() {
+  $hooks['menu'] = array(
+    'group' => 'registry',
+  );
+  $hooks['theme'] = array(
+    'group' => 'registry',
+  );
+  $hooks['token_info'] = array(
+    'group' => 'tokens',
+  );
+  $hooks['tokens'] = array(
+    'group' => 'tokens',
+  );
+  return $hooks;
+}
+
+/**
  * Implement hook_entity_info().
  */
 function system_entity_info() {
