/**
   * Determines if this RESTXQ Service can service the request
   *
   * <p>Rules are (must all apply): 1) Can this Service service the HTTP Method of the request 2)
   * Does the ResourceFunction of this RESTXQ Service apply to the Request Path 3) Can we consume
   * the request i.e. Content-Type header 4) Can we produce the response i.e. Accept header
   *
   * @see org.exquery.restxq.RestXqService#canService(org.exquery.http.HttpRequest)
   */
  @Override
  public boolean canService(final HttpRequest request) {

    // 1) check the method matches
    if (getServicedMethods().contains(request.getMethod())) {

      // 2) check the path matches
      if (getResourceFunction().getPathAnnotation() != null
          && !getResourceFunction().getPathAnnotation().matchesPath(request.getPath())) {
        return false;
      }

      // 3) check we can consume the request
      if (!canServiceConsume(request)) {
        // TODO HTTP 415 - in RESTXQServiceRegistry.findService, if no service matches we should be
        // able to return a HTTP reason!
        return false;
      }

      // 4) check we can produce the request
      if (!canServiceProduce(request)) {
        // TODO HTTP 406 - in RESTXQServiceRegistry.findService, if no service matches we should be
        // able to return a HTTP reason!
        return false;
      }

      return true;
    }

    return false;
  }
  /**
   * Extract Annotated Parameters from the Request
   *
   * @param request The HTTP Request to process
   * @return The Map of Parameters to values, the key is the parameter name and the value is the
   *     sequence of values extracted from the request
   * @throws RestXqServiceException If an error occurred whilst processing the request
   */
  protected Set<TypedArgumentValue> extractParameters(final HttpRequest request)
      throws RestXqServiceException {

    final Set<TypedArgumentValue> paramNameValues = new HashSet<TypedArgumentValue>();

    // extract the param mappings for the Path Annotation
    if (getResourceFunction().getPathAnnotation() != null) {
      for (final Entry<String, String> pathParameter :
          getResourceFunction()
              .getPathAnnotation()
              .extractPathParameters(request.getPath())
              .entrySet()) {

        paramNameValues.add(
            new TypedArgumentValue<String>() {
              @Override
              public String getArgumentName() {
                return pathParameter.getKey();
              }

              @Override
              public Sequence<String> getTypedValue() {
                return new SequenceImpl<String>(new StringTypedValue(pathParameter.getValue()));
              }
            });
      }
    }

    // extract the param mappings for the Body Content Annotations
    if (!getBodyContentAnnotations().isEmpty()) {
      final Sequence requestBody = extractRequestBody(request);
      for (final HttpMethodWithBodyAnnotation bodyContentAnnotation : getBodyContentAnnotations()) {
        paramNameValues.add(
            new TypedArgumentValue() {
              @Override
              public String getArgumentName() {
                return bodyContentAnnotation.getBodyParameterName();
              }

              @Override
              public Sequence getTypedValue() {
                if (requestBody != null) {
                  return requestBody;
                } else {
                  return Sequence.EMPTY_SEQUENCE;
                }
              }
            });
      }
    }

    // extract the param mappings for Param Annotations
    for (final ParameterAnnotation parameterAnnotation :
        getResourceFunction().getParameterAnnotations()) {
      final TypedArgumentValue typedArgumentValue = parameterAnnotation.extractParameter(request);
      paramNameValues.add(
          new TypedArgumentValue() {

            @Override
            public String getArgumentName() {
              return typedArgumentValue.getArgumentName();
            }

            @Override
            public Sequence getTypedValue() {
              return typedArgumentValue.getTypedValue();
            }
          });
    }

    return paramNameValues;
  }