diff --git a/core/composer.json b/core/composer.json
index 87e60b4..26030b0 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -14,7 +14,8 @@
     "twig/twig": "1.*@stable",
     "doctrine/common": "2.3.*@stable",
     "guzzle/http": "*",
-    "kriswallsmith/assetic": "1.1.*@alpha"
+    "kriswallsmith/assetic": "1.1.*@alpha",
+    "symfony-cmf/routing": "1.0.*@dev"
   },
   "minimum-stability": "dev"
 }
diff --git a/core/composer.lock b/core/composer.lock
index 1894ab3..d8e8608 100644
--- a/core/composer.lock
+++ b/core/composer.lock
@@ -1,5 +1,5 @@
 {
-    "hash": "5d17aee0bd24c24563c2c864600fc5bd",
+    "hash": "27b5fb7194e0d492c69d372d8ba17b2b",
     "packages": [
         {
             "name": "doctrine/common",
@@ -310,6 +310,56 @@
             ]
         },
         {
+            "name": "symfony-cmf/routing",
+            "version": "dev-master",
+            "target-dir": "Symfony/Cmf/Component/Routing",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony-cmf/Routing",
+                "reference": "1.0.0-alpha3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://github.com/symfony-cmf/Routing/archive/1.0.0-alpha3.zip",
+                "reference": "1.0.0-alpha3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2",
+                "symfony/routing": ">=2.1,<2.3-dev",
+                "symfony/http-kernel": ">=2.1,<2.3-dev"
+            },
+            "time": "2012-12-16 17:52:57",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "installation-source": "source",
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Cmf\\Component\\Routing": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony CMF Community",
+                    "homepage": "https://github.com/symfony-cmf/Routing/contributors"
+                }
+            ],
+            "description": "Extends the Symfony2 routing component for dynamic routes and chaining several routers",
+            "homepage": "http://cmf.symfony.com",
+            "keywords": [
+                "database",
+                "routing"
+            ]
+        },
+        {
             "name": "symfony/class-loader",
             "version": "dev-master",
             "target-dir": "Symfony/Component/ClassLoader",
@@ -848,6 +898,7 @@
     "stability-flags": {
         "twig/twig": 0,
         "doctrine/common": 0,
-        "kriswallsmith/assetic": 15
+        "kriswallsmith/assetic": 15,
+        "symfony-cmf/routing": 20
     }
 }
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 3f91b32..fc862cc 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -10,7 +10,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass;
-use Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterSerializationClassesPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -132,12 +132,32 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('lock'))
       ->addArgument(new Reference('event_dispatcher'));
 
+    $container->register('router.request_context', 'Symfony\Component\Routing\RequestContext')
+      ->addMethodCall('fromRequest', array(new Reference('request')));
+    $container->register('router.route_provider', 'Drupal\Core\Routing\RouteProvider')
+      ->addArgument(new Reference('database'));
+    $container->register('router.matcher.final_matcher', 'Drupal\Core\Routing\UrlMatcher');
+    $container->register('router.matcher', 'Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher')
+      ->addArgument(new Reference('router.route_provider'))
+      ->addMethodCall('setFinalMatcher', array(new Reference('router.matcher.final_matcher')));
+    $container->register('router.generator', 'Symfony\Cmf\Component\Routing\ProviderBasedGenerator')
+      ->addArgument(new Reference('router.route_provider'));
+    $container->register('router.dynamic', 'Symfony\Cmf\Component\Routing\DynamicRouter')
+      ->addArgument(new Reference('router.request_context'))
+      ->addArgument(new Reference('router.matcher'))
+      ->addArgument(new Reference('router.generator'));
+
+    $container->register('legacy_generator', 'Drupal\Core\Routing\NullGenerator');
+    $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher');
+    $container->register('legacy_router', 'Symfony\Cmf\Component\Routing\DynamicRouter')
+            ->addArgument(new Reference('router.request_context'))
+            ->addArgument(new Reference('legacy_url_matcher'))
+            ->addArgument(new Reference('legacy_generator'));
 
-    $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher');
-    $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher')
-      ->addTag('chained_matcher');
-    $container->register('nested_matcher', 'Drupal\Core\Routing\NestedMatcher')
-      ->addTag('chained_matcher', array('priority' => 5));
+    $container->register('router', 'Symfony\Cmf\Component\Routing\ChainRouter')
+      ->addMethodCall('setContext', array(new Reference('router.request_context')))
+      ->addMethodCall('add', array(new Reference('router.dynamic')))
+      ->addMethodCall('add', array(new Reference('legacy_router')));
 
     $container
       ->register('cache.path', 'Drupal\Core\Cache\CacheBackendInterface')
@@ -162,26 +182,15 @@ public function build(ContainerBuilder $container) {
     $container->register('password', 'Drupal\Core\Password\PhpassHashedPassword')
       ->addArgument(16);
 
-    // The following services are tagged as 'nested_matcher' services and are
-    // processed in the RegisterNestedMatchersPass compiler pass. Each one
-    // needs to be set on the matcher using a different method, so we use a
-    // tag attribute, 'method', which can be retrieved and passed to the
-    // addMethodCall() method that gets called on the matcher service in the
-    // compiler pass.
-    $container->register('path_matcher', 'Drupal\Core\Routing\PathMatcher')
-      ->addArgument(new Reference('database'))
-      ->addTag('nested_matcher', array('method' => 'setInitialMatcher'));
-    $container->register('http_method_matcher', 'Drupal\Core\Routing\HttpMethodMatcher')
-      ->addTag('nested_matcher', array('method' => 'addPartialMatcher'));
+    // The following services are tagged as 'route_filter' services and are
+    // processed in the RegisterRouteFiltersPass compiler pass.
     $container->register('mime_type_matcher', 'Drupal\Core\Routing\MimeTypeMatcher')
-      ->addTag('nested_matcher', array('method' => 'addPartialMatcher'));
-    $container->register('first_entry_final_matcher', 'Drupal\Core\Routing\FirstEntryFinalMatcher')
-      ->addTag('nested_matcher', array('method' => 'setFinalMatcher'));
+      ->addTag('route_filter');
 
     $container->register('router_processor_subscriber', 'Drupal\Core\EventSubscriber\RouteProcessorSubscriber')
       ->addTag('event_subscriber');
     $container->register('router_listener', 'Symfony\Component\HttpKernel\EventListener\RouterListener')
-      ->addArgument(new Reference('matcher'))
+      ->addArgument(new Reference('router'))
       ->addTag('event_subscriber');
     $container->register('content_negotiation', 'Drupal\Core\ContentNegotiation');
     $container->register('view_subscriber', 'Drupal\Core\EventSubscriber\ViewSubscriber')
@@ -239,7 +248,7 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('database'));
 
     $container->addCompilerPass(new RegisterMatchersPass());
-    $container->addCompilerPass(new RegisterNestedMatchersPass());
+    $container->addCompilerPass(new RegisterRouteFiltersPass());
     // Add a compiler pass for registering event subscribers.
     $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
     // Add a compiler pass for adding Normalizers and Encoders to Serializer.
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteFiltersPass.php
similarity index 65%
rename from core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php
rename to core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteFiltersPass.php
index b245952..de9d222 100644
--- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteFiltersPass.php
@@ -14,7 +14,7 @@
 /**
  * Adds services tagged 'nested_matcher' to the tagged_matcher service.
  */
-class RegisterNestedMatchersPass implements CompilerPassInterface {
+class RegisterRouteFiltersPass implements CompilerPassInterface {
 
   /**
    * Adds services tagged 'nested_matcher' to the tagged_matcher service.
@@ -23,13 +23,12 @@ class RegisterNestedMatchersPass implements CompilerPassInterface {
    *   The container to process.
    */
   public function process(ContainerBuilder $container) {
-    if (!$container->hasDefinition('nested_matcher')) {
+    if (!$container->hasDefinition('router.matcher')) {
       return;
     }
-    $nested = $container->getDefinition('nested_matcher');
-    foreach ($container->findTaggedServiceIds('nested_matcher') as $id => $attributes) {
-      $method = $attributes[0]['method'];
-      $nested->addMethodCall($method, array(new Reference($id)));
+    $nested = $container->getDefinition('router.matcher');
+    foreach ($container->findTaggedServiceIds('route_filter') as $id => $attributes) {
+      $nested->addMethodCall('addRouteFilter', array(new Reference($id)));
     }
   }
 }
diff --git a/core/lib/Drupal/Core/LegacyUrlMatcher.php b/core/lib/Drupal/Core/LegacyUrlMatcher.php
index 48987a9..c1175ab 100644
--- a/core/lib/Drupal/Core/LegacyUrlMatcher.php
+++ b/core/lib/Drupal/Core/LegacyUrlMatcher.php
@@ -150,6 +150,12 @@ protected function convertDrupalItem($router_item) {
       '_controller' => $router_item['page_callback']
     );
 
+    // A few menu items have a fake page callback temporarily. Skip those,
+    // we aren't going to route them.
+    if ($router_item['page_callback'] == 'NOT_USED') {
+      throw new ResourceNotFoundException();
+    }
+
     // @todo menu_get_item() does not unserialize page arguments when the access
     //   is denied. Remove this temporary hack that always does that.
     if (!is_array($router_item['page_arguments'])) {
diff --git a/core/lib/Drupal/Core/Routing/ChainMatcher.php b/core/lib/Drupal/Core/Routing/ChainMatcher.php
deleted file mode 100644
index 23410bf..0000000
--- a/core/lib/Drupal/Core/Routing/ChainMatcher.php
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\ChainMatcher.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
-use Symfony\Component\Routing\Exception\ResourceNotFoundException;
-use Symfony\Component\Routing\Exception\RouteNotFoundException;
-use Symfony\Component\Routing\Exception\MethodNotAllowedException;
-use Symfony\Component\Routing\RequestContextAwareInterface;
-use Symfony\Component\Routing\RequestContext;
-
-/**
- * Aggregates multiple matchers together in series.
- *
- * The RequestContext is entirely unused. It's included only to satisfy the
- * interface needed for RouterListener.  Hopefully we can remove it later.
- */
-class ChainMatcher implements RequestMatcherInterface, RequestContextAwareInterface {
-
-  /**
-   * Array of RequestMatcherInterface objects to be checked in order.
-   *
-   * @var array
-   */
-  protected $matchers = array();
-
-  /**
-   * Array of RequestMatcherInterface objects, sorted.
-   *
-   * @var type
-   */
-  protected $sortedMatchers = array();
-
-  /**
-   * The request context for this matcher.
-   *
-   * This is unused.  It's just to satisfy the interface.
-   *
-   * @var Symfony\Component\Routing\RequestContext
-   */
-  protected $context;
-
-  /**
-   * Constructor.
-   */
-  public function __construct() {
-    // We will not actually use this object, but it's needed to conform to
-    // the interface.
-    $this->context = new RequestContext();
-  }
-
-  /**
-   * Sets the request context.
-   *
-   * This method is just to satisfy the interface, and is largely vestigial.
-   * The request context object does not contain the information we need, so
-   * we will use the original request object.
-   *
-   * @param Symfony\Component\Routing\RequestContext $context
-   *   The context.
-   */
-  public function setContext(RequestContext $context) {
-    $this->context = $context;
-  }
-
-  /**
-   * Gets the request context.
-   *
-   * This method is just to satisfy the interface, and is largely vestigial.
-   * The request context object does not contain the information we need, so
-   * we will use the original request object.
-   *
-   * @return Symfony\Component\Routing\RequestContext
-   *   The context.
-   */
-  public function getContext() {
-    return $this->context;
-  }
-
-  /**
-   * Matches a request against all queued matchers.
-   *
-   * @param Request $request The request to match
-   *
-   * @return array An array of parameters
-   *
-   * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException
-   *   If no matching resource could be found
-   * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedException
-   *   If a matching resource was found but the request method is not allowed
-   */
-  public function matchRequest(Request $request) {
-    $methodNotAllowed = null;
-
-    foreach ($this->all() as $matcher) {
-      try {
-        return $matcher->matchRequest($request);
-      } catch (ResourceNotFoundException $e) {
-        // Needs special care
-      } catch (MethodNotAllowedException $e) {
-        $methodNotAllowed = $e;
-      }
-    }
-
-    throw $methodNotAllowed ?: new ResourceNotFoundException("None of the matchers in the chain matched this request.");
-  }
-
-  /**
-    * Adds a Matcher to the index.
-    *
-    * @param MatcherInterface $matcher
-    *   The matcher to add.
-    * @param int $priority
-    *   (optional) The priority of the matcher. Higher number matchers will be checked
-    *   first. Default to 0.
-    */
-  public function add(RequestMatcherInterface $matcher, $priority = 0) {
-    if (empty($this->matchers[$priority])) {
-      $this->matchers[$priority] = array();
-    }
-
-    $this->matchers[$priority][] = $matcher;
-    $this->sortedMatchers = array();
-  }
-
-  /**
-    * Sorts the matchers and flattens them.
-    *
-    * @return array
-    *   An array of RequestMatcherInterface objects.
-    */
-  public function all() {
-    if (empty($this->sortedMatchers)) {
-      $this->sortedMatchers = $this->sortMatchers();
-    }
-
-    return $this->sortedMatchers;
-  }
-
-  /**
-    * Sort matchers by priority.
-    *
-    * The highest priority number is the highest priority (reverse sorting).
-    *
-    * @return \Symfony\Component\Routing\RequestMatcherInterface[]
-    *   An array of Matcher objects in the order they should be used.
-    */
-  protected function sortMatchers() {
-    $sortedMatchers = array();
-    krsort($this->matchers);
-
-    foreach ($this->matchers as $matchers) {
-      $sortedMatchers = array_merge($sortedMatchers, $matchers);
-    }
-
-    return $sortedMatchers;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Routing/CompiledRoute.php b/core/lib/Drupal/Core/Routing/CompiledRoute.php
index c5cdde8..4ebd119 100644
--- a/core/lib/Drupal/Core/Routing/CompiledRoute.php
+++ b/core/lib/Drupal/Core/Routing/CompiledRoute.php
@@ -8,11 +8,12 @@
 namespace Drupal\Core\Routing;
 
 use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\CompiledRoute as SymfonyCompiledRoute;
 
 /**
- * Description of CompiledRoute
+ * A compiled route contains derived information from a route object.
  */
-class CompiledRoute {
+class CompiledRoute extends SymfonyCompiledRoute {
 
   /**
    * The fitness of this route.
@@ -43,14 +44,12 @@ class CompiledRoute {
   protected $route;
 
   /**
-   * The regular expression to match placeholders out of this path.
+   * Constructs a new compiled route object.
    *
-   * @var string
-   */
-  protected $regex;
-
-  /**
-   * Constructs a new CompiledRoute object.
+   * This is a ridiculously long set of constructor parameters, but as this
+   * object is little more than a collection of values it's not a serious
+   * problem. The parent Symfony class does the same, as well, making it
+   * difficult to override differently.
    *
    * @param \Symfony\Component\Routing\Route $route
    *   A original Route instance.
@@ -60,15 +59,30 @@ class CompiledRoute {
    *   The pattern outline for this route.
    * @param int $num_parts
    *   The number of parts in the path.
+   * @param string $staticPrefix
+   *   The static prefix of the compiled route
    * @param string $regex
-   *   The regular expression to match placeholders out of this path.
+   *   The regular expression to use to match this route
+   * @param array $tokens
+   *   An array of tokens to use to generate URL for this route
+   * @param array $pathVariables
+   *   An array of path variables
+   * @param string|null $hostnameRegex
+   *   Hostname regex
+   * @param array $hostnameTokens
+   *   Hostname tokens
+   * @param array $hostnameVariables
+   *   An array of hostname variables
+   * @param array $variables
+   *   An array of variables (variables defined in the path and in the hostname patterns)
    */
-  public function __construct(Route $route, $fit, $pattern_outline, $num_parts, $regex) {
+  public function __construct(Route $route, $fit, $pattern_outline, $num_parts, $staticPrefix, $regex, array $tokens, array $pathVariables, $hostnameRegex = null, array $hostnameTokens = array(), array $hostnameVariables = array(), array $variables = array()) {
+    parent::__construct($staticPrefix, $regex, $tokens, $pathVariables);
+
     $this->route = $route;
     $this->fit = $fit;
     $this->patternOutline = $pattern_outline;
     $this->numParts = $num_parts;
-    $this->regex = $regex;
   }
 
   /**
@@ -110,16 +124,6 @@ public function getPatternOutline() {
   }
 
   /**
-   * Returns the placeholder regex.
-   *
-   * @return string
-   *   The regex to locate placeholders in this pattern.
-   */
-  public function getRegex() {
-    return $this->regex;
-  }
-
-  /**
    * Returns the Route instance.
    *
    * @return Route
diff --git a/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php b/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php
deleted file mode 100644
index 8b85c21..0000000
--- a/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\FinalMatcherInterface.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * A FinalMatcher returns only one route from a collection of candidate routes.
- */
-interface FinalMatcherInterface {
-
-  /**
-   * Sets the route collection this matcher should use.
-   *
-   * @param \Symfony\Component\Routing\RouteCollection $collection
-   *   The collection against which to match.
-   *
-   * @return \Drupal\Core\Routing\FinalMatcherInterface
-   *   The current matcher.
-   */
-  public function setCollection(RouteCollection $collection);
-
-  /**
-   * Matches a request against multiple routes.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A Request object against which to match.
-   *
-   * @return array
-   *   An array of parameters.
-   */
-  public function matchRequest(Request $request);
-}
diff --git a/core/lib/Drupal/Core/Routing/FirstEntryFinalMatcher.php b/core/lib/Drupal/Core/Routing/FirstEntryFinalMatcher.php
deleted file mode 100644
index cc1adde..0000000
--- a/core/lib/Drupal/Core/Routing/FirstEntryFinalMatcher.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\FirstEntryFinalMatcher.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Final matcher that simply returns the first item in the remaining routes.
- *
- * This class simply matches the first remaining route.
- */
-class FirstEntryFinalMatcher implements FinalMatcherInterface {
-
-  /**
-   * The RouteCollection this matcher should match against.
-   *
-   * @var RouteCollection
-   */
-  protected $routes;
-
-  /**
-   * Sets the route collection this matcher should use.
-   *
-   * @param \Symfony\Component\Routing\RouteCollection $collection
-   *   The collection against which to match.
-   *
-   * @return \Drupal\Core\Routing\FinalMatcherInterface
-   *   The current matcher.
-   */
-  public function setCollection(RouteCollection $collection) {
-    $this->routes = $collection;
-
-    return $this;
-  }
-
-  /**
-   * Implements Drupal\Core\Routing\FinalMatcherInterface::matchRequest().
-   */
-  public function matchRequest(Request $request) {
-    // Return whatever the first route in the collection is.
-    foreach ($this->routes as $name => $route) {
-      $path = '/' . $request->attributes->get('system_path');
-
-      $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler');
-      $compiled = $route->compile();
-
-      preg_match($compiled->getRegex(), $path, $matches);
-
-      $route->setOption('_name', $name);
-      return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $route));
-    }
-  }
-
-  /**
-   * Get merged default parameters.
-   *
-   * @param array $params
-   *  The parameters.
-   * @param array $defaults
-   *   The defaults.
-   *
-   * @return array
-   *   Merged default parameters.
-   */
-  protected function mergeDefaults($params, $defaults) {
-    $parameters = $defaults;
-    foreach ($params as $key => $value) {
-      if (!is_int($key)) {
-        $parameters[$key] = $value;
-      }
-    }
-
-    return $parameters;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php b/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php
deleted file mode 100644
index 5064353..0000000
--- a/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\HttpMethodMatcher.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Exception\MethodNotAllowedException;
-
-/**
- * This class filters routes based on their HTTP Method.
- */
-class HttpMethodMatcher extends PartialMatcher {
-
-  /**
-   * Matches a request against multiple routes.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A Request object against which to match.
-   *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   A RouteCollection of matched routes.
-   */
-  public function matchRequestPartial(Request $request) {
-    $possible_methods = array();
-
-    $method = $request->getMethod();
-
-    $collection = new RouteCollection();
-
-    foreach ($this->routes->all() as $name => $route) {
-      // _method could be a |-delimited list of allowed methods, or null. If
-      // null, we accept any method.
-      $allowed_methods = array_filter(explode('|', strtoupper($route->getRequirement('_method'))));
-      if (empty($allowed_methods) || in_array($method, $allowed_methods)) {
-        $collection->add($name, $route);
-      }
-      else {
-        // Build a list of methods that would have matched. Note that we only
-        // need to do this if a route doesn't match, because if even one route
-        // passes then we'll never throw the exception that needs this array.
-        $possible_methods += $allowed_methods;
-      }
-    }
-
-    if (!count($collection->all())) {
-      throw new MethodNotAllowedException(array_unique($possible_methods));
-    }
-
-    return $collection;
-  }
-
-}
-
diff --git a/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php b/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php
deleted file mode 100644
index 53bc8e7..0000000
--- a/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\InitialMatcherInterface.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * A PartialMatcher works like a UrlMatcher, but will return multiple candidate routes.
- */
-interface InitialMatcherInterface {
-
-  /**
-   * Matches a request against multiple routes.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A Request object against which to match.
-   *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   A RouteCollection of matched routes.
-   */
-  public function matchRequestPartial(Request $request);
-}
diff --git a/core/lib/Drupal/Core/Routing/MimeTypeMatcher.php b/core/lib/Drupal/Core/Routing/MimeTypeMatcher.php
index a36bef2..8d86096 100644
--- a/core/lib/Drupal/Core/Routing/MimeTypeMatcher.php
+++ b/core/lib/Drupal/Core/Routing/MimeTypeMatcher.php
@@ -10,46 +10,41 @@
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
 use Symfony\Component\Routing\RouteCollection;
+use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface;
 
 /**
  * This class filters routes based on the media type in HTTP Accept headers.
  */
-class MimeTypeMatcher extends PartialMatcher {
+class MimeTypeMatcher implements RouteFilterInterface {
+
 
   /**
-   * Matches a request against multiple routes.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A Request object against which to match.
-   *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   A RouteCollection of matched routes.
+   * {@inheritDoc}
    */
-  public function matchRequestPartial(Request $request) {
-
+  public function filter(RouteCollection $collection, Request $request) {
     // Generates a list of Symfony formats matching the acceptable MIME types.
     // @todo replace by proper content negotiation library.
     $acceptable_mime_types = $request->getAcceptableContentTypes();
     $acceptable_formats = array_map(array($request, 'getFormat'), $acceptable_mime_types);
 
-    $collection = new RouteCollection();
+    $filtered_collection = new RouteCollection();
 
-    foreach ($this->routes->all() as $name => $route) {
+    foreach ($collection as $name => $route) {
       // _format could be a |-delimited list of supported formats.
       $supported_formats = array_filter(explode('|', $route->getRequirement('_format')));
       // The route partially matches if it doesn't care about format, if it
       // explicitly allows any format, or if one of its allowed formats is
       // in the request's list of acceptable formats.
       if (empty($supported_formats) || in_array('*/*', $acceptable_mime_types) || array_intersect($acceptable_formats, $supported_formats)) {
-        $collection->add($name, $route);
+        $filtered_collection->add($name, $route);
       }
     }
 
-    if (!count($collection)) {
+    if (!count($filtered_collection)) {
       throw new UnsupportedMediaTypeHttpException();
     }
 
-    return $collection;
+    return $filtered_collection;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Routing/NestedMatcher.php b/core/lib/Drupal/Core/Routing/NestedMatcher.php
deleted file mode 100644
index ff53715..0000000
--- a/core/lib/Drupal/Core/Routing/NestedMatcher.php
+++ /dev/null
@@ -1,199 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\NestedMatcher.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\Routing\RequestContext;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * The nested matcher layers multiple partial matchers together.
- */
-class NestedMatcher implements NestedMatcherInterface {
-
-  /**
-   * The final matcher.
-   *
-   * @var Symfony\Component\Routing\Matcher\RequestMatcherInterface
-   */
-  protected $finalMatcher;
-
-  /**
-   * An array of PartialMatchers.
-   *
-   * @var array
-   */
-  protected $partialMatchers = array();
-
-  /**
-   * Array of PartialMatcherInterface objects, sorted.
-   *
-   * @var type
-   */
-  protected $sortedMatchers = array();
-
-  /**
-   * The initial matcher to match against.
-   *
-   * @var Drupal\core\Routing\InitialMatcherInterface
-   */
-  protected $initialMatcher;
-
-  /**
-   * The request context.
-   *
-   * @var Symfony\Component\Routing\RequestContext
-   */
-  protected $context;
-
-  /**
-   * Adds a partial matcher to the matching plan.
-   *
-   * Partial matchers will be run in the order in which they are added.
-   *
-   * @param \Drupal\Core\Routing\PartialMatcherInterface $matcher
-   *   A partial matcher.
-   * @param int $priority
-   *   (optional) The priority of the matcher. Higher number matchers will be checked
-   *   first. Default to 0.
-   *
-   * @return NestedMatcherInterface
-   *   The current matcher.
-   */
-  public function addPartialMatcher(PartialMatcherInterface $matcher, $priority = 0) {
-    if (empty($this->matchers[$priority])) {
-      $this->matchers[$priority] = array();
-    }
-
-    $this->matchers[$priority][] = $matcher;
-    $this->sortedMatchers = array();
-  }
-
-  /**
-   * Sets the final matcher for the matching plan.
-   *
-   * @param \Drupal\Core\Routing\FinalMatcherInterface $final
-   *   The matcher that will be called last to ensure only a single route is
-   *   found.
-   *
-   * @return \Drupal\Core\Routing\NestedMatcherInterface
-   *   The current matcher.
-   */
-  public function setFinalMatcher(FinalMatcherInterface $final) {
-    $this->finalMatcher = $final;
-
-    return $this;
-  }
-
-  /**
-   * Sets the first matcher for the matching plan.
-   *
-   * Partial matchers will be run in the order in which they are added.
-   *
-   * @param \Drupal\Core\Routing\InitialMatcherInterface $matcher
-   *   An initial matcher.  It is responsible for its own configuration and
-   *   initial route collection
-   *
-   * @return \Drupal\Core\Routing\NestedMatcherInterface
-   *   The current matcher.
-   */
-  public function setInitialMatcher(InitialMatcherInterface $initial) {
-    $this->initialMatcher = $initial;
-
-    return $this;
-  }
-
-  /**
-   * Tries to match a request with a set of routes.
-   *
-   * If the matcher can not find information, it must throw one of the
-   * exceptions documented below.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request to match.
-   *
-   * @return array
-   *   An array of parameters.
-   *
-   * @throws ResourceNotFoundException
-   *   If no matching resource could be found.
-   * @throws MethodNotAllowedException
-   *   If a matching resource was found but the request method is not allowed.
-   */
-  public function matchRequest(Request $request) {
-    $collection = $this->initialMatcher->matchRequestPartial($request);
-
-    foreach ($this->getPartialMatchers() as $matcher) {
-      if ($collection) {
-        $matcher->setCollection($collection);
-      }
-      $collection = $matcher->matchRequestPartial($request);
-    }
-
-    $attributes = $this->finalMatcher->setCollection($collection)->matchRequest($request);
-
-    return $attributes;
-  }
-
-  /**
-    * Sorts the matchers and flattens them.
-    *
-    * @return array
-    *   An array of RequestMatcherInterface objects.
-    */
-  public function getPartialMatchers() {
-    if (empty($this->sortedMatchers)) {
-      $this->sortedMatchers = $this->sortMatchers();
-    }
-
-    return $this->sortedMatchers;
-  }
-
-  /**
-    * Sort matchers by priority.
-    *
-    * The highest priority number is the highest priority (reverse sorting).
-    *
-    * @return \Symfony\Component\Routing\RequestMatcherInterface[]
-    *   An array of Matcher objects in the order they should be used.
-    */
-  protected function sortMatchers() {
-    $sortedMatchers = array();
-    krsort($this->matchers);
-
-    foreach ($this->matchers as $matchers) {
-      $sortedMatchers = array_merge($sortedMatchers, $matchers);
-    }
-
-    return $sortedMatchers;
-  }
-
-  /**
-   * Sets the request context.
-   *
-   * This method is unused. It is here only to satisfy the interface.
-   *
-   * @param \Symfony\Component\Routing\RequestContext $context
-   *   The context
-   */
-  public function setContext(RequestContext $context) {
-    $this->context = $context;
-  }
-
-  /**
-   * Gets the request context.
-   *
-   * This method is unused. It is here only to satisfy the interface.
-   *
-   * @return \Symfony\Component\Routing\RequestContext
-   *   The context
-   */
-  public function getContext() {
-    return $this->context;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php b/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php
deleted file mode 100644
index 6ae0995..0000000
--- a/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\NestedMatcherInterface.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
-
-/**
- * A NestedMatcher allows for multiple-stage resolution of a route.
- */
-interface NestedMatcherInterface extends RequestMatcherInterface {
-
-  /**
-   * Sets the first matcher for the matching plan.
-   *
-   * Partial matchers will be run in the order in which they are added.
-   *
-   * @param \Drupal\Core\Routing\InitialMatcherInterface $matcher
-   *   An initial matcher.  It is responsible for its own configuration and
-   *   initial route collection
-   *
-   * @return \Drupal\Core\Routing\NestedMatcherInterface
-   *   The current matcher.
-   */
-  public function setInitialMatcher(InitialMatcherInterface $initial);
-
-  /**
-   * Adds a partial matcher to the matching plan.
-   *
-   * Partial matchers will be run in the order in which they are added.
-   *
-   * @param \Drupal\Core\Routing\PartialMatcherInterface $matcher
-   *   A partial matcher.
-   * @param int $priority
-   *   (optional) The priority of the matcher. Higher number matchers will be checked
-   *   first. Default to 0.
-   *
-   * @return NestedMatcherInterface
-   *   The current matcher.
-   */
-  public function addPartialMatcher(PartialMatcherInterface $matcher, $priority = 0);
-
-  /**
-   * Sets the final matcher for the matching plan.
-   *
-   * @param \Drupal\Core\Routing\FinalMatcherInterface $final
-   *   The matcher that will be called last to ensure only a single route is
-   *   found.
-   *
-   * @return \Drupal\Core\Routing\NestedMatcherInterface
-   *   The current matcher.
-   */
-  public function setFinalMatcher(FinalMatcherInterface $final);
-}
diff --git a/core/lib/Drupal/Core/Routing/NullGenerator.php b/core/lib/Drupal/Core/Routing/NullGenerator.php
new file mode 100644
index 0000000..7e72ec3
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/NullGenerator.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Routing\NullGenerator.
+ */
+
+namespace Drupal\Core\Routing;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+
+/**
+ * No-op implementation of a Url Generator, needed for backward compatibility.
+ */
+class NullGenerator implements UrlGeneratorInterface {
+
+  public function generate($name, $parameters = array(), $absolute = FALSE) {
+    return '';
+    throw new RouteNotFoundException();
+  }
+
+  public function setContext(RequestContext $context) {
+  }
+
+  public function getContext() {
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/PartialMatcher.php b/core/lib/Drupal/Core/Routing/PartialMatcher.php
deleted file mode 100644
index d28f9ac..0000000
--- a/core/lib/Drupal/Core/Routing/PartialMatcher.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\PartialMatcher.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Utility base class for partial matchers.
- */
-abstract class PartialMatcher implements PartialMatcherInterface {
-
-  /**
-   * The RouteCollection this matcher should match against.
-   *
-   * @var \Symfony\Component\Routing\RouteCollection
-   */
-  protected $routes;
-
-  /**
-   * Sets the route collection this matcher should use.
-   *
-   * @param \Symfony\Component\Routing\RouteCollection $collection
-   *   The collection against which to match.
-   *
-   * @return \Drupal\Core\Routing\PartialMatcherInterface
-   *   The current matcher.
-   */
-  public function setCollection(RouteCollection $collection) {
-    $this->routes = $collection;
-
-    return $this;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php b/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php
deleted file mode 100644
index 0d180c6..0000000
--- a/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Routing\PathMatcherInterface.
- */
-
-namespace Drupal\Core\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * A PartialMatcher works like a UrlMatcher, but will return multiple candidate routes.
- */
-interface PartialMatcherInterface {
-
-  /**
-   * Sets the route collection this matcher should use.
-   *
-   * @param \Symfony\Component\Routing\RouteCollection $collection
-   *   The collection against which to match.
-   *
-   * @return \Drupal\Core\Routing\PartialMatcherInterface
-   *   The current matcher.
-   */
-  public function setCollection(RouteCollection $collection);
-
-  /**
-   * Matches a request against multiple routes.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A Request object against which to match.
-   *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   A RouteCollection of matched routes.
-   */
-  public function matchRequestPartial(Request $request);
-}
diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index 2585d74..e6095d8 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -9,11 +9,12 @@
 
 use Symfony\Component\Routing\RouteCompilerInterface;
 use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCompiler as SymfonyRouteCompiler;
 
 /**
  * Compiler to generate derived information from a Route necessary for matching.
  */
-class RouteCompiler implements RouteCompilerInterface {
+class RouteCompiler extends SymfonyRouteCompiler implements RouteCompilerInterface {
 
   /**
    * The maximum number of path elements for a route pattern;
@@ -26,8 +27,31 @@ class RouteCompiler implements RouteCompilerInterface {
   const REGEX_DELIMITER = '#';
 
   /**
+   * Compiler for the Symfony route compilation data.
+   *
+   * @var \Symfony\Component\Routing\RouteCompiler
+   */
+  protected $symfonyCompiler;
+
+  /**
+   * Constructs a new RouteCompiler.
+   *
+   * Normally putting a dependent object in here like this would be a no-no,
+   * but Symfony's route compiler insists on class injection rather than object
+   * injection so we are given no choice.
+   */
+  public function __construct() {
+    //$this->symfonyCompiler = new SymfonyRouteCompiler();
+  }
+
+  /**
    * Compiles the current route instance.
    *
+   * Because so much of the parent class is private, we need to call the parent
+   * class's compile() method and then dissect its return value to build our
+   * new compiled object.  If upstream gets refactored so we can subclass more
+   * easily then this may not be necessary.
+   *
    * @param \Symfony\Component\Routing\Route $route
    *   A Route instance.
    *
@@ -36,142 +60,29 @@ class RouteCompiler implements RouteCompilerInterface {
    */
   public function compile(Route $route) {
 
-    $stripped_path = $this->getPathWithoutDefaults($route);
+    $symfony_compiled = parent::compile($route);
 
+    // The Drupal-specific compiled information.
+    $stripped_path = $this->getPathWithoutDefaults($route);
     $fit = $this->getFit($stripped_path);
-
     $pattern_outline = $this->getPatternOutline($stripped_path);
-
     $num_parts = count(explode('/', trim($pattern_outline, '/')));
 
-    $regex = $this->getRegex($route, $route->getPattern());
-
-    return new CompiledRoute($route, $fit, $pattern_outline, $num_parts, $regex);
-  }
-
-  /**
-   * Generates a regular expression that will match this pattern.
-   *
-   * This regex can be used in preg_match() to extract values inside {}.
-   *
-   * This algorithm was lifted directly from Symfony's RouteCompiler class.
-   * It is not factored out nicely there, so we cannot simply subclass it.
-   * @todo Refactor Symfony's RouteCompiler so that it's useful to subclass.
-   *
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   * @param string $pattern
-   *   The pattern for which we want a matching regex.
-   *
-   * @return string
-   *   A regular expression that will match a path against this route.
-   *
-   * @throws \LogicException
-   */
-  public function getRegex(Route $route, $pattern) {
-    $len = strlen($pattern);
-    $tokens = array();
-    $variables = array();
-    $pos = 0;
-    preg_match_all('#.\{(\w+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
-    foreach ($matches as $match) {
-      if ($text = substr($pattern, $pos, $match[0][1] - $pos)) {
-        $tokens[] = array('text', $text);
-      }
-
-      $pos = $match[0][1] + strlen($match[0][0]);
-      $var = $match[1][0];
-
-      if ($req = $route->getRequirement($var)) {
-        $regexp = $req;
-      }
-      else {
-        // Use the character preceding the variable as a separator
-        $separators = array($match[0][0][0]);
-
-        if ($pos !== $len) {
-          // Use the character following the variable as the separator when available
-          $separators[] = $pattern[$pos];
-        }
-        $regexp = sprintf('[^%s]+', preg_quote(implode('', array_unique($separators)), self::REGEX_DELIMITER));
-      }
-
-      $tokens[] = array('variable', $match[0][0][0], $regexp, $var);
-
-      if (in_array($var, $variables)) {
-        throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $route->getPattern(), $var));
-      }
-
-      $variables[] = $var;
-    }
-
-    if ($pos < $len) {
-      $tokens[] = array('text', substr($pattern, $pos));
-    }
-
-    // find the first optional token
-    $first_optional = INF;
-    for ($i = count($tokens) - 1; $i >= 0; $i--) {
-        $token = $tokens[$i];
-        if ('variable' === $token[0] && $route->hasDefault($token[3])) {
-            $first_optional = $i;
-        } else {
-            break;
-        }
-    }
-
-    // compute the matching regexp
-    $regexp = '';
-    for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) {
-        $regexp .= $this->computeRegexp($tokens, $i, $first_optional);
-    }
-
-    return self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s';
-  }
-
-  /**
-   * Computes the regexp used to match a specific token. It can be static text or a subpattern.
-   *
-   * @param array $tokens
-   *   The route tokens
-   * @param integer $index
-   *   The index of the current token
-   * @param integer $first_optional
-   *   The index of the first optional token
-   *
-   * @return string
-   *   The regexp pattern for a single token
-   */
-  private function computeRegexp(array $tokens, $index, $first_optional) {
-    $token = $tokens[$index];
-    if ('text' === $token[0]) {
-      // Text tokens
-      return preg_quote($token[1], self::REGEX_DELIMITER);
-    }
-    else {
-      // Variable tokens
-      if (0 === $index && 0 === $first_optional) {
-        // When the only token is an optional variable token, the separator is
-        // required.
-        return sprintf('%s(?<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
-      }
-      else {
-        $regexp = sprintf('%s(?<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
-        if ($index >= $first_optional) {
-          // Enclose each optional token in a subpattern to make it optional.
-          // "?:" means it is non-capturing, i.e. the portion of the subject
-          // string that matched the optional subpattern is not passed back.
-          $regexp = "(?:$regexp";
-          $nbTokens = count($tokens);
-          if ($nbTokens - 1 == $index) {
-            // Close the optional subpatterns.
-            $regexp .= str_repeat(")?", $nbTokens - $first_optional - (0 === $first_optional ? 1 : 0));
-          }
-        }
-
-        return $regexp;
-      }
-    }
+    return new CompiledRoute(
+      $route,
+      $fit,
+      $pattern_outline,
+      $num_parts,
+      // These are the Symfony compiled parts.
+      $symfony_compiled->getStaticPrefix(),
+      $symfony_compiled->getRegex(),
+      $symfony_compiled->getTokens(),
+      $symfony_compiled->getPathVariables(),
+      $symfony_compiled->getHostnameRegex(),
+      $symfony_compiled->getHostnameTokens(),
+      $symfony_compiled->getHostnameVariables(),
+      $symfony_compiled->getVariables()
+      );
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Routing/PathMatcher.php b/core/lib/Drupal/Core/Routing/RouteProvider.php
similarity index 54%
rename from core/lib/Drupal/Core/Routing/PathMatcher.php
rename to core/lib/Drupal/Core/Routing/RouteProvider.php
index 9b5bd5e..f2aea92 100644
--- a/core/lib/Drupal/Core/Routing/PathMatcher.php
+++ b/core/lib/Drupal/Core/Routing/RouteProvider.php
@@ -2,21 +2,22 @@
 
 /**
  * @file
- * Definition of Drupal\Core\Routing\PathMatcher.
+ * Contains Drupal\Core\Routing\RouteProvider.
  */
 
 namespace Drupal\Core\Routing;
 
+use Symfony\Cmf\Component\Routing\RouteProviderInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\RouteCollection;
 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 
-use Drupal\Core\Database\Connection;
+use \Drupal\Core\Database\Connection;
 
 /**
- * Initial matcher to match a route against a built database, by path.
+ * A Route Provider front-end for all Drupal-stored routes.
  */
-class PathMatcher implements InitialMatcherInterface {
+class RouteProvider implements RouteProviderInterface {
 
   /**
    * The database connection from which to read route information.
@@ -46,15 +47,29 @@ public function __construct(Connection $connection, $table = 'router') {
   }
 
   /**
-   * Matches a request against multiple routes.
+   * Finds routes that may potentially match the request.
    *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A Request object against which to match.
+   * This may return a mixed list of class instances, but all routes returned
+   * must extend the core symfony route. The classes may also implement
+   * RouteObjectInterface to link to a content document.
    *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   A RouteCollection of matched routes.
+   * This method may not throw an exception based on implementation specific
+   * restrictions on the url. That case is considered a not found - returning
+   * an empty array. Exceptions are only used to abort the whole request in
+   * case something is seriously broken, like the storage backend being down.
+   *
+   * Note that implementations may not implement an optimal matching
+   * algorithm, simply a reasonable first pass.  That allows for potentially
+   * very large route sets to be filtered down to likely candidates, which
+   * may then be filtered in memory more completely.
+   *
+   * @param Request $request A request against which to match.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection with all urls that
+   *      could potentially match $request. Empty collection if nothing can
+   *      match.
    */
-  public function matchRequestPartial(Request $request) {
+  public function getRouteCollectionForRequest(Request $request) {
 
     // The 'system_path' has language prefix stripped and path alias resolved,
     // whereas getPathInfo() returns the requested path. In Drupal, the request
@@ -97,6 +112,57 @@ public function matchRequestPartial(Request $request) {
   }
 
   /**
+   * Find the route using the provided route name (and parameters)
+   *
+   * @param string $name the route name to fetch
+   * @param array $parameters the parameters as they are passed to the
+   *      UrlGeneratorInterface::generate call
+   *
+   * @return \Symfony\Component\Routing\Route
+   *
+   * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException if
+   *      there is no route with that name in this repository
+   */
+  public function getRouteByName($name, $parameters = array()) {
+    $routes = $this->getRoutesByNames(array($name), $parameters);
+    if (empty($routes)) {
+      throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
+    }
+
+    return reset($routes);
+  }
+
+  /**
+   * Find many routes by their names using the provided list of names
+   *
+   * Note that this method may not throw an exception if some of the routes
+   * are not found. It will just return the list of those routes it found.
+   *
+   * This method exists in order to allow performance optimizations. The
+   * simple implementation could be to just repeatedly call
+   * $this->getRouteByName()
+   *
+   * @param array $names the list of names to retrieve
+   * @param array $parameters the parameters as they are passed to the
+   *      UrlGeneratorInterface::generate call. (Only one array, not one for
+   *      each entry in $names.
+   *
+   * @return \Symfony\Component\Routing\Route[] iterable thing with the keys
+   *      the names of the $names argument.
+   */
+  public function getRoutesByNames($names, $parameters = array()) {
+    $result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN :names', array(':names' => $names));
+    $routes = $result->fetchAllKeyed();
+
+    $return = array();
+    foreach ($routes as $name => $route) {
+      $return[$name] = unserialize($route);
+    }
+
+    return $return;
+  }
+
+  /**
    * Returns an array of path pattern outlines that could match the path parts.
    *
    * @param array $parts
@@ -145,4 +211,5 @@ public function getCandidateOutlines(array $parts) {
     }
     return $ancestors;
   }
+
 }
diff --git a/core/lib/Drupal/Core/Routing/UrlMatcher.php b/core/lib/Drupal/Core/Routing/UrlMatcher.php
new file mode 100644
index 0000000..1e302bd
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/UrlMatcher.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher as BaseUrlMatcher;
+
+/**
+ * Drupal-specific URL Matcher; handles the Drupal "system path" mapping.
+ */
+class UrlMatcher extends BaseUrlMatcher {
+
+  public function __construct() {}
+
+  public function finalMatch(RouteCollection $collection, Request $request) {
+    $this->routes = $collection;
+    $context = new RequestContext();
+    $context->fromRequest($request);
+    $this->setContext($context);
+    return $this->match('/' . $request->attributes->get('system_path'));
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php
deleted file mode 100644
index c6b28cc..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Routing\ChainMatcherTest.
- */
-
-namespace Drupal\system\Tests\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
-use Symfony\Component\Routing\Exception\ResourceNotFoundException;
-use Symfony\Component\Routing\Exception\RouteNotFoundException;
-use Symfony\Component\Routing\Exception\MethodNotAllowedException;
-
-use Drupal\simpletest\UnitTestBase;
-use Drupal\Core\Routing\ChainMatcher;
-
-use Exception;
-
-/**
- * Basic tests for the ChainMatcher.
- */
-class ChainMatcherTest extends UnitTestBase {
-  
-  public static function getInfo() {
-    return array(
-      'name' => 'Chain matcher tests',
-      'description' => 'Confirm that the chain matcher is working correctly.',
-      'group' => 'Routing',
-    );
-  }
-
-  /**
-   * Confirms that the expected exception is thrown.
-   */
-  public function testMethodNotAllowed() {
-
-    $chain = new ChainMatcher();
-
-    $method_not_allowed = new MockMatcher(function(Request $request) {
-      throw new MethodNotAllowedException(array('POST'));
-    });
-
-    try {
-      $chain->add($method_not_allowed);
-      $chain->matchRequest(Request::create('my/path'));
-    }
-    catch (MethodNotAllowedException $e) {
-      $this->pass('Correct exception thrown.');
-    }
-    catch (Exception $e) {
-      $this->fail('Incorrect exception thrown: ' . get_class($e));
-    }
-  }
-
-  /**
-   * Confirms that the expected exception is thrown.
-   */
-  public function testRequestNotFound() {
-
-    $chain = new ChainMatcher();
-
-    $resource_not_found = new MockMatcher(function(Request $request) {
-      throw new ResourceNotFoundException();
-    });
-
-    try {
-      $chain->add($resource_not_found);
-      $chain->matchRequest(Request::create('my/path'));
-    }
-    catch (ResourceNotFoundException $e) {
-      $this->pass('Correct exception thrown.');
-    }
-    catch (Exception $e) {
-      $this->fail('Incorrect exception thrown: ' . get_class($e));
-    }
-  }
-
-  /**
-   * Confirms that the expected exception is thrown.
-   */
-  public function testRequestFound() {
-
-    $chain = new ChainMatcher();
-
-    $method_not_allowed = new MockMatcher(function(Request $request) {
-      throw new MethodNotAllowedException(array('POST'));
-    });
-
-    $resource_not_found = new MockMatcher(function(Request $request) {
-      throw new ResourceNotFoundException();
-    });
-
-    $found_data = new MockMatcher(function(Request $request) {
-      return array('_controller' => 'foo');
-    });
-
-    try {
-      $chain->add($method_not_allowed);
-      $chain->add($resource_not_found);
-      $chain->add($found_data);
-      $request = Request::create('my/path');
-      $attributes = $chain->matchRequest($request);
-      $this->assertEqual($attributes['_controller'], 'foo', 'Correct attributes returned.');
-    }
-    catch (Exception $e) {
-      $this->fail('Exception thrown when a match should have been successful: ' . get_class($e));
-    }
-  }
-
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php
deleted file mode 100644
index c44a492..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Routing\NestedMatcherTest.
- */
-
-namespace Drupal\system\Tests\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Exception\MethodNotAllowedException;
-
-use Drupal\simpletest\UnitTestBase;
-use Drupal\Core\Routing\HttpMethodMatcher;
-use Drupal\Core\Routing\NestedMatcher;
-use Drupal\Core\Routing\FirstEntryFinalMatcher;
-
-use Exception;
-
-/**
- * Basic tests for the NestedMatcher class.
- */
-class FirstEntryFinalMatcherTest extends UnitTestBase {
-
-  /**
-   * A collection of shared fixture data for tests.
-   *
-   * @var RoutingFixtures
-   */
-  protected $fixtures;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'FirstEntryFinalMatcher tests',
-      'description' => 'Confirm that the FirstEntryFinalMatcher is working properly.',
-      'group' => 'Routing',
-    );
-  }
-
-  function __construct($test_id = NULL) {
-    parent::__construct($test_id);
-
-    $this->fixtures = new RoutingFixtures();
-  }
-
-  /**
-   * Confirms the final matcher returns correct attributes for static paths.
-   */
-  public function testFinalMatcherStatic() {
-
-    $collection = new RouteCollection();
-    $collection->add('route_a', new Route('/path/one', array(
-      '_controller' => 'foo',
-    )));
-
-    $request = Request::create('/path/one', 'GET');
-
-    $matcher = new FirstEntryFinalMatcher();
-    $matcher->setCollection($collection);
-    $attributes = $matcher->matchRequest($request);
-
-    $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
-    $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
-  }
-
-  /**
-   * Confirms the final matcher returns correct attributes for pattern paths.
-   */
-  public function testFinalMatcherPattern() {
-
-    $collection = new RouteCollection();
-    $collection->add('route_a', new Route('/path/one/{value}', array(
-      '_controller' => 'foo',
-    )));
-
-    $request = Request::create('/path/one/narf', 'GET');
-    $request->attributes->set('system_path', 'path/one/narf');
-
-    $matcher = new FirstEntryFinalMatcher();
-    $matcher->setCollection($collection);
-    $attributes = $matcher->matchRequest($request);
-
-    $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
-    $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
-    $this->assertEqual($attributes['value'], 'narf', 'Required placeholder value found.');
-  }
-
-  /**
-   * Confirms the final matcher returns correct attributes with default values.
-   */
-  public function testFinalMatcherPatternDefalts() {
-
-    $collection = new RouteCollection();
-    $collection->add('route_a', new Route('/path/one/{value}', array(
-      '_controller' => 'foo',
-      'value' => 'poink'
-    )));
-
-    $request = Request::create('/path/one', 'GET');
-    $request->attributes->set('system_path', 'path/one');
-
-    $matcher = new FirstEntryFinalMatcher();
-    $matcher->setCollection($collection);
-    $attributes = $matcher->matchRequest($request);
-
-    $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
-    $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
-    $this->assertEqual($attributes['value'], 'poink', 'Optional placeholder value used default.');
-  }
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php
deleted file mode 100644
index 8055743..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Routing\HttpMethodMMatcherTest.
- */
-
-namespace Drupal\system\Tests\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Exception\MethodNotAllowedException;
-
-use Drupal\simpletest\UnitTestBase;
-use Drupal\Core\Routing\HttpMethodMatcher;
-use Drupal\Core\Routing\NestedMatcher;
-use Drupal\Core\Routing\FirstEntryFinalMatcher;
-
-use Exception;
-
-/**
- * Basic tests for the HttpMethodMatcher class.
- */
-class HttpMethodMatcherTest extends UnitTestBase {
-
-  /**
-   * A collection of shared fixture data for tests.
-   *
-   * @var RoutingFixtures
-   */
-  protected $fixtures;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Partial matcher HTTP Method tests',
-      'description' => 'Confirm that the Http Method partial matcher is functioning properly.',
-      'group' => 'Routing',
-    );
-  }
-
-  function __construct($test_id = NULL) {
-    parent::__construct($test_id);
-
-    $this->fixtures = new RoutingFixtures();
-  }
-
-  /**
-   * Confirms that the HttpMethod matcher matches properly.
-   */
-  public function testFilterRoutes() {
-
-    $matcher = new HttpMethodMatcher();
-    $matcher->setCollection($this->fixtures->sampleRouteCollection());
-
-    $routes = $matcher->matchRequestPartial(Request::create('path/one', 'GET'));
-
-    $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.');
-    $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
-    $this->assertNull($routes->get('route_b'), 'The non-matching route was not found.');
-    $this->assertNotNull($routes->get('route_c'), 'The second matching route was found.');
-    $this->assertNotNull($routes->get('route_d'), 'The all-matching route was found.');
-    $this->assertNotNull($routes->get('route_e'), 'The multi-matching route was found.');
-  }
-
-  /**
-   * Confirms we can nest multiple partial matchers.
-   */
-  public function testNestedMatcher() {
-
-    $matcher = new NestedMatcher();
-
-    $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection()));
-    $matcher->addPartialMatcher(new HttpMethodMatcher());
-    $matcher->setFinalMatcher(new FirstEntryFinalMatcher());
-
-    $request = Request::create('/path/one', 'GET');
-
-    $attributes = $matcher->matchRequest($request);
-
-    $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
-  }
-
-  /**
-   * Confirms that the HttpMethod matcher throws an exception for no-route.
-   */
-  public function testNoRouteFound() {
-    $matcher = new HttpMethodMatcher();
-
-    // Remove the sample route that would match any method.
-    $routes = $this->fixtures->sampleRouteCollection();
-    $routes->remove('route_d');
-
-    $matcher->setCollection($routes);
-
-    try {
-      $routes = $matcher->matchRequestPartial(Request::create('path/one', 'DELETE'));
-      $this->fail(t('No exception was thrown.'));
-    }
-    catch (Exception $e) {
-      $this->assertTrue($e instanceof MethodNotAllowedException, 'The correct exception was thrown.');
-    }
-
-  }
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php
index 05f8d42..07bb859 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MimeTypeMatcherTest.php
@@ -47,52 +47,34 @@ function __construct($test_id = NULL) {
   public function testFilterRoutes() {
 
     $matcher = new MimeTypeMatcher();
-    $matcher->setCollection($this->fixtures->sampleRouteCollection());
+    $collection = $this->fixtures->sampleRouteCollection();
 
     // Tests basic JSON request.
     $request = Request::create('path/two', 'GET');
     $request->headers->set('Accept', 'application/json, text/xml;q=0.9');
-    $routes = $matcher->matchRequestPartial($request);
-    $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.');
+    $routes = $matcher->filter($collection, $request);
+    $this->assertEqual(count($routes), 4, 'The correct number of routes was found.');
     $this->assertNotNull($routes->get('route_c'), 'The json route was found.');
     $this->assertNull($routes->get('route_e'), 'The html route was not found.');
 
     // Tests JSON request with alternative JSON MIME type Accept header.
     $request = Request::create('path/two', 'GET');
     $request->headers->set('Accept', 'application/x-json, text/xml;q=0.9');
-    $routes = $matcher->matchRequestPartial($request);
-    $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.');
+    $routes = $matcher->filter($collection, $request);
+    $this->assertEqual(count($routes), 4, 'The correct number of routes was found.');
     $this->assertNotNull($routes->get('route_c'), 'The json route was found.');
     $this->assertNull($routes->get('route_e'), 'The html route was not found.');
 
     // Tests basic HTML request.
     $request = Request::create('path/two', 'GET');
     $request->headers->set('Accept', 'text/html, text/xml;q=0.9');
-    $routes = $matcher->matchRequestPartial($request);
-    $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.');
+    $routes = $matcher->filter($collection, $request);
+    $this->assertEqual(count($routes), 4, 'The correct number of routes was found.');
     $this->assertNull($routes->get('route_c'), 'The json route was not found.');
     $this->assertNotNull($routes->get('route_e'), 'The html route was found.');
   }
 
   /**
-   * Confirms we can nest multiple partial matchers.
-   */
-  public function testNestedMatcher() {
-
-    $matcher = new NestedMatcher();
-
-    $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection()));
-    $matcher->addPartialMatcher(new MimeTypeMatcher());
-    $matcher->setFinalMatcher(new FirstEntryFinalMatcher());
-
-    $request = Request::create('/path/two', 'GET');
-    $request->headers->set('Accept', 'text/html, text/xml;q=0.9');
-
-    $attributes = $matcher->matchRequest($request);
-    $this->assertEqual($attributes['_route']->getOption('_name'), 'route_e', 'The correct matching route was found.');
-  }
-
-  /**
    * Confirms that the MimeTypeMatcher matcher throws an exception for no-route.
    */
   public function testNoRouteFound() {
@@ -105,12 +87,10 @@ public function testNoRouteFound() {
     $routes->remove('route_c');
     $routes->remove('route_d');
 
-    $matcher->setCollection($routes);
-
     try {
       $request = Request::create('path/two', 'GET');
       $request->headers->set('Accept', 'application/json, text/xml;q=0.9');
-      $routes = $matcher->matchRequestPartial($request);
+      $routes = $matcher->filter($routes, $request);
       $this->fail(t('No exception was thrown.'));
     }
     catch (UnsupportedMediaTypeHttpException $e) {
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php
deleted file mode 100644
index 1592cbf..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-namespace Drupal\system\Tests\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-use Drupal\Core\Routing\InitialMatcherInterface;
-
-/**
- * Provides a mock path matcher.
- */
-class MockPathMatcher implements InitialMatcherInterface {
-
-  /**
-   * Routes to be matched.
-   *
-   * @var Symfony\Component\Routing\RouteCollection
-   */
-  protected $routes;
-
-  /**
-   * Construct the matcher given the route collection.
-   *
-   * @param Symfony\Component\Routing\RouteCollection $routes
-   *   The routes being matched.
-   */
-  public function __construct(RouteCollection $routes) {
-    $this->routes = $routes;
-  }
-
-  /**
-   * Matches a request against multiple routes.
-   *
-   * @param Request $request
-   *   A Request object against which to match.
-   *
-   * @return RouteCollection
-   *   A RouteCollection of matched routes.
-   */
-  public function matchRequestPartial(Request $request) {
-    // For now for testing we'll just do a straight string match.
-
-    $path = $request->getPathInfo();
-
-    $return = new RouteCollection();
-
-    foreach ($this->routes as $name => $route) {
-      if ($route->getPattern() == $path) {
-        $return->add($name, $route);
-      }
-    }
-
-    return $return;
-  }
-
-
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php
deleted file mode 100644
index de29538..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Routing\NestedMatcherTest.
- */
-
-namespace Drupal\system\Tests\Routing;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Exception\MethodNotAllowedException;
-
-use Drupal\simpletest\UnitTestBase;
-use Drupal\Core\Routing\HttpMethodMatcher;
-use Drupal\Core\Routing\NestedMatcher;
-use Drupal\Core\Routing\FirstEntryFinalMatcher;
-
-use Exception;
-
-/**
- * Basic tests for the NestedMatcher class.
- */
-class NestedMatcherTest extends UnitTestBase {
-
-  /**
-   * A collection of shared fixture data for tests.
-   *
-   * @var RoutingFixtures
-   */
-  protected $fixtures;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'NestedMatcher tests',
-      'description' => 'Confirm that the NestedMatcher system is working properly.',
-      'group' => 'Routing',
-    );
-  }
-
-  function __construct($test_id = NULL) {
-    parent::__construct($test_id);
-
-    $this->fixtures = new RoutingFixtures();
-  }
-
-  /**
-   * Confirms we can nest multiple partial matchers.
-   */
-  public function testNestedMatcher() {
-
-    $matcher = new NestedMatcher();
-
-    $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection()));
-    $matcher->addPartialMatcher(new HttpMethodMatcher(), 1);
-    $matcher->setFinalMatcher(new FirstEntryFinalMatcher());
-
-    $request = Request::create('/path/one', 'GET');
-
-    $attributes = $matcher->matchRequest($request);
-
-    $this->assertEqual($attributes['_route']->getOption('_name'), 'route_a', 'The correct matching route was found.');
-  }
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php
similarity index 81%
rename from core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
rename to core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php
index 6769107..9ebd2ed 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\system\Tests\Routing\PartialMatcherTest.
+ * Definition of Drupal\system\Tests\Routing\RouteProviderTest.
  */
 
 namespace Drupal\system\Tests\Routing;
@@ -13,16 +13,16 @@
 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 
 use Drupal\simpletest\UnitTestBase;
-use Drupal\Core\Routing\PathMatcher;
+use Drupal\Core\Routing\RouteProvider;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Routing\MatcherDumper;
 
 use Exception;
 
 /**
- * Basic tests for the UrlMatcherDumper.
+ * Basic tests for the RouteProvider.
  */
-class PathMatcherTest extends UnitTestBase {
+class RouteProviderTest extends UnitTestBase {
 
   /**
    * A collection of shared fixture data for tests.
@@ -33,8 +33,8 @@ class PathMatcherTest extends UnitTestBase {
 
   public static function getInfo() {
     return array(
-      'name' => 'Path matcher tests',
-      'description' => 'Confirm that the path matching library is working correctly.',
+      'name' => 'Route Provider tests',
+      'description' => 'Confirm that the default route provider is working correctly.',
       'group' => 'Routing',
     );
   }
@@ -57,11 +57,11 @@ public function tearDown() {
   public function testCandidateOutlines() {
 
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection);
+    $provider = new RouteProvider($connection);
 
     $parts = array('node', '5', 'edit');
 
-    $candidates = $matcher->getCandidateOutlines($parts);
+    $candidates = $provider->getCandidateOutlines($parts);
 
     $candidates = array_flip($candidates);
 
@@ -77,7 +77,7 @@ public function testCandidateOutlines() {
    */
   function testExactPathMatch() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -89,7 +89,7 @@ function testExactPathMatch() {
 
     $request = Request::create($path, 'GET');
 
-    $routes = $matcher->matchRequestPartial($request);
+    $routes = $provider->getRouteCollectionForRequest($request);
 
     foreach ($routes as $route) {
       $this->assertEqual($route->getPattern(), $path, 'Found path has correct pattern');
@@ -101,7 +101,7 @@ function testExactPathMatch() {
    */
   function testOutlinePathMatch() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -113,14 +113,14 @@ function testOutlinePathMatch() {
 
     $request = Request::create($path, 'GET');
 
-    $routes = $matcher->matchRequestPartial($request);
+    $routes = $provider->getRouteCollectionForRequest($request);
 
     // All of the matching paths have the correct pattern.
     foreach ($routes as $route) {
       $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern');
     }
 
-    $this->assertEqual(count($routes->all()), 2, 'The correct number of routes was found.');
+    $this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
     $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
     $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
   }
@@ -130,7 +130,7 @@ function testOutlinePathMatch() {
    */
   function testOutlinePathMatchTrailingSlash() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -142,14 +142,14 @@ function testOutlinePathMatchTrailingSlash() {
 
     $request = Request::create($path, 'GET');
 
-    $routes = $matcher->matchRequestPartial($request);
+    $routes = $provider->getRouteCollectionForRequest($request);
 
     // All of the matching paths have the correct pattern.
     foreach ($routes as $route) {
       $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern');
     }
 
-    $this->assertEqual(count($routes->all()), 2, 'The correct number of routes was found.');
+    $this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
     $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
     $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
   }
@@ -159,7 +159,7 @@ function testOutlinePathMatchTrailingSlash() {
    */
   function testOutlinePathMatchDefaults() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -177,15 +177,14 @@ function testOutlinePathMatchDefaults() {
     $request = Request::create($path, 'GET');
 
     try {
-      $routes = $matcher->matchRequestPartial($request);
+      $routes = $provider->getRouteCollectionForRequest($request);
 
       // All of the matching paths have the correct pattern.
       foreach ($routes as $route) {
-        $compiled = $route->compile();
         $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern');
       }
 
-      $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.');
+      $this->assertEqual(count($routes), 1, 'The correct number of routes was found.');
       $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
     }
     catch (ResourceNotFoundException $e) {
@@ -198,7 +197,7 @@ function testOutlinePathMatchDefaults() {
    */
   function testOutlinePathMatchDefaultsCollision() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -217,15 +216,14 @@ function testOutlinePathMatchDefaultsCollision() {
     $request = Request::create($path, 'GET');
 
     try {
-      $routes = $matcher->matchRequestPartial($request);
+      $routes = $provider->getRouteCollectionForRequest($request);
 
       // All of the matching paths have the correct pattern.
       foreach ($routes as $route) {
-        $compiled = $route->compile();
         $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern');
       }
 
-      $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.');
+      $this->assertEqual(count($routes), 1, 'The correct number of routes was found.');
       $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
     }
     catch (ResourceNotFoundException $e) {
@@ -238,7 +236,7 @@ function testOutlinePathMatchDefaultsCollision() {
    */
   function testOutlinePathMatchDefaultsCollision2() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -257,14 +255,14 @@ function testOutlinePathMatchDefaultsCollision2() {
     $request = Request::create($path, 'GET');
 
     try {
-      $routes = $matcher->matchRequestPartial($request);
+      $routes = $provider->getRouteCollectionForRequest($request);
 
       // All of the matching paths have the correct pattern.
       foreach ($routes as $route) {
         $this->assertEqual($route->compile()->getPatternOutline(), '/some/path/here', 'Found path has correct pattern');
       }
 
-      $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.');
+      $this->assertEqual(count($routes), 1, 'The correct number of routes was found.');
       $this->assertNotNull($routes->get('narf'), 'The first matching route was found.');
     }
     catch (ResourceNotFoundException $e) {
@@ -277,7 +275,7 @@ function testOutlinePathMatchDefaultsCollision2() {
    */
   function testOutlinePathNoMatch() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -290,13 +288,12 @@ function testOutlinePathNoMatch() {
     $request = Request::create($path, 'GET');
 
     try {
-      $routes = $matcher->matchRequestPartial($request);
+      $routes = $provider->getRouteCollectionForRequest($request);
       $this->fail(t('No exception was thrown.'));
     }
     catch (Exception $e) {
       $this->assertTrue($e instanceof ResourceNotFoundException, 'The correct exception was thrown.');
     }
-
   }
 
   /**
@@ -304,7 +301,7 @@ function testOutlinePathNoMatch() {
    */
   function testSystemPathMatch() {
     $connection = Database::getConnection();
-    $matcher = new PathMatcher($connection, 'test_routes');
+    $provider = new RouteProvider($connection, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -315,7 +312,7 @@ function testSystemPathMatch() {
     $request = Request::create('/path/one', 'GET');
     $request->attributes->set('system_path', 'path/two');
 
-    $routes = $matcher->matchRequestPartial($request);
+    $routes = $provider->getRouteCollectionForRequest($request);
 
     foreach ($routes as $route) {
       $this->assertEqual($route->getPattern(), '/path/two', 'Found path has correct pattern');
diff --git a/core/vendor/autoload.php b/core/vendor/autoload.php
index daa7ee7..7bc9123 100644
--- a/core/vendor/autoload.php
+++ b/core/vendor/autoload.php
@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer' . '/autoload_real.php';
 
-return ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592::getLoader();
+return ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a::getLoader();
diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php
index 9072401..ecb4b19 100644
--- a/core/vendor/composer/autoload_namespaces.php
+++ b/core/vendor/composer/autoload_namespaces.php
@@ -16,6 +16,7 @@
     'Symfony\\Component\\EventDispatcher\\' => $vendorDir . '/symfony/event-dispatcher/',
     'Symfony\\Component\\DependencyInjection\\' => $vendorDir . '/symfony/dependency-injection/',
     'Symfony\\Component\\ClassLoader\\' => $vendorDir . '/symfony/class-loader/',
+    'Symfony\\Cmf\\Component\\Routing' => $vendorDir . '/symfony-cmf/routing/',
     'Guzzle\\Stream' => $vendorDir . '/guzzle/stream/',
     'Guzzle\\Parser' => $vendorDir . '/guzzle/parser/',
     'Guzzle\\Http' => $vendorDir . '/guzzle/http/',
diff --git a/core/vendor/composer/autoload_real.php b/core/vendor/composer/autoload_real.php
index dbe463c..9634e0b 100644
--- a/core/vendor/composer/autoload_real.php
+++ b/core/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
 
 // autoload_real.php generated by Composer
 
-class ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592
+class ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a
 {
     private static $loader;
 
@@ -19,9 +19,9 @@ public static function getLoader()
             return static::$loader;
         }
 
-        spl_autoload_register(array('ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592', 'loadClassLoader'));
+        spl_autoload_register(array('ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a', 'loadClassLoader'));
         static::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        spl_autoload_unregister(array('ComposerAutoloaderInit295209ab8f7c3b45c210d28fa6db3592', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit23a41d5f637bb8e297fc063ef4ab931a', 'loadClassLoader'));
 
         $vendorDir = dirname(__DIR__);
         $baseDir = dirname($vendorDir);
diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json
index aaa926b..9dd2912 100644
--- a/core/vendor/composer/installed.json
+++ b/core/vendor/composer/installed.json
@@ -852,5 +852,56 @@
             "client",
             "Guzzle"
         ]
+    },
+    {
+        "name": "symfony-cmf/routing",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "target-dir": "Symfony/Cmf/Component/Routing",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony-cmf/Routing",
+            "reference": "1.0.0-alpha3"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://github.com/symfony-cmf/Routing/archive/1.0.0-alpha3.zip",
+            "reference": "1.0.0-alpha3",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.2",
+            "symfony/routing": ">=2.1,<2.3-dev",
+            "symfony/http-kernel": ">=2.1,<2.3-dev"
+        },
+        "time": "2012-12-16 17:52:57",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0-dev"
+            }
+        },
+        "installation-source": "source",
+        "autoload": {
+            "psr-0": {
+                "Symfony\\Cmf\\Component\\Routing": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Symfony CMF Community",
+                "homepage": "https://github.com/symfony-cmf/Routing/contributors"
+            }
+        ],
+        "description": "Extends the Symfony2 routing component for dynamic routes and chaining several routers",
+        "homepage": "http://cmf.symfony.com",
+        "keywords": [
+            "database",
+            "routing"
+        ]
     }
 ]
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/.travis.yml b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/.travis.yml
new file mode 100644
index 0000000..8bae46c
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/.travis.yml
@@ -0,0 +1,15 @@
+language: php
+
+env:
+  - SYMFONY_VERSION=2.1.*
+  - SYMFONY_VERSION=dev-master
+
+before_script:
+  - composer require symfony/routing:${SYMFONY_VERSION}
+  - composer install --dev
+
+script: phpunit --coverage-text
+
+notifications:
+  irc: "irc.freenode.org#symfony-cmf"
+  email: "symfony-cmf-devs@googlegroups.com"
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainRouter.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainRouter.php
new file mode 100644
index 0000000..6160fc3
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainRouter.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RequestContextAwareInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+
+/**
+ * ChainRouter
+ *
+ * Allows access to a lot of different routers.
+ *
+ * @author Henrik Bjornskov <henrik@bjrnskov.dk>
+ * @author Magnus Nordlander <magnus@e-butik.se>
+ */
+class ChainRouter implements RouterInterface, RequestMatcherInterface, WarmableInterface
+{
+    /**
+     * @var \Symfony\Component\Routing\RequestContext
+     */
+    private $context;
+
+    /**
+     * @var Symfony\Component\Routing\RouterInterface[]
+     */
+    private $routers = array();
+
+    /**
+     * @var \Symfony\Component\Routing\RouterInterface[] Array of routers, sorted by priority
+     */
+    private $sortedRouters;
+
+    /**
+     * @var \Symfony\Component\Routing\RouteCollection
+     */
+    private $routeCollection;
+
+    /**
+     * @var null|\Symfony\Component\HttpKernel\Log\LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * @param LoggerInterface $logger
+     */
+    public function __construct(LoggerInterface $logger = null)
+    {
+        $this->logger = $logger;
+    }
+
+    /**
+     * @return RequestContext
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * Add a Router to the index
+     *
+     * @param RouterInterface $router   The router instance
+     * @param integer         $priority The priority
+     */
+    public function add(RouterInterface $router, $priority = 0)
+    {
+        if (empty($this->routers[$priority])) {
+            $this->routers[$priority] = array();
+        }
+
+        $this->routers[$priority][] = $router;
+        $this->sortedRouters = array();
+    }
+
+    /**
+     * Sorts the routers and flattens them.
+     *
+     * @return array
+     */
+    public function all()
+    {
+        if (empty($this->sortedRouters)) {
+            $this->sortedRouters = $this->sortRouters();
+
+            // setContext() is done here instead of in add() to avoid fatal errors when clearing and warming up caches
+            // See https://github.com/symfony-cmf/Routing/pull/18
+            $context = $this->getContext();
+            if (null !== $context) {
+                foreach ($this->sortedRouters as $router) {
+                    if ($router instanceof RequestContextAwareInterface) {
+                        $router->setContext($context);
+                    }
+                }
+            }
+        }
+
+        return $this->sortedRouters;
+    }
+
+    /**
+     * Sort routers by priority.
+     * The highest priority number is the highest priority (reverse sorting)
+     *
+     * @return RouterInterface[]
+     */
+    protected function sortRouters()
+    {
+        $sortedRouters = array();
+        krsort($this->routers);
+
+        foreach ($this->routers as $routers) {
+            $sortedRouters = array_merge($sortedRouters, $routers);
+        }
+
+        return $sortedRouters;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Loops through all routes and tries to match the passed url.
+     *
+     * Note: You should use matchRequest if you can.
+     */
+    public function match($url)
+    {
+        $methodNotAllowed = null;
+
+        /** @var $router ChainedRouterInterface */
+        foreach ($this->all() as $router) {
+            try {
+                return $router->match($url);
+            } catch (ResourceNotFoundException $e) {
+                if ($this->logger) {
+                    $this->logger->info('Router '.get_class($router).' was not able to match, message "'.$e->getMessage().'"');
+                }
+                // Needs special care
+            } catch (MethodNotAllowedException $e) {
+                if ($this->logger) {
+                    $this->logger->info('Router '.get_class($router).' throws MethodNotAllowedException with message "'.$e->getMessage().'"');
+                }
+                $methodNotAllowed = $e;
+            }
+        }
+
+        throw $methodNotAllowed ?: new ResourceNotFoundException("None of the routers in the chain matched '$url'");
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Loops through all routes and tries to match the passed request.
+     */
+    public function matchRequest(Request $request)
+    {
+        $methodNotAllowed = null;
+
+        foreach ($this->all() as $router) {
+            try {
+                // the request/url match logic is the same as in Symfony/Component/HttpKernel/EventListener/RouterListener.php
+                // matching requests is more powerful than matching URLs only, so try that first
+                if ($router instanceof RequestMatcherInterface) {
+                    return $router->matchRequest($request);
+                }
+                return $router->match($request->getPathInfo());
+            } catch (ResourceNotFoundException $e) {
+                if ($this->logger) {
+                    $this->logger->info('Router '.get_class($router).' was not able to match, message "'.$e->getMessage().'"');
+                }
+                // Needs special care
+            } catch (MethodNotAllowedException $e) {
+                if ($this->logger) {
+                    $this->logger->info('Router '.get_class($router).' throws MethodNotAllowedException with message "'.$e->getMessage().'"');
+                }
+                $methodNotAllowed = $e;
+            }
+        }
+
+        throw $methodNotAllowed ?: new ResourceNotFoundException("None of the routers in the chain matched this request");
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Loops through all registered routers and returns a router if one is found.
+     * It will always return the first route generated.
+     */
+    public function generate($name, $parameters = array(), $absolute = false)
+    {
+        /** @var $router ChainedRouterInterface */
+        foreach ($this->all() as $router) {
+
+            // if $name and $router does not implement ChainedRouterInterface and $name is not a string, continue
+            // if $name and $router does not implement ChainedRouterInterface and $name is string but does not match a default Symfony2 route name, continue
+            if ($name && !$router instanceof ChainedRouterInterface) {
+                if (!is_string($name) || !preg_match('/^[a-z0-9A-Z_.]+$/', $name)) {
+                    continue;
+                }
+            }
+
+            // If $router implements ChainedRouterInterface but doesn't support this route name, continue
+            if ($router instanceof ChainedRouterInterface && !$router->supports($name)) {
+                continue;
+            }
+
+            try {
+                return $router->generate($name, $parameters, $absolute);
+            } catch (RouteNotFoundException $e) {
+                if ($this->logger) {
+                    $this->logger->info($e->getMessage());
+                }
+            }
+        }
+
+        throw new RouteNotFoundException(sprintf('None of the chained routers were able to generate route "%s".', $name));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        foreach ($this->all() as $router) {
+            if ($router instanceof RequestContextAwareInterface) {
+                $router->setContext($context);
+            }
+        }
+
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * check for each contained router if it can warmup
+     */
+    public function warmUp($cacheDir)
+    {
+        foreach ($this->all() as $router) {
+            if ($router instanceof WarmableInterface) {
+                $router->warmUp($cacheDir);
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRouteCollection()
+    {
+        if (!$this->routeCollection instanceof RouteCollection) {
+            $this->routeCollection = new RouteCollection();
+            foreach ($this->all() as $router) {
+                $this->routeCollection->addCollection($router->getRouteCollection());
+            }
+        }
+
+        return $this->routeCollection;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainedRouterInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainedRouterInterface.php
new file mode 100644
index 0000000..a4247aa
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ChainedRouterInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+use Symfony\Component\Routing\RouterInterface;
+
+/**
+ * Use this interface on custom routers that can handle non-string route
+ * "names".
+ */
+interface ChainedRouterInterface extends RouterInterface
+{
+    /**
+     * Whether the router supports the thing in $name to generate a route.
+     *
+     * This check does not need to look if the specific instance can be
+     * resolved to a route, only whether the router can generate routes from
+     * objects of this class.
+
+     * @param mixed $name The route name or route object
+     *
+     * @return bool
+     */
+    public function supports($name);
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentAwareGenerator.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentAwareGenerator.php
new file mode 100644
index 0000000..aff0c00
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentAwareGenerator.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+use Symfony\Component\Routing\Route as SymfonyRoute;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+
+use Symfony\Cmf\Component\Routing\RouteProviderInterface;
+
+/**
+ * A generator that tries to generate routes from object, route names or
+ * content objects or names.
+ *
+ * @author Philippo de Santis
+ * @author David Buchmann
+ * @author Uwe Jäger
+ */
+class ContentAwareGenerator extends ProviderBasedGenerator
+{
+    /**
+     * The content repository used to find content by it's id
+     * This can be used to specify a parameter content_id when generating urls
+     *
+     * This is optional and might not be initialized.
+     *
+     * @var  ContentRepositoryInterface
+     */
+    protected $contentRepository;
+
+    /**
+     * Set an optional content repository to find content by ids
+     *
+     * @param ContentRepositoryInterface $contentRepository
+     */
+    public function setContentRepository(ContentRepositoryInterface $contentRepository)
+    {
+        $this->contentRepository = $contentRepository;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param string $name       ignored
+     * @param array  $parameters must either contain the field 'route' with a
+     *      RouteObjectInterface or the field 'content' with the document
+     *      instance to get the route for (implementing RouteAwareInterface)
+     *
+     * @throws RouteNotFoundException If there is no such route in the database
+     */
+    public function generate($name, $parameters = array(), $absolute = false)
+    {
+        if ($name instanceof SymfonyRoute) {
+            $route = $this->getBestLocaleRoute($name, $parameters);
+        } elseif (is_string($name) && $name) {
+            $route = $this->getRouteByName($name, $parameters);
+        } else {
+            $route = $this->getRouteByContent($name, $parameters);
+        }
+
+        if (! $route instanceof SymfonyRoute) {
+            $hint = is_object($route) ? get_class($route) : gettype($route);
+            throw new RouteNotFoundException('Route of this document is not an instance of Symfony\Component\Routing\Route but: '.$hint);
+        }
+
+        return parent::generate($route, $parameters, $absolute);
+    }
+
+    /**
+     * Get the route by a string name
+     *
+     * @param string $route
+     * @param array  $parameters
+     *
+     * @return SymfonyRoute
+     *
+     * @throws RouteNotFoundException if there is no route found for the provided name
+     */
+    protected function getRouteByName($name, array $parameters)
+    {
+        $route = $this->provider->getRouteByName($name, $parameters);
+        if (empty($route)) {
+            throw new RouteNotFoundException('No route found for name: ' . $name);
+        }
+
+        return $this->getBestLocaleRoute($route, $parameters);
+    }
+
+    /**
+     * Determine if there is a route with matching locale associated with the
+     * given route via associated content.
+     *
+     * @param SymfonyRoute $route
+     * @param array        $parameters
+     *
+     * @return SymfonyRoute either the passed route or an alternative with better locale
+     */
+    protected function getBestLocaleRoute(SymfonyRoute $route, $parameters)
+    {
+        if (! $route instanceof RouteObjectInterface) {
+            // this route has no content, we can't get the alternatives
+            return $route;
+        }
+        $locale = $this->getLocale($parameters);
+        if (! $this->checkLocaleRequirement($route, $locale)) {
+            $content = $route->getRouteContent();
+            if ($content instanceof RouteAwareInterface) {
+                $routes = $content->getRoutes();
+                $contentRoute = $this->getRouteByLocale($routes, $locale);
+                if ($contentRoute) {
+                    return $contentRoute;
+                }
+            }
+        }
+
+        return $route;
+    }
+
+    /**
+     * Get the route based on the content field in parameters
+     *
+     * Called in generate when there is no route given in the parameters.
+     *
+     * If there is more than one route for the content, tries to find the
+     * first one that matches the _locale (provided in $parameters or otherwise
+     * defaulting to the request locale).
+     *
+     * If none is found, falls back to just return the first route.
+     *
+     * @param mixed $name
+     * @param array $parameters which should contain a content field containing a RouteAwareInterface object
+     *
+     * @return SymfonyRoute the route instance
+     *
+     * @throws RouteNotFoundException if there is no content field in the
+     *      parameters or its not possible to build a route from that object
+     */
+    protected function getRouteByContent($name, &$parameters)
+    {
+        if ($name instanceof RouteAwareInterface) {
+            $content = $name;
+        } elseif (isset($parameters['content_id']) && null !== $this->contentRepository) {
+            $content = $this->contentRepository->findById($parameters['content_id']);
+        } elseif (isset($parameters['content'])) {
+            $content = $parameters['content'];
+        }
+
+        unset($parameters['content'], $parameters['content_id']);
+
+        if (empty($content)) {
+            throw new RouteNotFoundException('Neither the route name, nor a parameter "content" or "content_id" could be resolved to an content instance');
+        }
+
+        if (!$content instanceof RouteAwareInterface) {
+            $hint = is_object($content) ? get_class($content) : gettype($content);
+            throw new RouteNotFoundException('The content does not implement RouteAwareInterface: ' . $hint);
+        }
+
+        $routes = $content->getRoutes();
+        if (empty($routes)) {
+            $hint = method_exists($content, 'getPath') ? $content->getPath() : get_class($content);
+            throw new RouteNotFoundException('Document has no route: ' . $hint);
+        }
+
+        $route = $this->getRouteByLocale($routes, $this->getLocale($parameters));
+        if ($route) {
+            return $route;
+        }
+
+        // if none matched, continue and randomly return the first one
+        return reset($routes);
+    }
+
+    /**
+     * @param RouteCollection $routes
+     * @param string          $locale
+     *
+     * @return bool|SymfonyRoute false if no route requirement matches the provided locale
+     */
+    protected function getRouteByLocale($routes, $locale)
+    {
+        foreach ($routes as $route) {
+            if (! $route instanceof SymfonyRoute) {
+                continue;
+            }
+
+            if ($this->checkLocaleRequirement($route, $locale)) {
+                return $route;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @param SymfonyRoute $route
+     * @param string       $locale
+     *
+     * @return bool TRUE if there is either no _locale, no _locale requirement or if the two match
+     */
+    private function checkLocaleRequirement(SymfonyRoute $route, $locale)
+    {
+        return empty($locale)
+            || !$route->getRequirement('_locale')
+            || preg_match('/'.$route->getRequirement('_locale').'/', $locale)
+        ;
+    }
+
+    /**
+     * Determine the locale to be used with this request
+     *
+     * @param array $parameters the parameters determined by the route
+     *
+     * @return string|null the locale following of the parameters or any other
+     *  information the router has available.
+     */
+    protected function getLocale($parameters)
+    {
+        if (isset($parameters['_locale'])) {
+            return $parameters['_locale'];
+        }
+
+        return null;
+    }
+
+    /**
+     * We additionally support empty name and data in parameters and RouteAware content
+     */
+    public function supports($name)
+    {
+        return ! $name || parent::supports($name) || $name instanceof RouteAwareInterface;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentRepositoryInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentRepositoryInterface.php
new file mode 100644
index 0000000..415cd7d
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ContentRepositoryInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+/**
+ * Interface used by the DynamicRouter to retrieve content by it's id when
+ * generating routes from content-id.
+ *
+ * This can be easily implemented using i.e. the Doctrine PHPCR-ODM
+ * DocumentManager.
+ *
+ * @author Uwe Jäger
+ */
+interface ContentRepositoryInterface
+{
+    /**
+     * Return a content object by it's id or null if there is none.
+     *
+     * If the returned content implements RouteAwareInterface, it will be used
+     * to get the route from it to generate an URL.
+     *
+     * @param string $id id of the content object
+     *
+     * @return object A content that matches this id.
+     */
+    public function findById($id);
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/DynamicRouter.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/DynamicRouter.php
new file mode 100644
index 0000000..44407e0
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/DynamicRouter.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+use Symfony\Component\Routing\RequestContextAwareInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface;
+
+/**
+ * A flexible router accepting matcher and generator through injection and
+ * using the RouteEnhancer concept to generate additional data on the routes.
+ *
+ * @author Crell
+ * @author David Buchmann
+ */
+class DynamicRouter implements RouterInterface, RequestMatcherInterface, ChainedRouterInterface
+{
+    /**
+     * @var RequestMatcherInterface|UrlMatcherInterface
+     */
+    protected $matcher;
+
+    /**
+     * @var UrlGeneratorInterface
+     */
+    protected $generator;
+
+    /**
+     * @var RouteEnhancerInterface[]
+     */
+    protected $enhancers = array();
+
+    /**
+     * Cached sorted list of enhancers
+     *
+     * @var RouteEnhancerInterface[]
+     */
+    protected $sortedEnhancers = array();
+
+    /**
+     * The regexp pattern that needs to be matched before a dynamic lookup is made
+     *
+     * @var string
+     */
+    protected $uriFilterRegexp;
+
+    /**
+     * @var RequestContext
+     */
+    protected $context;
+
+    /**
+     * @param RequestContext                              $context
+     * @param RequestMatcherInterface|UrlMatcherInterface $matcher
+     * @param UrlGeneratorInterface                       $generator
+     * @param string                                      $uriFilterRegexp
+     */
+    public function __construct(RequestContext $context, $matcher, UrlGeneratorInterface $generator, $uriFilterRegexp = '')
+    {
+        $this->context = $context;
+        if (! $matcher instanceof RequestMatcherInterface && ! $matcher instanceof UrlMatcherInterface) {
+            throw new \InvalidArgumentException('Invalid $matcher');
+        }
+        $this->matcher = $matcher;
+        $this->generator = $generator;
+        $this->uriFilterRegexp = $uriFilterRegexp;
+
+        $this->generator->setContext($context);
+    }
+
+    /**
+     * Not implemented.
+     */
+    public function getRouteCollection()
+    {
+        return new RouteCollection();
+    }
+
+    /**
+     * @return RequestMatcherInterface|UrlMatcherInterface
+     */
+    public function getMatcher()
+    {
+        // we may not set the context in DynamicRouter::setContext as this would lead to symfony cache warmup problems
+        // a request matcher does not need the request context separately as it can get it from the request.
+        if ($this->matcher instanceof RequestContextAwareInterface) {
+            $this->matcher->setContext($this->getContext());
+        }
+
+        return $this->matcher;
+    }
+
+    /**
+     * @return UrlGeneratorInterface
+     */
+    public function getGenerator()
+    {
+        $this->generator->setContext($this->getContext());
+
+        return $this->generator;
+    }
+
+    /**
+     * Generates a URL from the given parameters.
+     *
+     * If the generator is not able to generate the url, it must throw the RouteNotFoundException
+     * as documented below.
+     *
+     * @param string  $name       The name of the route
+     * @param mixed   $parameters An array of parameters
+     * @param Boolean $absolute   Whether to generate an absolute URL
+     *
+     * @return string The generated URL
+     *
+     * @throws RouteNotFoundException if route doesn't exist
+     *
+     * @api
+     */
+    public function generate($name, $parameters = array(), $absolute = false)
+    {
+        return $this->getGenerator()->generate($name, $parameters, $absolute);
+    }
+
+    /**
+     * Support any string as route name
+     *
+     * {@inheritDoc}
+     */
+    public function supports($name)
+    {
+        // TODO: check $this->generator instanceof VersatileGeneratorInterface
+        return $this->generator->supports($name);
+    }
+
+    /**
+     * Tries to match a URL path with a set of routes.
+     *
+     * If the matcher can not find information, it must throw one of the
+     * exceptions documented below.
+     *
+     * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded)
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If the resource could not be found
+     * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
+     *
+     * @api
+     */
+    public function match($pathinfo)
+    {
+        if (! empty($this->uriFilterRegexp) && ! preg_match($this->uriFilterRegexp, $pathinfo)) {
+            throw new ResourceNotFoundException("$pathinfo does not match the '{$this->uriFilterRegexp}' pattern");
+        }
+
+        $matcher = $this->getMatcher();
+        if (! $matcher instanceof UrlMatcherInterface) {
+            throw new \InvalidArgumentException('Wrong matcher type, you need to call matchRequest');
+        }
+
+        $defaults = $matcher->match($pathinfo);
+
+        return $this->applyRouteEnhancers($defaults, Request::create($pathinfo));
+    }
+
+    /**
+     * Tries to match a request with a set of routes and returns the array of
+     * information for that route.
+     *
+     * If the matcher can not find information, it must throw one of the
+     * exceptions documented below.
+     *
+     * @param Request $request The request to match
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If no matching resource could be found
+     * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed
+     */
+    public function matchRequest(Request $request)
+    {
+        if (! empty($this->uriFilterRegexp) && ! preg_match($this->uriFilterRegexp, $request->getPathInfo())) {
+            throw new ResourceNotFoundException("{$request->getPathInfo()} does not match the '{$this->uriFilterRegexp}' pattern");
+        }
+
+        $matcher = $this->getMatcher();
+        if ($matcher instanceof UrlMatcherInterface) {
+            return $this->match($request->getPathInfo());
+        }
+
+        $defaults = $matcher->matchRequest($request);
+
+        return $this->applyRouteEnhancers($defaults, $request);
+    }
+
+    /**
+     * Apply the route enhancers to the defaults, according to priorities
+     *
+     * @param array $defaults
+     * @param Request $request
+     * @return array
+     */
+    protected function applyRouteEnhancers($defaults, Request $request)
+    {
+        foreach ($this->getRouteEnhancers() as $enhancer) {
+            $defaults = $enhancer->enhance($defaults, $request);
+        }
+        return $defaults;
+    }
+
+    /**
+     * Add route enhancers to the router to let them generate information on
+     * matched routes.
+     *
+     * The order of the enhancers is determined by the priority, the higher the
+     * value, the earlier the enhancer is run.
+     *
+     * @param RouteEnhancerInterface $enhancer
+     * @param int                    $priority
+     */
+    public function addRouteEnhancer(RouteEnhancerInterface $enhancer, $priority = 0)
+    {
+        if (empty($this->enhancers[$priority])) {
+            $this->enhancers[$priority] = array();
+        }
+
+        $this->enhancers[$priority][] = $enhancer;
+        $this->sortedEnhancers = array();
+
+        return $this;
+    }
+
+    /**
+     * Sorts the enhancers and flattens them.
+     *
+     * @return RouteEnhancerInterface[] the enhancers ordered by priority
+     */
+    public function getRouteEnhancers()
+    {
+        if (empty($this->sortedEnhancers)) {
+            $this->sortedEnhancers = $this->sortRouteEnhancers();
+        }
+
+        return $this->sortedEnhancers;
+    }
+
+    /**
+     * Sort enhancers by priority.
+     *
+     * The highest priority number is the highest priority (reverse sorting).
+     *
+     * @return RouteEnhancerInterface[] the sorted enhancers
+     */
+    protected function sortRouteEnhancers()
+    {
+        $sortedEnhancers = array();
+        krsort($this->enhancers);
+
+        foreach ($this->enhancers as $enhancers) {
+            $sortedEnhancers = array_merge($sortedEnhancers, $enhancers);
+        }
+
+        return $sortedEnhancers;
+    }
+
+    /**
+     * Sets the request context.
+     *
+     * @param RequestContext $context The context
+     *
+     * @api
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    /**
+     * Gets the request context.
+     *
+     * @return RequestContext The context
+     *
+     * @api
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldByClassEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldByClassEnhancer.php
new file mode 100644
index 0000000..23bc714
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldByClassEnhancer.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Enhancer;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+/**
+ * This enhancer sets a field if not yet existing from the class of an object
+ * in another field.
+ *
+ * The comparison is done with instanceof to support proxy classes and such.
+ *
+ * Only works with RouteObjectInterface routes that can return a referenced
+ * content.
+ *
+ * @author David Buchmann
+ */
+class FieldByClassEnhancer implements RouteEnhancerInterface
+{
+    /**
+     * @var string field for the source class
+     */
+    protected $source;
+    /**
+     * @var string field to write hashmap lookup result into
+     */
+    protected $target;
+    /**
+     * @var array containing the mapping between a class name and the target value
+     */
+    protected $map;
+
+    /**
+     * @param string $source the field name of the class
+     * @param string $target the field name to set from the map
+     * @param array  $map    the map of class names to field values
+     */
+    public function __construct($source, $target, $map)
+    {
+        $this->source = $source;
+        $this->target = $target;
+        $this->map = $map;
+    }
+
+    /**
+     * If the source field is instance of one of the entries in the map,
+     * target is set to the value of that map entry.
+     *
+     * {@inheritDoc}
+     */
+    public function enhance(array $defaults, Request $request)
+    {
+        if (isset($defaults[$this->target])) {
+            // no need to do anything
+            return $defaults;
+        }
+
+        if (! isset($defaults[$this->source])) {
+            return $defaults;
+        }
+
+        // we need to loop over the array and do instanceof in case the content
+        // class extends the specified class
+        // i.e. phpcr-odm generates proxy class for the content.
+        foreach ($this->map as $class => $value) {
+            if ($defaults[$this->source] instanceof $class) {
+                // found a matching entry in the map
+                $defaults[$this->target] = $value;
+
+                return $defaults;
+            }
+        }
+
+        return $defaults;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldMapEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldMapEnhancer.php
new file mode 100644
index 0000000..53f6c0e
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldMapEnhancer.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Enhancer;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * This enhancer can fill one field with the result of a hashmap lookup of
+ * another field. If the target field is already set, it does nothing.
+ *
+ * @author David Buchmann
+ */
+class FieldMapEnhancer implements RouteEnhancerInterface
+{
+    /**
+     * @var string field for key in hashmap lookup
+     */
+    protected $source;
+    /**
+     * @var string field to write hashmap lookup result into
+     */
+    protected $target;
+    /**
+     * @var array containing the mapping between the source field value and target field value
+     */
+    protected $hashmap;
+
+    /**
+     * @param string $source  the field to read
+     * @param string $target  the field to write the result of the lookup into
+     * @param array  $hashmap for looking up value from source and get value for target
+     */
+    public function __construct($source, $target, array $hashmap)
+    {
+        $this->source = $source;
+        $this->target = $target;
+        $this->hashmap = $hashmap;
+    }
+
+    /**
+     * If the target field is not set but the source field is, map the field
+     *
+     * {@inheritDoc}
+     */
+    public function enhance(array $defaults, Request $request)
+    {
+        if (isset($defaults[$this->target])) {
+            return $defaults;
+        }
+        if (! isset($defaults[$this->source])) {
+            return $defaults;
+        }
+        if (! isset($this->hashmap[$defaults[$this->source]])) {
+            return $defaults;
+        }
+
+        $defaults[$this->target] = $this->hashmap[$defaults[$this->source]];
+
+        return $defaults;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldPresenceEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldPresenceEnhancer.php
new file mode 100644
index 0000000..9eb3a2a
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/FieldPresenceEnhancer.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Enhancer;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * This enhancer can set a field to a fixed value if an other field is present.
+ *
+ * @author David Buchmann
+ */
+class FieldPresenceEnhancer implements RouteEnhancerInterface
+{
+    /**
+     * @var string field for the source class
+     */
+    protected $source;
+    /**
+     * @var string field to write hashmap lookup result into
+     */
+    protected $target;
+    /**
+     * value to set the target field to
+     *
+     * @var string
+     */
+    private $value;
+
+    /**
+     * @param string $source the field name of the class
+     * @param string $target the field name to set from the map
+     * @param string $value  value to set target field to if source field exists
+     */
+    public function __construct($source, $target, $value)
+    {
+        $this->source = $source;
+        $this->target = $target;
+        $this->value = $value;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function enhance(array $defaults, Request $request)
+    {
+        if (isset($defaults[$this->target])) {
+            // no need to do anything
+            return $defaults;
+        }
+
+        if (! isset($defaults[$this->source])) {
+            return $defaults;
+        }
+
+        $defaults[$this->target] = $this->value;
+
+        return $defaults;
+    }
+
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteContentEnhancer.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteContentEnhancer.php
new file mode 100644
index 0000000..39e54d8
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteContentEnhancer.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Enhancer;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+/**
+ * This enhancer sets the content to target field if the route provides content
+ *
+ * Only works with RouteObjectInterface routes that can return a referenced
+ * content.
+ *
+ * @author David Buchmann
+ */
+class RouteContentEnhancer implements RouteEnhancerInterface
+{
+    /**
+     * @var string field for the route class
+     */
+    protected $routefield;
+    /**
+     * @var string field to write hashmap lookup result into
+     */
+    protected $target;
+
+    /**
+     * @param string $routefield the field name of the route class
+     * @param string $target     the field name to set from the map
+     * @param array  $hashmap    the map of class names to field values
+     */
+    public function __construct($routefield, $target)
+    {
+        $this->routefield = $routefield;
+        $this->target = $target;
+    }
+
+    /**
+     * If the route has a non-null content and if that content class is in the
+     * injected map, returns that controller.
+     *
+     * {@inheritDoc}
+     */
+    public function enhance(array $defaults, Request $request)
+    {
+        if (isset($defaults[$this->target])) {
+            // no need to do anything
+            return $defaults;
+        }
+
+        if (! isset($defaults[$this->routefield])
+            || ! $defaults[$this->routefield] instanceof RouteObjectInterface
+        ) {
+            // we can't determine the content
+            return $defaults;
+        }
+        $route = $defaults[$this->routefield];
+
+        $content = $route->getRouteContent();
+        if (! $content) {
+            // we have no content
+            return $defaults;
+        }
+        $defaults[$this->target] = $content;
+
+        return $defaults;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteEnhancerInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteEnhancerInterface.php
new file mode 100644
index 0000000..5932c71
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Enhancer/RouteEnhancerInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Enhancer;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * A route enhancer can change the values in the route data arrays
+ *
+ * This is useful to provide information to the rest of the routing system
+ * that can be inferred from other parameters rather than hardcode that
+ * information in every route.
+ *
+ * @author David Buchmann
+ */
+interface RouteEnhancerInterface
+{
+    /**
+     * Update the defaults based on its own data and the request.
+     *
+     * @param array $defaults the getRouteDefaults array
+     *
+     * @return array the modified defaults. Each enhancer MUST return the $defaults but may add or remove values
+     */
+    public function enhance(array $defaults, Request $request);
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/LICENSE b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/LICENSE
new file mode 100644
index 0000000..797b396
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/LICENSE
@@ -0,0 +1,23 @@
+Routing
+
+    The MIT License
+
+    Copyright (c) 2011-2012 Symfony2 CMF
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/ConfigurableUrlMatcher.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/ConfigurableUrlMatcher.php
new file mode 100644
index 0000000..e1e17b5
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/ConfigurableUrlMatcher.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\NestedMatcher;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Cmf\Component\Routing\NestedMatcher\FinalMatcherInterface;
+
+/**
+ * A final matcher that can proxy any matcher having the right constructor
+ * signature, the same way the symfony core Router class does.
+ *
+ * @author DavidBuchmann
+ */
+class ConfigurableUrlMatcher implements FinalMatcherInterface
+{
+    private $matcherClass;
+
+    public function __construct($matcherClass = 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher')
+    {
+        $this->matcherClass = $matcherClass;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function finalMatch(RouteCollection $collection, Request $request)
+    {
+        $context = new RequestContext();
+        $context->fromRequest($request);
+        $matcher = $this->getMatcher($collection, $context);
+        $attributes = $matcher->match($request->getPathInfo());
+
+        // cleanup route attributes
+        if (! isset($attributes['_route']) || ! $attributes['_route'] instanceof Route) {
+            $name = $attributes['_route'];
+            $route = $collection->get($attributes['_route']);
+            $attributes['_route'] = $route;
+
+            if ($route instanceof RouteObjectInterface && is_string($route->getRouteKey())) {
+                $name = $route->getRouteKey();
+            }
+
+            if (empty($attributes['_route_name']) && is_string($name)) {
+                $attributes['_route_name'] = $name;
+            }
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * @param RouteCollection $collection the route collection to match
+     * @param RequestContext  $context      the context to match in
+     *
+     * @return \Symfony\Component\Routing\Matcher\UrlMatcherInterface
+     */
+    protected function getMatcher(RouteCollection $collection, RequestContext $context)
+    {
+        return new $this->matcherClass($collection, $context);
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/FinalMatcherInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/FinalMatcherInterface.php
new file mode 100644
index 0000000..b48859c
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/FinalMatcherInterface.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\NestedMatcher;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+/**
+ * A FinalMatcher returns only one route from a collection of candidate routes.
+ *
+ * @author Larry Garfield
+ * @author David Buchmann
+ */
+interface FinalMatcherInterface
+{
+    /**
+    * Matches a request against a route collection and returns exactly one result.
+    *
+    * @param RouteCollection $collection The collection against which to match.
+    * @param Request $request The request to match.
+    *
+    * @return array An array of parameters
+    *
+    * @throws ResourceNotFoundException if none of the routes in $collection
+    *    matches $request
+    */
+    public function finalMatch(RouteCollection $collection, Request $request);
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/NestedMatcher.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/NestedMatcher.php
new file mode 100644
index 0000000..9d31d81
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/NestedMatcher.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\NestedMatcher;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Cmf\Component\Routing\RouteProviderInterface;
+
+/**
+ * A more flexible approach to matching. The route collection to match against
+ * can be dynamically determined based on the request and users can inject
+ * their own filters or use a custom final matching strategy.
+ *
+ * The nested matcher splits matching into three configurable steps:
+ *
+ * 1) Get potential matches from a RouteProviderInterface
+ * 2) Apply any RouteFilterInterface to reduce the route collection
+ * 3) Have FinalMatcherInterface select the best match of the remaining routes
+ *
+ * @author Larry Garfield
+ * @author David Buchmann
+ */
+class NestedMatcher implements RequestMatcherInterface
+{
+    /**
+     * The route provider responsible for the first-pass match.
+     *
+     * @var RouteProviderInterface
+     */
+    protected $routeProvider;
+
+    /**
+     * The final matcher.
+     *
+     * @var FinalMatcherInterface
+     */
+    protected $finalMatcher;
+
+    /**
+     * An array of RouteFilterInterface objects.
+     *
+     * @var RouteFilterInterface[]
+     */
+    protected $filters = array();
+
+    /**
+     * Array of RouteFilterInterface objects, sorted.
+     *
+     * @var RouteFilterInterface[]
+     */
+    protected $sortedFilters = array();
+
+    /**
+     * Constructs a new NestedMatcher
+     *
+     * @param RouteProviderInterface $provider The Route Provider this matcher should use.
+     */
+    public function __construct(RouteProviderInterface $provider)
+    {
+        $this->routeProvider = $provider;
+    }
+
+    /**
+     * Sets the route provider for the matching plan.
+     *
+     * @param RouteProviderInterface $provider A route provider. It is responsible for its own configuration.
+     *
+     * @return NestedMatcher this object to have a fluent interface
+     */
+    public function setRouteProvider(RouteProviderInterface $provider)
+    {
+        $this->routeProvider = $provider;
+
+        return $this;
+    }
+
+    /**
+     * Adds a partial matcher to the matching plan.
+     *
+     * Partial matchers will be run in the order in which they are added.
+     *
+     * @param RouteFilterInterface $filter
+     * @param int                  $priority (optional) The priority of the filter. Higher number filters will be used first. Default to 0.
+     *
+     * @return NestedMatcher this object to have a fluent interface
+     */
+    public function addRouteFilter(RouteFilterInterface $filter, $priority = 0)
+    {
+        if (empty($this->filters[$priority])) {
+            $this->filters[$priority] = array();
+        }
+
+        $this->filters[$priority][] = $filter;
+        $this->sortedFilters = array();
+
+        return $this;
+    }
+
+    /**
+     * Sets the final matcher for the matching plan.
+     *
+     * @param FinalMatcherInterface $final The final matcher that will have to pick the route that will be used.
+     *
+     * @return NestedMatcher this object to have a fluent interface
+     */
+    public function setFinalMatcher(FinalMatcherInterface $final)
+    {
+        $this->finalMatcher = $final;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function matchRequest(Request $request)
+    {
+        $collection = $this->routeProvider->getRouteCollectionForRequest($request);
+        if (!count($collection)) {
+            throw new ResourceNotFoundException();
+        }
+
+        // Route Filters are expected to throw an exception themselves if they
+        // end up filtering the list down to 0.
+        foreach ($this->getRouteFilters() as $filter) {
+            $collection = $filter->filter($collection, $request);
+        }
+
+        $attributes = $this->finalMatcher->finalMatch($collection, $request);
+
+        return $attributes;
+    }
+
+    /**
+     * Sorts the filters and flattens them.
+     *
+     * @return RouteFilterInterface[] the filters ordered by priority
+     */
+    public function getRouteFilters()
+    {
+        if (empty($this->sortedFilters)) {
+           $this->sortedFilters = $this->sortFilters();
+        }
+
+        return $this->sortedFilters;
+    }
+
+    /**
+     * Sort filters by priority.
+     *
+     * The highest priority number is the highest priority (reverse sorting).
+     *
+     * @return RouteFilterInterface[] the sorted filters
+     */
+    protected function sortFilters()
+    {
+        $sortedFilters = array();
+        krsort($this->filters);
+
+        foreach ($this->filters as $filters) {
+            $sortedFilters = array_merge($sortedFilters, $filters);
+        }
+
+        return $sortedFilters;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/RouteFilterInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/RouteFilterInterface.php
new file mode 100644
index 0000000..7fe4ca9
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/RouteFilterInterface.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\NestedMatcher;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * A RouteFilter takes a RouteCollection and returns a filtered subset.
+ *
+ * It is not implemented as a filter iterator because we want to allow
+ * router filters to handle their own empty-case handling, usually by throwing
+ * an appropriate exception if no routes match the object's rules.
+ *
+ * @author Larry Garfield
+ * @author David Buchmann
+ */
+interface RouteFilterInterface
+{
+    /**
+     * Filters the route collection against a request and returns all matching
+     * routes.
+     *
+     * @param RouteCollection $collection The collection against which to match.
+     * @param Request         $request    A Request object against which to match.
+     *
+     * @return RouteCollection A non-empty RouteCollection of matched routes.
+     *
+     * @throws ResourceNotFoundException if none of the routes in $collection
+     *      matches $request. This is a performance optimization to not continue
+     *      the match process when a match will no longer be possible.
+     */
+    public function filter(RouteCollection $collection, Request $request);
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/UrlMatcher.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/UrlMatcher.php
new file mode 100644
index 0000000..09bb2c4
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/NestedMatcher/UrlMatcher.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\NestedMatcher;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Matcher\UrlMatcher as SymfonyUrlMatcher;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RequestContext;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+/**
+ * Extended UrlMatcher to provide an additional interface and enhanced features.
+ *
+ * This class requires Symfony 2.2 for a refactoring done to the symfony UrlMatcher
+ *
+ * @author Larry Garfield
+ */
+class UrlMatcher extends SymfonyUrlMatcher implements FinalMatcherInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function finalMatch(RouteCollection $collection, Request $request)
+    {
+        $this->routes = $collection;
+        $context = new RequestContext();
+        $context->fromRequest($request);
+        $this->setContext($context);
+        return $this->match($request->getPathInfo());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getAttributes(Route $route, $name, array $attributes)
+    {
+        if ($route instanceof RouteObjectInterface && is_string($route->getRouteKey())) {
+            $name = $route->getRouteKey();
+        }
+        $attributes['_route_name'] = $name;
+        $attributes['_route'] = $route;
+        return $this->mergeDefaults($attributes, $route->getDefaults());
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ProviderBasedGenerator.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ProviderBasedGenerator.php
new file mode 100644
index 0000000..1c353f8
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/ProviderBasedGenerator.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+use Symfony\Component\Routing\Route as SymfonyRoute;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+
+use Symfony\Cmf\Component\Routing\RouteProviderInterface;
+
+/**
+ * A Generator that uses a RouteProvider rather than a RouteCollection
+ *
+ * @author Larry Garfield
+ */
+class ProviderBasedGenerator extends UrlGenerator
+{
+    /**
+     * The route provider for this generator.
+     *
+     * @var RouteProviderInterface
+     */
+    protected $provider;
+
+    public function __construct(RouteProviderInterface $provider, LoggerInterface $logger = null)
+    {
+        $this->provider = $provider;
+        $this->logger = $logger;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function generate($name, $parameters = array(), $absolute = false)
+    {
+        if ($name instanceof SymfonyRoute) {
+            $route = $name;
+        } elseif (null === $route = $this->provider->getRouteByName($name, $parameters)) {
+            throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
+        }
+
+        // the Route has a cache of its own and is not recompiled as long as it does not get modified
+        $compiledRoute = $route->compile();
+
+        // handle symfony 2.1 and 2.2
+        // getHostnameTokens exists only since 2.2
+        $hostnameTokens = null;
+        if (method_exists($compiledRoute, 'getHostnameTokens')) {
+            $hostnameTokens = $compiledRoute->getHostnameTokens();
+        }
+
+        return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute, $hostnameTokens);
+    }
+
+    /**
+     * Support a route object and any string as route name
+     *
+     * {@inheritDoc}
+     */
+    public function supports($name)
+    {
+        return is_string($name) || $name instanceof SymfonyRoute;
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/README.md b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/README.md
new file mode 100644
index 0000000..607fb72
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/README.md
@@ -0,0 +1,7 @@
+# Symfony CMF Routing Component [![Build Status](https://secure.travis-ci.org/symfony-cmf/Routing.png)](http://travis-ci.org/symfony-cmf/Routing)
+
+This library extends the Symfony2 Routing component. Even though it has Symfony
+in its name, it does not need the full Symfony2 framework and can be used in
+standalone projects.
+
+http://symfony.com/doc/master/cmf/components/routing.html
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RedirectRouteInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RedirectRouteInterface.php
new file mode 100644
index 0000000..c4ac783
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RedirectRouteInterface.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+/**
+ * Document for redirection entries with the RedirectController.
+ *
+ * Defines additional methods needed by the RedirectController to redirect
+ * based on the route.
+ *
+ * This document may define (in order of precedence - the others can be empty):
+ *
+ * - uri: an absolute uri
+ * - routeName and routeParameters: to be used with the standard symfony router
+ *   or a route entry in the routeParameters for the DynamicRouter. Precedency
+ *   between these is determined by the order of the routers in the chain
+ *   router.
+ *
+ * With standard Symfony routing, you can just use uri / routeName and a
+ * hashmap of parameters.
+ *
+ * For the dynamic router, you can return a RouteInterface instance in the
+ * field 'route' of the parameters.
+ *
+ * Note: getRedirectContent must return the redirect route itself for the
+ * integration with DynamicRouter to work.
+ *
+ * @author David Buchmann <david@liip.ch>
+ */
+interface RedirectRouteInterface extends RouteObjectInterface
+{
+    /**
+     * Get the absolute uri to redirect to external domains.
+     *
+     * If this is non-empty, the other methods won't be used.
+     *
+     * @return string target absolute uri
+     */
+    public function getUri();
+
+    /**
+     * Get the target route document this route redirects to.
+     *
+     * If non-null, it is added as route into the parameters, which will lead
+     * to have the generate call issued by the RedirectController to have
+     * the target route in the parameters.
+     *
+     * @return RouteObjectInterface the route this redirection points to
+     */
+    public function getRouteTarget();
+
+    /**
+     * Get the name of the target route for working with the symfony standard
+     * router.
+     *
+     * @return string target route name
+     */
+    public function getRouteName();
+
+    /**
+     * Whether this should be a permanent or temporary redirect
+     *
+     * @return boolean
+     */
+    public function isPermanent();
+
+    /**
+     * Get the parameters for the target route router::generate()
+     *
+     * Note that for the DynamicRouter, you return the target route
+     * document as field 'route' of the hashmap.
+     *
+     * @return array Information to build the route
+     */
+    public function getParameters();
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteAwareInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteAwareInterface.php
new file mode 100644
index 0000000..59983c6
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteAwareInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+/**
+ * Interface to be implemented by content that wants to be compatible with the
+ * DynamicRouter
+ */
+interface RouteAwareInterface
+{
+    /**
+     * Get the routes that point to this content.
+     *
+     * Note: For PHPCR, as explained in RouteObjectInterface the route must use
+     * the routeContent field to store the reference to the content so you can
+     * get the routes with Referrers(filter="routeContent")
+     *
+     * @return \Symfony\Component\Routing\Route[] Route instances that point to this content
+     */
+    public function getRoutes();
+}
+
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteObjectInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteObjectInterface.php
new file mode 100644
index 0000000..496e3cc
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteObjectInterface.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+/**
+ * Classes for entries in the routing table may implement this interface in
+ * addition to extending Symfony\Component\Routing\Route.
+ *
+ * If they do, the DynamicRouter will request the route content and put it into
+ * the RouteObjectInterface::CONTENT_OBJECT field. The DynamicRouter will also
+ * request getRouteKey and this will be used instead of the symfony core compatible
+ * route name and can contain any characters.
+ *
+ * Some fields in defaults have a special meaning in the getDefaults(). In addition
+ * to the constants defined in this class, _locale and _controller are also used.
+ */
+interface RouteObjectInterface
+{
+    /**
+     * Constant for the field that is given to the ControllerAliasMapper.
+     * The value must be configured in the controllers_by_alias mapping.
+     *
+     * This is ignored if a _controller default value is provided as well
+     */
+    const CONTROLLER_ALIAS = '_controller_alias';
+
+    /**
+     * Field name for an explicit controller name to be used with this route
+     */
+    const CONTROLLER_NAME = '_controller';
+
+    /**
+     * Field name for an explicit template to be used with this route.
+     * i.e. SymfonyCmfContentBundle:StaticContent:index.html.twig
+     */
+    const TEMPLATE_NAME = '_template';
+
+    /**
+     * Field name for the content of the current route, if any.
+     */
+    const CONTENT_OBJECT = '_content';
+
+    /**
+     * Get the content document this route entry stands for. If non-null,
+     * the ControllerClassMapper uses it to identify a controller and
+     * the content is passed to the controller.
+     *
+     * If there is no specific content for this url (i.e. its an "application"
+     * page), may return null.
+     *
+     * To interoperate with the standard Symfony\Cmf\Bundle\ContentBundle\Document\StaticContent
+     * the instance MUST store the property in the field <code>routeContent</code>
+     * to have referrer resolution work.
+     *
+     * @return object the document or entity this route entry points to
+     */
+    public function getRouteContent();
+
+    /**
+     * Get the route key.
+     *
+     * This key will be used as route name instead of the symfony core compatible
+     * route name and can contain any characters.
+     *
+     * Return null if you want to use the default key.
+     *
+     * @return string the route name
+     */
+    public function getRouteKey();
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteProviderInterface.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteProviderInterface.php
new file mode 100644
index 0000000..de9b646
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/RouteProviderInterface.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface for the route provider the DynamicRouter is using.
+ *
+ * Typically this could be a doctrine orm or odm repository, but you can
+ * implement something else if you need to.
+ */
+interface RouteProviderInterface
+{
+    /**
+     * Finds routes that may potentially match the request.
+     *
+     * This may return a mixed list of class instances, but all routes returned
+     * must extend the core symfony route. The classes may also implement
+     * RouteObjectInterface to link to a content document.
+     *
+     * This method may not throw an exception based on implementation specific
+     * restrictions on the url. That case is considered a not found - returning
+     * an empty array. Exceptions are only used to abort the whole request in
+     * case something is seriously broken, like the storage backend being down.
+     *
+     * Note that implementations may not implement an optimal matching
+     * algorithm, simply a reasonable first pass.  That allows for potentially
+     * very large route sets to be filtered down to likely candidates, which
+     * may then be filtered in memory more completely.
+     *
+     * @param Request $request A request against which to match.
+     *
+     * @return \Symfony\Component\Routing\RouteCollection with all urls that
+     *      could potentially match $request. Empty collection if nothing can
+     *      match.
+     */
+    public function getRouteCollectionForRequest(Request $request);
+
+    /**
+     * Find the route using the provided route name (and parameters)
+     *
+     * @param string $name the route name to fetch
+     * @param array $parameters the parameters as they are passed to the
+     *      UrlGeneratorInterface::generate call
+     *
+     * @return \Symfony\Component\Routing\Route
+     *
+     * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException if
+     *      there is no route with that name in this repository
+     */
+    public function getRouteByName($name, $parameters = array());
+
+    /**
+     * Find many routes by their names using the provided list of names
+     *
+     * Note that this method may not throw an exception if some of the routes
+     * are not found. It will just return the list of those routes it found.
+     *
+     * This method exists in order to allow performance optimizations. The
+     * simple implementation could be to just repeatedly call
+     * $this->getRouteByName()
+     *
+     * @param array $names the list of names to retrieve
+     * @param array $parameters the parameters as they are passed to the
+     *      UrlGeneratorInterface::generate call. (Only one array, not one for
+     *      each entry in $names.
+     *
+     * @return \Symfony\Component\Routing\Route[] iterable thing with the keys
+     *      the names of the $names argument.
+     */
+    public function getRoutesByNames($names, $parameters = array());
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Test/CmfUnitTestCase.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Test/CmfUnitTestCase.php
new file mode 100644
index 0000000..7898238
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Test/CmfUnitTestCase.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Test;
+
+class CmfUnitTestCase extends \PHPUnit_Framework_TestCase
+{
+
+    protected function buildMock($class, array $methods = array())
+    {
+        return $this->getMockBuilder($class)
+                ->disableOriginalConstructor()
+                ->setMethods($methods)
+                ->getMock();
+    }
+
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldByClassEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldByClassEnhancerTest.php
new file mode 100644
index 0000000..d7eb0a8
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldByClassEnhancerTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Enhancer;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+use Symfony\Cmf\Component\Routing\Enhancer\FieldByClassEnhancer;
+
+class FieldByClassEnhancerTest extends CmfUnitTestCase
+{
+    private $request;
+    /**
+     * @var FieldByClassEnhancer
+     */
+    private $mapper;
+    private $document;
+
+    public function setUp()
+    {
+        $this->document = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Enhancer\\RouteObject');
+
+        $mapping = array('Symfony\\Cmf\\Component\\Routing\\Tests\\Enhancer\\RouteObject'
+                            => 'symfony_cmf_content.controller:indexAction');
+
+        $this->mapper = new FieldByClassEnhancer('_content', '_controller', $mapping);
+
+        $this->request = Request::create('/test');
+    }
+
+    public function testClassFoundInMapping()
+    {
+        // this is the mock, thus a child class to make sure we properly check with instanceof
+        $defaults = array('_content' => $this->document);
+        $expected = array(
+            '_content' => $this->document,
+            '_controller' => 'symfony_cmf_content.controller:indexAction',
+        );
+        $this->assertEquals($expected, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testFieldAlreadyThere()
+    {
+        $defaults = array(
+            '_content' => $this->document,
+            '_controller' => 'custom.controller:indexAction',
+        );
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testClassNotFoundInMapping()
+    {
+        $defaults = array('_content' => $this);
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testNoClass()
+    {
+        $defaults = array('foo' => 'bar');
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldMapEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldMapEnhancerTest.php
new file mode 100644
index 0000000..982467a
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldMapEnhancerTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Mapper;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+use Symfony\Cmf\Component\Routing\Enhancer\FieldMapEnhancer;
+
+class FieldMapEnhancerTest extends CmfUnitTestCase
+{
+    /**
+     * @var Request
+     */
+    private $request;
+
+    /**
+     * @var FieldMapEnhancer
+     */
+    private $enhancer;
+
+    public function setUp()
+    {
+        $this->request = Request::create('/test');
+        $mapping = array('static_pages' => 'symfony_cmf_content.controller:indexAction');
+
+        $this->enhancer = new FieldMapEnhancer('type', '_controller', $mapping);
+    }
+
+    public function testFieldFoundInMapping()
+    {
+        $defaults = array('type' => 'static_pages');
+        $expected = array(
+            'type' => 'static_pages',
+            '_controller' => 'symfony_cmf_content.controller:indexAction',
+        );
+        $this->assertEquals($expected, $this->enhancer->enhance($defaults, $this->request));
+    }
+
+    public function testFieldAlreadyThere()
+    {
+        $defaults = array(
+            'type' => 'static_pages',
+            '_controller' => 'custom.controller:indexAction',
+        );
+        $this->assertEquals($defaults, $this->enhancer->enhance($defaults, $this->request));
+    }
+
+
+    public function testNoType()
+    {
+        $defaults = array();
+        $this->assertEquals(array(), $this->enhancer->enhance($defaults, $this->request));
+    }
+
+    public function testNotFoundInMapping()
+    {
+        $defaults = array('type' => 'unknown_route');
+        $this->assertEquals($defaults, $this->enhancer->enhance($defaults, $this->request));
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldPresenceEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldPresenceEnhancerTest.php
new file mode 100644
index 0000000..78c3e10
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/FieldPresenceEnhancerTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Enhancer;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Cmf\Component\Routing\Enhancer\FieldPresenceEnhancer;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class FieldPresenceEnhancerTest extends CmfUnitTestCase
+{
+    /**
+     * @var FieldPresenceEnhancer
+     */
+    private $mapper;
+    private $request;
+
+    public function setUp()
+    {
+        $this->mapper = new FieldPresenceEnhancer('_template', '_controller', 'symfony_cmf_content.controller:indexAction');
+
+        $this->request = Request::create('/test');
+    }
+
+    public function testHasTemplate()
+    {
+        $defaults = array('_template' => 'Bundle:Topic:template.html.twig');
+        $expected = array(
+            '_template' => 'Bundle:Topic:template.html.twig',
+            '_controller' => 'symfony_cmf_content.controller:indexAction',
+        );
+        $this->assertEquals($expected, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testFieldAlreadyThere()
+    {
+        $defaults = array(
+            '_template' => 'Bundle:Topic:template.html.twig',
+            '_controller' => 'custom.controller:indexAction',
+        );
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testHasNoTemplate()
+    {
+        $defaults = array('foo' => 'bar');
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteContentEnhancerTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteContentEnhancerTest.php
new file mode 100644
index 0000000..df9008a
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteContentEnhancerTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Enhancer;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\Enhancer\RouteContentEnhancer;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class RouteContentEnhancerTest extends CmfUnitTestCase
+{
+    /**
+     * @var RouteContentEnhancer
+     */
+    private $mapper;
+    private $document;
+    private $request;
+
+    public function setUp()
+    {
+        $this->document = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Enhancer\\RouteObject',
+                                            array('getRouteContent', 'getRouteDefaults', 'getUrl'));
+
+        $this->mapper = new RouteContentEnhancer('_route', '_content');
+
+        $this->request = Request::create('/test');
+    }
+
+    public function testContent()
+    {
+        $targetDocument = new TargetDocument();
+        $this->document->expects($this->once())
+            ->method('getRouteContent')
+            ->will($this->returnValue($targetDocument));
+
+        $defaults = array('_route' => $this->document);
+        $expected = array('_route' => $this->document, '_content' => $targetDocument);
+
+        $this->assertEquals($expected, $this->mapper->enhance($defaults, $this->request));
+    }
+
+
+    public function testFieldAlreadyThere()
+    {
+        $this->document->expects($this->never())
+            ->method('getRouteContent')
+        ;
+
+        $defaults = array('_route' => $this->document, '_content' => 'foo');
+
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testNoContent()
+    {
+        $this->document->expects($this->once())
+            ->method('getRouteContent')
+            ->will($this->returnValue(null));
+
+        $defaults = array('_route' => $this->document);
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+
+    public function testNoCmfRoute()
+    {
+        $defaults = array('_route' => $this->buildMock('Symfony\\Component\\Routing\\Route'));
+        $this->assertEquals($defaults, $this->mapper->enhance($defaults, $this->request));
+    }
+}
+
+class TargetDocument
+{
+}
+
+class UnknownDocument
+{
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteObject.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteObject.php
new file mode 100644
index 0000000..6109339
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Enhancer/RouteObject.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Enhancer;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+/**
+ * Empty abstract class to be able to mock an object that both extends Route
+ * and implements RouteObjectInterface
+ */
+abstract class RouteObject extends Route implements RouteObjectInterface
+{
+    public function getRouteKey()
+    {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/ConfigurableUrlMatcherTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/ConfigurableUrlMatcherTest.php
new file mode 100644
index 0000000..18bcae7
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/ConfigurableUrlMatcherTest.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\NestedMatcher;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Cmf\Component\Routing\NestedMatcher\ConfigurableUrlMatcher;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class ConfigurableUrlMatcherTest extends CmfUnitTestCase
+{
+    protected $routeDocument;
+    protected $routeCompiled;
+    protected $matcher;
+    protected $context;
+    protected $request;
+
+    protected $url = '/foo/bar';
+
+    public function setUp()
+    {
+        $this->routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'getRouteKey', 'compile'));
+        $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+
+        $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext');
+        $this->request = Request::create($this->url);
+
+        $this->matcher = new ConfigurableUrlMatcher();
+    }
+
+    public function testMatch()
+    {
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue($this->url))
+        ;
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getRegex')
+            ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#'))
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('getDefaults')
+            ->will($this->returnValue(array('foo' => 'bar')))
+        ;
+
+        $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+        $mockCompiled->expects($this->any())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue('/no/match'))
+        ;
+        $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $mockRoute->expects($this->any())
+            ->method('compile')
+            ->will($this->returnValue($mockCompiled))
+        ;
+        $routeCollection = new RouteCollection();
+        $routeCollection->add('some', $mockRoute);
+        $routeCollection->add('_company_more', $this->routeDocument);
+        $routeCollection->add('other', $mockRoute);
+
+        $results = $this->matcher->finalMatch($routeCollection, $this->request);
+
+        $expected = array(
+            '_route_name' => '_company_more',
+            '_route' => $this->routeDocument,
+            'foo' => 'bar',
+        );
+
+        $this->assertEquals($expected, $results);
+    }
+
+    public function testMatchNoRouteObject()
+    {
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue($this->url))
+        ;
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getRegex')
+            ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#'))
+        ;
+        $this->routeDocument = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+        $this->routeDocument->expects($this->never())
+            ->method('getRouteKey')
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('getDefaults')
+            ->will($this->returnValue(array('foo' => 'bar')))
+        ;
+
+        $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+        $mockCompiled->expects($this->any())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue('/no/match'))
+        ;
+        $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $mockRoute->expects($this->any())
+            ->method('compile')
+            ->will($this->returnValue($mockCompiled))
+        ;
+        $routeCollection = new RouteCollection();
+        $routeCollection->add('some', $mockRoute);
+        $routeCollection->add('_company_more', $this->routeDocument);
+        $routeCollection->add('other', $mockRoute);
+
+        $results = $this->matcher->finalMatch($routeCollection, $this->request);
+
+        $expected = array(
+            '_route_name' => '_company_more',
+            '_route' => $this->routeDocument,
+            'foo' => 'bar',
+        );
+
+        $this->assertEquals($expected, $results);
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/NestedMatcherTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/NestedMatcherTest.php
new file mode 100644
index 0000000..2bcee0e
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/NestedMatcherTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\NestedMatcher;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+use Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class NestedMatcherTest extends CmfUnitTestCase
+{
+    private $provider;
+    private $routeFilter1;
+    private $routeFilter2;
+    private $finalMatcher;
+
+    public function setUp()
+    {
+        $this->provider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface");
+        $this->routeFilter1 = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\NestedMatcher\\RouteFilterInterface');
+        $this->routeFilter2 = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\NestedMatcher\\RouteFilterInterface');
+        $this->finalMatcher = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\NestedMatcher\\FinalMatcherInterface');
+    }
+
+    public function testNestedMatcher()
+    {
+        $request = Request::create('/path/one');
+        $routeCollection = new RouteCollection();
+        $route = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $routeCollection->add('route', $route);
+
+        $this->provider->expects($this->once())
+            ->method('getRouteCollectionForRequest')
+            ->with($request)
+            ->will($this->returnValue($routeCollection))
+        ;
+        $this->routeFilter1->expects($this->once())
+            ->method('filter')
+            ->with($routeCollection, $request)
+            ->will($this->returnValue($routeCollection))
+        ;
+        $this->routeFilter2->expects($this->once())
+            ->method('filter')
+            ->with($routeCollection, $request)
+            ->will($this->returnValue($routeCollection))
+        ;
+        $this->finalMatcher->expects($this->once())
+            ->method('finalMatch')
+            ->with($routeCollection, $request)
+            ->will($this->returnValue(array('foo' => 'bar')))
+        ;
+
+        $matcher = new NestedMatcher($this->provider);
+        $matcher->addRouteFilter($this->routeFilter1);
+        $matcher->addRouteFilter($this->routeFilter2);
+        $matcher->setFinalMatcher($this->finalMatcher);
+
+        $attributes = $matcher->matchRequest($request);
+
+        $this->assertEquals(array('foo' => 'bar'), $attributes);
+    }
+
+    /**
+     * Test priorities and exception handling
+     */
+    public function testNestedMatcherPriority()
+    {
+        $request = Request::create('/path/one');
+        $routeCollection = new RouteCollection();
+        $route = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $routeCollection->add('route', $route);
+
+        $wrongProvider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface");
+        $wrongProvider->expects($this->never())
+            ->method('getRouteCollectionForRequest')
+        ;
+        $this->provider->expects($this->once())
+            ->method('getRouteCollectionForRequest')
+            ->with($request)
+            ->will($this->returnValue($routeCollection))
+        ;
+        $this->routeFilter1->expects($this->once())
+            ->method('filter')
+            ->with($routeCollection, $request)
+            ->will($this->throwException(new ResourceNotFoundException()))
+        ;
+        $this->routeFilter2->expects($this->never())
+            ->method('filter')
+        ;
+        $this->finalMatcher->expects($this->never())
+            ->method('finalMatch')
+        ;
+
+        $matcher = new NestedMatcher($wrongProvider);
+        $matcher->setRouteProvider($this->provider);
+        $matcher->addRouteFilter($this->routeFilter2, 10);
+        $matcher->addRouteFilter($this->routeFilter1, 20);
+        $matcher->setFinalMatcher($this->finalMatcher);
+
+        try {
+            $matcher->matchRequest($request);
+            fail('nested matcher is eating exception');
+        } catch(ResourceNotFoundException $e)
+        {
+            // expected
+        }
+    }
+
+    public function testProviderNoMatch()
+    {
+        $request = Request::create('/path/one');
+        $routeCollection = new RouteCollection();
+        $this->provider->expects($this->once())
+            ->method('getRouteCollectionForRequest')
+            ->with($request)
+            ->will($this->returnValue($routeCollection))
+        ;
+        $this->finalMatcher->expects($this->never())
+            ->method('finalMatch')
+        ;
+
+        $matcher = new NestedMatcher($this->provider);
+        $matcher->setFinalMatcher($this->finalMatcher);
+
+        $this->setExpectedException('Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException');
+        $matcher->matchRequest($request);
+    }
+
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/UrlMatcherTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/UrlMatcherTest.php
new file mode 100644
index 0000000..08a1dec
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/NestedMatcher/UrlMatcherTest.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\NestedMatcher;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class UrlMatcherTest extends CmfUnitTestCase
+{
+    protected $routeDocument;
+    protected $routeCompiled;
+    protected $matcher;
+    protected $context;
+    protected $request;
+
+    protected $url = '/foo/bar';
+
+    public function setUp()
+    {
+        $reflection = new \ReflectionClass('Symfony\\Component\\Routing\\Matcher\\UrlMatcher');
+        if (! $reflection->hasMethod('getAttributes')) {
+            $this->markTestSkipped('This only works with symfony 2.2');
+        }
+
+        $this->routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'getRouteKey', 'compile'));
+        $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+
+        $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext');
+        $this->request = Request::create($this->url);
+
+        $this->matcher = new UrlMatcher(new RouteCollection(), $this->context);
+    }
+
+    public function testMatchRouteKey()
+    {
+        $this->doTestMatchRouteKey($this->url);
+    }
+
+    public function testMatchNoKey()
+    {
+        $this->doTestMatchRouteKey(null);
+    }
+
+    public function doTestMatchRouteKey($routeKey)
+    {
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue($this->url))
+        ;
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getRegex')
+            ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#'))
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('getRouteKey')
+            ->will($this->returnValue($routeKey))
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('getDefaults')
+            ->will($this->returnValue(array('foo' => 'bar')))
+        ;
+
+        $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+        $mockCompiled->expects($this->any())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue('/no/match'))
+        ;
+        $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $mockRoute->expects($this->any())
+            ->method('compile')
+            ->will($this->returnValue($mockCompiled))
+        ;
+        $routeCollection = new RouteCollection();
+        $routeCollection->add('some', $mockRoute);
+        $routeCollection->add('_company_more', $this->routeDocument);
+        $routeCollection->add('other', $mockRoute);
+
+        $results = $this->matcher->finalMatch($routeCollection, $this->request);
+
+        $expected = array(
+            '_route_name' => ($routeKey) ? $routeKey : '_company_more',
+            '_route' => $this->routeDocument,
+            'foo' => 'bar',
+        );
+
+        $this->assertEquals($expected, $results);
+    }
+
+    public function testMatchNoRouteObject()
+    {
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue($this->url))
+        ;
+        $this->routeCompiled->expects($this->atLeastOnce())
+            ->method('getRegex')
+            ->will($this->returnValue('#'.str_replace('/', '\\/', $this->url).'#'))
+        ;
+        $this->routeDocument = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+        $this->routeDocument->expects($this->never())
+            ->method('getRouteKey')
+        ;
+        $this->routeDocument->expects($this->atLeastOnce())
+            ->method('getDefaults')
+            ->will($this->returnValue(array('foo' => 'bar')))
+        ;
+
+        $mockCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+        $mockCompiled->expects($this->any())
+            ->method('getStaticPrefix')
+            ->will($this->returnValue('/no/match'))
+        ;
+        $mockRoute = $this->getMockBuilder('Symfony\\Component\\Routing\\Route')->disableOriginalConstructor()->getMock();
+        $mockRoute->expects($this->any())
+            ->method('compile')
+            ->will($this->returnValue($mockCompiled))
+        ;
+        $routeCollection = new RouteCollection();
+        $routeCollection->add('some', $mockRoute);
+        $routeCollection->add('_company_more', $this->routeDocument);
+        $routeCollection->add('other', $mockRoute);
+
+        $results = $this->matcher->finalMatch($routeCollection, $this->request);
+
+        $expected = array(
+            '_route_name' => '_company_more',
+            '_route' => $this->routeDocument,
+            'foo' => 'bar',
+        );
+
+        $this->assertEquals($expected, $results);
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ChainRouterTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ChainRouterTest.php
new file mode 100644
index 0000000..d76c1f9
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ChainRouterTest.php
@@ -0,0 +1,617 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Routing;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Cmf\Component\Routing\ChainRouter;
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class ChainRouterTest extends CmfUnitTestCase
+{
+    public function setUp()
+    {
+        $this->router = new ChainRouter($this->getMock('Symfony\Component\HttpKernel\Log\LoggerInterface'));
+        $this->context = $this->getMock('Symfony\\Component\\Routing\\RequestContext');
+    }
+
+    public function testPriority()
+    {
+        $this->assertEquals(array(), $this->router->all());
+
+        list($low, $high) = $this->createRouterMocks();
+
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->assertEquals(array(
+            $high,
+            $low,
+        ), $this->router->all());
+    }
+
+    /**
+     * Routers are supposed to be sorted only once.
+     * This test will check that by trying to get all routers several times.
+     *
+     * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters
+     * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all
+     */
+    public function testSortRouters()
+    {
+        list($low, $medium, $high) = $this->createRouterMocks();
+        // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once.
+        $router = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\ChainRouter', array('sortRouters'));
+        $router
+            ->expects($this->once())
+            ->method('sortRouters')
+            ->will(
+                $this->returnValue(
+                    array($high, $medium, $low)
+                )
+            )
+        ;
+
+        $router->add($low, 10);
+        $router->add($medium, 50);
+        $router->add($high, 100);
+        $expectedSortedRouters = array($high, $medium, $low);
+        // Let's get all routers 5 times, we should only sort once.
+        for ($i = 0; $i < 5; ++$i) {
+            $this->assertSame($expectedSortedRouters, $router->all());
+        }
+    }
+
+    /**
+     * This test ensures that if a router is being added on the fly, the sorting is reset.
+     *
+     * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters
+     * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all
+     * @covers \Symfony\Cmf\Component\Routing\ChainRouter::add
+     */
+    public function testReSortRouters()
+    {
+        list($low, $medium, $high) = $this->createRouterMocks();
+        $highest = clone $high;
+        // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once.
+        $router = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\ChainRouter', array('sortRouters'));
+        $router
+            ->expects($this->at(0))
+            ->method('sortRouters')
+            ->will(
+                $this->returnValue(
+                    array($high, $medium, $low)
+                )
+            )
+        ;
+        // The second time sortRouters() is called, we're supposed to get the newly added router ($highest)
+        $router
+            ->expects($this->at(1))
+            ->method('sortRouters')
+            ->will(
+                $this->returnValue(
+                    array($highest, $high, $medium, $low)
+                )
+            )
+        ;
+
+        $router->add($low, 10);
+        $router->add($medium, 50);
+        $router->add($high, 100);
+        $this->assertSame(array($high, $medium, $low), $router->all());
+
+        // Now adding another router on the fly, sorting must have been reset
+        $router->add($highest, 101);
+        $this->assertSame(array($highest, $high, $medium, $low), $router->all());
+    }
+
+    /**
+     * context must be propagated to chained routers and be stored locally
+     */
+    public function testContext()
+    {
+        list($low, $high) = $this->createRouterMocks();
+
+        $low
+            ->expects($this->once())
+            ->method('setContext')
+            ->with($this->context)
+        ;
+
+        $high
+            ->expects($this->once())
+            ->method('setContext')
+            ->with($this->context)
+        ;
+
+
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->setContext($this->context);
+        $this->assertSame($this->context, $this->router->getContext());
+    }
+
+    /**
+     * context must be propagated also when routers are added after context is set
+     */
+    public function testContextOrder()
+    {
+        list($low, $high) = $this->createRouterMocks();
+
+        $low
+            ->expects($this->once())
+            ->method('setContext')
+            ->with($this->context)
+        ;
+
+        $high
+            ->expects($this->once())
+            ->method('setContext')
+            ->with($this->context)
+        ;
+
+        $this->router->setContext($this->context);
+
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->all();
+
+        $this->assertSame($this->context, $this->router->getContext());
+    }
+
+    /**
+     * The first usable match is used, no further routers are queried once a match is found
+     */
+    public function testMatch()
+    {
+        $url = '/test';
+        list($lower, $low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->returnValue(array('test')))
+        ;
+        $lower
+            ->expects($this->never())
+            ->method('match');
+        $this->router->add($lower, 5);
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $result = $this->router->match('/test');
+        $this->assertEquals(array('test'), $result);
+    }
+
+    /**
+     * The first usable match is used, no further routers are queried once a match is found
+     */
+    public function testMatchRequest()
+    {
+        $url = '/test';
+        list($lower, $low, $high) = $this->createRouterMocks();
+
+        $highest = $this->getMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RequestMatcher');
+
+        $request = Request::create('/test');
+
+        $highest
+            ->expects($this->once())
+            ->method('matchRequest')
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->returnValue(array('test')))
+        ;
+        $lower
+            ->expects($this->never())
+            ->method('match')
+        ;
+
+        $this->router->add($lower, 5);
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+        $this->router->add($highest, 200);
+
+        $result = $this->router->matchRequest($request);
+        $this->assertEquals(array('test'), $result);
+    }
+
+    /**
+     * If there is a method not allowed but another router matches, that one is used
+     */
+    public function testMatchAndNotAllowed()
+    {
+        $url = '/test';
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->returnValue(array('test')))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $result = $this->router->match('/test');
+        $this->assertEquals(array('test'), $result);
+    }
+
+    /**
+     * If there is a method not allowed but another router matches, that one is used
+     */
+    public function testMatchRequestAndNotAllowed()
+    {
+        $url = '/test';
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->returnValue(array('test')))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $result = $this->router->matchRequest(Request::create('/test'));
+        $this->assertEquals(array('test'), $result);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testMatchNotFound()
+    {
+        $url = '/test';
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->match('/test');
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testMatchRequestNotFound()
+    {
+        $url = '/test';
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->matchRequest(Request::create('/test'));
+    }
+
+    /**
+     * If any of the routers throws a not allowed exception and no other matches, we need to see this
+     *
+     * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
+     */
+    public function testMatchMethodNotAllowed()
+    {
+        $url = '/test';
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->match('/test');
+    }
+
+    /**
+     * If any of the routers throws a not allowed exception and no other matches, we need to see this
+     *
+     * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
+     */
+    public function testMatchRequestMethodNotAllowed()
+    {
+        $url = '/test';
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('match')
+            ->with($url)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->matchRequest(Request::create('/test'));
+    }
+
+    public function testGenerate()
+    {
+        $url = '/test';
+        $name = 'test';
+        $parameters = array('test' => 'value');
+        list($lower, $low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, false)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\RouteNotFoundException()))
+        ;
+        $low
+            ->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, false)
+            ->will($this->returnValue($url))
+        ;
+        $lower
+            ->expects($this->never())
+            ->method('generate')
+        ;
+
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $result = $this->router->generate($name, $parameters);
+        $this->assertEquals($url, $result);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateNotFound()
+    {
+        $url = '/test';
+        $name = 'test';
+        $parameters = array('test' => 'value');
+        list($low, $high) = $this->createRouterMocks();
+
+        $high
+            ->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, false)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\RouteNotFoundException()))
+        ;
+        $low->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, false)
+            ->will($this->throwException(new \Symfony\Component\Routing\Exception\RouteNotFoundException()))
+        ;
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $result = $this->router->generate($name, $parameters);
+        $this->assertEquals($url, $result);
+    }
+
+    public function testGenerateObjectName()
+    {
+        $name = new \stdClass();
+        $parameters = array('test' => 'value');
+
+        $defaultRouter = $this->getMock('Symfony\\Component\\Routing\\RouterInterface');
+        $chainedRouter = $this->getMock('Symfony\\Cmf\\Component\\Routing\\ChainedRouterInterface');
+
+        $defaultRouter
+            ->expects($this->never())
+            ->method('generate')
+        ;
+        $chainedRouter
+            ->expects($this->once())
+            ->method('supports')
+            ->will($this->returnValue(true))
+        ;
+        $chainedRouter
+            ->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, false)
+            ->will($this->returnValue($name))
+        ;
+
+        $this->router->add($defaultRouter, 200);
+        $this->router->add($chainedRouter, 100);
+
+        $result = $this->router->generate($name, $parameters);
+        $this->assertEquals($name, $result);
+    }
+
+    public function testGenerateNonDefaultStringName()
+    {
+        $name = '/test/this';
+        $parameters = array('test' => 'value');
+
+        $defaultRouter = $this->getMock('Symfony\\Component\\Routing\\RouterInterface');
+        $chainedRouter = $this->getMock('Symfony\\Cmf\\Component\\Routing\\ChainedRouterInterface');
+
+        $defaultRouter
+            ->expects($this->never())
+            ->method('generate')
+        ;
+        $chainedRouter
+            ->expects($this->once())
+            ->method('supports')
+            ->will($this->returnValue(true))
+        ;
+        $chainedRouter
+            ->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, false)
+            ->will($this->returnValue($name))
+        ;
+
+        $this->router->add($defaultRouter, 200);
+        $this->router->add($chainedRouter, 100);
+
+        $result = $this->router->generate($name, $parameters);
+        $this->assertEquals($name, $result);
+    }
+
+    public function testWarmup()
+    {
+        $dir = 'test_dir';
+        list($low) = $this->createRouterMocks();
+
+        $low
+            ->expects($this->never())
+            ->method('warmUp')
+        ;
+        $high = $this->getMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\WarmableRouterMock');
+        $high
+            ->expects($this->once())
+            ->method('warmUp')
+            ->with($dir)
+        ;
+
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $this->router->warmUp($dir);
+    }
+
+    public function testRouteCollection()
+    {
+        list($low, $high) = $this->createRouterMocks();
+        $lowcol = new RouteCollection();
+        $lowcol->add('low', $this->buildMock('Symfony\\Component\\Routing\\Route'));
+        $highcol = new RouteCollection();
+        $highcol->add('high', $this->buildMock('Symfony\\Component\\Routing\\Route'));
+
+        $low
+            ->expects($this->once())
+            ->method('getRouteCollection')
+            ->will($this->returnValue($lowcol))
+        ;
+        $high
+            ->expects($this->once())
+            ->method('getRouteCollection')
+            ->will($this->returnValue($highcol))
+        ;
+
+        $this->router->add($low, 10);
+        $this->router->add($high, 100);
+
+        $collection = $this->router->getRouteCollection();
+        $this->assertInstanceOf('Symfony\\Component\\Routing\\RouteCollection', $collection);
+
+        $names = array();
+        foreach ($collection->all() as $name => $route) {
+            $this->assertInstanceOf('Symfony\\Component\\Routing\\Route', $route);
+            $names[] = $name;
+        }
+        $this->assertEquals(array('high', 'low'), $names);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testSupport()
+    {
+
+        $router = $this->getMock('Symfony\Cmf\Component\Routing\ChainedRouterInterface');
+        $router
+            ->expects($this->once())
+            ->method('supports')
+            ->will($this->returnValue(false))
+        ;
+
+        $router
+            ->expects($this->never())
+            ->method('generate')
+            ->will($this->returnValue(false))
+        ;
+
+        $this->router->add($router);
+
+        $this->router->generate('foobar');
+    }
+
+    protected function createRouterMocks()
+    {
+        return array(
+            $this->getMock('Symfony\\Component\\Routing\\RouterInterface'),
+            $this->getMock('Symfony\\Component\\Routing\\RouterInterface'),
+            $this->getMock('Symfony\\Component\\Routing\\RouterInterface'),
+        );
+    }
+}
+
+abstract class WarmableRouterMock implements \Symfony\Component\Routing\RouterInterface, \Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface
+{
+}
+
+abstract class RequestMatcher implements \Symfony\Component\Routing\RouterInterface, \Symfony\Component\Routing\Matcher\RequestMatcherInterface
+{
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ContentAwareGeneratorTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ContentAwareGeneratorTest.php
new file mode 100644
index 0000000..6ddf3c7
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ContentAwareGeneratorTest.php
@@ -0,0 +1,285 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Routing;
+
+use Symfony\Component\Routing\Route as SymfonyRoute;
+
+use Symfony\Cmf\Component\Routing\ContentAwareGenerator;
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class ContentAwareGeneratorTest extends CmfUnitTestCase
+{
+    protected $contentDocument;
+    protected $routeDocument;
+    protected $routeCompiled;
+    protected $provider;
+
+    protected $generator;
+    protected $context;
+
+    public function setUp()
+    {
+        $this->contentDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\RouteAwareInterface');
+        $this->routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile'));
+        $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+        $this->provider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface");
+        $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext');
+
+        $this->generator = new TestableContentAwareGenerator($this->provider);
+    }
+
+
+    public function testGenerateFromContent()
+    {
+        $this->provider->expects($this->never())
+            ->method('getRouteByName')
+        ;
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($this->routeDocument)))
+        ;
+        $this->routeDocument->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($this->contentDocument));
+    }
+
+    public function testGenerateFromContentId()
+    {
+        $this->provider->expects($this->never())
+            ->method('getRouteByName')
+        ;
+
+        $contentRepository = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\ContentRepositoryInterface", array('findById'));
+        $contentRepository->expects($this->once())
+            ->method('findById')
+            ->with('/content/id')
+            ->will($this->returnValue($this->contentDocument))
+        ;
+        $this->generator->setContentRepository($contentRepository);
+
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($this->routeDocument)))
+        ;
+
+        $this->routeDocument->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate('', array('content_id' => '/content/id')));
+    }
+
+    public function testGenerateEmptyRouteString()
+    {
+        $this->provider->expects($this->never())
+            ->method('getRouteByName')
+        ;
+
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($this->routeDocument)))
+        ;
+
+        $this->routeDocument->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate('', array('content'=>$this->contentDocument)));
+    }
+
+    public function testGenerateRouteMultilang()
+    {
+        $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile', 'getRouteContent'));
+        $route_en->setLocale('en');
+        $route_de = $this->routeDocument;
+        $route_de->setLocale('de');
+
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($route_en, $route_de)))
+        ;
+        $route_en->expects($this->once())
+            ->method('getRouteContent')
+            ->will($this->returnValue($this->contentDocument))
+        ;
+        $route_en->expects($this->never())
+            ->method('compile')
+        ;
+        $route_de->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($route_en, array('_locale' => 'de')));
+    }
+
+
+    public function testGenerateRouteMultilangNomatch()
+    {
+        $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile', 'getRouteContent'));
+        $route_en->setLocale('en');
+        $route_de = $this->routeDocument;
+        $route_de->setLocale('de');
+
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($route_en, $route_de)))
+        ;
+        $route_en->expects($this->once())
+            ->method('getRouteContent')
+            ->will($this->returnValue($this->contentDocument))
+        ;
+        $route_en->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+        $route_de->expects($this->never())
+            ->method('compile')
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($route_en, array('_locale' => 'fr')));
+    }
+
+    public function testGenerateNoncmfRouteMultilang()
+    {
+        $route_en = $this->buildMock('Symfony\\Component\\Routing\\Route', array('getDefaults', 'compile', 'getRouteContent'));
+
+        $route_en->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($route_en, array('_locale' => 'de')));
+    }
+
+    public function testGenerateRoutenameMultilang()
+    {
+        $name = 'foo/bar';
+        $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile', 'getRouteContent'));
+        $route_en->setLocale('en');
+        $route_de = $this->routeDocument;
+        $route_de->setLocale('de');
+
+        $this->provider->expects($this->once())
+            ->method('getRouteByName')
+            ->with($name)
+            ->will($this->returnValue($route_en))
+        ;
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($route_en, $route_de)))
+        ;
+        $route_en->expects($this->once())
+            ->method('getRouteContent')
+            ->will($this->returnValue($this->contentDocument))
+        ;
+        $route_en->expects($this->never())
+            ->method('compile')
+        ;
+        $route_de->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($name, array('_locale' => 'de')));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateRoutenameMultilangNotFound()
+    {
+        $name = 'foo/bar';
+
+        $this->provider->expects($this->once())
+            ->method('getRouteByName')
+            ->with($name)
+            ->will($this->returnValue(null))
+        ;
+
+        $this->generator->generate($name, array('_locale' => 'de'));
+    }
+
+    public function testGenerateDocumentMultilang()
+    {
+        $route_en = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults', 'compile'));
+        $route_en->setLocale('en');
+        $route_de = $this->routeDocument;
+        $route_de->setLocale('de');
+
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($route_en, $route_de)))
+        ;
+        $route_en->expects($this->never())
+            ->method('compile')
+        ;
+        $route_de->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate('', array('content'=>$this->contentDocument, '_locale' => 'de')));
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateNoContent()
+    {
+        $this->generator->generate('', array());
+    }
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateInvalidContent()
+    {
+        $this->generator->generate('', array('content' => $this));
+    }
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateNoRoutes()
+    {
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array()));
+
+        $this->generator->generate('', array('content'=>$this->contentDocument));
+    }
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateInvalidRoute()
+    {
+        $this->contentDocument->expects($this->once())
+            ->method('getRoutes')
+            ->will($this->returnValue(array($this)));
+
+        $this->generator->generate('', array('content'=>$this->contentDocument));
+    }
+
+    public function testSupports()
+    {
+        $this->assertTrue($this->generator->supports(''));
+        $this->assertTrue($this->generator->supports(null));
+        $this->assertTrue($this->generator->supports($this->contentDocument));
+        $this->assertFalse($this->generator->supports($this));
+    }
+}
+
+/**
+ * Overwrite doGenerate to reduce amount of mocking needed
+ */
+class TestableContentAwareGenerator extends ContentAwareGenerator
+{
+    protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute, $hostnameTokens = null)
+    {
+        return 'result_url';
+    }
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/DynamicRouterTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/DynamicRouterTest.php
new file mode 100644
index 0000000..af790a8
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/DynamicRouterTest.php
@@ -0,0 +1,241 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Cmf\Component\Routing\DynamicRouter;
+
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class DynamicRouterTest extends CmfUnitTestCase
+{
+    protected $routeDocument;
+    protected $matcher;
+    protected $generator;
+    protected $enhancer;
+    protected $router;
+    protected $context;
+    protected $request;
+
+    protected $url = '/foo/bar';
+
+    public function setUp()
+    {
+        $this->routeDocument = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Tests\\Routing\\RouteMock', array('getDefaults'));
+
+        $this->matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface');
+        $this->generator = $this->buildMock('Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface', array('supports', 'generate', 'setContext', 'getContext'));
+        $this->enhancer = $this->buildMock('Symfony\\Cmf\\Component\\Routing\\Enhancer\\RouteEnhancerInterface', array('enhance'));
+
+        $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext');
+        $this->request = Request::create($this->url);
+
+        $this->router = new DynamicRouter($this->context, $this->matcher, $this->generator);
+        $this->router->addRouteEnhancer($this->enhancer);
+    }
+
+    /**
+     * rather trivial, but we want 100% coverage
+     */
+    public function testContext()
+    {
+        $this->router->setContext($this->context);
+        $this->assertSame($this->context, $this->router->getContext());
+    }
+
+    public function testRouteCollection()
+    {
+        $collection = $this->router->getRouteCollection();
+        $this->assertInstanceOf('Symfony\\Component\\Routing\\RouteCollection', $collection);
+        // TODO: once this is implemented, check content of collection
+    }
+
+
+    /// generator tests ///
+
+    public function testGetGenerator()
+    {
+        $this->generator->expects($this->once())
+            ->method('setContext')
+            ->with($this->equalTo($this->context));
+
+        $generator = $this->router->getGenerator();
+        $this->assertInstanceOf('Symfony\Component\Routing\Generator\UrlGeneratorInterface', $generator);
+        $this->assertSame($this->generator, $generator);
+    }
+
+    public function testGenerate()
+    {
+        $name = 'my_route_name';
+        $parameters = array('foo' => 'bar');
+        $absolute = true;
+
+        $this->generator->expects($this->once())
+            ->method('generate')
+            ->with($name, $parameters, $absolute)
+            ->will($this->returnValue('http://test'))
+        ;
+
+        $url = $this->router->generate($name, $parameters, $absolute);
+        $this->assertEquals('http://test', $url);
+    }
+
+    public function testSupports()
+    {
+        $name = 'foo/bar';
+        $this->generator->expects($this->once())
+            ->method('supports')
+            ->with($this->equalTo($name))
+            ->will($this->returnValue(true))
+        ;
+
+        $this->assertTrue($this->router->supports($name));
+    }
+
+    /// match tests ///
+
+    public function testGetMatcher()
+    {
+        $this->matcher->expects($this->once())
+            ->method('setContext')
+            ->with($this->equalTo($this->context));
+
+        $matcher = $this->router->getMatcher();
+        $this->assertInstanceOf('Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface', $matcher);
+        $this->assertSame($this->matcher, $matcher);
+    }
+
+    public function testMatchUrl()
+    {
+        $routeDefaults = array('foo' => 'bar');
+        $this->matcher->expects($this->once())
+            ->method('match')
+            ->with($this->url)
+            ->will($this->returnValue($routeDefaults))
+        ;
+
+        $expected = array('this' => 'that');
+        $this->enhancer->expects($this->once())
+            ->method('enhance')
+            ->with($this->equalTo($routeDefaults), $this->equalTo($this->request))
+            ->will($this->returnValue($expected))
+        ;
+
+        $results = $this->router->match($this->url);
+
+        $this->assertEquals($expected, $results);
+    }
+
+
+    public function testMatchRequestWithUrlMatcher()
+    {
+        $routeDefaults = array('foo' => 'bar');
+
+        $this->matcher->expects($this->once())
+            ->method('match')
+            ->with($this->url)
+            ->will($this->returnValue($routeDefaults))
+        ;
+
+        $expected = array('this' => 'that');
+        $this->enhancer->expects($this->once())
+            ->method('enhance')
+            // somehow request object gets confused, check on instance only
+            ->with($this->equalTo($routeDefaults), $this->isInstanceOf('Symfony\\Component\\HttpFoundation\\Request'))
+            ->will($this->returnValue($expected))
+        ;
+
+        $results = $this->router->matchRequest($this->request);
+
+        $this->assertEquals($expected, $results);
+    }
+
+    public function testMatchRequest()
+    {
+        $routeDefaults = array('foo' => 'bar');
+
+        $matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface', array('matchRequest', 'setContext', 'getContext'));
+        $router = new DynamicRouter($this->context, $matcher, $this->generator);
+
+        $matcher->expects($this->once())
+            ->method('matchRequest')
+            ->with($this->request)
+            ->will($this->returnValue($routeDefaults))
+        ;
+
+        $expected = array('this' => 'that');
+        $this->enhancer->expects($this->once())
+            ->method('enhance')
+            ->with($this->equalTo($routeDefaults), $this->equalTo($this->request)) // TODO: why do we not get the right thing?
+            ->will($this->returnValue($expected))
+        ;
+
+        $router->addRouteEnhancer($this->enhancer);
+
+        $this->assertEquals($expected, $router->matchRequest($this->request));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testMatchFilter()
+    {
+        $router = new DynamicRouter($this->context, $this->matcher, $this->generator, '#/different/prefix.*#');
+        $router->addRouteEnhancer($this->enhancer);
+
+        $this->matcher->expects($this->never())
+            ->method('match')
+        ;
+
+        $this->enhancer->expects($this->never())
+            ->method('enhance')
+        ;
+
+        $router->match($this->url);
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testMatchRequestFilter()
+    {
+        $matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface', array('matchRequest', 'setContext', 'getContext'));
+
+        $router = new DynamicRouter($this->context, $matcher, $this->generator, '#/different/prefix.*#');
+        $router->addRouteEnhancer($this->enhancer);
+
+        $matcher->expects($this->never())
+            ->method('matchRequest')
+        ;
+
+        $this->enhancer->expects($this->never())
+            ->method('enhance')
+        ;
+
+        $router->matchRequest($this->request);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testMatchUrlWithRequestMatcher()
+    {
+        $matcher = $this->buildMock('Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface', array('matchRequest', 'setContext', 'getContext'));
+        $router = new DynamicRouter($this->context, $matcher, $this->generator);
+
+        $router->match($this->url);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testInvalidMatcher()
+    {
+        new DynamicRouter($this->context, $this, $this->generator);
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ProviderBasedGeneratorTest.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ProviderBasedGeneratorTest.php
new file mode 100644
index 0000000..509232d
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/ProviderBasedGeneratorTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Symfony\Cmf\Component\Routing\Tests\Routing;
+
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+use Symfony\Cmf\Component\Routing\ProviderBasedGenerator;
+use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
+
+class ProviderBasedGeneratorTest extends CmfUnitTestCase
+{
+    protected $routeDocument;
+    protected $routeCompiled;
+    protected $provider;
+
+    protected $generator;
+    protected $context;
+
+    public function setUp()
+    {
+        $this->routeDocument = $this->buildMock('Symfony\\Component\\Routing\\Route', array('getDefaults', 'compile'));
+        $this->routeCompiled = $this->buildMock('Symfony\\Component\\Routing\\CompiledRoute');
+        $this->provider = $this->buildMock("Symfony\\Cmf\\Component\\Routing\\RouteProviderInterface");
+        $this->context = $this->buildMock('Symfony\\Component\\Routing\\RequestContext');
+
+        $this->generator= new TestableProviderBasedGenerator($this->provider);
+    }
+
+    public function testGenerateFromName()
+    {
+        $name = 'foo/bar';
+
+        $this->provider->expects($this->once())
+            ->method('getRouteByName')
+            ->with($name)
+            ->will($this->returnValue($this->routeDocument))
+        ;
+        $this->routeDocument->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($name));
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateNotFound()
+    {
+        $name = 'foo/bar';
+
+        $this->provider->expects($this->once())
+            ->method('getRouteByName')
+            ->with($name)
+            ->will($this->returnValue(null))
+        ;
+
+        $this->generator->generate($name);
+    }
+
+    public function testGenerateFromRoute()
+    {
+        $this->provider->expects($this->never())
+            ->method('getRouteByName')
+        ;
+        $this->routeDocument->expects($this->once())
+            ->method('compile')
+            ->will($this->returnValue($this->routeCompiled))
+        ;
+
+        $this->assertEquals('result_url', $this->generator->generate($this->routeDocument));
+    }
+
+    public function testSupports()
+    {
+        $this->assertTrue($this->generator->supports('foo/bar'));
+        $this->assertTrue($this->generator->supports($this->routeDocument));
+        $this->assertFalse($this->generator->supports($this));
+    }
+}
+
+/**
+ * Overwrite doGenerate to reduce amount of mocking needed
+ */
+class TestableProviderBasedGenerator extends ProviderBasedGenerator
+{
+    protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute, $hostnameTokens = null)
+    {
+        return 'result_url';
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/RouteMock.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/RouteMock.php
new file mode 100644
index 0000000..e028d2c
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/Routing/RouteMock.php
@@ -0,0 +1,39 @@
+<?php
+namespace Symfony\Cmf\Component\Routing\Tests\Routing;
+
+use Symfony\Component\Routing\Route as SymfonyRoute;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+class RouteMock extends SymfonyRoute implements RouteObjectInterface
+{
+    private $locale;
+
+    public function setLocale($locale)
+    {
+        $this->locale = $locale;
+    }
+    public function getRouteContent()
+    {
+        return null;
+    }
+    public function getDefaults()
+    {
+        $defaults = array();
+        if (! is_null($this->locale)) {
+            $defaults['_locale'] = $this->locale;
+        }
+        return $defaults;
+    }
+    public function getRequirement($key)
+    {
+        if (! $key == '_locale') {
+            throw new \Exception;
+        }
+        return $this->locale;
+    }
+    public function getRouteKey()
+    {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/bootstrap.php b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/bootstrap.php
new file mode 100644
index 0000000..cd9b88e
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/Tests/bootstrap.php
@@ -0,0 +1,19 @@
+<?php
+
+$file = __DIR__.'/../vendor/autoload.php';
+if (!file_exists($file)) {
+    throw new RuntimeException('Install dependencies to run test suite.');
+}
+
+require_once $file;
+
+spl_autoload_register(function($class) {
+    if (0 === strpos($class, 'Symfony\\Cmf\\Component\\Routing\\')) {
+        $path = __DIR__.'/../'.implode('/', array_slice(explode('\\', $class), 4)).'.php';
+        if (!stream_resolve_include_path($path)) {
+            return false;
+        }
+        require_once $path;
+        return true;
+    }
+});
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/composer.json b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/composer.json
new file mode 100644
index 0000000..7f0a263
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/composer.json
@@ -0,0 +1,29 @@
+{
+    "name": "symfony-cmf/routing",
+    "type": "library",
+    "description": "Extends the Symfony2 routing component for dynamic routes and chaining several routers",
+    "keywords": ["routing", "database"],
+    "homepage": "http://cmf.symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Symfony CMF Community",
+            "homepage": "https://github.com/symfony-cmf/Routing/contributors"
+        }
+    ],
+    "minimum-stability": "dev",
+    "require": {
+        "php": ">=5.3.2",
+        "symfony/routing": ">=2.1,<2.3-dev",
+        "symfony/http-kernel": ">=2.1,<2.3-dev"
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Cmf\\Component\\Routing": "" }
+    },
+    "target-dir": "Symfony/Cmf/Component/Routing",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0-dev"
+        }
+    }
+}
diff --git a/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/phpunit.xml.dist b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/phpunit.xml.dist
new file mode 100644
index 0000000..2564ef1
--- /dev/null
+++ b/core/vendor/symfony-cmf/routing/Symfony/Cmf/Component/Routing/phpunit.xml.dist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
+<phpunit
+    colors="true"
+    bootstrap="Tests/bootstrap.php"
+>
+
+    <testsuites>
+        <testsuite name="Symfony Cmf Routing Test Suite">
+            <directory>./Tests</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist addUncoveredFilesFromWhitelist="true">
+            <directory>.</directory>
+            <exclude>
+                <directory>Test/</directory>
+                <directory>Tests/</directory>
+                <directory>vendor/</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+
+</phpunit>
