/******************************************************************************* * Copyright 2013 SAP AG * * Licensed 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 com.sap.core.odata.core; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.sap.core.odata.api.exception.ODataBadRequestException; import com.sap.core.odata.api.exception.ODataException; import com.sap.core.odata.api.exception.ODataNotAcceptableException; import com.sap.core.odata.core.commons.ContentType; import com.sap.core.odata.core.commons.ContentType.ODataFormat; import com.sap.core.odata.core.uri.UriInfoImpl; import com.sap.core.odata.core.uri.UriType; /** * @author SAP AG */ public class ContentNegotiator { private static final String URI_INFO_FORMAT_JSON = "json"; private static final String URI_INFO_FORMAT_ATOM = "atom"; private static final String URI_INFO_FORMAT_XML = "xml"; static final String DEFAULT_CHARSET = "utf-8"; public String doContentNegotiation(final UriInfoImpl uriInfo, final List<String> acceptHeaderContentTypes, final List<String> supportedContentTypes) throws ODataException { ContentType contentType; if (uriInfo.getFormat() == null) { contentType = doContentNegotiationForAcceptHeader(acceptHeaderContentTypes, ContentType.create(supportedContentTypes)); } else { contentType = doContentNegotiationForFormat(uriInfo, ContentType.create(supportedContentTypes)); } if (contentType.getODataFormat() == ODataFormat.CUSTOM) { return contentType.getType(); } return contentType.toContentTypeString(); } private ContentType doContentNegotiationForFormat(final UriInfoImpl uriInfo, final List<ContentType> supportedContentTypes) throws ODataException { validateFormatQuery(uriInfo); ContentType formatContentType = mapFormat(uriInfo); formatContentType = formatContentType.receiveWithCharsetParameter(DEFAULT_CHARSET); for (final ContentType contentType : supportedContentTypes) { if (contentType.equals(formatContentType)) { return formatContentType; } } throw new ODataNotAcceptableException(ODataNotAcceptableException.NOT_SUPPORTED_CONTENT_TYPE.addContent(uriInfo.getFormat())); } /** * Validates that <code>dollar format query/syntax</code> is correct for further processing. * If some validation error occurs an exception is thrown. * * @param uriInfo * @throws ODataBadRequestException */ private void validateFormatQuery(final UriInfoImpl uriInfo) throws ODataBadRequestException { if (uriInfo.isValue()) { throw new ODataBadRequestException(ODataBadRequestException.INVALID_SYNTAX); } } private ContentType mapFormat(final UriInfoImpl uriInfo) { final String format = uriInfo.getFormat(); if (URI_INFO_FORMAT_XML.equals(format)) { return ContentType.APPLICATION_XML; } else if (URI_INFO_FORMAT_ATOM.equals(format)) { if (uriInfo.getUriType() == UriType.URI0) { // special handling for serviceDocument uris (UriType.URI0) return ContentType.APPLICATION_ATOM_SVC; } return ContentType.APPLICATION_ATOM_XML; } else if (URI_INFO_FORMAT_JSON.equals(format)) { return ContentType.APPLICATION_JSON; } return ContentType.create(format); } private ContentType doContentNegotiationForAcceptHeader(final List<String> acceptHeaderContentTypes, final List<ContentType> supportedContentTypes) throws ODataException { return contentNegotiation(extractAcceptHeaders(acceptHeaderContentTypes), supportedContentTypes); } private List<ContentType> extractAcceptHeaders(final List<String> acceptHeaderValues) throws ODataBadRequestException { final List<ContentType> mediaTypes = new ArrayList<ContentType>(); if (acceptHeaderValues != null) { for (final String mediaType : acceptHeaderValues) { try { mediaTypes.add(ContentType.create(mediaType.toString())); } catch (IllegalArgumentException e) { throw new ODataBadRequestException(ODataBadRequestException.INVALID_HEADER.addContent("Accept") .addContent(mediaType.toString()), e); } } } return mediaTypes; } ContentType contentNegotiation(final List<ContentType> acceptedContentTypes, final List<ContentType> supportedContentTypes) throws ODataException { final Set<ContentType> setSupported = new HashSet<ContentType>(supportedContentTypes); if (acceptedContentTypes.isEmpty()) { if (!setSupported.isEmpty()) { return supportedContentTypes.get(0); } } else { for (ContentType contentType : acceptedContentTypes) { contentType = contentType.receiveWithCharsetParameter(DEFAULT_CHARSET); final ContentType match = contentType.match(supportedContentTypes); if (match != null) { return match; } } } throw new ODataNotAcceptableException(ODataNotAcceptableException.NOT_SUPPORTED_ACCEPT_HEADER.addContent(acceptedContentTypes.toString())); } }