diff --git a/fit/src/test/java/org/apache/olingo/fit/base/MetadataTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/base/MetadataTestITCase.java index 184593d3e..46687cce5 100644 --- a/fit/src/test/java/org/apache/olingo/fit/base/MetadataTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/base/MetadataTestITCase.java @@ -41,7 +41,7 @@ public class MetadataTestITCase extends AbstractTestITCase { final Edm edm = client.getRetrieveRequestFactory(). getMetadataRequest(testVocabulariesServiceRootURL).execute().getBody(); assertNotNull(edm); - + final EdmTerm isLanguageDependent = edm.getTerm(new FullQualifiedName("Core", "IsLanguageDependent")); assertNotNull(isLanguageDependent); assertTrue(isLanguageDependent.getAppliesTo().contains(TargetType.Property)); diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java index 8694605d4..57bd7af19 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java @@ -142,7 +142,7 @@ public final class AsyncSupportITCase extends AbstractParamTecSvcITCase { .getEntitySetRequest(uri).execute(); assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); ClientEntitySet responseBody = response.getBody(); - assertEquals(3, responseBody.getEntities().size()); + assertEquals(4, responseBody.getEntities().size()); checkEntityAvailableWith(responseBody, "PropertyInt16", 32767); // first async request @@ -174,7 +174,7 @@ public final class AsyncSupportITCase extends AbstractParamTecSvcITCase { ResWrap firWrap = client.getDeserializer(getContentType()) .toEntitySet(firstResponse.getRawResponse()); EntityCollection firstResponseEntitySet = firWrap.getPayload(); - assertEquals(3, firstResponseEntitySet.getEntities().size()); + assertEquals(4, firstResponseEntitySet.getEntities().size()); Entity firstResponseEntity = firstResponseEntitySet.getEntities().get(0); assertShortOrInt(32767, firstResponseEntity.getProperty("PropertyInt16").asPrimitive()); assertEquals("First Resource - positive values", firstResponseEntity.getProperty("PropertyString").asPrimitive()); diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java index e04f668b6..fdde68a64 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java @@ -294,7 +294,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { assertNotNull(res); final ResWrap entitySet = res.getBodyAs(ClientEntitySet.class); - assertEquals(3, entitySet.getPayload().getEntities().size()); + assertEquals(4, entitySet.getPayload().getEntities().size()); } @Test @@ -1303,7 +1303,6 @@ public class BasicITCase extends AbstractParamTecSvcITCase { getFactory().newPrimitiveValueBuilder().buildInt64(null))); entity.getProperties().add(getFactory().newPrimitiveProperty(PROPERTY_DECIMAL, getFactory().newPrimitiveValueBuilder().buildDecimal(null))); - final ODataEntityUpdateRequest requestUpdate = getEdmEnabledClient().getCUDRequestFactory() .getEntityUpdateRequest(uri, UpdateType.PATCH, entity); requestUpdate.setContentType(CONTENT_TYPE_JSON_IEEE754_COMPATIBLE); @@ -1456,7 +1455,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); final List entities = response.getBody().getEntities(); - assertEquals(3, entities.size()); + assertEquals(4, entities.size()); ClientEntity entity = entities.get(0); assertEquals(-32768, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); @@ -1469,6 +1468,11 @@ public class BasicITCase extends AbstractParamTecSvcITCase { assertEquals(BigDecimal.valueOf(0), entity.getProperty(PROPERTY_DECIMAL).getPrimitiveValue().toValue()); entity = entities.get(2); + assertEquals(10, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + assertEquals(0L, entity.getProperty(PROPERTY_INT64).getPrimitiveValue().toValue()); + assertEquals(BigDecimal.valueOf(0), entity.getProperty(PROPERTY_DECIMAL).getPrimitiveValue().toValue()); + + entity = entities.get(3); assertEquals(32767, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); assertEquals(Long.MAX_VALUE, entity.getProperty(PROPERTY_INT64).getPrimitiveValue().toValue()); assertEquals(BigDecimal.valueOf(34), entity.getProperty(PROPERTY_DECIMAL).getPrimitiveValue().toValue()); diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BoundOperationITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BoundOperationITCase.java index ef0af2b55..38a985ece 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BoundOperationITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BoundOperationITCase.java @@ -96,7 +96,7 @@ public class BoundOperationITCase extends AbstractParamTecSvcITCase { final List entities = entitySet.getEntities(); assertNotNull(entities); - assertEquals(3, entities.size()); + assertEquals(4, entities.size()); ClientEntity entity = entities.get(0); assertNotNull(entity); diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java index 654aea7b7..77cd7d591 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java @@ -68,7 +68,7 @@ public class EntityReferencesITCase extends AbstractParamTecSvcITCase { .appendRefSegment() .orderBy(PROPERTY_INT16).build(); - sendRequest(uri, 3, "ESAllPrim(-32768)", "ESAllPrim(0)", "ESAllPrim(32767)"); + sendRequest(uri, 4, "ESAllPrim(-32768)", "ESAllPrim(0)", "ESAllPrim(10)", "ESAllPrim(32767)"); } @Test @@ -78,7 +78,7 @@ public class EntityReferencesITCase extends AbstractParamTecSvcITCase { .appendRefSegment() .orderBy(PROPERTY_INT16 + DESCENDING).build(); - sendRequest(uri, 3, "ESAllPrim(32767)", "ESAllPrim(0)", "ESAllPrim(-32768)"); + sendRequest(uri, 4, "ESAllPrim(32767)", "ESAllPrim(10)", "ESAllPrim(0)", "ESAllPrim(-32768)"); } @Test @@ -122,7 +122,7 @@ public class EntityReferencesITCase extends AbstractParamTecSvcITCase { .getEntitySetRequest(uri) .execute(); - assertEquals(Integer.valueOf(3), response.getBody().getCount()); + assertEquals(Integer.valueOf(4), response.getBody().getCount()); } @Test @@ -132,7 +132,7 @@ public class EntityReferencesITCase extends AbstractParamTecSvcITCase { .appendRefSegment() .orderBy(PROPERTY_INT16).skip(2).build(); - sendRequest(uri, 1, "ESAllPrim(32767)"); + sendRequest(uri, 2, "ESAllPrim(10)", "ESAllPrim(32767)"); } @Test diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java index 017cabaab..ad94444cf 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java @@ -88,7 +88,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { @Test public void booleanLiteral() { ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "PropertyBoolean eq false"); - assertEquals(2, response.getBody().getEntities().size()); + assertEquals(3, response.getBody().getEntities().size()); ClientEntity clientEntity = response.getBody().getEntities().get(0); assertShortOrInt(-32768, clientEntity.getProperty("PropertyInt16").getPrimitiveValue().toValue()); @@ -256,18 +256,18 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { // One representative of "stringFuntion" "residue class" ODataRetrieveResponse result = sendRequest(ES_ALL_PRIM, "endswith(PropertyString,null) eq null"); // null eq null => true - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); // One representative of "stringifiedValueFunction" "residue class" result = sendRequest(ES_ALL_PRIM, "substring(PropertyString,null) eq null"); // null eq null => true - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); // Substring result = sendRequest(ES_ALL_PRIM, "hour(null) eq null"); // null eq null => true - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "substring(PropertyString,0,null) eq null"); // null eq null => true - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); } @Test @@ -283,7 +283,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { // -1 should be treated as 0, Same values substring(PropertyString, 0, 0) returns the empty String response = sendRequest(ES_ALL_PRIM, "substring(PropertyString,0,-1) eq ''"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); } @Test @@ -340,10 +340,10 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { assertEquals(3, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "PropertyDouble ge -179000"); - assertEquals(2, result.getBody().getEntities().size()); + assertEquals(3, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "PropertyDouble gt -179000"); - assertEquals(1, result.getBody().getEntities().size()); + assertEquals(2, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "PropertyDouble lt -179000"); assertEquals(1, result.getBody().getEntities().size()); @@ -507,7 +507,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { @Test public void monthFunctionDateTimeOffset() { ODataRetrieveResponse result = sendRequest(ES_ALL_PRIM, "month(PropertyDateTimeOffset) eq 12"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); ClientEntity clientEntity = result.getBody().getEntities().get(0); assertShortOrInt(32767, clientEntity.getProperty("PropertyInt16").getPrimitiveValue().toValue()); @@ -531,7 +531,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { @Test public void dayFunctionDateTimeOffset() { ODataRetrieveResponse result = sendRequest(ES_ALL_PRIM, "day(PropertyDateTimeOffset) eq 3"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); ClientEntity clientEntity = result.getBody().getEntities().get(0); assertShortOrInt(32767, clientEntity.getProperty("PropertyInt16").getPrimitiveValue().toValue()); @@ -628,7 +628,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { public void fractionalsecondsDateOfTime() { ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "fractionalseconds(PropertyTimeOfDay) eq 0"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); } @Test @@ -636,22 +636,22 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { ODataRetrieveResponse response; response = sendRequest(ES_ALL_PRIM, "year(null) eq null"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); response = sendRequest(ES_ALL_PRIM, "month(null) eq null"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); response = sendRequest(ES_ALL_PRIM, "day(null) eq null"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); response = sendRequest(ES_ALL_PRIM, "hour(null) eq null"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); response = sendRequest(ES_ALL_PRIM, "minute(null) eq null"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); response = sendRequest(ES_ALL_PRIM, "second(null) eq null"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); } @Test @@ -794,7 +794,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { public void numericPromotionToInt64() { ODataRetrieveResponse result = sendRequest(ES_ALL_PRIM, "PropertyInt64 eq 0"); - assertEquals(1, result.getBody().getEntities().size()); + assertEquals(2, result.getBody().getEntities().size()); ClientEntity clientEntity = result.getBody().getEntities().get(0); assertShortOrInt(0, clientEntity.getProperty("PropertyInt16").getPrimitiveValue().toValue()); @@ -805,7 +805,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { double value = Float.MAX_VALUE + 1; ODataRetrieveResponse result = sendRequest(ES_ALL_PRIM, "PropertyInt64 lt " + value); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); } @Test @@ -879,7 +879,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { public void dateSubDate() { ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "PropertyDuration eq 2012-12-04 sub 2012-12-04"); - assertEquals(1, response.getBody().getEntities().size()); + assertEquals(2, response.getBody().getEntities().size()); final ClientEntity clientEntity = response.getBody().getEntities().get(0); assertShortOrInt(0, clientEntity.getProperty("PropertyInt16").getPrimitiveValue().toValue()); @@ -889,7 +889,7 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { public void dateTimeOffsetSubDateTimeOffset() { ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "PropertyDuration eq 2005-12-03T00:00:00Z sub 2005-12-03T00:00:00Z"); - assertEquals(1, response.getBody().getEntities().size()); + assertEquals(2, response.getBody().getEntities().size()); final ClientEntity clientEntity = response.getBody().getEntities().get(0); assertShortOrInt(0, clientEntity.getProperty("PropertyInt16").getPrimitiveValue().toValue()); @@ -1008,28 +1008,28 @@ public class FilterSystemQueryITCase extends AbstractParamTecSvcITCase { public void comparisonOnStringOperands() { // If check if the expression is true => All entry are returned ODataRetrieveResponse result = sendRequest(ES_ALL_PRIM, "'Tes' lt 'Test'"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Test' le 'Test'"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Test1' le 'Test'"); assertEquals(0, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Test1' gt 'Test'"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Tes' gt 'Test'"); assertEquals(0, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Test' ge 'Test'"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Test' eq 'Test'"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); result = sendRequest(ES_ALL_PRIM, "'Test1' ne 'Test'"); - assertEquals(3, result.getBody().getEntities().size()); + assertEquals(4, result.getBody().getEntities().size()); } @Test diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java index 8e16a7095..8252a547f 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/OrderBySystemQueryITCase.java @@ -42,22 +42,25 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase { ODataRetrieveResponse response = null; response = sendRequest(ES_ALL_PRIM, "PropertyDate"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); ClientEntity clientEntity = response.getBody().getEntities().get(0); assertEquals("0", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); clientEntity = response.getBody().getEntities().get(1); + assertEquals("10", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); + + clientEntity = response.getBody().getEntities().get(2); assertEquals("32767", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); - clientEntity = response.getBody().getEntities().get(2); + clientEntity = response.getBody().getEntities().get(3); assertEquals("-32768", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); } @Test public void simpleOrderByDescending() { ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "PropertyDate desc"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); ClientEntity clientEntity = response.getBody().getEntities().get(0); assertEquals("-32768", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); @@ -72,7 +75,7 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase { @Test public void multipleOrderBy() { final ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); ClientEntity clientEntity = response.getBody().getEntities().get(0); assertEquals("-32768", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); @@ -81,6 +84,9 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase { assertEquals("0", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); clientEntity = response.getBody().getEntities().get(2); + assertEquals("10", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); + + clientEntity = response.getBody().getEntities().get(3); assertEquals("32767", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); } @@ -88,15 +94,18 @@ public class OrderBySystemQueryITCase extends AbstractParamTecSvcITCase { public void multipleOrderByDescending() { final ODataRetrieveResponse response = sendRequest(ES_ALL_PRIM, "PropertyByte,PropertyInt16 desc"); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(4, response.getBody().getEntities().size()); ClientEntity clientEntity = response.getBody().getEntities().get(0); + assertEquals("10", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); + + clientEntity = response.getBody().getEntities().get(1); assertEquals("0", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); - clientEntity = response.getBody().getEntities().get(1); + clientEntity = response.getBody().getEntities().get(2); assertEquals("-32768", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); - clientEntity = response.getBody().getEntities().get(2); + clientEntity = response.getBody().getEntities().get(3); assertEquals("32767", ((ClientValuable) clientEntity.getProperty("PropertyInt16")).getValue().toString()); } diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java index bb7fd31ef..c128e3b8a 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java @@ -50,8 +50,8 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase { ODataRetrieveResponse response = request.execute(); saveCookieHeader(response); - assertEquals(Integer.valueOf(3), response.getBody().getCount()); - assertEquals(3, response.getBody().getEntities().size()); + assertEquals(Integer.valueOf(4), response.getBody().getCount()); + assertEquals(4, response.getBody().getEntities().size()); } @Test diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/InOperatorITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/InOperatorITCase.java new file mode 100644 index 000000000..f95b3fed6 --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/InOperatorITCase.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.fit.tecsvc.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.fit.AbstractBaseTestITCase; +import org.apache.olingo.fit.tecsvc.TecSvcConst; +import org.junit.Test; + +public class InOperatorITCase extends AbstractBaseTestITCase { + + private static final String SERVICE_URI = TecSvcConst.BASE_URI + "/"; + + @Override + protected ODataClient getClient() { + return null; + } + + @Test + public void querySimple() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$filter=PropertyString%20in%20(" + + "%27Second%20Resource%20-%20negative%20values%27,%27xyz%27)"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;odata.metadata=minimal"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE))); + + final String content = IOUtils.toString(connection.getInputStream()); + assertTrue(content.contains("\"value\":[{\"PropertyInt16\":-32768," + + "\"PropertyString\":\"Second Resource - negative values\"," + + "\"PropertyBoolean\":false,\"PropertyByte\":0,\"PropertySByte\":-128," + + "\"PropertyInt32\":-2147483648,\"PropertyInt64\":-9223372036854775808," + + "\"PropertySingle\":-1.79E8,\"PropertyDouble\":-179000.0,\"PropertyDecimal\":-34," + + "\"PropertyBinary\":\"ASNFZ4mrze8=\",\"PropertyDate\":\"2015-11-05\"," + + "\"PropertyDateTimeOffset\":\"2005-12-03T07:17:08Z\",\"PropertyDuration\":\"PT9S\"," + + "\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789dddfff\"," + + "\"PropertyTimeOfDay\":\"23:49:14\"}]")); + } + + @Test + public void queryInOperatorWithFunction() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$filter=PropertyString%20in%20olingo.odata.test1.UFCRTCollString()"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;odata.metadata=minimal"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE))); + + final String content = IOUtils.toString(connection.getInputStream()); + assertTrue(content.contains("\"value\":[{\"PropertyInt16\":10," + + "\"PropertyString\":\"Employee1@company.example\"," + + "\"PropertyBoolean\":false,\"PropertyByte\":0,\"PropertySByte\":0," + + "\"PropertyInt32\":0,\"PropertyInt64\":0,\"PropertySingle\":0.0," + + "\"PropertyDouble\":0.0,\"PropertyDecimal\":0,\"PropertyBinary\":\"\"," + + "\"PropertyDate\":\"1970-01-01\"," + + "\"PropertyDateTimeOffset\":\"2005-12-03T00:00:00Z\"," + + "\"PropertyDuration\":\"PT0S\"," + + "\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789cccddd\"," + + "\"PropertyTimeOfDay\":\"00:01:01\"}]")); + } + + @Test + public void queryInOperatorOnNavProperty() throws Exception { + URL url = new URL(SERVICE_URI + "ESKeyNav?$filter=PropertyCompTwoPrim/" + + "PropertyInt16%20in%20NavPropertyETKeyNavOne/CollPropertyInt16"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;odata.metadata=minimal"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE))); + + final String content = IOUtils.toString(connection.getInputStream()); + assertTrue(content.contains("\"value\":[]")); + } +} diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/EdmProviderImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/EdmProviderImpl.java index c8b1c71a3..34870d6a5 100644 --- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/EdmProviderImpl.java +++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/EdmProviderImpl.java @@ -293,7 +293,8 @@ public class EdmProviderImpl extends AbstractEdm { try { if (null != provider.getAliasInfos()) { for (CsdlAliasInfo aliasInfo : provider.getAliasInfos()) { - if (aliasInfo.getNamespace().equalsIgnoreCase(namespace)) { + if (null != aliasInfo.getNamespace() && + aliasInfo.getNamespace().equalsIgnoreCase(namespace)) { return aliasInfo.getAlias(); } } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/Binary.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/Binary.java index c13d15fb4..aa6fd876e 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/Binary.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/Binary.java @@ -18,6 +18,8 @@ */ package org.apache.olingo.server.api.uri.queryoption.expression; +import java.util.List; + /** * Represents a binary expression node in the expression tree *
@@ -41,5 +43,11 @@ public interface Binary extends Expression { * @return Expression sub tree of the right operand */ public Expression getRightOperand(); + + /** + * + * @return list of expressions of the right operand + */ + public List getExpressions(); } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/BinaryOperatorKind.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/BinaryOperatorKind.java index 2ce4eeef7..157c4efdb 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/BinaryOperatorKind.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/BinaryOperatorKind.java @@ -28,6 +28,11 @@ public enum BinaryOperatorKind { * OData has operator used for OData enumerations */ HAS("has"), + + /** + * In operator + */ + IN("in"), /** * Multiplication operator diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/ExpressionVisitor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/ExpressionVisitor.java index 402db3a81..f67a4958d 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/ExpressionVisitor.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/expression/ExpressionVisitor.java @@ -132,5 +132,17 @@ public interface ExpressionVisitor { * @throws ODataApplicationException Thrown by the application */ T visitEnum(EdmEnumType type, List enumValues) throws ExpressionVisitException, ODataApplicationException; + + /** + * Called for each traversed {@link Binary} expression + * @param operator Operator kind + * @param left Application return value of left sub tree + * @param right Application return value of right sub tree + * @return Application return value of type T + * @throws ExpressionVisitException Thrown if an exception while traversing occured + * @throws ODataApplicationException Thrown by the application + */ + T visitBinaryOperator(BinaryOperatorKind operator, T left, List right) + throws ExpressionVisitException, ODataApplicationException; } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java index 4f1288282..e1936c149 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java @@ -330,6 +330,7 @@ public class ExpressionJsonVisitor implements ExpressionVisitor { case NE: case AND: case OR: + case IN: return BOOLEAN_NAME; } return UNKNOWN_NAME; @@ -343,4 +344,19 @@ public class ExpressionJsonVisitor implements ExpressionVisitor { final EdmType type = segment instanceof UriResourcePartTyped ? ((UriResourcePartTyped) segment).getType() : null; return type == null ? UNKNOWN_NAME : type.getFullQualifiedName().getFullQualifiedNameAsString(); } + + @Override + public JsonNode visitBinaryOperator(BinaryOperatorKind operator, JsonNode left, List right) + throws ExpressionVisitException, ODataApplicationException { + ObjectNode result = nodeFactory.objectNode() + .put(NODE_TYPE_NAME, BINARY_NAME) + .put(OPERATOR_NAME, operator.toString()) + .put(TYPE_NAME, getType(operator)); + result.set(LEFT_NODE_NAME, left); + ArrayNode jsonExprs = result.putArray(RIGHT_NODE_NAME); + for (final JsonNode exp : right) { + jsonExprs.add(exp); + } + return result; + } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java index 6be186d88..935c9ed3c 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpressionParser.java @@ -332,9 +332,64 @@ public class ExpressionParser { final Expression right = createEnumExpression(tokenizer.getText()); return new BinaryImpl(left, BinaryOperatorKind.HAS, right, odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean)); - } else { - return left; + } else if (tokenizer.next(TokenKind.InOperator)) { + EdmType leftExprType = getType(left); + EdmPrimitiveTypeKind kinds = EdmPrimitiveTypeKind.valueOfFQN(leftExprType.getFullQualifiedName()); + if (tokenizer.next(TokenKind.OPEN)) { + ParserHelper.bws(tokenizer); + List expressionList = parseInExpr(); + checkInExpressionTypes(expressionList, kinds); + return new BinaryImpl(left, BinaryOperatorKind.IN, expressionList, + odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean)); + } else { + ParserHelper.bws(tokenizer); + final Expression right = parseExpression(); + checkType(right, kinds); + return new BinaryImpl(left, BinaryOperatorKind.IN, right, + odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Boolean)); + } } + return left; + } + + /** + * @param expressionList + * @param kinds + * @throws UriParserException + * @throws UriParserSemanticException + */ + private void checkInExpressionTypes(List expressionList, EdmPrimitiveTypeKind kinds) + throws UriParserException, UriParserSemanticException { + for (Expression expr : expressionList) { + EdmType inExprType = getType(expr); + if (!isType(inExprType, kinds)) { + throw new UriParserSemanticException("Incompatible types.", + UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE, + inExprType == null ? "" : inExprType.getFullQualifiedName().getFullQualifiedNameAsString(), + kinds.getFullQualifiedName().getFullQualifiedNameAsString()); + } + } + } + + /** + * @param expressionList + * @throws UriParserException + * @throws UriValidationException + */ + private List parseInExpr() throws UriParserException, UriValidationException { + List expressionList = new ArrayList(); + while(!tokenizer.next(TokenKind.CLOSE)) { + Expression expression = parseExpression(); + expressionList.add(expression); + ParserHelper.bws(tokenizer); + if (tokenizer.next(TokenKind.COMMA)) { + ParserHelper.bws(tokenizer); + expression = parseExpression(); + expressionList.add(expression); + ParserHelper.bws(tokenizer); + } + } + return expressionList; } private Expression parseExprValue() throws UriParserException, UriValidationException { diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java index 50c906020..5b05111f8 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/UriTokenizer.java @@ -126,6 +126,7 @@ public class UriTokenizer { LessThanOperator, LessThanOrEqualsOperator, HasOperator, + InOperator, AddOperator, SubOperator, MulOperator, @@ -508,6 +509,9 @@ public class UriTokenizer { case HasOperator: found = nextBinaryOperator("has"); break; + case InOperator: + found = nextBinaryOperator("in"); + break; case AddOperator: found = nextBinaryOperator("add"); break; diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java index 7c3f65df6..ea84f6bcb 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/expression/BinaryImpl.java @@ -18,6 +18,9 @@ */ package org.apache.olingo.server.core.uri.queryoption.expression; +import java.util.ArrayList; +import java.util.List; + import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.uri.queryoption.expression.Binary; @@ -32,6 +35,7 @@ public class BinaryImpl implements Binary { private final BinaryOperatorKind operator; private final Expression right; private final EdmType type; + private final List expressions; public BinaryImpl(final Expression left, final BinaryOperatorKind operator, final Expression right, final EdmType type) { @@ -39,6 +43,16 @@ public class BinaryImpl implements Binary { this.operator = operator; this.right = right; this.type = type; + this.expressions = null; + } + + public BinaryImpl(final Expression left, final BinaryOperatorKind operator, final List right, + final EdmType type) { + this.left = left; + this.operator = operator; + this.right = null; + this.type = type; + this.expressions = right; } @Override @@ -63,12 +77,26 @@ public class BinaryImpl implements Binary { @Override public T accept(final ExpressionVisitor visitor) throws ExpressionVisitException, ODataApplicationException { T localLeft = this.left.accept(visitor); - T localRight = this.right.accept(visitor); - return visitor.visitBinaryOperator(operator, localLeft, localRight); + if (this.right != null) { + T localRight = this.right.accept(visitor); + return visitor.visitBinaryOperator(operator, localLeft, localRight); + } else if (this.expressions != null) { + List expressions = new ArrayList(); + for (final Expression expression : this.expressions) { + expressions.add(expression.accept(visitor)); + } + return visitor.visitBinaryOperator(operator, localLeft, expressions); + } + return null; } @Override public String toString() { - return "{" + left + " " + operator.name() + " " + right + '}'; + return "{" + left + " " + operator.name() + " " + (null != right ? right : expressions) + '}'; + } + + @Override + public List getExpressions() { + return expressions; } } diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java index 92c09da41..5671688f2 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/parser/ExpressionParserTest.java @@ -392,10 +392,23 @@ public class ExpressionParserTest { Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container); UriTokenizer tokenizer = new UriTokenizer("a eq \'abc\'"); - final Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, + Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, entityType, null, null); assertNotNull(expression); assertEquals("{[a] EQ \'abc\'}", expression.toString()); + + tokenizer = new UriTokenizer("a in (\'abc\', \'xyz\')"); + expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, + entityType, null, null); + assertNotNull(expression); + assertEquals("{[a] IN [\'abc\', \'xyz\']}", expression.toString()); + try { + tokenizer = new UriTokenizer("a in (\'abc\', 10)"); + expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, + entityType, null, null); + } catch (UriParserSemanticException e) { + assertEquals("Incompatible types.", e.getMessage()); + } } /** @@ -519,10 +532,16 @@ public class ExpressionParserTest { Mockito.when(mockedEdm.getEntityContainer()).thenReturn(container); UriTokenizer tokenizer = new UriTokenizer("comp/prop eq \'abc\'"); - final Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, + Expression expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, entityType, null, null); assertNotNull(expression); assertEquals("{[comp, prop] EQ \'abc\'}", expression.toString()); + + tokenizer = new UriTokenizer("comp/prop in (\'abc\','xyz')"); + expression = new ExpressionParser(mockedEdm, odata).parse(tokenizer, + entityType, null, null); + assertNotNull(expression); + assertEquals("{[comp, prop] IN [\'abc\', \'xyz\']}", expression.toString()); } /** diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java index 898fc39e1..c5b62dcab 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java @@ -1149,6 +1149,25 @@ public class DataCreator { .addProperty(createPrimitive("PropertyDuration", BigDecimal.valueOf(0))) .addProperty(createPrimitive("PropertyGuid", UUID.fromString("76543201-23ab-cdef-0123-456789cccddd"))) .addProperty(createPrimitive("PropertyTimeOfDay", getTime(0, 1, 1)))); + + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("PropertyInt16", (short) 10)) + .addProperty(createPrimitive("PropertyString", "Employee1@company.example")) + .addProperty(createPrimitive("PropertyBoolean", false)) + .addProperty(createPrimitive("PropertyByte", (short) 0)) + .addProperty(createPrimitive("PropertySByte", 0)) + .addProperty(createPrimitive("PropertyInt32", 0)) + .addProperty(createPrimitive("PropertyInt64", 0L)) + .addProperty(createPrimitive("PropertySingle", (float) 0)) + .addProperty(createPrimitive("PropertyDouble", 0D)) + .addProperty(createPrimitive("PropertyDecimal", BigDecimal.valueOf(0))) + .addProperty(createPrimitive("PropertyBinary", new byte[] {})) + .addProperty(createPrimitive("PropertyDate", getDate(1970, 1, 1))) + .addProperty(createPrimitive("PropertyDateTimeOffset", getDateTime(2005, 12, 3, 0, 0, 0))) + .addProperty(createPrimitive("PropertyDuration", BigDecimal.valueOf(0))) + .addProperty(createPrimitive("PropertyGuid", UUID.fromString("76543201-23ab-cdef-0123-456789cccddd"))) + .addProperty(createPrimitive("PropertyTimeOfDay", getTime(0, 1, 1)))); + setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETAllPrim)); createEntityId(edm, odata, "ESAllPrim", entityCollection); diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java index 98b3f7f9a..c3d01e971 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/ExpressionVisitorImpl.java @@ -24,11 +24,13 @@ import java.util.Locale; import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEnumType; import org.apache.olingo.commons.api.edm.EdmFunction; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; @@ -43,6 +45,7 @@ import org.apache.olingo.server.api.uri.UriResource; import org.apache.olingo.server.api.uri.UriResourceFunction; import org.apache.olingo.server.api.uri.UriResourceLambdaAny; import org.apache.olingo.server.api.uri.UriResourceLambdaVariable; +import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourceProperty; import org.apache.olingo.server.api.uri.queryoption.expression.Binary; import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind; @@ -112,6 +115,8 @@ public class ExpressionVisitorImpl implements ExpressionVisitor return binaryOperator.arithmeticOperator(operator); case HAS: return binaryOperator.hasOperator(); + case IN: + return binaryOperator.inOperator(); default: return throwNotImplemented(); @@ -278,6 +283,25 @@ public class ExpressionVisitorImpl implements ExpressionVisitor } return new TypedOperand(currentProperty == null ? null : currentProperty.getValue(), currentEdmProperty.getType(), currentEdmProperty); + } else if (initialPart instanceof UriResourceNavigation) { + EdmNavigationProperty currentEdmNavProperty = ((UriResourceNavigation) initialPart).getProperty(); + EdmProperty currentEdmProperty = null; + Link link = entity.getNavigationLink(currentEdmNavProperty.getName()); + Entity inlineEntity = link != null ? link.getInlineEntity() : null; + Property currentProperty = null; + for (int i = 1; i < uriResourceParts.size(); i++) { + currentEdmProperty = ((UriResourceProperty) uriResourceParts.get(i)).getProperty(); + if (null != inlineEntity) { + for (Property property : inlineEntity.getProperties()) { + if (property.getName().equalsIgnoreCase(currentEdmProperty.getName())) { + currentProperty = property; + break; + } + } + } + } + return new TypedOperand(currentProperty != null ? currentProperty.getValue() : null, + currentEdmProperty.getType(), currentEdmProperty); } else { return throwNotImplemented(); } @@ -324,4 +348,15 @@ public class ExpressionVisitorImpl implements ExpressionVisitor throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } + + @Override + public VisitorOperand visitBinaryOperator(BinaryOperatorKind operator, VisitorOperand left, + List right) throws ExpressionVisitException, ODataApplicationException { + BinaryOperator binaryOperator = new BinaryOperator(left, right); + switch (operator) { + case IN : return binaryOperator.inOperator(); + default: + return throwNotImplemented(); + } + } } diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/TypedOperand.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/TypedOperand.java index c9370b53e..c564950a8 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/TypedOperand.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/TypedOperand.java @@ -20,7 +20,9 @@ package org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operan import java.math.BigDecimal; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Locale; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; @@ -55,11 +57,62 @@ public class TypedOperand extends VisitorOperand { return value.getClass() == getDefaultType((EdmPrimitiveType) type) ? this : asTypedOperand((EdmPrimitiveType) type); + } else if (type instanceof EdmPrimitiveType && value instanceof Collection) { + return value.getClass() == getDefaultType((EdmPrimitiveType) type) ? + this : + asTypedOperandForCollection((EdmPrimitiveType) type); } else { throw new ODataApplicationException("A single primitive-type instance is expected.", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); } } + + @SuppressWarnings("unchecked") + @Override + public TypedOperand asTypedOperandForCollection(final EdmPrimitiveType asType) throws ODataApplicationException { + if (is(primNull)) { + return this; + } else if (isNull()) { + return new TypedOperand(null, asType); + } + List newValue = new ArrayList(); + List list = (List) value; + for (Object val : list) { + // Use BigInteger for arbitrarily large whole numbers. + if (asType.equals(primSByte) || asType.equals(primByte) + || asType.equals(primInt16) || asType.equals(primInt32) || asType.equals(primInt64)) { + if (val instanceof BigInteger) { + newValue.add(val); + } else if (val instanceof Byte || val instanceof Short + || val instanceof Integer || val instanceof Long) { + newValue.add(BigInteger.valueOf(((Number) val).longValue())); + } + // Use BigDecimal for unlimited precision. + } else if (asType.equals(primDouble) || asType.equals(primSingle) || asType.equals(primDecimal)) { + try { + newValue.add(new BigDecimal(val.toString())); + } catch (NumberFormatException e) { + throw new ODataApplicationException("Format exception", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT, e.getCause()); + } + } else { + // Use type conversion of EdmPrimitive types + try { + final String literal = getLiteral(val); + newValue.add(tryCast(literal, asType)); + } catch (EdmPrimitiveTypeException e) { + throw new ODataApplicationException("Cast Failed", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT, e.getCause()); + } + } + } + if (!newValue.isEmpty()) { + return new TypedOperand(newValue, asType); + } + + throw new ODataApplicationException("Cast failed", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); + + } @Override public TypedOperand asTypedOperand(final EdmPrimitiveType asType) throws ODataApplicationException { @@ -67,7 +120,7 @@ public class TypedOperand extends VisitorOperand { return this; } else if (isNull()) { return new TypedOperand(null, asType); - } + } Object newValue = null; // Use BigInteger for arbitrarily large whole numbers. @@ -119,19 +172,20 @@ public class TypedOperand extends VisitorOperand { } if (type.equals(primDouble) || oType.equals(primDouble)) { - return asTypedOperand(primDouble); + return (value instanceof ArrayList) ? asTypedOperandForCollection(primDouble) : asTypedOperand(primDouble); } else if (type.equals(primSingle) || oType.equals(primSingle)) { - return asTypedOperand(primSingle); + return (value instanceof ArrayList) ? asTypedOperandForCollection(primSingle) : asTypedOperand(primSingle); } else if (type.equals(primDecimal) || oType.equals(primDecimal)) { - return asTypedOperand(primDecimal); + return (value instanceof ArrayList) ? asTypedOperandForCollection(primDecimal) : asTypedOperand(primDecimal); } else if (type.equals(primInt64) || oType.equals(primInt64)) { - return asTypedOperand(primInt64); + return (value instanceof ArrayList) ? asTypedOperandForCollection(primInt64) : asTypedOperand(primInt64); } else if (type.equals(primInt32) || oType.equals(primInt32)) { - return asTypedOperand(primInt32); + return (value instanceof ArrayList) ? asTypedOperandForCollection(primInt32) : asTypedOperand(primInt32); } else if (type.equals(primInt16) || oType.equals(primInt16)) { - return asTypedOperand(primInt16); + return (value instanceof ArrayList) ? asTypedOperandForCollection(primInt16) : asTypedOperand(primInt16); } else { - return asTypedOperand((EdmPrimitiveType) type); + return (value instanceof ArrayList) ? asTypedOperandForCollection((EdmPrimitiveType) type) : + asTypedOperand((EdmPrimitiveType) type); } } @@ -142,6 +196,16 @@ public class TypedOperand extends VisitorOperand { public T getTypedValue(final Class clazz) { return clazz.cast(value); } + + public List getTypedValueList(final Class clazz) { + List list = (List) value; + List newList = new ArrayList(); + for (Object obj : list) { + newList.add(clazz.cast(obj)); + } + return (List) newList; + } + public boolean isNull() { return is(primNull) || value == null; diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/UntypedOperand.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/UntypedOperand.java index dba114bca..101aa0c37 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/UntypedOperand.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/UntypedOperand.java @@ -18,6 +18,8 @@ */ package org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operand; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; @@ -54,6 +56,31 @@ public class UntypedOperand extends VisitorOperand { throw new ODataApplicationException("Cast failed", HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT); } + + @SuppressWarnings( "unchecked") + @Override + public TypedOperand asTypedOperandForCollection(EdmPrimitiveType type) throws ODataApplicationException { + List newValue = new ArrayList(); + List list = (List) value; + for (Object val : list) { + final String literal = (String) val; + + // First try the null literal. + if (null != tryCast(literal, primNull)) { + newValue.add(tryCast(literal, primNull)); + type = primNull; + } + // Then try the given type. + if (null != tryCast(literal, type)) { + newValue.add(tryCast(literal, type)); + } + } + if (!newValue.isEmpty()) { + return new TypedOperand(newValue, type); + } + throw new ODataApplicationException("Cast failed", HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), + Locale.ROOT); + } public TypedOperand determineType() throws ODataApplicationException { final String literal = (String) value; diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/VisitorOperand.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/VisitorOperand.java index c06b6bb38..7e949d084 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/VisitorOperand.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operand/VisitorOperand.java @@ -86,6 +86,8 @@ public abstract class VisitorOperand { public abstract TypedOperand asTypedOperand() throws ODataApplicationException; public abstract TypedOperand asTypedOperand(EdmPrimitiveType type) throws ODataApplicationException; + + public abstract TypedOperand asTypedOperandForCollection(EdmPrimitiveType type) throws ODataApplicationException; public abstract EdmProperty getEdmProperty(); diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/BinaryOperator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/BinaryOperator.java index 57f2196a8..0395ce1e8 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/BinaryOperator.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/expression/operation/BinaryOperator.java @@ -21,9 +21,12 @@ package org.apache.olingo.server.tecsvc.processor.queryoptions.expression.operat import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Calendar; +import java.util.List; import java.util.Locale; +import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmType; @@ -84,8 +87,9 @@ public class BinaryOperator { primDouble = oData.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Double); } - private TypedOperand right; + private TypedOperand right = null; private TypedOperand left; + private List rightValues = null; public BinaryOperator(final VisitorOperand leftOperand, final VisitorOperand rightOperand) throws ODataApplicationException { @@ -96,6 +100,15 @@ public class BinaryOperator { right = right.castToCommonType(left); } + public BinaryOperator(final VisitorOperand leftOperand, final List rightOperand) + throws ODataApplicationException { + rightValues = new ArrayList(); + left = leftOperand.asTypedOperand(); + for (VisitorOperand right : rightOperand) { + rightValues.add(right.asTypedOperand()); + } + } + public VisitorOperand andOperator() throws ODataApplicationException { Boolean result = null; if (left.is(primBoolean) && right.is(primBoolean)) { @@ -353,4 +366,36 @@ public class BinaryOperator { Locale.ROOT); } } + + @SuppressWarnings("unchecked") + public VisitorOperand inOperator() throws ODataApplicationException { + if (null != rightValues) { + for (TypedOperand rightOperand : rightValues) { + if (rightOperand.getTypedValue(String.class).equals(left.getTypedValue(String.class))) { + return new TypedOperand(true, primBoolean); + } + } + } else { + if (right.getValue() instanceof String) { + String value = (String) right.getValue(); + value = value.substring(value.indexOf("[") + 1, value.indexOf("]")); + String values[] = value.split(","); + for (String val : values) { + if (val.equals(left.getValue())) { + return new TypedOperand(true, primBoolean); + } + } + } else if (right.getValue() instanceof ArrayList && left.isIntegerType()) { + List list = (List) right.getTypedValue(ArrayList.class); + for (BigInteger val : list) { + if (val == left.getTypedValue(BigInteger.class)) { + return new TypedOperand(true, primBoolean); + } + } + } else if (right.getValue() == null && left.isNull()) { + return new TypedOperand(true, primBoolean); + } + } + return new TypedOperand(false, primBoolean); + } } diff --git a/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java b/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java index 4686cba73..a53d66dc6 100644 --- a/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java +++ b/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java @@ -90,7 +90,7 @@ public class DataProviderTest { final DataProvider data = new DataProvider(oData, edm); EntityCollection outSet = data.readAll(esAllPrim); - Assert.assertEquals(3, outSet.getEntities().size()); + Assert.assertEquals(4, outSet.getEntities().size()); Entity first = outSet.getEntities().get(0); Assert.assertEquals(16, first.getProperties().size()); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index 786403ede..52849b212 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -486,7 +486,7 @@ public class ODataJsonSerializerTest { while ((index = resultString.indexOf("PropertyInt16\":", ++index)) > 0) { count++; } - Assert.assertEquals(3, count); + Assert.assertEquals(4, count); } @Test @@ -1938,7 +1938,7 @@ public class ODataJsonSerializerTest { Assert.assertEquals("{\"@odata.context\":\"../$metadata#Collection($ref)\"," + "\"value\":[{\"@odata.id\":\"ESAllPrim(32767)\"}," + "{\"@odata.id\":\"ESAllPrim(-32768)\"}," - + "{\"@odata.id\":\"ESAllPrim(0)\"}]}", + + "{\"@odata.id\":\"ESAllPrim(0)\"},{\"@odata.id\":\"ESAllPrim(10)\"}]}", resultString); } @@ -2129,7 +2129,7 @@ public class ODataJsonSerializerTest { Assert.assertThat(resultString, CoreMatchers.startsWith("{" + "\"@odata.context\":\"$metadata#ESAllPrim\"," + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"@odata.count\":\"3\",\"value\":[")); + + "\"@odata.count\":\"4\",\"value\":[")); Assert.assertThat(resultString, CoreMatchers.endsWith("]," + "\"@odata.nextLink\":\"/next\"}")); @@ -2138,7 +2138,7 @@ public class ODataJsonSerializerTest { while ((index = resultString.indexOf("PropertyInt16\":", ++index)) > 0) { count++; } - Assert.assertEquals(3, count); + Assert.assertEquals(4, count); } @Test @@ -2158,7 +2158,7 @@ public class ODataJsonSerializerTest { Assert.assertThat(resultString, CoreMatchers.startsWith("{" + "\"@odata.context\":\"../$metadata#Collection($ref)\"," - + "\"@odata.count\":\"3\",\"value\":[")); + + "\"@odata.count\":\"4\",\"value\":[")); Assert.assertThat(resultString, CoreMatchers.endsWith("]," + "\"@odata.nextLink\":\"/next\"}")); @@ -2167,7 +2167,7 @@ public class ODataJsonSerializerTest { while ((index = resultString.indexOf("ESAllPrim(", ++index)) > 0) { count++; } - Assert.assertEquals(3, count); + Assert.assertEquals(4, count); } @Test diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerv01Test.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerv01Test.java index 37afbe553..f5ffa7133 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerv01Test.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerv01Test.java @@ -482,7 +482,7 @@ public class ODataJsonSerializerv01Test { while ((index = resultString.indexOf("PropertyInt16\":", ++index)) > 0) { count++; } - Assert.assertEquals(3, count); + Assert.assertEquals(4, count); } @Test @@ -1943,7 +1943,7 @@ public class ODataJsonSerializerv01Test { Assert.assertEquals("{\"@context\":\"../$metadata#Collection($ref)\"," + "\"value\":[{\"@id\":\"ESAllPrim(32767)\"}," + "{\"@id\":\"ESAllPrim(-32768)\"}," - + "{\"@id\":\"ESAllPrim(0)\"}]}", + + "{\"@id\":\"ESAllPrim(0)\"},{\"@id\":\"ESAllPrim(10)\"}]}", resultString); } @@ -2134,7 +2134,7 @@ public class ODataJsonSerializerv01Test { Assert.assertThat(resultString, CoreMatchers.startsWith("{" + "\"@context\":\"$metadata#ESAllPrim\"," + "\"@metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"@count\":\"3\",\"value\":[")); + + "\"@count\":\"4\",\"value\":[")); Assert.assertThat(resultString, CoreMatchers.endsWith("]," + "\"@nextLink\":\"/next\"}")); @@ -2143,7 +2143,7 @@ public class ODataJsonSerializerv01Test { while ((index = resultString.indexOf("PropertyInt16\":", ++index)) > 0) { count++; } - Assert.assertEquals(3, count); + Assert.assertEquals(4, count); } @Test @@ -2163,7 +2163,7 @@ public class ODataJsonSerializerv01Test { Assert.assertThat(resultString, CoreMatchers.startsWith("{" + "\"@context\":\"../$metadata#Collection($ref)\"," - + "\"@count\":\"3\",\"value\":[")); + + "\"@count\":\"4\",\"value\":[")); Assert.assertThat(resultString, CoreMatchers.endsWith("]," + "\"@nextLink\":\"/next\"}")); @@ -2172,7 +2172,7 @@ public class ODataJsonSerializerv01Test { while ((index = resultString.indexOf("ESAllPrim(", ++index)) > 0) { count++; } - Assert.assertEquals(3, count); + Assert.assertEquals(4, count); } @Test diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java index 2f977d1c5..56ddf1988 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java @@ -322,6 +322,49 @@ public class ODataXmlSerializerTest { + "title=\"olingo.odata.test1.BAETAllPrimRT\" " + "target=\"ESAllPrim(0)/olingo.odata.test1.BAETAllPrimRT\" />\n" + " \n" + + "\n" + + "ESAllPrim(10)\n" + + "\n" + + "\n" + + ""+ UPDATED_FORMAT.format(new Date(currentTimeMillis)) +"\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "10\n" + + "Employee1@company.example\n" + + "false\n" + + "0\n" + + "0\n" + + "0\n" + + "0\n" + + "0.0\n" + + "0.0\n" + + "0\n" + + "\n" + + "1970-01-01\n" + + "" + + "2005-12-03T00:00:00Z\n" + + "PT0S\n"+ + "" + + "76543201-23ab-cdef-0123-456789cccddd\n" + + "00:01:01\n" + + "\n" + + "\n" + + "\n" + + "\n" + ""; checkXMLEqual(expected, resultString); } @@ -2834,6 +2877,7 @@ public class ODataXmlSerializerTest { " \n" + " \n" + " \n" + + " \n" + ""; checkXMLEqual(expected, resultString); } diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java index 2103dd160..e00bdb62b 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java @@ -150,4 +150,10 @@ public class FilterTreeToText implements ExpressionVisitor { return "<" + type.getFullQualifiedName().getFullQualifiedNameAsString() + "<" + tmp + ">>"; } + @Override + public String visitBinaryOperator(BinaryOperatorKind operator, String left, List right) + throws ExpressionVisitException, ODataApplicationException { + return "<" + left + " " + operator.toString() + " " + right.toString() + ">"; + } + }