/**
   * Build a runtime model.
   *
   * @param subResourceMode if {@code true}, all resources will be processed as sub-resources.
   * @return runtime request routing root.
   */
  public Router buildModel(boolean subResourceMode) {
    final PushMatchedUriRouter uriPushingRouter =
        locator.createAndInitialize(PushMatchedUriRouter.class);
    RouteToPathBuilder<PathPattern> lastRoutedBuilder = null;

    // route resource method acceptors
    if (!rootAcceptors.isEmpty()) {
      for (Map.Entry<PathPattern, List<MethodAcceptorPair>> entry : rootAcceptors.entrySet()) {
        final PathPattern closedResourcePathPattern = entry.getKey();
        List<MethodAcceptorPair> methodAcceptorPairs = entry.getValue();

        lastRoutedBuilder =
            routeMethodAcceptor(
                lastRoutedBuilder,
                closedResourcePathPattern,
                uriPushingRouter,
                methodSelectingAcceptorBuilder.build(workers, methodAcceptorPairs),
                subResourceMode);
      }
      rootAcceptors.clear();
    }

    // route sub-resource method and locator acceptors
    if (!subResourceAcceptors.isEmpty()) {
      for (Map.Entry<PathPattern, TreeMap<PathPattern, List<MethodAcceptorPair>>>
          singleResourcePathEntry : subResourceAcceptors.entrySet()) {

        RouteToPathBuilder<PathPattern> srRoutedBuilder = null;
        for (Map.Entry<PathPattern, List<MethodAcceptorPair>> singlePathEntry :
            singleResourcePathEntry.getValue().entrySet()) {

          // there can be multiple sub-resource methods on the same path
          // but only a single sub-resource locator.
          List<MethodAcceptorPair> resourceMethods = Lists.newLinkedList();
          MethodAcceptorPair resourceLocator = null;

          for (MethodAcceptorPair methodAcceptorPair : singlePathEntry.getValue()) {
            if (methodAcceptorPair.model.getType() == ResourceMethod.JaxrsType.RESOURCE_METHOD) {
              resourceMethods.add(methodAcceptorPair);
            } else {
              resourceLocator = methodAcceptorPair;
            }
          }
          if (!resourceMethods.isEmpty()) {
            final PathPattern subResourceMethodPath =
                PathPattern.asClosed(singlePathEntry.getKey());
            srRoutedBuilder =
                routedBuilder(srRoutedBuilder)
                    .route(subResourceMethodPath)
                    .to(uriPushingRouter)
                    .to(methodSelectingAcceptorBuilder.build(workers, resourceMethods));
          }

          if (resourceLocator != null) {
            srRoutedBuilder =
                routedBuilder(srRoutedBuilder)
                    .route(singlePathEntry.getKey())
                    .to(uriPushingRouter)
                    .to(resourceLocator.router);
          }
        }
        assert srRoutedBuilder != null;
        lastRoutedBuilder =
            routeMethodAcceptor(
                lastRoutedBuilder,
                singleResourcePathEntry.getKey(),
                uriPushingRouter,
                srRoutedBuilder.build(),
                subResourceMode);
      }
      subResourceAcceptors.clear();
    }
    return createRootTreeAcceptor(lastRoutedBuilder, subResourceMode);
  }
  /**
   * Process a single resource model and add it to the currently build runtime routing and accepting
   * model.
   *
   * @param resource resource model to be processed.
   * @param subResourceMode if {@code true}, all resources will be processed as sub-resources.
   */
  public void process(final Resource resource, final boolean subResourceMode) {

    if (!(resource.isRootResource() || subResourceMode)) {
      // ignore sub-resources if not in a sub-resource modelling mode.
      return;
    }
    // prepare & add resource method acceptors
    if (!resource.getResourceMethods().isEmpty()) {
      final PathPattern closedResourcePathPattern =
          (subResourceMode)
              ? PathPattern.END_OF_PATH_PATTERN
              : PathPattern.asClosed(resource.getPathPattern());

      List<MethodAcceptorPair> sameResourcePathList =
          getAcceptorList(rootAcceptors, closedResourcePathPattern);

      sameResourcePathList.addAll(
          Lists.transform(
              resource.getResourceMethods(),
              new Function<ResourceMethod, MethodAcceptorPair>() {
                @Override
                public MethodAcceptorPair apply(ResourceMethod methodModel) {
                  return new MethodAcceptorPair(
                      methodModel,
                      resource,
                      createSingleMethodAcceptor(methodModel, subResourceMode));
                }
              }));
    }

    final ResourceMethod resourceLocator = resource.getResourceLocator();
    if (resourceLocator != null) {
      TreeMap<PathPattern, List<MethodAcceptorPair>> sameResourcePathMap =
          getPatternAcceptorListMap(resource.getPathPattern());

      List<MethodAcceptorPair> locatorList =
          getAcceptorList(sameResourcePathMap, PathPattern.OPEN_ROOT_PATH_PATTERN);

      locatorList.add(
          new MethodAcceptorPair(
              resourceLocator,
              resource,
              createSingleMethodAcceptor(resourceLocator, subResourceMode)));
    }

    // prepare & add sub-resource method and locator acceptors.
    if (resource.getChildResources().size() > 0) {
      final PathPattern resourcePath =
          (subResourceMode) ? PathPattern.OPEN_ROOT_PATH_PATTERN : resource.getPathPattern();

      TreeMap<PathPattern, List<MethodAcceptorPair>> sameResourcePathMap =
          getPatternAcceptorListMap(resourcePath);
      for (Resource child : resource.getChildResources()) {
        PathPattern childRelativePath = new PathPattern(child.getPath());

        List<MethodAcceptorPair> samePathMethodAcceptorPairs =
            getAcceptorList(sameResourcePathMap, childRelativePath);

        for (ResourceMethod resourceMethod : child.getAllMethods()) {
          samePathMethodAcceptorPairs.add(
              new MethodAcceptorPair(
                  resourceMethod,
                  resource,
                  createSingleMethodAcceptor(resourceMethod, subResourceMode)));
        }
      }
    }
  }