/*******************************************************************************
* 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()));
}
}