/**
   * @param request {@link RestRequest}
   * @param routingResult {@link RoutingResult}
   * @param responseObject response value
   * @return {@link RestResponse}
   * @throws IOException if cannot build response
   */
  public RestResponse buildResponse(
      final RestRequest request, final RoutingResult routingResult, final Object responseObject)
      throws IOException {
    Map<String, String> headers = new HashMap<String, String>();
    headers.putAll(((ServerResourceContext) routingResult.getContext()).getResponseHeaders());

    headers.put(
        RestConstants.HEADER_RESTLI_PROTOCOL_VERSION,
        ((ServerResourceContext) routingResult.getContext()).getRestliProtocolVersion().toString());

    if (responseObject == null) {
      boolean isAction = routingResult.getResourceMethod().getType().equals(ResourceMethod.ACTION);
      RestResponseBuilder builder = new RestResponseBuilder();
      builder.setStatus(
          isAction ? HttpStatus.S_200_OK.getCode() : HttpStatus.S_404_NOT_FOUND.getCode());
      builder.setHeaders(headers);
      if (!isAction) {
        builder.setHeader(
            RestConstants.HEADER_LINKEDIN_ERROR_RESPONSE,
            RestConstants.HEADER_VALUE_ERROR_APPLICATION);
      }
      return builder.build();
    }

    RestLiResponseBuilder responseBuilder = chooseResponseBuilder(responseObject, routingResult);

    if (responseBuilder == null) {
      // this should not happen if valid return types are specified
      ResourceMethodDescriptor resourceMethod = routingResult.getResourceMethod();
      String fqMethodName =
          resourceMethod.getResourceModel().getResourceClass().getName()
              + '#'
              + routingResult.getResourceMethod().getMethod().getName();
      throw new RestLiInternalException(
          "Invalid return type '"
              + responseObject.getClass()
              + " from method '"
              + fqMethodName
              + '\'');
    }

    PartialRestResponse partialResponse =
        responseBuilder.buildResponse(request, routingResult, responseObject, headers);

    RestResponseBuilder builder =
        new RestResponseBuilder()
            .setHeaders(headers)
            .setStatus(partialResponse.getStatus().getCode());

    if (partialResponse.hasData()) {
      DataMap dataMap = partialResponse.getDataMap();
      String acceptTypes = request.getHeader(RestConstants.HEADER_ACCEPT);
      builder = encodeResult(builder, dataMap, acceptTypes);
    }

    return builder.build();
  }
  @Override
  @SuppressWarnings("fallthrough")
  public RestResponse processDocumentationRequest(RestRequest request) {
    final String path = request.getURI().getRawPath();
    final List<UriComponent.PathSegment> pathSegments = UriComponent.decodePath(path, true);

    String prefixSegment = null;
    String actionSegment = null;
    String typeSegment = null;
    String objectSegment = null;

    switch (pathSegments.size()) {
      case 5:
        objectSegment = pathSegments.get(4).getPath();
      case 4:
        typeSegment = pathSegments.get(3).getPath();
      case 3:
        actionSegment = pathSegments.get(2).getPath();
      case 2:
        prefixSegment = pathSegments.get(1).getPath();
    }

    assert (prefixSegment.equals(DOC_PREFIX)
        || (HttpMethod.valueOf(request.getMethod()) == HttpMethod.OPTIONS));

    final ByteArrayOutputStream out = new ByteArrayOutputStream(BAOS_BUFFER_SIZE);
    final RestLiDocumentationRenderer renderer;

    if (HttpMethod.valueOf(request.getMethod()) == HttpMethod.OPTIONS) {
      renderer = _jsonRenderer;
      renderer.renderResource(prefixSegment, out);
    } else if (HttpMethod.valueOf(request.getMethod()) == HttpMethod.GET) {
      if (!DOC_VIEW_DOCS_ACTION.equals(actionSegment)) {
        throw createRoutingError(path);
      }

      final MultivaluedMap queryMap = UriComponent.decodeQuery(request.getURI().getQuery(), false);
      final List<String> formatList = queryMap.get("format");
      if (formatList == null) {
        renderer = _htmlRenderer;
      } else if (formatList.size() > 1) {
        throw new RoutingException(
            String.format(
                "\"format\" query parameter must be unique, where multiple are specified: %s",
                Arrays.toString(formatList.toArray())),
            HttpStatus.S_400_BAD_REQUEST.getCode());
      } else {
        renderer = (formatList.contains(DOC_JSON_FORMAT) ? _jsonRenderer : _htmlRenderer);
      }

      if (renderer == _htmlRenderer) {
        _htmlRenderer.setJsonFormatUri(
            UriBuilder.fromUri(request.getURI()).queryParam("format", DOC_JSON_FORMAT).build());
      }

      try {
        if (typeSegment == null || typeSegment.isEmpty()) {
          renderer.renderHome(out);
        } else {
          if (DOC_RESOURCE_TYPE.equals(typeSegment)) {
            if (objectSegment == null || objectSegment.isEmpty()) {
              renderer.renderResourceHome(out);
            } else {
              renderer.renderResource(objectSegment, out);
            }
          } else if (DOC_DATA_TYPE.equals(typeSegment)) {
            if (objectSegment == null || objectSegment.isEmpty()) {
              renderer.renderDataModelHome(out);
            } else {
              renderer.renderDataModel(objectSegment, out);
            }
          } else {
            throw createRoutingError(path);
          }
        }
      } catch (RuntimeException e) {
        if (!renderer.handleException(e, out)) {
          throw e;
        }
      }
    } else {
      throw new RoutingException(HttpStatus.S_405_METHOD_NOT_ALLOWED.getCode());
    }

    return new RestResponseBuilder()
        .setStatus(HttpStatus.S_200_OK.getCode())
        .setHeader(RestConstants.HEADER_CONTENT_TYPE, renderer.getMIMEType())
        .setEntity(out.toByteArray())
        .build();
  }