diff --git a/core/core.services.yml b/core/core.services.yml
index 1aadb75..b7b480d 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -822,6 +822,10 @@ services:
     lazy: true
     tags:
       - { name: needs_destruction }
+  route_manager:
+    class: Drupal\Core\Routing\RouteManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@class_resolver']
+    private: true
   menu.rebuild_subscriber:
     class: Drupal\Core\EventSubscriber\MenuRouterRebuildSubscriber
     arguments: ['@lock', '@plugin.manager.menu.link']
@@ -865,31 +869,27 @@ services:
     tags:
       - { name: event_subscriber }
     arguments: ['@paramconverter_manager']
-  paramconverter.entity:
-    class: Drupal\Core\ParamConverter\EntityConverter
-    tags:
-      - { name: paramconverter }
-    arguments: ['@entity.manager']
-  paramconverter.configentity_admin:
-    class: Drupal\Core\ParamConverter\AdminPathConfigEntityConverter
-    tags:
+#  paramconverter.configentity_admin:
+#    class: Drupal\Core\ParamConverter\AdminPathConfigEntityConverter
+#    tags:
       # Use a higher priority than EntityConverter, see the class for details.
-      - { name: paramconverter, priority: 5 }
-    arguments: ['@entity.manager', '@config.factory', '@router.admin_context']
-    lazy: true
+#      - { name: paramconverter, priority: 5 }
+#    arguments: ['@entity.manager', '@config.factory', '@router.admin_context']
+#    lazy: true
   route_subscriber.module:
     class: Drupal\Core\EventSubscriber\ModuleRouteSubscriber
     tags:
       - { name: event_subscriber }
     arguments: ['@module_handler']
-  resolver_manager.entity:
-    class: Drupal\Core\Entity\EntityResolverManager
-    arguments: ['@entity.manager', '@class_resolver']
   route_subscriber.entity:
     class: Drupal\Core\EventSubscriber\EntityRouteAlterSubscriber
     tags:
       - { name: event_subscriber }
-    arguments: ['@resolver_manager.entity']
+  route_subscriber.plugin:
+    class: Drupal\Core\EventSubscriber\PluginRouteAlterSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@route_manager']
   ajax_response.subscriber:
     class: Drupal\Core\EventSubscriber\AjaxResponseSubscriber
     arguments: ['@ajax_response.attachments_processor']
@@ -916,10 +916,11 @@ services:
     tags:
       - { name: route_enhancer }
       - { name: event_subscriber }
-  route_enhancer.entity:
-    class: Drupal\Core\Entity\Enhancer\EntityRouteEnhancer
+  route_enhancer.plugins:
+    class: Drupal\Core\Routing\Enhancer\PluginConversionEnhancer
+    arguments: ['@route_manager']
     tags:
-      - { name: route_enhancer, priority: 20 }
+      - { name: route_enhancer }
   route_content_controller_subscriber:
     class: Drupal\Core\EventSubscriber\ContentControllerSubscriber
     tags:
diff --git a/core/lib/Drupal/Component/Plugin/Mapper/MapperInterface.php b/core/lib/Drupal/Component/Plugin/Mapper/MapperInterface.php
index 67f091d..45b8437 100644
--- a/core/lib/Drupal/Component/Plugin/Mapper/MapperInterface.php
+++ b/core/lib/Drupal/Component/Plugin/Mapper/MapperInterface.php
@@ -30,5 +30,4 @@
    *   will be returned.
    */
   public function getInstance(array $options);
-
 }
diff --git a/core/lib/Drupal/Core/Annotation/Route.php b/core/lib/Drupal/Core/Annotation/Route.php
new file mode 100644
index 0000000..53a81bf
--- /dev/null
+++ b/core/lib/Drupal/Core/Annotation/Route.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Annotation\Route.
+ */
+
+
+namespace Drupal\Core\Annotation;
+use Doctrine\Common\Annotations\Annotation;
+use Drupal\Component\Annotation\Plugin;
+
+
+/**
+ * Defines a Route annotation object.
+ *
+ * Plugin Namespace: Plugin\Route
+ *
+ * For a working example, see \Drupal\Core\Routing\Plugin\Route\Entity
+ *
+ * @see \Drupal\Core\*\*Interface
+ * @see \Drupal\Core\Routing\RouteManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class Route extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the route plugin.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $label;
+
+  /**
+   * A short description of the route plugin.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $description;
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php
deleted file mode 100644
index 3300c71..0000000
--- a/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Enhancer\EntityRouteEnhancer.
- */
-
-namespace Drupal\Core\Entity\Enhancer;
-
-use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
-
-/**
- * Enhances an entity form route with the appropriate controller.
- */
-class EntityRouteEnhancer implements RouteEnhancerInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function enhance(array $defaults, Request $request) {
-    if (empty($defaults['_controller'])) {
-      if (!empty($defaults['_entity_form'])) {
-        $defaults = $this->enhanceEntityForm($defaults, $request);
-      }
-      elseif (!empty($defaults['_entity_list'])) {
-        $defaults = $this->enhanceEntityList($defaults, $request);
-      }
-      elseif (!empty($defaults['_entity_view'])) {
-        $defaults = $this->enhanceEntityView($defaults, $request);
-      }
-    }
-    return $defaults;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function applies(Route $route) {
-    return !$route->hasDefault('_controller') &&
-      ($route->hasDefault('_entity_form')
-        || $route->hasDefault('_entity_list')
-        || $route->hasDefault('_entity_view')
-      );
-  }
-
-  /**
-   * Update defaults for entity forms.
-   *
-   * @param array $defaults
-   *   The defaults to modify.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The Request instance.
-   *
-   * @return array
-   *   The modified defaults.
-   */
-  protected function enhanceEntityForm(array $defaults, Request $request) {
-    $defaults['_controller'] = 'controller.entity_form:getContentResult';
-
-    return $defaults;
-  }
-
-
-  /**
-   * Update defaults for an entity list.
-   *
-   * @param array $defaults
-   *   The defaults to modify.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The Request instance.
-   *
-   * @return array
-   *   The modified defaults.
-   */
-  protected function enhanceEntityList(array $defaults, Request $request) {
-    $defaults['_controller'] = '\Drupal\Core\Entity\Controller\EntityListController::listing';
-    $defaults['entity_type'] = $defaults['_entity_list'];
-    unset($defaults['_entity_list']);
-
-    return $defaults;
-  }
-
-  /**
-   * Update defaults for an entity view.
-   *
-   * @param array $defaults
-   *   The defaults to modify.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The Request instance.
-   *
-   * @throws \RuntimeException
-   *   Thrown when an entity of a type cannot be found in a route.
-   *
-   * @return array
-   *   The modified defaults.
-   */
-  protected function enhanceEntityView(array $defaults, Request $request) {
-    $defaults['_controller'] = '\Drupal\Core\Entity\Controller\EntityViewController::view';
-    if (strpos($defaults['_entity_view'], '.') !== FALSE) {
-      // The _entity_view entry is of the form entity_type.view_mode.
-      list($entity_type, $view_mode) = explode('.', $defaults['_entity_view']);
-      $defaults['view_mode'] = $view_mode;
-    }
-    else {
-      // Only the entity type is nominated, the view mode will use the
-      // default.
-      $entity_type = $defaults['_entity_view'];
-    }
-    // Set by reference so that we get the upcast value.
-    if (!empty($defaults[$entity_type])) {
-      $defaults['_entity'] = &$defaults[$entity_type];
-    }
-    else {
-      // The entity is not keyed by its entity_type. Attempt to find it
-      // using a converter.
-      $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
-      if ($route && is_object($route)) {
-        $options = $route->getOptions();
-        if (isset($options['parameters'])) {
-          foreach ($options['parameters'] as $name => $details) {
-            if (!empty($details['type'])) {
-              $type = $details['type'];
-              // Type is of the form entity:{entity_type}.
-              $parameter_entity_type = substr($type, strlen('entity:'));
-              if ($entity_type == $parameter_entity_type) {
-                // We have the matching entity type. Set the '_entity' key
-                // to point to this named placeholder. The entity in this
-                // position is the one being rendered.
-                $defaults['_entity'] = &$defaults[$name];
-              }
-            }
-          }
-        }
-        else {
-          throw new \RuntimeException(sprintf('Failed to find entity of type %s in route named %s', $entity_type, $defaults[RouteObjectInterface::ROUTE_NAME]));
-        }
-      }
-      else {
-        throw new \RuntimeException(sprintf('Failed to find entity of type %s in route named %s', $entity_type, $defaults[RouteObjectInterface::ROUTE_NAME]));
-      }
-    }
-    unset($defaults['_entity_view']);
-
-    return $defaults;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Entity/EntityResolverManager.php b/core/lib/Drupal/Core/Entity/EntityResolverManager.php
deleted file mode 100644
index cadd158..0000000
--- a/core/lib/Drupal/Core/Entity/EntityResolverManager.php
+++ /dev/null
@@ -1,231 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\EntityResolverManager.
- */
-
-namespace Drupal\Core\Entity;
-
-use Drupal\Core\DependencyInjection\ClassResolverInterface;
-use Symfony\Component\Routing\Route;
-
-/**
- * Sets the entity route parameter converter options automatically.
- *
- * If controllers of routes with route parameters, type-hint the parameters with
- * an entity interface, upcasting is done automatically.
- */
-class EntityResolverManager {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The class resolver.
-   *
-   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
-   */
-  protected $classResolver;
-
-  /**
-   * Constructs a new EntityRouteAlterSubscriber.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
-   *   The class resolver.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, ClassResolverInterface $class_resolver) {
-    $this->entityManager = $entity_manager;
-    $this->classResolver = $class_resolver;
-  }
-
-  /**
-   * Gets the controller class using route defaults.
-   *
-   * By design we cannot support all possible routes, but just the ones which
-   * use the defaults provided by core, which are _controller and _form.
-   *
-   * Rather than creating an instance of every controller determine the class
-   * and method that would be used. This is not possible for the service:method
-   * notation as the runtime container does not allow static introspection.
-   *
-   * @see \Drupal\Core\Controller\ControllerResolver::getControllerFromDefinition()
-   * @see \Drupal\Core\Controller\ClassResolver::getInstanceFromDefinition()
-   *
-   * @param array $defaults
-   *   The default values provided by the route.
-   *
-   * @return string|null
-   *   Returns the controller class, otherwise NULL.
-   */
-  protected function getControllerClass(array $defaults) {
-    $controller = NULL;
-    if (isset($defaults['_controller'])) {
-      $controller = $defaults['_controller'];
-    }
-
-    if (isset($defaults['_form'])) {
-      $controller = $defaults['_form'];
-      // Check if the class exists and if so use the buildForm() method from the
-      // interface.
-      if (class_exists($controller)) {
-        return array($controller, 'buildForm');
-      }
-    }
-
-    if (strpos($controller, ':') === FALSE) {
-      if (method_exists($controller, '__invoke')) {
-        return array($controller, '__invoke');
-      }
-      if (function_exists($controller)) {
-        return $controller;
-      }
-      return NULL;
-    }
-
-    $count = substr_count($controller, ':');
-    if ($count == 1) {
-      // Controller in the service:method notation. Get the information from the
-      // service. This is dangerous as the controller could depend on services
-      // that could not exist at this point. There is however no other way to
-      // do it, as the container does not allow static introspection.
-      list($class_or_service, $method) = explode(':', $controller, 2);
-      return array($this->classResolver->getInstanceFromDefinition($class_or_service), $method);
-    }
-    elseif (strpos($controller, '::') !== FALSE) {
-      // Controller in the class::method notation.
-      return explode('::', $controller, 2);
-    }
-
-    return NULL;
-  }
-
-  /**
-   * Sets the upcasting information using reflection.
-   *
-   * @param string|array $controller
-   *   A PHP callable representing the controller.
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object to populate without upcasting information.
-   *
-   * @return bool
-   *   Returns TRUE if the upcasting parameters could be set, FALSE otherwise.
-   */
-  protected function setParametersFromReflection($controller, Route $route) {
-    $entity_types = $this->getEntityTypes();
-    $parameter_definitions = $route->getOption('parameters') ?: array();
-
-    $result = FALSE;
-
-    if (is_array($controller)) {
-      list($instance, $method) = $controller;
-      $reflection = new \ReflectionMethod($instance, $method);
-    }
-    else {
-      $reflection = new \ReflectionFunction($controller);
-    }
-
-    $parameters = $reflection->getParameters();
-    foreach ($parameters as $parameter) {
-      $parameter_name = $parameter->getName();
-      // If the parameter name matches with an entity type try to set the
-      // upcasting information automatically. Therefore take into account that
-      // the user has specified some interface, so the upcasting is intended.
-      if (isset($entity_types[$parameter_name])) {
-        $entity_type = $entity_types[$parameter_name];
-        $entity_class = $entity_type->getClass();
-        if (($reflection_class = $parameter->getClass()) && (is_subclass_of($entity_class, $reflection_class->name) || $entity_class == $reflection_class->name)) {
-          $parameter_definitions += array($parameter_name => array());
-          $parameter_definitions[$parameter_name] += array(
-            'type' => 'entity:' . $parameter_name,
-          );
-          $result = TRUE;
-        }
-      }
-    }
-    if (!empty($parameter_definitions)) {
-      $route->setOption('parameters', $parameter_definitions);
-    }
-    return $result;
-  }
-
-  /**
-   * Sets the upcasting information using the _entity_* route defaults.
-   *
-   * Supports the '_entity_view' and '_entity_form' route defaults.
-   *
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   */
-  protected function setParametersFromEntityInformation(Route $route) {
-    if ($entity_view = $route->getDefault('_entity_view')) {
-      list($entity_type) = explode('.', $entity_view, 2);
-    }
-    elseif ($entity_form = $route->getDefault('_entity_form')) {
-      list($entity_type) = explode('.', $entity_form, 2);
-    }
-
-    if (isset($entity_type) && isset($this->getEntityTypes()[$entity_type])) {
-      $parameter_definitions = $route->getOption('parameters') ?: array();
-
-      // First try to figure out whether there is already a parameter upcasting
-      // the same entity type already.
-      foreach ($parameter_definitions as $info) {
-        if (isset($info['type'])) {
-          // The parameter types are in the form 'entity:$entity_type'.
-          list(, $parameter_entity_type) = explode(':', $info['type'], 2);
-          if ($parameter_entity_type == $entity_type) {
-            return;
-          }
-        }
-      }
-
-      if (!isset($parameter_definitions[$entity_type])) {
-        $parameter_definitions[$entity_type] = array();
-      }
-      $parameter_definitions[$entity_type] += array(
-        'type' => 'entity:' . $entity_type,
-      );
-      if (!empty($parameter_definitions)) {
-        $route->setOption('parameters', $parameter_definitions);
-      }
-    }
-  }
-
-  /**
-   * Set the upcasting route objects.
-   *
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object to add the upcasting information onto.
-   */
-  public function setRouteOptions(Route $route) {
-    if ($controller = $this->getControllerClass($route->getDefaults())) {
-      // Try to use reflection.
-      if ($this->setParametersFromReflection($controller, $route)) {
-        return;
-      }
-    }
-
-    // Try to use _entity_* information on the route.
-    $this->setParametersFromEntityInformation($route);
-  }
-
-  /**
-   * Gets the list of all entity types.
-   *
-   * @return \Drupal\Core\Entity\EntityTypeInterface[]
-   */
-  protected function getEntityTypes() {
-    if (!isset($this->entityTypes)) {
-      $this->entityTypes = $this->entityManager->getDefinitions();
-    }
-    return $this->entityTypes;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php
index 1099686..dd153db 100644
--- a/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php
@@ -7,50 +7,43 @@
 
 namespace Drupal\Core\EventSubscriber;
 
-use Drupal\Core\Entity\EntityResolverManager;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Drupal\Core\Routing\RoutingEvents;
 use Drupal\Core\Routing\RouteBuildEvent;
 
 /**
- * Registers the 'type' of route parameter names that match an entity type.
- *
- * @todo Matching on parameter *name* is not ideal, because it breaks
- *   encapsulation: parameter names are local to the controller and route, and
- *   controllers and routes can't be expected to know what all possible entity
- *   types might exist across all modules in order to pick names that don't
- *   conflict. Instead, the 'type' should be determined from introspecting what
- *   kind of PHP variable (e.g., a type hinted interface) the controller
- *   requires: https://www.drupal.org/node/2041907.
+ * Adds controller for _entity_*
  */
 class EntityRouteAlterSubscriber implements EventSubscriberInterface {
 
-  /**
-   * The entity resolver manager.
-   *
-   * @var \Drupal\Core\Entity\EntityResolverManager
-   */
-  protected $resolverManager;
-
-  /**
-   * Constructs an EntityRouteAlterSubscriber instance.
-   *
-   * @param \Drupal\Core\Entity\EntityResolverManager
-   *   The entity resolver manager.
-   */
-  public function __construct(EntityResolverManager $entity_resolver_manager) {
-    $this->resolverManager = $entity_resolver_manager;
-  }
-
-  /**
-   * Applies parameter converters to route parameters.
-   *
-   * @param \Drupal\Core\Routing\RouteBuildEvent $event
-   *   The event to process.
-   */
-  public function onRoutingRouteAlterSetType(RouteBuildEvent $event) {
+  public function onRoutingRouteAlterSetController(RouteBuildEvent $event) {
+    $map = [
+      '_entity_form' => 'controller.entity_form:getContentResult',
+      '_entity_list' => '\Drupal\Core\Entity\Controller\EntityListController::listing',
+      '_entity_view' => '\Drupal\Core\Entity\Controller\EntityViewController::view',
+    ];
+    /** @var \Symfony\Component\Routing\Route $route */
     foreach ($event->getRouteCollection() as $route) {
-      $this->resolverManager->setRouteOptions($route);
+      $defaults = $route->getDefaults();
+      if (empty($defaults['_controller']) && ($local_map = array_intersect_key($map, $defaults))) {
+        list($key, $controller) = each($local_map);
+        unset($defaults[$key]);
+        $defaults['_controller'] = $controller;
+        if ($key == '_entity_view') {
+          $parts = explode('.', $route->getDefault('_entity_view'));
+          $entity_type = array_shift($parts);
+          $parameters = $route->getOption('parameters') ?: [];
+          $plugin_id = "entity:$entity_type";
+          $parameters['_entity']['plugin_id'] = $plugin_id;
+          $parameters['_entity']['default'] = $entity_type;
+          $parameters[$entity_type]['plugin_id'] = $plugin_id;
+          $route->setOption('parameters', $parameters);
+          if ($parts) {
+            $defaults['view_mode'] = reset($parts);
+          }
+        }
+      }
+      $route->setDefaults($defaults);
     }
   }
 
@@ -58,7 +51,7 @@ public function onRoutingRouteAlterSetType(RouteBuildEvent $event) {
    * {@inheritdoc}
    */
   static function getSubscribedEvents() {
-    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetType', -150);
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetController', -150);
     return $events;
   }
 }
diff --git a/core/lib/Drupal/Core/EventSubscriber/PluginRouteAlterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PluginRouteAlterSubscriber.php
new file mode 100644
index 0000000..799307c
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/PluginRouteAlterSubscriber.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\PluginRouteAlterSubscriber.
+ */
+
+
+namespace Drupal\Core\EventSubscriber;
+
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RouteManager;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class PluginRouteAlterSubscriber implements EventSubscriberInterface {
+
+  public function __construct(RouteManager $route_manager) {
+    $this->routeManager = $route_manager;
+  }
+
+  public function onRoutingRouteAlterSetPluginId(RouteBuildEvent $event) {
+    foreach ($event->getRouteCollection() as $route) {
+      $this->routeManager->setRouteOptions($route);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetPluginId', -150);
+    return $events;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
deleted file mode 100644
index 172b27b..0000000
--- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\ParamConverter\EntityConverter.
- */
-
-namespace Drupal\Core\ParamConverter;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\TypedData\TranslatableInterface;
-use Symfony\Component\Routing\Route;
-
-/**
- * Parameter converter for upcasting entity IDs to full objects.
- *
- * This is useful in cases where the dynamic elements of the path can't be
- * auto-determined; for example, if your path refers to multiple of the same
- * type of entity ("example/{node1}/foo/{node2}") or if the path can act on any
- * entity type ("example/{entity_type}/{entity}/foo").
- *
- * In order to use it you should specify some additional options in your route:
- * @code
- * example.route:
- *   path: foo/{example}
- *   options:
- *     parameters:
- *       example:
- *         type: entity:node
- * @endcode
- *
- * If you want to have the entity type itself dynamic in the url you can
- * specify it like the following:
- * @code
- * example.route:
- *   path: foo/{entity_type}/{example}
- *   options:
- *     parameters:
- *       example:
- *         type: entity:{entity_type}
- * @endcode
- */
-class EntityConverter implements ParamConverterInterface {
-
-  /**
-   * Entity manager which performs the upcasting in the end.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * Constructs a new EntityConverter.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager) {
-    $this->entityManager = $entity_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function convert($value, $definition, $name, array $defaults) {
-    $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults);
-    if ($storage = $this->entityManager->getStorage($entity_type_id)) {
-      $entity = $storage->load($value);
-      // If the entity type is translatable, ensure we return the proper
-      // translation object for the current context.
-      if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) {
-        $entity = $this->entityManager->getTranslationFromContext($entity, NULL, array('operation' => 'entity_upcast'));
-      }
-      return $entity;
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function applies($definition, $name, Route $route) {
-    if (!empty($definition['type']) && strpos($definition['type'], 'entity:') === 0) {
-      $entity_type_id = substr($definition['type'], strlen('entity:'));
-      if (strpos($definition['type'], '{') !== FALSE) {
-        $entity_type_slug = substr($entity_type_id, 1, -1);
-        return $name != $entity_type_slug && in_array($entity_type_slug, $route->compile()->getVariables(), TRUE);
-      }
-      return $this->entityManager->hasDefinition($entity_type_id);
-    }
-    return FALSE;
-  }
-
-  /**
-   * Determines the entity type ID given a route definition and route defaults.
-   *
-   * @param mixed $definition
-   *   The parameter definition provided in the route options.
-   * @param string $name
-   *   The name of the parameter.
-   * @param array $defaults
-   *   The route defaults array.
-   *
-   * @throws \Drupal\Core\ParamConverter\ParamNotConvertedException
-   *   Thrown when the dynamic entity type is not found in the route defaults.
-   *
-   * @return string
-   *   The entity type ID.
-   */
-  protected function getEntityTypeFromDefaults($definition, $name, array $defaults) {
-    $entity_type_id = substr($definition['type'], strlen('entity:'));
-
-    // If the entity type is dynamic, it will be pulled from the route defaults.
-    if (strpos($entity_type_id, '{') === 0) {
-      $entity_type_slug = substr($entity_type_id, 1, -1);
-      if (!isset($defaults[$entity_type_slug])) {
-        throw new ParamNotConvertedException(sprintf('The "%s" parameter was not converted because the "%s" parameter is missing', $name, $entity_type_slug));
-      }
-      $entity_type_id = $defaults[$entity_type_slug];
-    }
-    return $entity_type_id;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Routing/Enhancer/PluginConversionEnhancer.php b/core/lib/Drupal/Core/Routing/Enhancer/PluginConversionEnhancer.php
new file mode 100644
index 0000000..fdddd90
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/Enhancer/PluginConversionEnhancer.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\Enhancer\PluginConversionEnhancer.
+ */
+
+
+namespace Drupal\Core\Routing\Enhancer;
+
+
+use Drupal\Core\Routing\RouteManager;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+class PluginConversionEnhancer implements RouteEnhancerInterface {
+
+  public function __construct(RouteManager $route_manager) {
+    $this->routeManager = $route_manager;
+  }
+
+  /**
+   * Declares if the route enhancer applies to the given route.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *  The route to consider attaching to.
+   *
+   * @return bool
+   *   TRUE if the check applies to the passed route, False otherwise.
+   */
+  public function applies(Route $route) {
+    return TRUE;
+  }
+
+  /**
+   * Update the defaults based on its own data and the request.
+   *
+   * @param array $defaults the getRouteDefaults array.
+   * @param Request $request the Request instance.
+   *
+   * @return array the modified defaults. Each enhancer MUST return the
+   *               $defaults but may add or remove values.
+   */
+  public function enhance(array $defaults, Request $request) {
+    $defaults['_raw_variables'] = $this->copyRawVariables($defaults);
+    return $this->routeManager->apply($defaults, $request);
+  }
+
+  /**
+   * Store a backup of the raw values that corresponding to the route pattern.
+   *
+   * @param array $defaults
+   *   The route defaults array.
+   *
+   * @return \Symfony\Component\HttpFoundation\ParameterBag
+   */
+  protected function copyRawVariables(array $defaults) {
+    /** @var $route \Symfony\Component\Routing\Route */
+    $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
+    $variables = array_flip($route->compile()->getVariables());
+    // Foreach will copy the values from the array it iterates. Even if they
+    // are references, use it to break them. This avoids any scenarios where raw
+    // variables also get replaced with converted values.
+    $raw_variables = array();
+    foreach (array_intersect_key($defaults, $variables) as $key => $value) {
+      $raw_variables[$key] = $value;
+    }
+    return new ParameterBag($raw_variables);
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/Plugin/Route/Entity.php b/core/lib/Drupal/Core/Routing/Plugin/Route/Entity.php
new file mode 100644
index 0000000..ece4951
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/Plugin/Route/Entity.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\Plugin\Route\Entity.
+ */
+
+
+namespace Drupal\Core\Routing\Plugin\Route;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\Core\Routing\RoutePluginInterface;
+use Drupal\Core\TypedData\TranslatableInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @Route(
+ *   id = "entity",
+ *   label = @Translation("Entity"),
+ *   deriver = "Drupal\Core\Routing\Plugin\Route\EntityDeriver"
+ * )
+ */
+class Entity extends PluginBase implements RoutePluginInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * @var EntityManagerInterface
+   */
+  protected $entityManager;
+
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityManager = $entity_manager;
+  }
+
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity.manager')
+    );
+  }
+
+  public function convert($value) {
+    $entity_type_id = $this->getDerivativeId();
+    if ($storage = $this->entityManager->getStorage($entity_type_id)) {
+      $entity = $storage->load($value);
+      if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) {
+        $entity = $this->entityManager->getTranslationFromContext($entity, NULL, array('operation' => 'entity_upcast'));
+      }
+      return $entity;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/Plugin/Route/EntityDeriver.php b/core/lib/Drupal/Core/Routing/Plugin/Route/EntityDeriver.php
new file mode 100644
index 0000000..39d0879
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/Plugin/Route/EntityDeriver.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\Plugin\Route\EntityDeriver.
+ */
+
+
+namespace Drupal\Core\Routing\Plugin\Route;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a resource plugin definition for every entity type.
+ *
+ * @see \Drupal\rest\Plugin\rest\resource\EntityResource
+ */
+class EntityDeriver extends DeriverBase implements ContainerDeriverInterface {
+
+  /**
+   * List of derivative definitions.
+   *
+   * @var array
+   */
+  protected $derivatives;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs an EntityDerivative object.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('entity.manager')
+    );
+  }
+
+  /**
+   * Implements DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    if (!isset($this->derivatives)) {
+      // Add in the default plugin configuration and the resource type.
+      foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
+        $this->derivatives[$entity_type_id] = array(
+          'id' => 'entity:' . $entity_type_id,
+          'entity_type' => $entity_type_id,
+          'parameter_class' => $entity_type->getClass(),
+          'label' => $entity_type->getLabel(),
+        ) + $base_plugin_definition;
+      }
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteBuildEvent.php b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
index df88540..8a769af 100644
--- a/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
+++ b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
@@ -34,6 +34,9 @@ public function __construct(RouteCollection $route_collection) {
 
   /**
    * Gets the route collection.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection $route_collection
+   *   The route collection.
    */
   public function getRouteCollection() {
     return $this->routeCollection;
diff --git a/core/lib/Drupal/Core/Routing/RouteManager.php b/core/lib/Drupal/Core/Routing/RouteManager.php
new file mode 100644
index 0000000..8df8151
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteManager.php
@@ -0,0 +1,188 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\RouteManager.
+ */
+
+
+namespace Drupal\Core\Routing;
+
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\ClassResolverInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+class RouteManager extends DefaultPluginManager {
+
+  protected $classResolver = [];
+  protected $parameterClasses;
+
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ClassResolverInterface $class_resolver) {
+    parent::__construct('Plugin/Route', $namespaces, $module_handler, 'Drupal\Core\Routing\RoutePluginInterface', 'Drupal\Core\Annotation\Route');
+    $this->alterInfo('route_plugin_info');
+    $this->setCacheBackend($cache_backend, 'route_plugins');
+    $this->classResolver = $class_resolver;
+  }
+
+  public function apply(array $defaults, Request $request) {
+    /** @var $route \Symfony\Component\Routing\Route */
+    $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
+    foreach ($route->getOption('parameters') as $parameter_name => $route_parameter_definition) {
+      if (isset($route_parameter_definition['plugin_id'])) {
+        $plugin_id = $route_parameter_definition['plugin_id'];
+        if (substr($plugin_id, -1) == '}') {
+          $plugin_id = strtr($plugin_id, $this->getReplacementsFromDefaults($defaults));
+        }
+        $default = $parameter_name;
+        if (isset($route_parameter_definition['default'])) {
+          $default = $route_parameter_definition['default'];
+        }
+        if ($this->hasDefinition($plugin_id) && isset($defaults[$default])) {
+          $defaults[$parameter_name] = $this->createInstance($plugin_id)->convert($defaults[$default], $route, $request);
+        }
+      }
+    }
+    return $defaults;
+  }
+
+  protected function getReplacementsFromDefaults(array $defaults) {
+    $replacements = [];
+    foreach ($defaults as $id => $value) {
+      if (is_scalar($value)) {
+        $replacements['{' . $id . '}'] = $value;
+      }
+    }
+    return $replacements;
+  }
+
+  public function setRouteOptions(Route $route) {
+    if ($controller = $this->getControllerClass($route->getDefaults())) {
+      // Try to use reflection.
+      $this->setParametersFromReflection($controller, $route);
+    }
+  }
+
+  public function setParametersFromReflection($controller, Route $route) {
+    $route_parameter_definitions = $route->getOption('parameters') ?: array();
+    if (is_array($controller)) {
+      list($instance, $method) = $controller;
+      $reflection = new \ReflectionMethod($instance, $method);
+    }
+    else {
+      $reflection = new \ReflectionFunction($controller);
+    }
+    foreach ($reflection->getParameters() as $parameter) {
+      $plugin_ids = [];
+      $reflection_parameter_name = $parameter->getName();
+      if ($this->hasDefinition("entity:$reflection_parameter_name")) {
+        $plugin_ids[] = "entity:$reflection_parameter_name";
+      }
+      elseif ($this->hasDefinition($reflection_parameter_name)) {
+        $plugin_ids[] = $reflection_parameter_name;
+      }
+      foreach ($plugin_ids as $i => $plugin_id) {
+        $plugin_definition = $this->getDefinition($plugin_id);
+        $parameter_class = $parameter->getClass();
+        // If there's a parameter class defined and this parameter won't
+        // accept it then this is not the right plugin.
+        if (isset($plugin_definition['parameter_class']) && (!$parameter_class || !is_subclass_of($plugin_definition['parameter_class'], $parameter_class->getName(), TRUE))) {
+          // If we have two plugin IDs, try the other one.
+          if (count($plugin_ids) > 1 && !$i) {
+            continue;
+          }
+          // Do not use a plugin.
+          $plugin_id = FALSE;
+        }
+        $route_parameter_definitions += array($reflection_parameter_name => array());
+        $route_parameter_definitions[$reflection_parameter_name] += array(
+          'plugin_id' => $plugin_id,
+        );
+      }
+    }
+    // Backwards compatibility.
+    foreach ($route_parameter_definitions as $parameter_name => $route_parameter_definition) {
+      if (isset($route_parameter_definition['type']) && substr($route_parameter_definition['type'], strlen('entity:')) == 'entity:') {
+        $route_parameter_definitions[$parameter_name]['plugin_id'] = $route_parameter_definition['type'];
+      }
+    }
+    $route->setOption('parameters', $route_parameter_definitions);
+  }
+
+  protected function getParameterClasses() {
+    if (!$this->parameterClasses) {
+      foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
+        if (isset($plugin_definition['parameter_class'])) {
+          $this->parameterClasses[$plugin_id] = $plugin_definition['parameter_class'];
+        }
+      }
+    }
+    return $this->parameterClasses;
+  }
+
+  /**
+   * Gets the controller class using route defaults.
+   *
+   * By design we cannot support all possible routes, but just the ones which
+   * use the defaults provided by core, which are _controller and _form.
+   *
+   * Rather than creating an instance of every controller determine the class
+   * and method that would be used. This is not possible for the service:method
+   * notation as the runtime container does not allow static introspection.
+   *
+   * @see \Drupal\Core\Controller\ControllerResolver::getControllerFromDefinition()
+   * @see \Drupal\Core\Controller\ClassResolver::getInstanceFromDefinition()
+   *
+   * @param array $defaults
+   *   The default values provided by the route.
+   *
+   * @return string|null
+   *   Returns the controller class, otherwise NULL.
+   */
+  protected function getControllerClass(array $defaults) {
+    $controller = NULL;
+    if (isset($defaults['_controller'])) {
+      $controller = $defaults['_controller'];
+    }
+
+    if (isset($defaults['_form'])) {
+      $controller = $defaults['_form'];
+      // Check if the class exists and if so use the buildForm() method from the
+      // interface.
+      if (class_exists($controller)) {
+        return array($controller, 'buildForm');
+      }
+    }
+
+    if (strpos($controller, ':') === FALSE) {
+      if (method_exists($controller, '__invoke')) {
+        return array($controller, '__invoke');
+      }
+      if (function_exists($controller)) {
+        return $controller;
+      }
+      return NULL;
+    }
+
+    $count = substr_count($controller, ':');
+    if ($count == 1) {
+      // Controller in the service:method notation. Get the information from the
+      // service. This is dangerous as the controller could depend on services
+      // that could not exist at this point. There is however no other way to
+      // do it, as the container does not allow static introspection.
+      list($class_or_service, $method) = explode(':', $controller, 2);
+      return array($this->classResolver->getInstanceFromDefinition($class_or_service), $method);
+    }
+    elseif (strpos($controller, '::') !== FALSE) {
+      // Controller in the class::method notation.
+      return explode('::', $controller, 2);
+    }
+
+    return NULL;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RoutePluginInterface.php b/core/lib/Drupal/Core/Routing/RoutePluginInterface.php
new file mode 100644
index 0000000..062c4f9
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RoutePluginInterface.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * @file
+ * Contains
+ */
+
+namespace Drupal\Core\Routing;
+
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+interface RoutePluginInterface {
+
+   public function convert($value, Route $route, Request $request);
+}
