[OLINGO-317] Refactored ContentType
This commit is contained in:
parent
176bee4b02
commit
ab6fd5e2b8
|
@ -354,9 +354,9 @@ abstract class AbstractPersistenceManager implements PersistenceManager {
|
|||
final ODataMediaEntityUpdateRequest<?> req =
|
||||
factory.getClient().getCUDRequestFactory().getMediaEntityUpdateRequest(uri, input);
|
||||
|
||||
req.setContentType(StringUtils.isBlank(handler.getEntity().getMediaContentType())
|
||||
? ODataFormat.WILDCARD.toString()
|
||||
: ODataFormat.fromString(handler.getEntity().getMediaContentType()).toString());
|
||||
if (StringUtils.isNotBlank(handler.getEntity().getMediaContentType())) {
|
||||
req.setContentType(ODataFormat.fromString(handler.getEntity().getMediaContentType()).toString());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(handler.getETag())) {
|
||||
req.setIfMatch(handler.getETag());
|
||||
|
|
|
@ -320,13 +320,11 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
|
|||
&& typeRef.getAnnotation(EntityType.class).hasStream()
|
||||
&& contentSource != null) {
|
||||
|
||||
final String contentType =
|
||||
StringUtils.isBlank(getEntity().getMediaContentType()) ? "*/*" : getEntity().getMediaContentType();
|
||||
|
||||
final ODataMediaRequest retrieveReq = getClient().getRetrieveRequestFactory().
|
||||
getMediaEntityRequest(contentSource);
|
||||
retrieveReq.setFormat(ODataFormat.fromString(contentType));
|
||||
|
||||
final ODataMediaRequest retrieveReq = getClient().getRetrieveRequestFactory()
|
||||
.getMediaEntityRequest(contentSource);
|
||||
if (StringUtils.isNotBlank(getEntity().getMediaContentType())) {
|
||||
retrieveReq.setFormat(ODataFormat.fromString(getEntity().getMediaContentType()));
|
||||
}
|
||||
this.stream = retrieveReq.execute().getBody();
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,8 @@ public class TransactionalPersistenceManagerImpl extends AbstractPersistenceMana
|
|||
protected void doFlush(final PersistenceChanges changes, final TransactionItems items) {
|
||||
final CommonODataBatchRequest request =
|
||||
factory.getClient().getBatchRequestFactory().getBatchRequest(factory.getClient().getServiceRoot());
|
||||
((ODataRequest) request).setAccept(factory.getClient().getConfiguration().getDefaultBatchAcceptFormat());
|
||||
String accept = factory.getClient().getConfiguration().getDefaultBatchAcceptFormat().toContentTypeString();
|
||||
((ODataRequest) request).setAccept(accept);
|
||||
|
||||
final BatchManager streamManager = (BatchManager) ((ODataStreamedRequest) request).payloadManager();
|
||||
|
||||
|
|
|
@ -123,6 +123,8 @@ public abstract class AbstractServices {
|
|||
private static final Pattern REF_PATTERN = Pattern.compile("([$]\\d+)");
|
||||
|
||||
protected static final String BOUNDARY = "batch_243234_25424_ef_892u748";
|
||||
protected static final String MULTIPART_MIXED = "multipart/mixed";//ContentType.MULTIPART_MIXED.toContentTypeString();
|
||||
protected static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
|
||||
|
||||
protected final ODataServiceVersion version;
|
||||
protected final Metadata metadata;
|
||||
|
@ -194,8 +196,8 @@ public abstract class AbstractServices {
|
|||
|
||||
@POST
|
||||
@Path("/$batch")
|
||||
@Consumes(ContentType.MULTIPART_MIXED)
|
||||
@Produces(ContentType.APPLICATION_OCTET_STREAM + ";boundary=" + BOUNDARY)
|
||||
@Consumes(MULTIPART_MIXED)
|
||||
@Produces(APPLICATION_OCTET_STREAM + ";boundary=" + BOUNDARY)
|
||||
public Response batch(
|
||||
@HeaderParam("Authorization") @DefaultValue(StringUtils.EMPTY) String authorization,
|
||||
@HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
|
||||
|
@ -611,7 +613,7 @@ public abstract class AbstractServices {
|
|||
final String entityKey;
|
||||
if (xml.isMediaContent(entitySetName)) {
|
||||
entry = new EntityImpl();
|
||||
entry.setMediaContentType(ContentType.WILDCARD);
|
||||
entry.setMediaContentType(ContentType.APPLICATION_OCTET_STREAM.toContentTypeString());
|
||||
entry.setType(entitySet.getType());
|
||||
|
||||
entityKey = xml.getDefaultEntryKey(entitySetName, entry);
|
||||
|
|
|
@ -150,7 +150,7 @@ public class EntityRetrieveTestITCase extends AbstractTestITCase {
|
|||
appendEntitySetSegment("Car").appendKeySegment(16);
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
|
|
@ -150,7 +150,7 @@ public class EntitySetTestITCase extends AbstractTestITCase {
|
|||
uriBuilder.appendEntitySetSegment("Product").inlineCount(URIBuilder.InlineCount.allpages);
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
@ -164,7 +164,7 @@ public class EntitySetTestITCase extends AbstractTestITCase {
|
|||
uriBuilder.appendEntitySetSegment("Car");
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
|
|
@ -51,7 +51,7 @@ public class MediaEntityTestITCase extends AbstractTestITCase {
|
|||
appendEntitySetSegment("Car").appendKeySegment(12);
|
||||
|
||||
final ODataMediaRequest retrieveReq = client.getRetrieveRequestFactory().getMediaEntityRequest(builder.build());
|
||||
retrieveReq.setFormat(ODataFormat.WILDCARD);
|
||||
retrieveReq.setAccept("*/*");
|
||||
|
||||
final ODataRetrieveResponse<InputStream> retrieveRes = retrieveReq.execute();
|
||||
assertEquals(200, retrieveRes.getStatusCode());
|
||||
|
|
|
@ -348,7 +348,7 @@ public class PropertyTestITCase extends AbstractTestITCase {
|
|||
appendEntitySetSegment("Customer").appendKeySegment(-10).appendPropertySegment("BackupContactInfo");
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
|
|
@ -106,7 +106,7 @@ public class QueryOptionsTestITCase extends AbstractTestITCase {
|
|||
final ODataRetrieveResponse<ODataEntity> res = req.execute();
|
||||
assertNotNull(res);
|
||||
assertTrue(res.getContentType().replaceAll(" ", "").
|
||||
startsWith(ODataFormat.JSON.toString(client.getServiceVersion())));
|
||||
startsWith(ODataFormat.JSON.getContentType(client.getServiceVersion()).toContentTypeString()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,7 +41,7 @@ import org.junit.Test;
|
|||
|
||||
public class AuthBatchTestITCase extends AbstractTestITCase {
|
||||
|
||||
private final static String ACCEPT = ContentType.APPLICATION_OCTET_STREAM;
|
||||
private final static ContentType ACCEPT = ContentType.APPLICATION_OCTET_STREAM;
|
||||
|
||||
@Test
|
||||
public void clean() throws EdmPrimitiveTypeException {
|
||||
|
@ -66,8 +66,8 @@ public class AuthBatchTestITCase extends AbstractTestITCase {
|
|||
private void batchRequest(final ODataClient client, final String baseURL) throws EdmPrimitiveTypeException {
|
||||
// create your request
|
||||
final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(baseURL);
|
||||
request.setAccept(ACCEPT);
|
||||
request.addCustomHeader("User-Agent", "Microsoft ADO.NET Data Client xxx");
|
||||
request.setAccept(ACCEPT.toContentTypeString());
|
||||
request.addCustomHeader("User-Agent", "Apache Olingo OData Client");
|
||||
request.addCustomHeader(HeaderName.acceptCharset, "UTF-8");
|
||||
|
||||
final BatchManager streamManager = request.payloadManager();
|
||||
|
|
|
@ -77,7 +77,7 @@ public class BatchTestITCase extends AbstractTestITCase {
|
|||
|
||||
private static final int MAX = 10000;
|
||||
|
||||
private final static String ACCEPT = ContentType.APPLICATION_OCTET_STREAM;
|
||||
private final static String ACCEPT = ContentType.APPLICATION_OCTET_STREAM.toContentTypeString();
|
||||
|
||||
@Test
|
||||
public void stringStreaming() {
|
||||
|
|
|
@ -124,13 +124,13 @@ public class ConformanceTestITCase extends AbstractTestITCase {
|
|||
|
||||
// check for Content-Type
|
||||
assertEquals(
|
||||
ODataFormat.JSON_FULL_METADATA.toString(ODataServiceVersion.V40),
|
||||
ODataFormat.JSON_FULL_METADATA.getContentType(ODataServiceVersion.V40).toContentTypeString(),
|
||||
req.getHeader("Content-Type"));
|
||||
assertEquals(
|
||||
ODataFormat.JSON_FULL_METADATA.toString(ODataServiceVersion.V40),
|
||||
ODataFormat.JSON_FULL_METADATA.getContentType(ODataServiceVersion.V40).toContentTypeString(),
|
||||
req.getHeader(HeaderName.contentType.toString()));
|
||||
assertEquals(
|
||||
ODataFormat.JSON_FULL_METADATA.toString(ODataServiceVersion.V40),
|
||||
ODataFormat.JSON_FULL_METADATA.getContentType(ODataServiceVersion.V40).toContentTypeString(),
|
||||
req.getContentType());
|
||||
|
||||
final ODataEntity created = req.execute().getBody();
|
||||
|
|
|
@ -172,7 +172,7 @@ public class EntityRetrieveTestITCase extends AbstractTestITCase {
|
|||
appendEntitySetSegment("People").appendKeySegment(5);
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
|
|
@ -49,7 +49,7 @@ public class EntitySetTestITCase extends AbstractTestITCase {
|
|||
final URIBuilder uriBuilder = client.newURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("People");
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
@ -74,7 +74,7 @@ public class EntitySetTestITCase extends AbstractTestITCase {
|
|||
appendEntitySetSegment("People").count(true);
|
||||
|
||||
final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
|
||||
req.setFormat(format.toString(client.getServiceVersion()));
|
||||
req.setFormat(format.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
|
||||
final ODataRawResponse res = req.execute();
|
||||
assertNotNull(res);
|
||||
|
|
|
@ -101,7 +101,7 @@ public class PropertyValueTestITCase extends AbstractTestITCase {
|
|||
final URIBuilder uriBuilder = client.newURIBuilder(testStaticServiceRootURL).
|
||||
appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("PDC");
|
||||
final ODataValueRequest req = client.getRetrieveRequestFactory().getPropertyValueRequest(uriBuilder.build());
|
||||
req.setAccept(ODataFormat.ATOM.toString(ODataServiceVersion.V40));
|
||||
req.setAccept(ODataFormat.ATOM.getContentType(ODataServiceVersion.V40).toContentTypeString());
|
||||
req.execute().getBody();
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ public class PropertyValueTestITCase extends AbstractTestITCase {
|
|||
final URIBuilder uriBuilder = client.newURIBuilder(testStaticServiceRootURL).
|
||||
appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("PDC");
|
||||
final ODataValueRequest req = client.getRetrieveRequestFactory().getPropertyValueRequest(uriBuilder.build());
|
||||
req.setAccept(ODataFormat.XML.toString(client.getServiceVersion()));
|
||||
req.setAccept(ODataFormat.XML.getContentType(client.getServiceVersion()).toContentTypeString());
|
||||
req.execute().getBody();
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ public class QueryOptionsTestITCase extends AbstractTestITCase {
|
|||
final ODataRetrieveResponse<ODataEntity> res = req.execute();
|
||||
assertNotNull(res);
|
||||
assertTrue(res.getContentType().replaceAll(" ", "").
|
||||
startsWith(ODataFormat.JSON.toString(client.getServiceVersion())));
|
||||
startsWith(ODataFormat.JSON.getContentType(client.getServiceVersion()).toContentTypeString()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.concurrent.ExecutorService;
|
|||
|
||||
import org.apache.olingo.client.api.http.HttpClientFactory;
|
||||
import org.apache.olingo.client.api.http.HttpUriRequestFactory;
|
||||
import org.apache.olingo.commons.api.format.ContentType;
|
||||
import org.apache.olingo.commons.api.format.ODataFormat;
|
||||
|
||||
/**
|
||||
|
@ -33,13 +34,13 @@ public interface CommonConfiguration {
|
|||
* Gets the configured default <tt>Accept</tt> header value format for a batch request.
|
||||
* @return configured default <tt>Accept</tt> header value for a batch request.
|
||||
*/
|
||||
String getDefaultBatchAcceptFormat();
|
||||
ContentType getDefaultBatchAcceptFormat();
|
||||
|
||||
/**
|
||||
* Set the default <tt>Accept</tt> header value format for a batch request.
|
||||
* @param contentType default <tt>Accept</tt> header value.
|
||||
*/
|
||||
void setDefaultBatchAcceptFormat(String contentType);
|
||||
void setDefaultBatchAcceptFormat(ContentType contentType);
|
||||
|
||||
/**
|
||||
* Gets the configured OData format for AtomPub exchanges. If this configuration parameter doesn't exist the
|
||||
|
@ -69,8 +70,8 @@ public interface CommonConfiguration {
|
|||
* Gets the configured OData value format. If this configuration parameter doesn't exist the TEXT format will be used
|
||||
* as default.
|
||||
*
|
||||
* @return configured OData value format if specified; TEXT format otherwise.
|
||||
* @see ODataFormat#TEXT
|
||||
* @return configured OData value format if specified; TEXT_PLAIN format otherwise.
|
||||
* @see ODataFormat#TEXT_PLAIN
|
||||
*/
|
||||
ODataFormat getDefaultValueFormat();
|
||||
|
||||
|
|
|
@ -82,12 +82,12 @@ public class Configuration implements CommonConfiguration {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultBatchAcceptFormat() {
|
||||
return getProperty(DEFAULT_BATCH_ACCEPT_FORMAT, ContentType.MULTIPART_MIXED).toString();
|
||||
public ContentType getDefaultBatchAcceptFormat() {
|
||||
return (ContentType) getProperty(DEFAULT_BATCH_ACCEPT_FORMAT, ContentType.MULTIPART_MIXED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultBatchAcceptFormat(final String contentType) {
|
||||
public void setDefaultBatchAcceptFormat(final ContentType contentType) {
|
||||
setProperty(DEFAULT_BATCH_ACCEPT_FORMAT, contentType);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public abstract class AbstractODataBasicRequest<T extends ODataResponse>
|
|||
@Override
|
||||
public void setFormat(final ODataFormat format) {
|
||||
if (format != null) {
|
||||
final String formatString = format.toString(odataClient.getServiceVersion());
|
||||
final String formatString = format.getContentType(odataClient.getServiceVersion()).toContentTypeString();
|
||||
setAccept(formatString);
|
||||
setContentType(formatString);
|
||||
}
|
||||
|
|
|
@ -184,7 +184,9 @@ public abstract class AbstractODataRequest extends AbstractRequest implements OD
|
|||
@Override
|
||||
public String getAccept() {
|
||||
final String acceptHead = odataHeaders.getHeader(HeaderName.accept);
|
||||
return StringUtils.isBlank(acceptHead) ? getDefaultFormat().toString(odataClient.getServiceVersion()) : acceptHead;
|
||||
return StringUtils.isBlank(acceptHead) ?
|
||||
getDefaultFormat().getContentType(odataClient.getServiceVersion()).toContentTypeString() :
|
||||
acceptHead;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -205,8 +207,9 @@ public abstract class AbstractODataRequest extends AbstractRequest implements OD
|
|||
@Override
|
||||
public String getContentType() {
|
||||
final String contentTypeHead = odataHeaders.getHeader(HeaderName.contentType);
|
||||
return StringUtils.isBlank(contentTypeHead)
|
||||
? getDefaultFormat().toString(odataClient.getServiceVersion()) : contentTypeHead;
|
||||
return StringUtils.isBlank(contentTypeHead) ?
|
||||
getDefaultFormat().getContentType(odataClient.getServiceVersion()).toContentTypeString() :
|
||||
contentTypeHead;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -95,7 +95,7 @@ public class ODataBatchUtilities {
|
|||
* <p>
|
||||
* Usually used to read an entire batch part.
|
||||
*
|
||||
* @param batchController batch controller.
|
||||
* @param controller batch controller.
|
||||
* @param os destination stream of batch part (null to discard).
|
||||
* @param checkCurrent if 'TRUE' the current line will be included into the delimiter verification.
|
||||
* @return latest read line.
|
||||
|
@ -111,7 +111,7 @@ public class ODataBatchUtilities {
|
|||
* <p>
|
||||
* Usually used to read an entire batch part.
|
||||
*
|
||||
* @param batchController batch controller.
|
||||
* @param controller batch controller.
|
||||
* @param os destination stream of batch part (null to discard).
|
||||
* @param count number of batch line to be read.
|
||||
* @param checkCurrent if 'TRUE' the current line will be included into the delimiter verification.
|
||||
|
@ -302,7 +302,7 @@ public class ODataBatchUtilities {
|
|||
final String contentType = headers.containsKey(HeaderName.contentType.toString())
|
||||
? headers.get(HeaderName.contentType.toString()).toString() : StringUtils.EMPTY;
|
||||
|
||||
if (contentType.contains(ContentType.MULTIPART_MIXED)) {
|
||||
if (contentType.contains(ContentType.MULTIPART_MIXED.toContentTypeString())) {
|
||||
nextItemType = BatchItemType.CHANGESET;
|
||||
} else if (contentType.contains(ODataBatchConstants.ITEM_CONTENT_TYPE)) {
|
||||
nextItemType = BatchItemType.RETRIEVE;
|
||||
|
|
|
@ -49,7 +49,7 @@ public class ODataBatchRequestImpl
|
|||
|
||||
public ODataBatchRequestImpl(final ODataClient odataClient, final URI uri) {
|
||||
super(odataClient, uri);
|
||||
setAccept(odataClient.getConfiguration().getDefaultBatchAcceptFormat());
|
||||
setAccept(odataClient.getConfiguration().getDefaultBatchAcceptFormat().toContentTypeString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -48,7 +48,7 @@ public class ODataBatchRequestImpl
|
|||
|
||||
public ODataBatchRequestImpl(final ODataClient odataClient, final URI uri) {
|
||||
super(odataClient, uri);
|
||||
setAccept(odataClient.getConfiguration().getDefaultBatchAcceptFormat());
|
||||
setAccept(odataClient.getConfiguration().getDefaultBatchAcceptFormat().toContentTypeString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -97,9 +97,9 @@ public abstract class AbstractODataInvokeRequest<T extends ODataInvokeResult>
|
|||
}
|
||||
|
||||
private String getActualFormat(final ODataFormat format) {
|
||||
return (CommonODataProperty.class.isAssignableFrom(reference) && format == ODataFormat.ATOM)
|
||||
? ODataFormat.XML.toString(odataClient.getServiceVersion())
|
||||
: format.toString(odataClient.getServiceVersion());
|
||||
return ((CommonODataProperty.class.isAssignableFrom(reference) && format == ODataFormat.ATOM)
|
||||
? ODataFormat.XML : format)
|
||||
.getContentType(odataClient.getServiceVersion()).toContentTypeString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -50,7 +50,7 @@ public abstract class AbstractODataStreamedEntityRequest<V extends ODataResponse
|
|||
final URI uri) {
|
||||
|
||||
super(odataClient, method, uri);
|
||||
setAccept(getFormat().toString(odataClient.getServiceVersion()));
|
||||
setAccept(getFormat().getContentType(odataClient.getServiceVersion()).toContentTypeString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,6 +61,6 @@ public abstract class AbstractODataStreamedEntityRequest<V extends ODataResponse
|
|||
@Override
|
||||
public final void setFormat(final ODataFormat format) {
|
||||
this.format = format;
|
||||
setAccept(format.toString(odataClient.getServiceVersion()));
|
||||
setAccept(format.getContentType(odataClient.getServiceVersion()).toContentTypeString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,11 @@ public enum ODataLinkType {
|
|||
/**
|
||||
* Entity navigation link.
|
||||
*/
|
||||
ENTITY_NAVIGATION(ContentType.APPLICATION_ATOM_XML + ";type=entry"),
|
||||
ENTITY_NAVIGATION(ContentType.APPLICATION_ATOM_XML_ENTRY),
|
||||
/**
|
||||
* Entity set navigation link.
|
||||
*/
|
||||
ENTITY_SET_NAVIGATION(ContentType.APPLICATION_ATOM_XML + ";type=feed"),
|
||||
ENTITY_SET_NAVIGATION(ContentType.APPLICATION_ATOM_XML_FEED),
|
||||
/**
|
||||
* Association link.
|
||||
*/
|
||||
|
@ -50,6 +50,10 @@ public enum ODataLinkType {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
private ODataLinkType(ContentType contentType) {
|
||||
this(contentType.toContentTypeString());
|
||||
}
|
||||
|
||||
private ODataLinkType setType(final String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
|
|
|
@ -1,70 +1,705 @@
|
|||
/*
|
||||
/*******************************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* 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
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
******************************************************************************/
|
||||
package org.apache.olingo.commons.api.format;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class ContentType {
|
||||
/**
|
||||
* Internally used {@link ContentType} for OData library.
|
||||
*
|
||||
* For more details on format and content of a {@link ContentType} see
|
||||
* <em>Media Type</em> format as defined in
|
||||
* <a href="http://www.ietf.org/rfc/rfc7231.txt">RFC 7231</a>, chapter 3.1.1.1.
|
||||
* <pre>
|
||||
* media-type = type "/" subtype *( OWS ";" OWS parameter )
|
||||
* type = token
|
||||
* subtype = token
|
||||
* OWS = *( SP / HTAB ) ; optional whitespace
|
||||
* </pre>
|
||||
*
|
||||
* Especially for <code>Accept</code> Header as defined in
|
||||
* RFC 7231, chapter 5.3.2:
|
||||
* <pre>
|
||||
* Accept = #( media-range [ accept-params ] )
|
||||
* media-range = ( "*/*"
|
||||
* / ( type "/" "*" )
|
||||
* / ( type "/" subtype )
|
||||
* ) *( OWS ";" OWS parameter )
|
||||
* accept-params = weight *( accept-ext )
|
||||
* accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ]
|
||||
* weight = OWS ";" OWS "q=" qvalue
|
||||
* qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
|
||||
* </pre>
|
||||
*
|
||||
* Once created a {@link ContentType} is <b>IMMUTABLE</b>.
|
||||
*/
|
||||
public class ContentType {
|
||||
|
||||
public static final String APPLICATION_ATOM_XML = "application/atom+xml";
|
||||
private static final Comparator<String> Q_PARAMETER_COMPARATOR = new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(final String o1, final String o2) {
|
||||
Float f1 = parseQParameterValue(o1);
|
||||
Float f2 = parseQParameterValue(o2);
|
||||
return f2.compareTo(f1);
|
||||
}
|
||||
};
|
||||
|
||||
public static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
|
||||
private static final char WHITESPACE_CHAR = ' ';
|
||||
private static final String PARAMETER_SEPARATOR = ";";
|
||||
private static final String PARAMETER_KEY_VALUE_SEPARATOR = "=";
|
||||
private static final String TYPE_SUBTYPE_SEPARATOR = "/";
|
||||
private static final String MEDIA_TYPE_WILDCARD = "*";
|
||||
|
||||
public static final String APPLICATION_JSON = "application/json";
|
||||
public static final String PARAMETER_Q = "q";
|
||||
public static final String PARAMETER_TYPE = "type";
|
||||
public static final String PARAMETER_CHARSET = "charset";
|
||||
public static final String CHARSET_UTF_8 = "UTF-8";
|
||||
|
||||
public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
|
||||
private static final Pattern Q_PARAMETER_VALUE_PATTERN = Pattern.compile("1|0|1\\.0{1,3}|0\\.\\d{1,3}");
|
||||
|
||||
public static final String MULTIPART_MIXED = "multipart/mixed";
|
||||
public static final ContentType WILDCARD = create(MEDIA_TYPE_WILDCARD, MEDIA_TYPE_WILDCARD);
|
||||
|
||||
public static final String APPLICATION_SVG_XML = "application/svg+xml";
|
||||
public static final ContentType APPLICATION_XHTML_XML = create("application", "xhtml+xml");
|
||||
public static final ContentType APPLICATION_SVG_XML = create("application", "svg+xml");
|
||||
public static final ContentType APPLICATION_FORM_URLENCODED = create("application", "x-www-form-urlencoded");
|
||||
public static final ContentType MULTIPART_FORM_DATA = create("multipart", "form-data");
|
||||
public static final ContentType TEXT_XML = create("text", "xml");
|
||||
public static final ContentType TEXT_HTML = create("text", "html");
|
||||
|
||||
public static final String APPLICATION_XHTML_XML = "application/xhtml+xml";
|
||||
public static final ContentType APPLICATION_XML = create("application", "xml");
|
||||
public static final ContentType APPLICATION_XML_CS_UTF_8 = create(APPLICATION_XML, PARAMETER_CHARSET,
|
||||
CHARSET_UTF_8);
|
||||
public static final ContentType APPLICATION_ATOM_XML = create("application", "atom+xml");
|
||||
public static final ContentType APPLICATION_ATOM_XML_CS_UTF_8 = create(APPLICATION_ATOM_XML,
|
||||
PARAMETER_CHARSET, CHARSET_UTF_8);
|
||||
public static final ContentType APPLICATION_ATOM_XML_ENTRY = create(APPLICATION_ATOM_XML, PARAMETER_TYPE, "entry");
|
||||
public static final ContentType APPLICATION_ATOM_XML_ENTRY_CS_UTF_8 = create(APPLICATION_ATOM_XML_ENTRY,
|
||||
PARAMETER_CHARSET, CHARSET_UTF_8);
|
||||
public static final ContentType APPLICATION_ATOM_XML_FEED = create(APPLICATION_ATOM_XML, PARAMETER_TYPE, "feed");
|
||||
public static final ContentType APPLICATION_ATOM_XML_FEED_CS_UTF_8 = ContentType.create(APPLICATION_ATOM_XML_FEED,
|
||||
PARAMETER_CHARSET, CHARSET_UTF_8);
|
||||
public static final ContentType APPLICATION_ATOM_SVC = create("application", "atomsvc+xml");
|
||||
public static final ContentType APPLICATION_ATOM_SVC_CS_UTF_8 = create(APPLICATION_ATOM_SVC,
|
||||
PARAMETER_CHARSET, CHARSET_UTF_8);
|
||||
public static final ContentType APPLICATION_JSON = create("application", "json");
|
||||
public static final ContentType APPLICATION_JSON_CS_UTF_8 = create(APPLICATION_JSON,
|
||||
PARAMETER_CHARSET, CHARSET_UTF_8);
|
||||
public static final ContentType APPLICATION_OCTET_STREAM = create("application", "octet-stream");
|
||||
public static final ContentType TEXT_PLAIN = create("text", "plain");
|
||||
public static final ContentType TEXT_PLAIN_CS_UTF_8 = ContentType
|
||||
.create(TEXT_PLAIN, PARAMETER_CHARSET, CHARSET_UTF_8);
|
||||
public static final ContentType MULTIPART_MIXED = create("multipart", "mixed");
|
||||
|
||||
public static final String APPLICATION_XML = "application/xml";
|
||||
private final String type;
|
||||
private final String subtype;
|
||||
private final Map<String, String> parameters;
|
||||
|
||||
public static final String MULTIPART_FORM_DATA = "multipart/form-data";
|
||||
private ContentType(final String type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Type parameter MUST NOT be null.");
|
||||
}
|
||||
this.type = validateType(type);
|
||||
subtype = null;
|
||||
parameters = Collections.emptyMap();
|
||||
}
|
||||
|
||||
public static final String TEXT_HTML = "text/html";
|
||||
/**
|
||||
* Creates a content type from type, subtype, and parameters.
|
||||
* @param type
|
||||
* @param subtype
|
||||
* @param parameters
|
||||
*/
|
||||
private ContentType(final String type, final String subtype, final Map<String, String> parameters) {
|
||||
if ((type == null || MEDIA_TYPE_WILDCARD.equals(type)) && !MEDIA_TYPE_WILDCARD.equals(subtype)) {
|
||||
throw new IllegalArgumentException("Illegal combination of WILDCARD type with NONE WILDCARD subtype.");
|
||||
}
|
||||
this.type = validateType(type);
|
||||
this.subtype = validateType(subtype);
|
||||
|
||||
public static final String TEXT_PLAIN = "text/plain";
|
||||
if (parameters == null) {
|
||||
this.parameters = Collections.emptyMap();
|
||||
} else {
|
||||
this.parameters = new TreeMap<String, String>(new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(final String o1, final String o2) {
|
||||
return o1.compareToIgnoreCase(o2);
|
||||
}
|
||||
});
|
||||
this.parameters.putAll(parameters);
|
||||
this.parameters.remove(PARAMETER_Q);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String TEXT_XML = "text/xml";
|
||||
private String validateType(final String type) {
|
||||
if (type == null || type.isEmpty()) {
|
||||
return MEDIA_TYPE_WILDCARD;
|
||||
}
|
||||
int len = type.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (type.charAt(i) == WHITESPACE_CHAR) {
|
||||
throw new IllegalArgumentException("Illegal whitespace found for type '" + type + "'.");
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static final String WILDCARD = "*/*";
|
||||
/**
|
||||
* Validates if given <code>format</code> is parseable and can be used as input for {@link #create(String)} method.
|
||||
* @param format to be validated string
|
||||
* @return <code>true</code> if format is parseable otherwise <code>false</code>
|
||||
*/
|
||||
public static boolean isParseable(final String format) {
|
||||
try {
|
||||
return ContentType.create(format) != null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final EnumMap<ODataServiceVersion, Map<String, String>> formatPerVersion =
|
||||
new EnumMap<ODataServiceVersion, Map<String, String>>(ODataServiceVersion.class);
|
||||
/**
|
||||
* Creates a content type from type and subtype.
|
||||
* @param type
|
||||
* @param subtype
|
||||
* @return a new <code>ContentType</code> object
|
||||
*/
|
||||
public static ContentType create(final String type, final String subtype) {
|
||||
return new ContentType(type, subtype, null);
|
||||
}
|
||||
|
||||
static {
|
||||
final Map<String, String> v3 = new HashMap<String, String>();
|
||||
v3.put(ODataFormat.JSON_NO_METADATA.name(), ContentType.APPLICATION_JSON + ";odata=nometadata");
|
||||
v3.put(ODataFormat.JSON.name(), ContentType.APPLICATION_JSON + ";odata=minimalmetadata");
|
||||
v3.put(ODataFormat.JSON_FULL_METADATA.name(), ContentType.APPLICATION_JSON + ";odata=fullmetadata");
|
||||
formatPerVersion.put(ODataServiceVersion.V30, v3);
|
||||
/**
|
||||
*
|
||||
* @param contentType
|
||||
* @param parameterKey
|
||||
* @param parameterValue
|
||||
* @return a new <code>ContentType</code> object
|
||||
*/
|
||||
public static ContentType create(final ContentType contentType,
|
||||
final String parameterKey, final String parameterValue) {
|
||||
ContentType ct = new ContentType(contentType.type, contentType.subtype, contentType.parameters);
|
||||
ct.parameters.put(parameterKey, parameterValue);
|
||||
return ct;
|
||||
}
|
||||
|
||||
final Map<String, String> v4 = new HashMap<String, String>();
|
||||
v4.put(ODataFormat.JSON_NO_METADATA.name(), ContentType.APPLICATION_JSON + ";odata.metadata=none");
|
||||
v4.put(ODataFormat.JSON.name(), ContentType.APPLICATION_JSON + ";odata.metadata=minimal");
|
||||
v4.put(ODataFormat.JSON_FULL_METADATA.name(), ContentType.APPLICATION_JSON + ";odata.metadata=full");
|
||||
formatPerVersion.put(ODataServiceVersion.V40, v4);
|
||||
/**
|
||||
* Create a {@link ContentType} based on given input string (<code>format</code>).
|
||||
* Supported format is <code>Media Type</code> format as defined in RFC 7231, chapter 3.1.1.1.
|
||||
* @param format a string in format as defined in RFC 7231, chapter 3.1.1.1
|
||||
* @return a new <code>ContentType</code> object
|
||||
* @throws IllegalArgumentException if input string is not parseable
|
||||
*/
|
||||
public static ContentType create(final String format) {
|
||||
if (format == null) {
|
||||
throw new IllegalArgumentException("Parameter format MUST NOT be NULL.");
|
||||
}
|
||||
|
||||
// split 'types' and 'parameters'
|
||||
String[] typesAndParameters = format.split(PARAMETER_SEPARATOR, 2);
|
||||
String types = typesAndParameters[0];
|
||||
String parameters = (typesAndParameters.length > 1 ? typesAndParameters[1] : null);
|
||||
//
|
||||
Map<String, String> parametersMap = parseParameters(parameters);
|
||||
//
|
||||
if (types.contains(TYPE_SUBTYPE_SEPARATOR)) {
|
||||
String[] tokens = types.split(TYPE_SUBTYPE_SEPARATOR);
|
||||
if (tokens.length == 2) {
|
||||
if (tokens[0] == null || tokens[0].isEmpty()) {
|
||||
throw new IllegalArgumentException("No type found in format '" + format + "'.");
|
||||
} else if (tokens[1] == null || tokens[1].isEmpty()) {
|
||||
throw new IllegalArgumentException("No subtype found in format '" + format + "'.");
|
||||
} else {
|
||||
return new ContentType(tokens[0], tokens[1], parametersMap);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Too many '" + TYPE_SUBTYPE_SEPARATOR + "' in format '" + format + "'.");
|
||||
}
|
||||
} else if (MEDIA_TYPE_WILDCARD.equals(types)) {
|
||||
return ContentType.WILDCARD;
|
||||
} else {
|
||||
throw new IllegalArgumentException("No separator '" + TYPE_SUBTYPE_SEPARATOR + "' was found in format '" + format
|
||||
+ "'.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of {@link ContentType} based on given input strings (<code>contentTypes</code>).
|
||||
*
|
||||
* Supported format is <code>Media Type</code> format as defined in RFC 7231, chapter 3.1.1.1.
|
||||
* If one of the given strings can not be parsed an exception is thrown (hence no list is returned with the parseable
|
||||
* strings).
|
||||
* @param contentTypeStrings a list of strings in format as defined in <code>RFC 2616 section 3.7</code>
|
||||
* @return a list of new <code>ContentType</code> object
|
||||
* @throws IllegalArgumentException if one of the given input string is not parseable this exceptions is thrown
|
||||
*/
|
||||
public static List<ContentType> create(final List<String> contentTypeStrings) {
|
||||
List<ContentType> contentTypes = new ArrayList<ContentType>(contentTypeStrings.size());
|
||||
for (String contentTypeString : contentTypeStrings) {
|
||||
contentTypes.add(create(contentTypeString));
|
||||
}
|
||||
return contentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given input string (<code>format</code>) and returns created {@link ContentType} if input was valid or
|
||||
* return <code>NULL</code> if input was not parseable.
|
||||
*
|
||||
* For the definition of the supported format see {@link #create(String)}.
|
||||
*
|
||||
* @param format a string in format as defined in RFC 7231, chapter 3.1.1.1
|
||||
* @return a new <code>ContentType</code> object
|
||||
*/
|
||||
public static ContentType parse(final String format) {
|
||||
try {
|
||||
return ContentType.create(format);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort given list (which must contains content-type formatted string) for their {@value #PARAMETER_Q} value
|
||||
* as defined in RFC 7231, chapter 3.1.1.1, and RFC 7231, chapter 5.3.1.
|
||||
*
|
||||
* <b>Attention:</b> For invalid values a {@value #PARAMETER_Q} value from <code>-1</code> is used for sorting.
|
||||
*
|
||||
* @param toSort list which is sorted and hence re-arranged
|
||||
*/
|
||||
public static void sortForQParameter(final List<String> toSort) {
|
||||
Collections.sort(toSort, ContentType.Q_PARAMETER_COMPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid input are <code>;</code> separated <code>key=value</code> pairs
|
||||
* without spaces between key and value.
|
||||
* <b>Attention:</b> <code>q</code> parameter is validated but not added to result map
|
||||
*
|
||||
* <p>
|
||||
* See RFC 7231:
|
||||
* The type, subtype, and parameter name tokens are case-insensitive.
|
||||
* Parameter values might or might not be case-sensitive, depending on
|
||||
* the semantics of the parameter name. The presence or absence of a
|
||||
* parameter might be significant to the processing of a media-type,
|
||||
* depending on its definition within the media type registry.
|
||||
* </p>
|
||||
*
|
||||
* @param parameters
|
||||
* @return Map with keys mapped to values
|
||||
*/
|
||||
private static Map<String, String> parseParameters(final String parameters) {
|
||||
Map<String, String> parameterMap = new HashMap<String, String>();
|
||||
if (parameters != null) {
|
||||
String[] splittedParameters = parameters.split(PARAMETER_SEPARATOR);
|
||||
for (String parameter : splittedParameters) {
|
||||
String[] keyValue = parameter.split(PARAMETER_KEY_VALUE_SEPARATOR);
|
||||
String key = keyValue[0].trim().toLowerCase(Locale.ENGLISH);
|
||||
String value = keyValue.length > 1 ? keyValue[1] : null;
|
||||
if (value != null && Character.isWhitespace(value.charAt(0))) {
|
||||
throw new IllegalArgumentException("Value of parameter '" + key + "' starts with whitespace ('" + parameters
|
||||
+ "').");
|
||||
}
|
||||
if (PARAMETER_Q.equals(key.toLowerCase(Locale.ENGLISH))) {
|
||||
// q parameter is only validated but not added
|
||||
if (!Q_PARAMETER_VALUE_PATTERN.matcher(value).matches()) {
|
||||
throw new IllegalArgumentException("Value of 'q' parameter is not valid (q='" + value + "').");
|
||||
}
|
||||
} else {
|
||||
parameterMap.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameterMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse value of {@value #PARAMETER_Q} <code>parameter</code> out of content type/parameters.
|
||||
* If no {@value #PARAMETER_Q} <code>parameter</code> is in <code>content type/parameters</code> parameter found
|
||||
* <code>1</code> is returned.
|
||||
* If {@value #PARAMETER_Q} <code>parameter</code> is invalid <code>-1</code> is returned.
|
||||
*
|
||||
* @param contentType parameter which is parsed for {@value #PARAMETER_Q} <code>parameter</code> value
|
||||
* @return value of {@value #PARAMETER_Q} <code>parameter</code> or <code>1</code> or <code>-1</code>
|
||||
*/
|
||||
private static Float parseQParameterValue(final String contentType) {
|
||||
if (contentType != null) {
|
||||
String[] splittedParameters = contentType.split(PARAMETER_SEPARATOR);
|
||||
for (String parameter : splittedParameters) {
|
||||
String[] keyValue = parameter.split(PARAMETER_KEY_VALUE_SEPARATOR);
|
||||
String key = keyValue[0].trim().toLowerCase(Locale.ENGLISH);
|
||||
if (PARAMETER_Q.equalsIgnoreCase(key)) {
|
||||
String value = keyValue.length > 1 ? keyValue[1] : null;
|
||||
if (Q_PARAMETER_VALUE_PATTERN.matcher(value).matches()) {
|
||||
return Float.valueOf(value);
|
||||
}
|
||||
return Float.valueOf(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Float.valueOf(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if parameter with key value is an allowed parameter.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private static boolean isParameterAllowed(final String key) {
|
||||
return key != null && !PARAMETER_Q.equals(key.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return parameters of this {@link ContentType} as unmodifiable map.
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
return Collections.unmodifiableMap(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ContentType}s are equal
|
||||
* <ul>
|
||||
* <li>if <code>type</code>, <code>subtype</code> and all <code>parameters</code> have the same value.</li>
|
||||
* <li>if <code>type</code> and/or <code>subtype</code> is set to "*" (in such a case the <code>parameters</code> are
|
||||
* ignored).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
// NULL validation is done in method 'isEqualWithoutParameters(obj)'
|
||||
Boolean compatible = isEqualWithoutParameters(obj);
|
||||
|
||||
if (compatible == null) {
|
||||
ContentType other = (ContentType) obj;
|
||||
|
||||
// parameter checks
|
||||
if (parameters == null) {
|
||||
if (other.parameters != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (parameters.size() == other.parameters.size()) {
|
||||
Iterator<Entry<String, String>> entries = parameters.entrySet().iterator();
|
||||
Iterator<Entry<String, String>> otherEntries = other.parameters.entrySet().iterator();
|
||||
while (entries.hasNext()) {
|
||||
Entry<String, String> e = entries.next();
|
||||
Entry<String, String> oe = otherEntries.next();
|
||||
|
||||
if (!areEqual(e.getKey(), oe.getKey())) {
|
||||
return false;
|
||||
}
|
||||
if (!areEqual(e.getValue(), oe.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// all tests run
|
||||
return compatible.booleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ContentType}s are <b>compatible</b>
|
||||
* <ul>
|
||||
* <li>if <code>type</code>, <code>subtype</code> have the same value.</li>
|
||||
* <li>if <code>type</code> and/or <code>subtype</code> is set to "*"</li>
|
||||
* </ul>
|
||||
* The set <code>parameters</code> are <b>always</b> ignored (for compare with parameters see {@link #equals(Object)}
|
||||
* ).
|
||||
*
|
||||
* @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>.
|
||||
*/
|
||||
public boolean isCompatible(final ContentType obj) {
|
||||
Boolean compatible = isEqualWithoutParameters(obj);
|
||||
if (compatible == null) {
|
||||
return true;
|
||||
}
|
||||
return compatible.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equal without parameters.
|
||||
* It is possible that no decision about <code>equal/none equal</code> can be determined a <code>NULL</code> is
|
||||
* returned.
|
||||
*
|
||||
* @param obj to checked object
|
||||
* @return <code>true</code> if both instances are equal (see definition above), otherwise <code>false</code>
|
||||
* or <code>NULL</code> if no decision about <code>equal/none equal</code> could be determined.
|
||||
*/
|
||||
private Boolean isEqualWithoutParameters(final Object obj) {
|
||||
// basic checks
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentType other = (ContentType) obj;
|
||||
|
||||
// subtype checks
|
||||
if (subtype == null) {
|
||||
if (other.subtype != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!subtype.equals(other.subtype)) {
|
||||
if (other.subtype == null) {
|
||||
return false;
|
||||
} else if (!subtype.equals(MEDIA_TYPE_WILDCARD) && !other.subtype.equals(MEDIA_TYPE_WILDCARD)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// type checks
|
||||
if (type == null) {
|
||||
if (other.type != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!type.equals(other.type)) {
|
||||
if (!type.equals(MEDIA_TYPE_WILDCARD) && !other.type.equals(MEDIA_TYPE_WILDCARD)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if wildcards are set, content types are defined as 'equal'
|
||||
if (countWildcards() > 0 || other.countWildcards() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether both string are equal ignoring the case of the strings.
|
||||
*
|
||||
* @param first first string
|
||||
* @param second second string
|
||||
* @return <code>true</code> if both strings are equal (by ignoring the case), otherwise <code>false</code> is
|
||||
* returned
|
||||
*/
|
||||
private static boolean areEqual(final String first, final String second) {
|
||||
if (first == null) {
|
||||
if (second != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!first.equalsIgnoreCase(second)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link ContentType} as string as defined in RFC 7231 (http://www.ietf.org/rfc/rfc7231.txt, chapter 3.1.1.1:
|
||||
* Media Type)
|
||||
* @return string representation of <code>ContentType</code> object
|
||||
*/
|
||||
public String toContentTypeString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(type).append(TYPE_SUBTYPE_SEPARATOR).append(subtype);
|
||||
|
||||
for (String key : parameters.keySet()) {
|
||||
if (isParameterAllowed(key)) {
|
||||
String value = parameters.get(key);
|
||||
sb.append(";").append(key).append("=").append(value);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toContentTypeString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find best match between this {@link ContentType} and the {@link ContentType} in the list.
|
||||
* If a match (this {@link ContentType} is equal to a {@link ContentType} in list) is found either this or the
|
||||
* {@link ContentType} from the list is returned based on which {@link ContentType} has less "**" characters set
|
||||
* (checked with {@link #compareWildcardCounts(ContentType)}.
|
||||
* If no match (none {@link ContentType} in list is equal to this {@link ContentType}) is found <code>NULL</code> is
|
||||
* returned.
|
||||
*
|
||||
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
|
||||
* @return best matched content type in list or <code>NULL</code> if none content type match to this content type
|
||||
* instance
|
||||
*/
|
||||
public ContentType match(final List<ContentType> toMatchContentTypes) {
|
||||
for (ContentType supportedContentType : toMatchContentTypes) {
|
||||
if (equals(supportedContentType)) {
|
||||
if (compareWildcardCounts(supportedContentType) < 0) {
|
||||
return this;
|
||||
} else {
|
||||
return supportedContentType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find best match between this {@link ContentType} and the {@link ContentType} in the list ignoring all set
|
||||
* parameters.
|
||||
* If a match (this {@link ContentType} is equal to a {@link ContentType} in list) is found either this or the
|
||||
* {@link ContentType} from the list is returned based on which {@link ContentType} has less "**" characters set
|
||||
* (checked with {@link #compareWildcardCounts(ContentType)}.
|
||||
* If no match (none {@link ContentType} in list is equal to this {@link ContentType}) is found <code>NULL</code> is
|
||||
* returned.
|
||||
*
|
||||
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
|
||||
* @return best matched content type in list or <code>NULL</code> if none content type match to this content type
|
||||
* instance
|
||||
*/
|
||||
public ContentType matchCompatible(final List<ContentType> toMatchContentTypes) {
|
||||
for (ContentType supportedContentType : toMatchContentTypes) {
|
||||
if (isCompatible(supportedContentType)) {
|
||||
if (compareWildcardCounts(supportedContentType) < 0) {
|
||||
return this;
|
||||
} else {
|
||||
return supportedContentType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a valid compatible match for this {@link ContentType} exists in given list.
|
||||
* Compatible in this case means that <b>all set parameters are ignored</b>.
|
||||
* For more detail what a valid match is see {@link #matchCompatible(List)}.
|
||||
*
|
||||
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
|
||||
* @return <code>true</code> if a compatible content type was found in given list
|
||||
* or <code>false</code> if none compatible content type match was found
|
||||
*/
|
||||
public boolean hasCompatible(final List<ContentType> toMatchContentTypes) {
|
||||
return matchCompatible(toMatchContentTypes) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a valid match for this {@link ContentType} exists in given list.
|
||||
* For more detail what a valid match is see {@link #match(List)}.
|
||||
*
|
||||
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
|
||||
* @return <code>true</code> if a matching content type was found in given list
|
||||
* or <code>false</code> if none matching content type match was found
|
||||
*/
|
||||
public boolean hasMatch(final List<ContentType> toMatchContentTypes) {
|
||||
return match(toMatchContentTypes) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare wildcards counts/weights of both {@link ContentType}.
|
||||
*
|
||||
* The smaller {@link ContentType} has lesser weighted wildcards then the bigger {@link ContentType}.
|
||||
* As result this method returns this object weighted wildcards minus the given parameter object weighted wildcards.
|
||||
*
|
||||
* A type wildcard is weighted with <code>2</code> and a subtype wildcard is weighted with <code>1</code>.
|
||||
*
|
||||
* @param otherContentType {@link ContentType} to be compared to
|
||||
* @return this object weighted wildcards minus the given parameter object weighted wildcards.
|
||||
*/
|
||||
public int compareWildcardCounts(final ContentType otherContentType) {
|
||||
return countWildcards() - otherContentType.countWildcards();
|
||||
}
|
||||
|
||||
private int countWildcards() {
|
||||
int count = 0;
|
||||
if (MEDIA_TYPE_WILDCARD.equals(type)) {
|
||||
count += 2;
|
||||
}
|
||||
if (MEDIA_TYPE_WILDCARD.equals(subtype)) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>true</code> if <code>type</code> or <code>subtype</code> of this instance is a "*".
|
||||
*/
|
||||
public boolean hasWildcard() {
|
||||
return (MEDIA_TYPE_WILDCARD.equals(type) || MEDIA_TYPE_WILDCARD.equals(subtype));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>true</code> if both <code>type</code> and <code>subtype</code> of this instance are a "*".
|
||||
*/
|
||||
public boolean isWildcard() {
|
||||
return (MEDIA_TYPE_WILDCARD.equals(type) && MEDIA_TYPE_WILDCARD.equals(subtype));
|
||||
}
|
||||
|
||||
public static List<ContentType> convert(final List<String> types) {
|
||||
List<ContentType> results = new ArrayList<ContentType>();
|
||||
for (String contentType : types) {
|
||||
results.add(ContentType.create(contentType));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a valid match for given content type formated string (<code>toMatch</code>) exists in given list.
|
||||
* Therefore the given content type formated string (<code>toMatch</code>) is converted into a {@link ContentType}
|
||||
* with a simple {@link #create(String)} call (during which an exception can occur).
|
||||
*
|
||||
* For more detail in general see {@link #hasMatch(List)} and for what a valid match is see {@link #match(List)}.
|
||||
*
|
||||
* @param toMatch content type formated string (<code>toMatch</code>) for which is checked if a match exists in given
|
||||
* list
|
||||
* @param matchExamples list of {@link ContentType}s which are matches against content type formated string
|
||||
* (<code>toMatch</code>)
|
||||
* @return <code>true</code> if a matching content type was found in given list
|
||||
* or <code>false</code> if none matching content type match was found
|
||||
*/
|
||||
public static boolean match(final String toMatch, final ContentType... matchExamples) {
|
||||
ContentType toMatchContentType = ContentType.create(toMatch);
|
||||
|
||||
return toMatchContentType.hasMatch(Arrays.asList(matchExamples));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ package org.apache.olingo.commons.api.format;
|
|||
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Available formats to be used in various contexts.
|
||||
*/
|
||||
|
@ -38,8 +41,6 @@ public enum ODataFormat {
|
|||
ATOM(ContentType.APPLICATION_ATOM_XML),
|
||||
|
||||
// media formats
|
||||
MEDIA_TYPE_WILDCARD("*"),
|
||||
WILDCARD(ContentType.WILDCARD),
|
||||
APPLICATION_XML(ContentType.APPLICATION_XML),
|
||||
APPLICATION_ATOM_XML(ContentType.APPLICATION_ATOM_XML),
|
||||
APPLICATION_XHTML_XML(ContentType.APPLICATION_XHTML_XML),
|
||||
|
@ -52,79 +53,127 @@ public enum ODataFormat {
|
|||
TEXT_XML(ContentType.TEXT_XML),
|
||||
TEXT_HTML(ContentType.TEXT_HTML);
|
||||
|
||||
private final String format;
|
||||
private static final String JSON_METADATA_PARAMETER_V3 = "odata";
|
||||
private static final String JSON_METADATA_PARAMETER_V4 = "odata.metadata";
|
||||
|
||||
ODataFormat() {
|
||||
this.format = null;
|
||||
private static final Map<ODataServiceVersion, Map<ODataFormat, ContentType>> FORMAT_PER_VERSION = new
|
||||
HashMap<ODataServiceVersion, Map<ODataFormat, ContentType>>();
|
||||
|
||||
static {
|
||||
final Map<ODataFormat, ContentType> v3 = new HashMap<ODataFormat, ContentType>();
|
||||
v3.put(ODataFormat.JSON_NO_METADATA, ContentType.create(
|
||||
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "nometadata"));
|
||||
v3.put(ODataFormat.JSON, ContentType.create(
|
||||
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "minimalmetadata"));
|
||||
v3.put(ODataFormat.JSON_FULL_METADATA, ContentType.create(
|
||||
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "fullmetadata"));
|
||||
FORMAT_PER_VERSION.put(ODataServiceVersion.V30, v3);
|
||||
|
||||
final Map<ODataFormat, ContentType> v4 = new HashMap<ODataFormat, ContentType>();
|
||||
v4.put(ODataFormat.JSON_NO_METADATA, ContentType.create(
|
||||
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "none"));
|
||||
v4.put(ODataFormat.JSON, ContentType.create(
|
||||
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "minimal"));
|
||||
v4.put(ODataFormat.JSON_FULL_METADATA, ContentType.create(
|
||||
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "full"));
|
||||
FORMAT_PER_VERSION.put(ODataServiceVersion.V40, v4);
|
||||
}
|
||||
|
||||
ODataFormat(final String format) {
|
||||
this.format = format;
|
||||
private final ContentType contentType;
|
||||
|
||||
ODataFormat(final ContentType contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
ODataFormat() {
|
||||
this.contentType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets format as a string.
|
||||
*
|
||||
* Gets format as {@link ContentType}.
|
||||
* @param version OData service version.
|
||||
* @return format as a string.
|
||||
* @return format as ContentType.
|
||||
*/
|
||||
public String toString(final ODataServiceVersion version) {
|
||||
public ContentType getContentType(final ODataServiceVersion version) {
|
||||
if (version.ordinal() < ODataServiceVersion.V30.ordinal()) {
|
||||
throw new IllegalArgumentException("Unsupported version " + version);
|
||||
}
|
||||
|
||||
return format == null ? ContentType.formatPerVersion.get(version).get(this.name()) : format;
|
||||
return contentType == null ? FORMAT_PER_VERSION.get(version).get(this) : contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (format == null) {
|
||||
if (contentType == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
} else {
|
||||
return format;
|
||||
return contentType.toContentTypeString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets OData format from its string representation.
|
||||
* Gets OData format from a content type.
|
||||
*
|
||||
* @param format string representation of the format.
|
||||
* @param contentType content type
|
||||
* @return OData format.
|
||||
*/
|
||||
public static ODataFormat fromString(final String format) {
|
||||
ODataFormat result = null;
|
||||
|
||||
final StringBuffer _format = new StringBuffer();
|
||||
|
||||
final String[] parts = format.split(";");
|
||||
_format.append(parts[0].trim());
|
||||
if (ContentType.APPLICATION_JSON.equals(parts[0].trim())) {
|
||||
if (parts.length > 1) {
|
||||
if (parts[1].trim().equalsIgnoreCase("charset=UTF-8")) {
|
||||
result = ODataFormat.JSON;
|
||||
} else {
|
||||
_format.append(';').append(parts[1].trim());
|
||||
}
|
||||
} else {
|
||||
result = ODataFormat.JSON;
|
||||
}
|
||||
public static ODataFormat fromContentType(final ContentType contentType) {
|
||||
if (contentType == null) {
|
||||
return null;
|
||||
}
|
||||
if (contentType.hasWildcard()) {
|
||||
throw new IllegalArgumentException("Content Type must be fully specified!");
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
final String candidate = _format.toString();
|
||||
for (ODataFormat value : values()) {
|
||||
if (candidate.equals(value.toString(ODataServiceVersion.V30))
|
||||
|| candidate.equals(value.toString(ODataServiceVersion.V40))) {
|
||||
result = value;
|
||||
break;
|
||||
if (contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)
|
||||
|| contentType.isCompatible(ContentType.APPLICATION_ATOM_SVC)) {
|
||||
return ATOM;
|
||||
} else if (contentType.isCompatible(ContentType.APPLICATION_XML)) {
|
||||
return XML;
|
||||
} else if (contentType.isCompatible(ContentType.APPLICATION_JSON)) {
|
||||
String jsonVariant = contentType.getParameters().get(JSON_METADATA_PARAMETER_V3);
|
||||
if (jsonVariant != null) {
|
||||
for (ODataFormat candidate : FORMAT_PER_VERSION.get(ODataServiceVersion.V30).keySet()) {
|
||||
if (FORMAT_PER_VERSION.get(ODataServiceVersion.V30).get(candidate).getParameters()
|
||||
.get(JSON_METADATA_PARAMETER_V3)
|
||||
.equals(jsonVariant)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
jsonVariant = contentType.getParameters().get(JSON_METADATA_PARAMETER_V4);
|
||||
if (jsonVariant != null) {
|
||||
for (ODataFormat candidate : FORMAT_PER_VERSION.get(ODataServiceVersion.V40).keySet()) {
|
||||
if (FORMAT_PER_VERSION.get(ODataServiceVersion.V40).get(candidate).getParameters()
|
||||
.get(JSON_METADATA_PARAMETER_V4)
|
||||
.equals(jsonVariant)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JSON;
|
||||
} else if (contentType.isCompatible(ContentType.APPLICATION_OCTET_STREAM)) {
|
||||
return APPLICATION_OCTET_STREAM;
|
||||
} else if (contentType.isCompatible(ContentType.TEXT_PLAIN)) {
|
||||
return TEXT_PLAIN;
|
||||
} else if (contentType.isCompatible(ContentType.APPLICATION_XHTML_XML)) {
|
||||
return APPLICATION_XHTML_XML;
|
||||
} else if (contentType.isCompatible(ContentType.APPLICATION_SVG_XML)) {
|
||||
return APPLICATION_SVG_XML;
|
||||
} else if (contentType.isCompatible(ContentType.APPLICATION_FORM_URLENCODED)) {
|
||||
return APPLICATION_FORM_URLENCODED;
|
||||
} else if (contentType.isCompatible(ContentType.MULTIPART_FORM_DATA)) {
|
||||
return MULTIPART_FORM_DATA;
|
||||
} else if (contentType.isCompatible(ContentType.TEXT_XML)) {
|
||||
return TEXT_XML;
|
||||
} else if (contentType.isCompatible(ContentType.TEXT_HTML)) {
|
||||
return TEXT_HTML;
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("Unsupported format: " + format);
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported content Type: " + contentType);
|
||||
}
|
||||
|
||||
return result;
|
||||
public static ODataFormat fromString(final String contentType) {
|
||||
return contentType == null ? null : fromContentType(ContentType.parse(contentType));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -319,7 +319,7 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
|
|||
writer.writeStartElement(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.PROPERTIES);
|
||||
properties(writer, entity.getProperties());
|
||||
} else {
|
||||
writer.writeAttribute(Constants.ATTR_TYPE, ContentType.APPLICATION_XML);
|
||||
writer.writeAttribute(Constants.ATTR_TYPE, ContentType.APPLICATION_XML.toContentTypeString());
|
||||
writer.writeStartElement(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.PROPERTIES);
|
||||
properties(writer, entity.getProperties());
|
||||
writer.writeEndElement();
|
||||
|
|
|
@ -72,7 +72,7 @@ public class SampleJsonProcessor implements EntitySetProcessor, EntityProcessor
|
|||
LOG.info("Finished in " + (System.nanoTime() - time) / 1000 + " microseconds");
|
||||
|
||||
response.setStatusCode(200);
|
||||
response.setHeader("Content-Type", ContentType.APPLICATION_JSON);
|
||||
response.setHeader("Content-Type", ContentType.APPLICATION_JSON.toContentTypeString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,7 +91,7 @@ public class SampleJsonProcessor implements EntitySetProcessor, EntityProcessor
|
|||
LOG.info("Finished in " + (System.nanoTime() - time) / 1000 + " microseconds");
|
||||
|
||||
response.setStatusCode(200);
|
||||
response.setHeader("Content-Type", ContentType.APPLICATION_JSON);
|
||||
response.setHeader("Content-Type", ContentType.APPLICATION_JSON.toContentTypeString());
|
||||
}
|
||||
|
||||
protected Entity createEntity() {
|
||||
|
|
Loading…
Reference in New Issue