Fixing incorrect millis <-> nanos conversion (with Timestamp) in EdmDateTimeOffset

This commit is contained in:
Francesco Chicchiriccò 2014-04-17 15:57:56 +02:00
parent 3bb39defd4
commit b157e26caf
3 changed files with 99 additions and 79 deletions

View File

@ -18,14 +18,19 @@
*/
package org.apache.olingo.client.core.it.v3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import org.apache.olingo.client.api.communication.ODataClientErrorException;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
import org.apache.olingo.client.api.communication.request.retrieve.ODataPropertyRequest;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.uri.CommonURIBuilder;
import org.apache.olingo.client.api.uri.v3.URIBuilder;
import org.apache.olingo.commons.api.domain.CommonODataEntity;
import org.apache.olingo.commons.api.domain.CommonODataEntitySet;
import org.apache.olingo.commons.api.domain.ODataCollectionValue;
import org.apache.olingo.commons.api.domain.ODataComplexValue;
import org.apache.olingo.commons.api.domain.ODataPrimitiveValue;
@ -33,17 +38,12 @@ import org.apache.olingo.commons.api.domain.v3.ODataEntity;
import org.apache.olingo.commons.api.domain.v3.ODataEntitySet;
import org.apache.olingo.commons.api.domain.v3.ODataProperty;
import org.apache.olingo.commons.api.format.ODataFormat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
public class PropertyRetrieveTestITCase extends AbstractTestITCase {
private void retreivePropertyTest(final ODataFormat format, String entitySegment, String structuralSegment) {
final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
private void retrievePropertyTest(final ODataFormat format, String entitySegment, String structuralSegment) {
final URIBuilder uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
appendEntitySetSegment(entitySegment).appendPropertySegment(structuralSegment);
final ODataPropertyRequest<ODataProperty> req = client.getRetrieveRequestFactory().
getPropertyRequest(uriBuilder.build());
@ -75,48 +75,48 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void jsonRetrieveProperty() {
//Primitive types
retreivePropertyTest(ODataFormat.JSON, "Customer(-10)", "Name");
retreivePropertyTest(ODataFormat.JSON, "Customer(-10)", "CustomerId");
retreivePropertyTest(ODataFormat.JSON, "Message(FromUsername='1',MessageId=-10)", "Sent");
retreivePropertyTest(ODataFormat.JSON, "Message(FromUsername='1',MessageId=-10)", "IsRead");
retrievePropertyTest(ODataFormat.JSON, "Customer(-10)", "Name");
retrievePropertyTest(ODataFormat.JSON, "Customer(-10)", "CustomerId");
retrievePropertyTest(ODataFormat.JSON, "Message(FromUsername='1',MessageId=-10)", "Sent");
retrievePropertyTest(ODataFormat.JSON, "Message(FromUsername='1',MessageId=-10)", "IsRead");
//Collection of Complex types
retreivePropertyTest(ODataFormat.JSON, "Customer(-10)", "BackupContactInfo");
retrievePropertyTest(ODataFormat.JSON, "Customer(-10)", "BackupContactInfo");
//Collection of primitives
retreivePropertyTest(ODataFormat.JSON, "Customer(-10)/PrimaryContactInfo", "EmailBag");
retrievePropertyTest(ODataFormat.JSON, "Customer(-10)/PrimaryContactInfo", "EmailBag");
//complex types
retreivePropertyTest(ODataFormat.JSON, "Order(-9)", "Concurrency");
retrievePropertyTest(ODataFormat.JSON, "Order(-9)", "Concurrency");
}
//test with json full metadata
@Test
public void jsonFullMetadataRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)", "Name");
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)", "CustomerId");
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Message(FromUsername='1',MessageId=-10)", "Sent");
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Message(FromUsername='1',MessageId=-10)", "IsRead");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)", "Name");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)", "CustomerId");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Message(FromUsername='1',MessageId=-10)", "Sent");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Message(FromUsername='1',MessageId=-10)", "IsRead");
//Collection of Complex types
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)", "BackupContactInfo");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)", "BackupContactInfo");
//Collection of primitives
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)/PrimaryContactInfo", "EmailBag");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customer(-10)/PrimaryContactInfo", "EmailBag");
//Complex types
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Order(-9)", "Concurrency");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Order(-9)", "Concurrency");
}
// json with no metadata
@Test
public void jsonNoMetadataRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "Name");
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "CustomerId");
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "Sent");
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "IsRead");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "Name");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "CustomerId");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "Sent");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "IsRead");
//Collection of Complex types
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "BackupContactInfo");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "BackupContactInfo");
//Collection of Primitives
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)/PrimaryContactInfo", "EmailBag");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)/PrimaryContactInfo", "EmailBag");
//Complex types
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Order(-9)", "Concurrency");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Order(-9)", "Concurrency");
}
// json with minimla metadata
@ -124,55 +124,55 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void jsonmininalRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "Name");
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "CustomerId");
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "Sent");
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "IsRead");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "Name");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "CustomerId");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "Sent");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Message(FromUsername='1',MessageId=-10)", "IsRead");
//Collection of complex types
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "BackupContactInfo");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)", "BackupContactInfo");
//Collection of primitives
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)/PrimaryContactInfo", "EmailBag");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Customer(-10)/PrimaryContactInfo", "EmailBag");
//Complex types
retreivePropertyTest(ODataFormat.JSON_NO_METADATA, "Order(-9)", "Concurrency");
retrievePropertyTest(ODataFormat.JSON_NO_METADATA, "Order(-9)", "Concurrency");
}
// with xml header
@Test
public void xmlRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.XML, "Customer(-10)", "Name");
retreivePropertyTest(ODataFormat.XML, "Customer(-10)", "CustomerId");
retreivePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "Sent");
retreivePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "IsRead");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)", "Name");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)", "CustomerId");
retrievePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "Sent");
retrievePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "IsRead");
//Collection of Complex types
retreivePropertyTest(ODataFormat.XML, "Customer(-10)", "BackupContactInfo");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)", "BackupContactInfo");
//Collection of primitives
retreivePropertyTest(ODataFormat.XML, "Customer(-10)/PrimaryContactInfo", "EmailBag");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)/PrimaryContactInfo", "EmailBag");
//Complex types
retreivePropertyTest(ODataFormat.XML, "Order(-9)", "Concurrency");
retrievePropertyTest(ODataFormat.XML, "Order(-9)", "Concurrency");
}
// with atom header
@Test
public void atomRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.XML, "Customer(-10)", "Name");
retreivePropertyTest(ODataFormat.XML, "Customer(-10)", "CustomerId");
retreivePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "Sent");
retreivePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "IsRead");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)", "Name");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)", "CustomerId");
retrievePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "Sent");
retrievePropertyTest(ODataFormat.XML, "Message(FromUsername='1',MessageId=-10)", "IsRead");
//Collection of Complex types
retreivePropertyTest(ODataFormat.XML, "Customer(-10)", "BackupContactInfo");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)", "BackupContactInfo");
//Collection of primitives
retreivePropertyTest(ODataFormat.XML, "Customer(-10)/PrimaryContactInfo", "EmailBag");
retrievePropertyTest(ODataFormat.XML, "Customer(-10)/PrimaryContactInfo", "EmailBag");
//complex types
retreivePropertyTest(ODataFormat.XML, "Order(-9)", "Concurrency");
retrievePropertyTest(ODataFormat.XML, "Order(-9)", "Concurrency");
}
// with invalid structural segment
@Test
public void invalidSegmentRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.XML, "Customers(-10)", "Name");
retrievePropertyTest(ODataFormat.XML, "Customers(-10)", "Name");
}
// with null pub format
@ -180,7 +180,7 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void nullSegmentRetrieveProperty() {
//primitive types
retreivePropertyTest(null, "Customers(-10)", "Name");
retrievePropertyTest(null, "Customers(-10)", "Name");
}
// with null accept header format
@ -188,7 +188,7 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void nullAcceptRetrieveProperty() {
//primitive types
retreivePropertyTest(ODataFormat.XML, "Customers(-10)", "Name");
retrievePropertyTest(ODataFormat.XML, "Customers(-10)", "Name");
}
// with json pub format and atom accept format
@ -196,7 +196,7 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void differentFormatAndAcceptRetrieveProperty() {
//
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customers(-10)", "Name");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Customers(-10)", "Name");
}
//bad request 400 error. Message takes two keys
@ -204,22 +204,22 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void badRequestTest() {
//primitive types
retreivePropertyTest(ODataFormat.JSON_FULL_METADATA, "Message(FromUsername='1')", "Sent");
retrievePropertyTest(ODataFormat.JSON_FULL_METADATA, "Message(FromUsername='1')", "Sent");
}
//navigation link of stream
@Test
public void navigationMediaLink() {
CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
final URIBuilder uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
appendNavigationSegment("Product").appendKeySegment(-7).appendLinksSegment("Photos");
ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().
final ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().
getEntitySetRequest(uriBuilder.build());
req.setAccept("application/json");
ODataRetrieveResponse<ODataEntitySet> res = req.execute();
final ODataRetrieveResponse<ODataEntitySet> res = req.execute();
assertEquals(200, res.getStatusCode());
CommonODataEntitySet entitySet = res.getBody();
final ODataEntitySet entitySet = res.getBody();
assertNotNull(entitySet);
List<? extends CommonODataEntity> entity = entitySet.getEntities();
final List<? extends CommonODataEntity> entity = entitySet.getEntities();
assertNotNull(entity);
assertEquals(entity.size(), 2);
assertEquals(testStaticServiceRootURL + "/ProductPhoto(PhotoId=-3,ProductId=-3)",
@ -234,17 +234,17 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void navigationMediaLinkInvalidQuery() {
CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
final URIBuilder uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
appendNavigationSegment("Product").appendKeySegment(-7).appendLinksSegment("Photo");
ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().
final ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().
getEntitySetRequest(uriBuilder.build());
req.setAccept("application/json");
try {
ODataRetrieveResponse<ODataEntitySet> res = req.execute();
final ODataRetrieveResponse<ODataEntitySet> res = req.execute();
assertEquals(200, res.getStatusCode());
CommonODataEntitySet entitySet = res.getBody();
ODataEntitySet entitySet = res.getBody();
assertNotNull(entitySet);
List<? extends CommonODataEntity> entity = entitySet.getEntities();
final List<? extends CommonODataEntity> entity = entitySet.getEntities();
assertNotNull(entity);
assertEquals(entity.size(), 2);
assertEquals(testStaticServiceRootURL + "/ProductPhoto(PhotoId=-3,ProductId=-3)", entity.get(0).
@ -258,17 +258,17 @@ public class PropertyRetrieveTestITCase extends AbstractTestITCase {
@Test
public void navigationMediaLinkInvalidFormat() {
CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
final URIBuilder uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
appendNavigationSegment("Product").appendKeySegment(-7).appendLinksSegment("Photos");
ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().
final ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().
getEntitySetRequest(uriBuilder.build());
req.setAccept("application/atom+xml");
try {
ODataRetrieveResponse<ODataEntitySet> res = req.execute();
final ODataRetrieveResponse<ODataEntitySet> res = req.execute();
assertEquals(200, res.getStatusCode());
ODataEntitySet entitySet = res.getBody();
final ODataEntitySet entitySet = res.getBody();
assertNotNull(entitySet);
List<ODataEntity> entity = entitySet.getEntities();
final List<ODataEntity> entity = entitySet.getEntities();
assertNotNull(entity);
assertEquals(entity.size(), 2);
assertEquals(testStaticServiceRootURL + "/ProductPhoto(PhotoId=-3,ProductId=-3)", entity.get(0).

View File

@ -152,9 +152,13 @@ public class PrimitiveValueTest extends AbstractTest {
assertEquals(expected.get(Calendar.MILLISECOND), asCalendar.get(Calendar.MILLISECOND));
final Timestamp asTimestamp = value.asPrimitive().toCastValue(Timestamp.class);
assertEquals(expected.get(Calendar.MILLISECOND), asTimestamp.getNanos());
assertEquals(expected.get(Calendar.MILLISECOND), asTimestamp.getNanos() / 1000000);
assertEquals("2013-01-10T02:00:00.022Z", value.asPrimitive().toString());
final ODataValue parsed = getClient().getObjectFactory().newPrimitiveValueBuilder().
setType(EdmPrimitiveTypeKind.DateTimeOffset).setText(value.asPrimitive().toString()).build();
assertEquals("2013-01-10T03:00:00.022+01:00", parsed.asPrimitive().toString());
}
@Test

View File

@ -19,6 +19,7 @@
package org.apache.olingo.commons.core.edm.primitivetype;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
@ -32,6 +33,13 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
*/
public final class EdmDateTimeOffset extends SingletonPrimitiveType {
public static final ThreadLocal<DecimalFormat> NANO_FORMAT = new ThreadLocal<DecimalFormat>() {
@Override
protected DecimalFormat initialValue() {
return new DecimalFormat("000000000");
}
};
private static final Pattern PATTERN = Pattern.compile(
"(-?\\p{Digit}{4,})-(\\p{Digit}{2})-(\\p{Digit}{2})"
+ "T(\\p{Digit}{2}):(\\p{Digit}{2})(?::(\\p{Digit}{2})(\\.(\\p{Digit}{0,12}?)0*)?)?"
@ -95,7 +103,9 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
dateTimeValue.set(Calendar.MILLISECOND, Short.parseShort(milliSeconds));
if (!decimals.isEmpty()) {
timestamp.setNanos(Integer.parseInt(decimals));
final int fractionalSecs = Integer.parseInt(decimals);
// if fractional are just milliseconds, convert to nanoseconds
timestamp.setNanos(fractionalSecs < 1000 ? fractionalSecs * 1000000 : fractionalSecs);
}
}
@ -253,8 +263,13 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
*/
protected static void appendMilliseconds(final StringBuilder result, final int milliseconds,
final Integer precision) throws IllegalArgumentException {
final int digits = milliseconds % 1000 == 0 ? 0 : milliseconds % 100 == 0 ? 1 : milliseconds % 10 == 0 ? 2 : 3;
if (digits > 0) {
if (precision == null || precision < digits) {
throw new IllegalArgumentException();
}
result.append('.');
for (int d = 100; d > 0; d /= 10) {
final byte digit = (byte) (milliseconds % (d * 10) / d);
@ -262,10 +277,6 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
result.append((char) ('0' + digit));
}
}
if (precision == null || precision < digits) {
throw new IllegalArgumentException();
}
}
}
@ -285,7 +296,12 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType {
throw new IllegalArgumentException();
}
result.append('.').append(fractionalSeconds);
String fractionals = NANO_FORMAT.get().format(fractionalSeconds);
// Keep output similar to Calendar's, if possible
if ("000000".equals(fractionals.substring(3))) {
fractionals = fractionals.substring(0, 3);
}
result.append('.').append(fractionals);
}
}
}