/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 ******************************************************************************/
package org.apache.olingo.odata2.core;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.olingo.odata2.api.ODataDebugResponseWrapperCallback;
import org.apache.olingo.odata2.api.ODataService;
import org.apache.olingo.odata2.api.ODataServiceFactory;
import org.apache.olingo.odata2.api.ODataServiceVersion;
import org.apache.olingo.odata2.api.commons.HttpHeaders;
import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
import org.apache.olingo.odata2.api.commons.ODataHttpHeaders;
import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
import org.apache.olingo.odata2.api.edm.EdmConcurrencyMode;
import org.apache.olingo.odata2.api.edm.EdmEntityType;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.edm.EdmFacets;
import org.apache.olingo.odata2.api.edm.EdmProperty;
import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
import org.apache.olingo.odata2.api.exception.ODataBadRequestException;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.exception.ODataMethodNotAllowedException;
import org.apache.olingo.odata2.api.exception.ODataPreconditionRequiredException;
import org.apache.olingo.odata2.api.exception.ODataUnsupportedMediaTypeException;
import org.apache.olingo.odata2.api.processor.ODataContext;
import org.apache.olingo.odata2.api.processor.ODataProcessor;
import org.apache.olingo.odata2.api.processor.ODataRequest;
import org.apache.olingo.odata2.api.processor.ODataResponse;
import org.apache.olingo.odata2.api.processor.ODataResponse.ODataResponseBuilder;
import org.apache.olingo.odata2.api.processor.part.EntityLinkProcessor;
import org.apache.olingo.odata2.api.processor.part.EntityLinksProcessor;
import org.apache.olingo.odata2.api.processor.part.EntityMediaProcessor;
import org.apache.olingo.odata2.api.processor.part.EntityProcessor;
import org.apache.olingo.odata2.api.processor.part.EntitySetProcessor;
import org.apache.olingo.odata2.api.processor.part.EntitySimplePropertyValueProcessor;
import org.apache.olingo.odata2.api.processor.part.FunctionImportProcessor;
import org.apache.olingo.odata2.api.processor.part.FunctionImportValueProcessor;
import org.apache.olingo.odata2.api.uri.PathSegment;
import org.apache.olingo.odata2.api.uri.UriInfo;
import org.apache.olingo.odata2.api.uri.UriParser;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.core.commons.ContentType.ODataFormat;
import org.apache.olingo.odata2.core.debug.ODataDebugResponseWrapper;
import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
import org.apache.olingo.odata2.core.rest.ODataExceptionWrapper;
import org.apache.olingo.odata2.core.uri.UriInfoImpl;
import org.apache.olingo.odata2.core.uri.UriParserImpl;
import org.apache.olingo.odata2.core.uri.UriType;

/**
 *  
 */
public class ODataRequestHandler {

  private final ODataServiceFactory serviceFactory;
  private final ODataService service;
  private final ODataContext context;

  public ODataRequestHandler(final ODataServiceFactory factory, final ODataService service,
      final ODataContext context) {
    serviceFactory = factory;
    this.service = service;
    this.context = context;
  }

  /**
   * <p>Handles the {@link ODataRequest} in a way that it results in a corresponding {@link ODataResponse}.</p>
   * <p>This includes delegation of URI parsing and dispatching of the request internally.
   * Building of the {@link ODataContext} takes place outside of this method.</p>
   * @param request the incoming request
   * @return the corresponding result
   */
  public ODataResponse handle(final ODataRequest request) {
    UriInfoImpl uriInfo = null;
    Exception exception = null;
    ODataResponse odataResponse;
    final int timingHandle = context.startRuntimeMeasurement("ODataRequestHandler", "handle");
    try {
      UriParser uriParser = new UriParserImpl(service.getEntityDataModel());
      Dispatcher dispatcher = new Dispatcher(serviceFactory, service);

      final String serverDataServiceVersion = getServerDataServiceVersion();
      final String requestDataServiceVersion = context.getRequestHeader(ODataHttpHeaders.DATASERVICEVERSION);
      validateDataServiceVersion(serverDataServiceVersion, requestDataServiceVersion);

      final List<PathSegment> pathSegments = context.getPathInfo().getODataSegments();
      int timingHandle2 = context.startRuntimeMeasurement("UriParserImpl", "parse");
      uriInfo = (UriInfoImpl) uriParser.parseAll(pathSegments, request.getAllQueryParameters());
      context.stopRuntimeMeasurement(timingHandle2);

      final ODataHttpMethod method = request.getMethod();
      validateMethodAndUri(method, uriInfo);

      if (method == ODataHttpMethod.POST || method == ODataHttpMethod.PUT || method == ODataHttpMethod.PATCH
          || method == ODataHttpMethod.MERGE) {
        checkRequestContentType(uriInfo, request.getContentType());
      }

      List<String> supportedContentTypes = getSupportedContentTypes(uriInfo, method);
      ContentType acceptContentType =
          new ContentNegotiator().doContentNegotiation(request, uriInfo, supportedContentTypes);

      checkConditions(method, uriInfo,
          context.getRequestHeader(HttpHeaders.IF_MATCH),
          context.getRequestHeader(HttpHeaders.IF_NONE_MATCH),
          context.getRequestHeader(HttpHeaders.IF_MODIFIED_SINCE),
          context.getRequestHeader(HttpHeaders.IF_UNMODIFIED_SINCE));

      timingHandle2 = context.startRuntimeMeasurement("Dispatcher", "dispatch");
      odataResponse =
          dispatcher.dispatch(method, uriInfo, request.getBody(), request.getContentType(), acceptContentType
              .toContentTypeString());
      context.stopRuntimeMeasurement(timingHandle2);

      ODataResponseBuilder extendedResponse = ODataResponse.fromResponse(odataResponse);
      final UriType uriType = uriInfo.getUriType();
      final String location =
          (method == ODataHttpMethod.POST && (uriType == UriType.URI1 || uriType == UriType.URI6B)) ? odataResponse
              .getIdLiteral() : null;
      final HttpStatusCodes s = getStatusCode(odataResponse, method, uriType);
      extendedResponse = extendedResponse.idLiteral(location).status(s);

      if (!odataResponse.containsHeader(ODataHttpHeaders.DATASERVICEVERSION)) {
        extendedResponse = extendedResponse.header(ODataHttpHeaders.DATASERVICEVERSION, serverDataServiceVersion);
      }
      if (!HttpStatusCodes.NO_CONTENT.equals(s) && !odataResponse.containsHeader(HttpHeaders.CONTENT_TYPE)) {
        extendedResponse.header(HttpHeaders.CONTENT_TYPE, acceptContentType.toContentTypeString());
      }

      odataResponse = extendedResponse.build();
    } catch (final Exception e) {
      exception = e;
      odataResponse = new ODataExceptionWrapper(context, request.getQueryParameters(), request.getAcceptHeaders())
          .wrapInExceptionResponse(e);
    }
    context.stopRuntimeMeasurement(timingHandle);

    if (context.isInDebugMode()) {
      final String debugValue = getQueryDebugValue(request.getQueryParameters());
      if (debugValue == null) {
        ODataDebugResponseWrapperCallback callback =
            context.getServiceFactory().getCallback(ODataDebugResponseWrapperCallback.class);
        return callback == null ? odataResponse : callback.handle(context, request, odataResponse, uriInfo, exception);
      } else {
        return new ODataDebugResponseWrapper(context, odataResponse, uriInfo, exception, debugValue).wrapResponse();
      }
    } else {
      return odataResponse;
    }
  }

  private HttpStatusCodes getStatusCode(final ODataResponse odataResponse, final ODataHttpMethod method,
      final UriType uriType) {
    if (odataResponse.getStatus() == null) {
      if (method == ODataHttpMethod.POST) {
        if (uriType == UriType.URI9) {
          return HttpStatusCodes.OK;
        } else if (uriType == UriType.URI7B) {
          return HttpStatusCodes.NO_CONTENT;
        }
        return HttpStatusCodes.CREATED;
      } else if (method == ODataHttpMethod.PUT
          || method == ODataHttpMethod.PATCH
          || method == ODataHttpMethod.MERGE
          || method == ODataHttpMethod.DELETE) {
        return HttpStatusCodes.NO_CONTENT;
      }
      return HttpStatusCodes.OK;
    }
    return odataResponse.getStatus();
  }

  private String getServerDataServiceVersion() throws ODataException {
    return service.getVersion() == null ? ODataServiceVersion.V20 : service.getVersion();
  }

  private static void validateDataServiceVersion(final String serverDataServiceVersion,
      final String requestDataServiceVersion) throws ODataException {
    if (requestDataServiceVersion != null) {
      try {
        final boolean isValid = ODataServiceVersion.validateDataServiceVersion(requestDataServiceVersion);
        if (!isValid || ODataServiceVersion.isBiggerThan(requestDataServiceVersion, serverDataServiceVersion)) {
          throw new ODataBadRequestException(ODataBadRequestException.VERSIONERROR
              .addContent(requestDataServiceVersion));
        }
      } catch (final IllegalArgumentException e) {
        throw new ODataBadRequestException(ODataBadRequestException.PARSEVERSIONERROR
            .addContent(requestDataServiceVersion), e);
      }
    }
  }

  private static void validateMethodAndUri(final ODataHttpMethod method, final UriInfoImpl uriInfo)
      throws ODataException {
    validateUriMethod(method, uriInfo);
    checkFunctionImport(method, uriInfo);
    if (method != ODataHttpMethod.GET) {
      checkNotGetSystemQueryOptions(method, uriInfo);
      checkNumberOfNavigationSegments(uriInfo);
      checkProperty(method, uriInfo);
    }
  }

  private static void validateUriMethod(final ODataHttpMethod method, final UriInfoImpl uriInfo) throws ODataException {
    switch (uriInfo.getUriType()) {
    case URI0:
    case URI8:
    case URI15:
    case URI16:
    case URI50A:
    case URI50B:
      if (method != ODataHttpMethod.GET) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI1:
    case URI6B:
    case URI7B:
      if (method != ODataHttpMethod.GET && method != ODataHttpMethod.POST) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI2:
    case URI6A:
    case URI7A:
      if (method != ODataHttpMethod.GET && method != ODataHttpMethod.PUT && method != ODataHttpMethod.DELETE
          && method != ODataHttpMethod.PATCH && method != ODataHttpMethod.MERGE) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI3:
      if (method != ODataHttpMethod.GET && method != ODataHttpMethod.PUT && method != ODataHttpMethod.PATCH
          && method != ODataHttpMethod.MERGE) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI4:
    case URI5:
      if (method != ODataHttpMethod.GET && method != ODataHttpMethod.PUT && method != ODataHttpMethod.DELETE
          && method != ODataHttpMethod.PATCH && method != ODataHttpMethod.MERGE) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      } else if (method == ODataHttpMethod.DELETE && !uriInfo.isValue()) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI9:
      if (method != ODataHttpMethod.POST) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI10:
    case URI10a:
    case URI11:
    case URI12:
    case URI13:
    case URI14:
      break;

    case URI17:
      if (method != ODataHttpMethod.GET && method != ODataHttpMethod.PUT && method != ODataHttpMethod.DELETE) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      } else {
        if (uriInfo.getFormat() != null) {
          throw new ODataBadRequestException(ODataBadRequestException.INVALID_SYNTAX);
        }
      }
      break;

    default:
      throw new ODataRuntimeException("Unknown or not implemented URI type: " + uriInfo.getUriType());
    }
  }

  private static void checkFunctionImport(final ODataHttpMethod method, final UriInfoImpl uriInfo)
      throws ODataException {
    if (uriInfo.getFunctionImport() != null && uriInfo.getFunctionImport().getHttpMethod() != null
        && !uriInfo.getFunctionImport().getHttpMethod().equals(method.toString())) {
      throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
    }
  }

  private static void checkNotGetSystemQueryOptions(final ODataHttpMethod method, final UriInfoImpl uriInfo)
      throws ODataException {
    switch (uriInfo.getUriType()) {
    case URI1:
    case URI6B:
      if (uriInfo.getFormat() != null || uriInfo.getFilter() != null || uriInfo.getInlineCount() != null
          || uriInfo.getOrderBy() != null || uriInfo.getSkipToken() != null || uriInfo.getSkip() != null
          || uriInfo.getTop() != null || !uriInfo.getExpand().isEmpty() || !uriInfo.getSelect().isEmpty()) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI2:
      if (uriInfo.getFormat() != null || !uriInfo.getExpand().isEmpty() || !uriInfo.getSelect().isEmpty()) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      if (method == ODataHttpMethod.DELETE) {
        if (uriInfo.getFilter() != null) {
          throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
        }
      }
      break;

    case URI3:
      if (uriInfo.getFormat() != null) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI4:
    case URI5:
      if (method == ODataHttpMethod.PUT || method == ODataHttpMethod.PATCH || method == ODataHttpMethod.MERGE) {
        if (!uriInfo.isValue() && uriInfo.getFormat() != null) {
          throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
        }
      }
      break;

    case URI7A:
      if (uriInfo.getFormat() != null || uriInfo.getFilter() != null) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI7B:
      if (uriInfo.getFormat() != null || uriInfo.getFilter() != null || uriInfo.getInlineCount() != null
          || uriInfo.getOrderBy() != null || uriInfo.getSkipToken() != null || uriInfo.getSkip() != null
          || uriInfo.getTop() != null) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    case URI17:
      if (uriInfo.getFormat() != null || uriInfo.getFilter() != null) {
        throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
      }
      break;

    default:
      break;
    }
  }

  private static void checkNumberOfNavigationSegments(final UriInfoImpl uriInfo) throws ODataException {
    switch (uriInfo.getUriType()) {
    case URI1:
    case URI6B:
    case URI7A:
    case URI7B:
      if (uriInfo.getNavigationSegments().size() > 1) {
        throw new ODataBadRequestException(ODataBadRequestException.NOTSUPPORTED);
      }
      break;

    case URI3:
    case URI4:
    case URI5:
    case URI17:
      if (!uriInfo.getNavigationSegments().isEmpty()) {
        throw new ODataBadRequestException(ODataBadRequestException.NOTSUPPORTED);
      }
      break;

    default:
      break;
    }
  }

  private static void checkProperty(final ODataHttpMethod method, final UriInfoImpl uriInfo) throws ODataException {
    if ((uriInfo.getUriType() == UriType.URI4 || uriInfo.getUriType() == UriType.URI5)
        && (isPropertyKey(uriInfo) || method == ODataHttpMethod.DELETE && !isPropertyNullable(getProperty(uriInfo)))) {
      throw new ODataMethodNotAllowedException(ODataMethodNotAllowedException.DISPATCH);
    }
  }

  private static EdmProperty getProperty(final UriInfo uriInfo) {
    final List<EdmProperty> propertyPath = uriInfo.getPropertyPath();
    return propertyPath == null || propertyPath.isEmpty() ? null : propertyPath.get(propertyPath.size() - 1);
  }

  private static boolean isPropertyKey(final UriInfo uriInfo) throws EdmException {
    return uriInfo.getTargetEntitySet().getEntityType().getKeyProperties().contains(getProperty(uriInfo));
  }

  private static boolean isPropertyNullable(final EdmProperty property) throws EdmException {
    return property != null && (property.getFacets() == null || property.getFacets().isNullable());
  }

  /**
   * <p>Checks if <code>content type</code> is a valid request content type for the given {@link UriInfoImpl}.</p>
   * <p>If the combination of <code>content type</code> and {@link UriInfoImpl} is not valid, an
   * {@link ODataUnsupportedMediaTypeException} is thrown.</p>
   * @param uriInfo information about request URI
   * @param contentType request content type
   * @throws ODataException in the case of an error during {@link UriInfoImpl} access;
   * if the combination of <code>content type</code> and {@link UriInfoImpl} is invalid, as
   * {@link ODataUnsupportedMediaTypeException}
   */
  private void checkRequestContentType(final UriInfoImpl uriInfo, final String contentType) throws ODataException {
    Class<? extends ODataProcessor> processorFeature = Dispatcher.mapUriTypeToProcessorFeature(uriInfo);

    // Don't check the request content type for function imports
    // because the request body is not used at all.
    if (processorFeature == FunctionImportProcessor.class || processorFeature == FunctionImportValueProcessor.class) {
      return;
    }

    // Adjust processor feature.
    if (processorFeature == EntitySetProcessor.class) {
      processorFeature = uriInfo.getTargetEntitySet().getEntityType().hasStream() ? EntityMediaProcessor.class :
          EntityProcessor.class; // The request must contain a single entity!
    } else if (processorFeature == EntityLinksProcessor.class) {
      processorFeature = EntityLinkProcessor.class; // The request must contain a single link!
    }

    final ContentType parsedContentType = ContentType.parse(contentType);
    if (parsedContentType == null || parsedContentType.hasWildcard()) {
      throw new ODataUnsupportedMediaTypeException(ODataUnsupportedMediaTypeException.NOT_SUPPORTED
          .addContent(parsedContentType));
    }

    // Get list of supported content types based on processor feature.
    final List<ContentType> supportedContentTypes =
        processorFeature == EntitySimplePropertyValueProcessor.class ? getSupportedContentTypes(getProperty(uriInfo))
            : getSupportedContentTypes(processorFeature);

    if (!hasMatchingContentType(parsedContentType, supportedContentTypes)) {
      throw new ODataUnsupportedMediaTypeException(ODataUnsupportedMediaTypeException.NOT_SUPPORTED
          .addContent(parsedContentType));
    }
  }

  /**
   * Checks if the given list of {@link ContentType}s contains a matching {@link ContentType} for the given
   * <code>contentType</code> parameter.
   * @param contentType for which a matching content type is searched
   * @param allowedContentTypes list against which is checked for possible matching {@link ContentType}s
   * @return <code>true</code> if a matching content type is in given list, otherwise <code>false</code>
   */
  private static boolean hasMatchingContentType(final ContentType contentType,
      final List<ContentType> allowedContentTypes) {
    final ContentType requested = contentType.receiveWithCharsetParameter(ContentNegotiator.DEFAULT_CHARSET);
    if (requested.getODataFormat() == ODataFormat.CUSTOM || requested.getODataFormat() == ODataFormat.MIME) {
      return requested.hasCompatible(allowedContentTypes);
    }
    return requested.hasMatch(allowedContentTypes);
  }

  private static List<ContentType> getSupportedContentTypes(final EdmProperty property) throws EdmException {
    if (property != null) {
      return property.getType() == EdmSimpleTypeKind.Binary.getEdmSimpleTypeInstance()
          ? Collections.singletonList(property.getMimeType() == null
            ? ContentType.WILDCARD : ContentType.create(property.getMimeType()))
            : Arrays.asList(ContentType.TEXT_PLAIN, ContentType.TEXT_PLAIN_CS_UTF_8);
    } else {
      return null;
    }
    
  }

  private List<String> getSupportedContentTypes(final UriInfoImpl uriInfo, final ODataHttpMethod method)
      throws ODataException {
    Class<? extends ODataProcessor> processorFeature = Dispatcher.mapUriTypeToProcessorFeature(uriInfo);
    UriType uriType = uriInfo.getUriType();
    //
    if (uriType == UriType.URI11) {
      processorFeature = EntitySetProcessor.class;
    } else if ((uriType == UriType.URI10)) {
      processorFeature = EntityProcessor.class;
    } else if (ODataHttpMethod.POST.equals(method)) {
      if (uriType == UriType.URI1 || uriType == UriType.URI6B) {
        processorFeature = EntityProcessor.class;
      }
    }
    return service.getSupportedContentTypes(processorFeature);
  }

  private List<ContentType> getSupportedContentTypes(final Class<? extends ODataProcessor> processorFeature)
      throws ODataException {
    return ContentType.createAsCustom(service.getSupportedContentTypes(processorFeature));
  }

  /**
   * A modifying request that targets an entity with enabled concurrency control
   * must contain at least one concurrency-control HTTP request header field.
   */
  private static void checkConditions(final ODataHttpMethod method, final UriInfoImpl uriInfo,
      final String ifMatch, final String ifNoneMatch, final String ifModifiedSince, final String ifUnmodifiedSince)
      throws ODataException {
    if ((method == ODataHttpMethod.PUT || method == ODataHttpMethod.PATCH || method == ODataHttpMethod.MERGE
        || method == ODataHttpMethod.DELETE)
        && ifMatch == null && ifNoneMatch == null && ifModifiedSince == null && ifUnmodifiedSince == null
        && checkUriType(uriInfo.getUriType())
        && hasConcurrencyControl(uriInfo.getTargetEntitySet().getEntityType())) {
      throw new ODataPreconditionRequiredException(ODataPreconditionRequiredException.COMMON);
    }
  }

  private static boolean checkUriType(UriType uriType) {
    return uriType == UriType.URI2 || uriType == UriType.URI6A || uriType == UriType.URI3
         || uriType == UriType.URI4 || uriType == UriType.URI5 || uriType == UriType.URI17;
  }

  private static boolean hasConcurrencyControl(final EdmEntityType entityType) throws EdmException {
    boolean concurrency = false;
    for (final String propertyName : entityType.getPropertyNames()) {
      final EdmFacets facets = ((EdmProperty) entityType.getProperty(propertyName)).getFacets();
      if (facets != null && facets.getConcurrencyMode() != null
          && facets.getConcurrencyMode() == EdmConcurrencyMode.Fixed) {
        concurrency = true;
        break;
      }
    }
    return concurrency;
  }

  private static String getQueryDebugValue(final Map<String, String> queryParameters) {
    final String debugValue = queryParameters.get(ODataDebugResponseWrapper.ODATA_DEBUG_QUERY_PARAMETER);
    return ODataDebugResponseWrapper.ODATA_DEBUG_JSON.equals(debugValue)
        || ODataDebugResponseWrapper.ODATA_DEBUG_HTML.equals(debugValue)
        || ODataDebugResponseWrapper.ODATA_DEBUG_DOWNLOAD.equals(debugValue) ? debugValue : null;
  }
}