[OLINGO-545] Count, Skip, Top System Query Options and Server-Driven Paging added to TecSvc
Signed-off-by: Michael Bolz <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/2ebdea80 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/2ebdea80 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/2ebdea80 Branch: refs/heads/master Commit: 2ebdea806c9d2129416f3396ef5e3db2d7c9dd0c Parents: 394d0f8 Author: Christian Holzer <[email protected]> Authored: Mon Feb 23 09:48:03 2015 +0100 Committer: Michael Bolz <[email protected]> Committed: Thu Feb 26 09:13:54 2015 +0100 ---------------------------------------------------------------------- .../tecsvc/client/FilterSystemQueryITCase.java | 19 +- .../tecsvc/client/SystemQueryOptionITCase.java | 309 +++++++++++++++++ .../processor/TechnicalEntityProcessor.java | 7 +- .../tecsvc/processor/TechnicalProcessor.java | 11 +- .../expression/ExpressionVisitorImpl.java | 236 ------------- .../expression/FilterRuntimeException.java | 38 --- .../expression/FilterSystemQueryHandler.java | 135 -------- .../expression/operand/TypedOperand.java | 199 ----------- .../expression/operand/UntypedOperand.java | 155 --------- .../expression/operand/VisitorOperand.java | 93 ------ .../expression/operation/BinaryOperator.java | 318 ------------------ .../operation/MethodCallOperator.java | 334 ------------------- .../expression/operation/UnaryOperator.java | 63 ---- .../processor/expression/primitive/EdmNull.java | 58 ---- .../queryoptions/SystemQueryOptions.java | 41 +++ .../expression/ExpressionVisitorImpl.java | 236 +++++++++++++ .../expression/FilterRuntimeException.java | 38 +++ .../expression/operand/TypedOperand.java | 199 +++++++++++ .../expression/operand/UntypedOperand.java | 161 +++++++++ .../expression/operand/VisitorOperand.java | 93 ++++++ .../expression/operation/BinaryOperator.java | 318 ++++++++++++++++++ .../operation/MethodCallOperator.java | 334 +++++++++++++++++++ .../expression/operation/UnaryOperator.java | 63 ++++ .../expression/primitive/EdmNull.java | 58 ++++ .../queryoptions/options/CountHandler.java | 31 ++ .../queryoptions/options/FilterHandler.java | 82 +++++ .../queryoptions/options/OrderByHandler.java | 105 ++++++ .../options/ServerSidePagingHandler.java | 110 ++++++ .../queryoptions/options/SkipHandler.java | 54 +++ .../queryoptions/options/TopHandler.java | 47 +++ 30 files changed, 2304 insertions(+), 1641 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/FilterSystemQueryITCase.java ---------------------------------------------------------------------- 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 65da795..e996763 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 @@ -61,7 +61,24 @@ public class FilterSystemQueryITCase extends AbstractBaseTestITCase { ODataEntity oDataEntity = result.getBody().getEntities().get(0); assertEquals("32767", ((ODataValuable) oDataEntity.getProperty("PropertyInt16")).getValue().toString()); } - + + @Test + public void testBooleanLiteral() { + ODataRetrieveResponse<ODataEntitySet> response = sendRequest(ES_ALL_PRIM, "PropertyBoolean eq false"); + assertEquals(2, response.getBody().getEntities().size()); + + ODataEntity oDataEntity = response.getBody().getEntities().get(0); + assertEquals("-32768", ((ODataValuable) oDataEntity.getProperty("PropertyInt16")).getValue().toString()); + oDataEntity = response.getBody().getEntities().get(1); + assertEquals("0", ((ODataValuable) oDataEntity.getProperty("PropertyInt16")).getValue().toString()); + + response = sendRequest(ES_ALL_PRIM, "PropertyBoolean eq true"); + assertEquals(1, response.getBody().getEntities().size()); + + oDataEntity = response.getBody().getEntities().get(0); + assertEquals("32767", ((ODataValuable) oDataEntity.getProperty("PropertyInt16")).getValue().toString()); + } + @Test public void testDateLiteral() { ODataRetrieveResponse<ODataEntitySet> result = sendRequest(ES_ALL_PRIM, "PropertyDate eq 2012-12-03"); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..193800d --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/SystemQueryOptionITCase.java @@ -0,0 +1,309 @@ +/* + * 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.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.net.URI; + +import org.apache.olingo.client.api.CommonODataClient; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.communication.ODataClientErrorException; +import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; +import org.apache.olingo.client.api.uri.QueryOption; +import org.apache.olingo.client.core.ODataClientFactory; +import org.apache.olingo.commons.api.domain.CommonODataEntity; +import org.apache.olingo.commons.api.domain.CommonODataEntitySet; +import org.apache.olingo.commons.api.format.ODataFormat; +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 SystemQueryOptionITCas extends AbstractBaseTestITCase { + private static final String PROPERTY_INT16 = "PropertyInt16"; + private static final String ES_SERVER_SIDE_PAGING = "ESServerSidePaging"; + private static final String ES_ALL_PRIM = "ESAllPrim"; + private static final String SERVICE_URI = TecSvcConst.BASE_URI; + + @Test + public void testCountSimple() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .addQueryOption(QueryOption.COUNT, "true") + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(Integer.valueOf(3), response.getBody().getCount()); + assertEquals(3, response.getBody().getEntities().size()); + } + + @Test + public void testServerSidePagingCount() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .addQueryOption(QueryOption.COUNT, "true") + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(10, response.getBody().getEntities().size()); + assertEquals(Integer.valueOf(503), response.getBody().getCount()); + } + + @Test + public void testTopSimple() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .addQueryOption(QueryOption.TOP, new Integer(5).toString()) + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(5, response.getBody().getEntities().size()); + + for (int i = 0; i < 5; i++) { + CommonODataEntity entity = response.getBody().getEntities().get(i); + assertEquals(new Integer(i + 1).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString()); + } + } + + @Test + public void testSkipSimple() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .addQueryOption(QueryOption.SKIP, new Integer(5).toString()) + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(10, response.getBody().getEntities().size()); + + for (int i = 0; i < 10; i++) { + CommonODataEntity entity = response.getBody().getEntities().get(i); + assertEquals(new Integer(i + 6).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString()); + } + } + + @Test + public void testTopNothing() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .addQueryOption(QueryOption.TOP, new Integer(20).toString()) + .addQueryOption(QueryOption.SKIP, new Integer(503).toString()) + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(0, response.getBody().getEntities().size()); + } + + @Test + public void testSkipNothing() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .addQueryOption(QueryOption.SKIP, new Integer(10000).toString()) + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(0, response.getBody().getEntities().size()); + } + + @Test + public void testFilterWithTopSkipOrderByAndServerSidePaging() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .filter("PropertyInt16 le 105") // 1, 2, ... , 105 + .orderBy("PropertyInt16 desc") // 105, 104, ..., 2, 1 + .addQueryOption(QueryOption.COUNT, Boolean.TRUE.toString()) // 105 + .addQueryOption(QueryOption.SKIP, new Integer(3).toString()) // 102, 101, ..., 2, 1 + .addQueryOption(QueryOption.TOP, new Integer(43).toString()) // 102, 101, ...., 59 + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(Integer.valueOf(105), response.getBody().getCount()); + assertEquals(10, response.getBody().getEntities().size()); + + int id = 102; + + // Check first 10 entities + for (int i = 0; i < 10; i++) { + CommonODataEntity entity = response.getBody().getEntities().get(i); + assertEquals(new Integer(id).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString()); + id--; + } + + // Get 3 * 10 = 30 Entities and check the key + for (int j = 0; j < 3; j++) { + response = client.getRetrieveRequestFactory().getEntitySetRequest(response.getBody().getNext()).execute(); + assertEquals(Integer.valueOf(105), response.getBody().getCount()); + assertEquals(10, response.getBody().getEntities().size()); + for (int i = 0; i < 10; i++) { + CommonODataEntity entity = response.getBody().getEntities().get(i); + assertEquals(new Integer(id).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString()); + id--; + } + } + + // Get the last 3 items + response = client.getRetrieveRequestFactory().getEntitySetRequest(response.getBody().getNext()).execute(); + assertEquals(Integer.valueOf(105), response.getBody().getCount()); + assertEquals(3, response.getBody().getEntities().size()); + for (int i = 0; i < 3; i++) { + CommonODataEntity entity = response.getBody().getEntities().get(i); + assertEquals(new Integer(id).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString()); + id--; + } + + // Make sure that the body no not contain a next link + assertEquals(null, response.getBody().getNext()); + } + + @Test + public void testNextLinkFormat() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + // Check initial next link format + URI nextLink = response.getBody().getNext(); + assertEquals("http://localhost:9080/odata-server-tecsvc/odata.svc/ESServerSidePaging?%24skiptoken=1", nextLink + .toASCIIString()); + + // Check subsequent next links + response = client.getRetrieveRequestFactory() + .getEntitySetRequest(nextLink) + .execute(); + + nextLink = response.getBody().getNext(); + assertEquals("http://localhost:9080/odata-server-tecsvc/odata.svc/ESServerSidePaging?%24skiptoken=2", nextLink + .toASCIIString()); + } + + @Test + public void testNextLinkFormatWithQueryOptions() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .addQueryOption(QueryOption.COUNT, Boolean.TRUE.toString()) + .build(); + + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + // Check initial next link format + URI nextLink = response.getBody().getNext(); + assertEquals("http://localhost:9080/odata-server-tecsvc/odata.svc/ESServerSidePaging?%24count=true&%24skiptoken=1", + nextLink.toASCIIString()); + + int token = 1; + while (nextLink != null) { + token++; + + // Check subsequent next links + response = client.getRetrieveRequestFactory() + .getEntitySetRequest(nextLink) + .execute(); + + nextLink = response.getBody().getNext(); + if(nextLink != null) { + assertEquals( + "http://localhost:9080/odata-server-tecsvc/odata.svc/ESServerSidePaging?%24count=true&%24skiptoken=" + token, + nextLink.toASCIIString()); + } + } + + assertEquals(50 + 1, token); + } + + @Test + @SuppressWarnings("unused") + public void testNegativeSkip() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .addQueryOption(QueryOption.SKIP, new Integer(-5).toString()) + .build(); + + try { + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + fail(); + } catch (ODataClientErrorException e) { + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode()); + } + } + + @Test + @SuppressWarnings("unused") + public void testNegativeTop() { + CommonODataClient<?> client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .addQueryOption(QueryOption.TOP, new Integer(-5).toString()) + .build(); + try { + ODataRetrieveResponse<CommonODataEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + fail(); + } catch (ODataClientErrorException e) { + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode()); + } + } + + @Override + protected CommonODataClient<?> getClient() { + ODataClient odata = ODataClientFactory.getV4(); + odata.getConfiguration().setDefaultPubFormat(ODataFormat.JSON); + return odata; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java index 3113028..8fe5879 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java @@ -53,7 +53,8 @@ import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.tecsvc.data.DataProvider; -import org.apache.olingo.server.tecsvc.processor.expression.FilterSystemQueryHandler; +import org.apache.olingo.server.tecsvc.processor.queryoptions.SystemQueryOptions; +import org.apache.olingo.server.tecsvc.processor.queryoptions.options.ServerSidePagingHandler; /** * Technical Processor for entity-related functionality. @@ -82,8 +83,8 @@ public class TechnicalEntityProcessor extends TechnicalProcessor entitySet.getEntities().addAll(entitySetInitial.getEntities()); // Apply system query options - FilterSystemQueryHandler.applyFilterSystemQuery(uriInfo.getFilterOption(), entitySet, edmEntitySet); - FilterSystemQueryHandler.applyOrderByOption(uriInfo.getOrderByOption(), entitySet, edmEntitySet); + SystemQueryOptions.applySystemQueryOptions(entitySet, edmEntitySet, uriInfo); + ServerSidePagingHandler.applyServerSidePaging(entitySet, request.getRawRequestUri(), uriInfo); final ODataFormat format = ODataFormat.fromContentType(requestedContentType); ODataSerializer serializer = odata.createSerializer(format); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java index 31257cb..c5853b1 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java @@ -149,18 +149,13 @@ public abstract class TechnicalProcessor implements Processor { && resourcePaths.get(navigationCount) instanceof UriResourceNavigation) { navigationCount++; } - + return (UriResourceNavigation) resourcePaths.get(--navigationCount); } protected void validateOptions(final UriInfoResource uriInfo) throws ODataApplicationException { - if (uriInfo.getCountOption() != null - || !uriInfo.getCustomQueryOptions().isEmpty() - || uriInfo.getIdOption() != null - || uriInfo.getSearchOption() != null - || uriInfo.getSkipOption() != null - || uriInfo.getSkipTokenOption() != null - || uriInfo.getTopOption() != null) { + if (uriInfo.getIdOption() != null + || uriInfo.getSearchOption() != null) { throw new ODataApplicationException("Not all of the specified options are supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/ExpressionVisitorImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/ExpressionVisitorImpl.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/ExpressionVisitorImpl.java deleted file mode 100644 index 12a4940..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/ExpressionVisitorImpl.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression; - -import java.util.List; -import java.util.Locale; - -import org.apache.olingo.commons.api.data.Entity; -import org.apache.olingo.commons.api.data.Property; -import org.apache.olingo.commons.api.edm.EdmComplexType; -import org.apache.olingo.commons.api.edm.EdmEntitySet; -import org.apache.olingo.commons.api.edm.EdmEnumType; -import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.api.edm.EdmType; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.uri.UriInfoResource; -import org.apache.olingo.server.api.uri.UriResource; -import org.apache.olingo.server.api.uri.UriResourcePartTyped; -import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind; -import org.apache.olingo.server.api.uri.queryoption.expression.Expression; -import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException; -import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor; -import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind; -import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind; -import org.apache.olingo.server.tecsvc.processor.expression.operand.TypedOperand; -import org.apache.olingo.server.tecsvc.processor.expression.operand.UntypedOperand; -import org.apache.olingo.server.tecsvc.processor.expression.operand.VisitorOperand; -import org.apache.olingo.server.tecsvc.processor.expression.operation.BinaryOperator; -import org.apache.olingo.server.tecsvc.processor.expression.operation.MethodCallOperator; -import org.apache.olingo.server.tecsvc.processor.expression.operation.UnaryOperator; - -public class ExpressionVisitorImpl implements ExpressionVisitor<VisitorOperand> { - - final private Entity entity; - final private EdmEntitySet edmEntitySet; - - public ExpressionVisitorImpl(Entity entity, EdmEntitySet edmEntitySet) { - this.entity = entity; - this.edmEntitySet = edmEntitySet; - } - - @Override - public VisitorOperand visitBinaryOperator(BinaryOperatorKind operator, VisitorOperand left, VisitorOperand right) - throws ExpressionVisitException, ODataApplicationException { - - final BinaryOperator binaryOperator = new BinaryOperator(left, right); - - switch (operator) { - case AND: - return binaryOperator.andOperator(); - case OR: - return binaryOperator.orOperator(); - case EQ: - return binaryOperator.equalsOperator(); - case NE: - return binaryOperator.notEqualsOperator(); - case GE: - return binaryOperator.greaterEqualsOperator(); - case GT: - return binaryOperator.greaterThanOperator(); - case LE: - return binaryOperator.lessEqualsOperator(); - case LT: - return binaryOperator.lessThanOperator(); - case ADD: - case SUB: - case MUL: - case DIV: - case MOD: - return binaryOperator.arithmeticOperator(operator); - default: - return throwNotImplemented(); - } - } - - @Override - public VisitorOperand visitUnaryOperator(UnaryOperatorKind operator, VisitorOperand operand) - throws ExpressionVisitException, ODataApplicationException { - - final UnaryOperator unaryOperator = new UnaryOperator(operand); - - switch (operator) { - case MINUS: - return unaryOperator.minusOperation(); - case NOT: - return unaryOperator.notOperation(); - default: - // Can`t happen - return throwNotImplemented(); - } - } - - @Override - public VisitorOperand visitMethodCall(MethodKind methodCall, List<VisitorOperand> parameters) - throws ExpressionVisitException, ODataApplicationException { - - final MethodCallOperator methodCallOperation = new MethodCallOperator(parameters); - - switch (methodCall) { - case ENDSWITH: - return methodCallOperation.endsWith(); - case INDEXOF: - return methodCallOperation.indexOf(); - case STARTSWITH: - return methodCallOperation.startsWith(); - case TOLOWER: - return methodCallOperation.toLower(); - case TOUPPER: - return methodCallOperation.toUpper(); - case TRIM: - return methodCallOperation.trim(); - case SUBSTRING: - return methodCallOperation.substring(); - case CONTAINS: - return methodCallOperation.contains(); - case CONCAT: - return methodCallOperation.concat(); - case LENGTH: - return methodCallOperation.length(); - case YEAR: - return methodCallOperation.year(); - case MONTH: - return methodCallOperation.month(); - case DAY: - return methodCallOperation.day(); - case HOUR: - return methodCallOperation.hour(); - case MINUTE: - return methodCallOperation.minute(); - case SECOND: - return methodCallOperation.second(); - case FRACTIONALSECONDS: - return methodCallOperation.fractionalseconds(); - case ROUND: - return methodCallOperation.round(); - case FLOOR: - return methodCallOperation.floor(); - case CEILING: - return methodCallOperation.ceiling(); - - default: - return throwNotImplemented(); - } - } - - @Override - public VisitorOperand visitLambdaExpression(String lambdaFunction, String lambdaVariable, Expression expression) - throws ExpressionVisitException, ODataApplicationException { - - return throwNotImplemented(); - } - - @Override - public VisitorOperand visitLiteral(String literal) throws ExpressionVisitException, ODataApplicationException { - - return new UntypedOperand(literal); - } - - @Override - public VisitorOperand visitMember(UriInfoResource member) throws ExpressionVisitException, - ODataApplicationException { - - final List<UriResource> uriResourceParts = member.getUriResourceParts(); - - // UriResourceParts contains at least one UriResource - Property currentProperty = entity.getProperty(uriResourceParts.get(0).toString()); - EdmType currentType = ((UriResourcePartTyped) uriResourceParts.get(0)).getType(); - - EdmProperty currentEdmProperty = edmEntitySet.getEntityType() - .getStructuralProperty(uriResourceParts.get(0).toString()); - - for (int i = 1; i < uriResourceParts.size(); i++) { - currentType = ((UriResourcePartTyped) uriResourceParts.get(i)).getType(); - - if (currentProperty.isComplex() || currentProperty.isLinkedComplex()) { - final List<Property> complex = currentProperty.isLinkedComplex() ? - currentProperty.asLinkedComplex().getValue() : currentProperty.asComplex(); - - for (final Property innerProperty : complex) { - if (innerProperty.getName().equals(uriResourceParts.get(i).toString())) { - EdmComplexType edmComplexType = (EdmComplexType) currentEdmProperty.getType(); - currentEdmProperty = edmComplexType.getStructuralProperty(uriResourceParts.get(i).toString()); - currentProperty = innerProperty; - break; - } - } - } - } - - return new TypedOperand(((Property) currentProperty).getValue(), currentType, currentEdmProperty); - } - - @Override - public VisitorOperand visitAlias(String aliasName) throws ExpressionVisitException, ODataApplicationException { - return throwNotImplemented(); - } - - @Override - public VisitorOperand visitTypeLiteral(EdmType type) throws ExpressionVisitException, ODataApplicationException { - return throwNotImplemented(); - } - - @Override - public VisitorOperand visitLambdaReference(String variableName) throws ExpressionVisitException, - ODataApplicationException { - return throwNotImplemented(); - } - - @Override - public VisitorOperand visitEnum(EdmEnumType type, List<String> enumValues) throws ExpressionVisitException, - ODataApplicationException { - return throwNotImplemented(); - } - - private VisitorOperand throwNotImplemented() throws ODataApplicationException { - throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), - Locale.ROOT); - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterRuntimeException.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterRuntimeException.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterRuntimeException.java deleted file mode 100644 index 21fc110..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterRuntimeException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression; - -import org.apache.olingo.commons.api.ODataRuntimeException; - -public class FilterRuntimeException extends ODataRuntimeException { - - private static final long serialVersionUID = 1L; - - public FilterRuntimeException(Exception cause) { - super(cause); - } - - public FilterRuntimeException(String msg, Exception cause) { - super(msg, cause); - } - - public FilterRuntimeException(String msg) { - super(msg); - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterSystemQueryHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterSystemQueryHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterSystemQueryHandler.java deleted file mode 100644 index 5b5b506..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/FilterSystemQueryHandler.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Locale; - -import org.apache.olingo.commons.api.data.Entity; -import org.apache.olingo.commons.api.data.EntitySet; -import org.apache.olingo.commons.api.edm.EdmEntitySet; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.commons.core.edm.primitivetype.EdmBoolean; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.uri.queryoption.FilterOption; -import org.apache.olingo.server.api.uri.queryoption.OrderByItem; -import org.apache.olingo.server.api.uri.queryoption.OrderByOption; -import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException; -import org.apache.olingo.server.tecsvc.processor.expression.operand.TypedOperand; -import org.apache.olingo.server.tecsvc.processor.expression.operand.VisitorOperand; - -public class FilterSystemQueryHandler { - - public static void applyFilterSystemQuery(FilterOption filterOption, EntitySet entitySet, EdmEntitySet edmEntitySet) - throws ODataApplicationException { - - if (filterOption == null) { - return; - } - - try { - final Iterator<Entity> iter = entitySet.getEntities().iterator(); - - while (iter.hasNext()) { - final VisitorOperand operand = filterOption.getExpression() - .accept(new ExpressionVisitorImpl(iter.next(), edmEntitySet)); - final TypedOperand typedOperand = operand.asTypedOperand(); - - if (!(typedOperand.is(EdmBoolean.getInstance()) - && Boolean.TRUE.equals(typedOperand.getTypedValue(Boolean.class)))) { - iter.remove(); - } - } - - } catch (ExpressionVisitException e) { - throw new ODataApplicationException("Exception in filter evaluation", - HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT); - } - } - - public static void applyOrderByOption(final OrderByOption orderByOption, final EntitySet entitySet, - final EdmEntitySet edmEntitySet) throws ODataApplicationException { - - if (orderByOption == null) { - return; - } - - try { - applyOrderByOptionInternal(orderByOption, entitySet, edmEntitySet); - } catch (FilterRuntimeException e) { - if (e.getCause() instanceof ODataApplicationException) { - // Throw the nested exception, to send the correct HTTP status code in the HTTP response - throw (ODataApplicationException) e.getCause(); - } else { - throw new ODataApplicationException("Exception in orderBy evaluation", HttpStatusCode.INTERNAL_SERVER_ERROR - .getStatusCode(), Locale.ROOT); - } - } - } - - private static void applyOrderByOptionInternal(final OrderByOption orderByOption, final EntitySet entitySet, - final EdmEntitySet edmEntitySet) throws ODataApplicationException { - Collections.sort(entitySet.getEntities(), new Comparator<Entity>() { - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - public int compare(final Entity e1, final Entity e2) { - // Evaluate the first order option for both entity - // If and only if the result of the previous order option is equals to 0 - // evaluate the next order option until all options are evaluated or they are not equals - int result = 0; - - for (int i = 0; i < orderByOption.getOrders().size() && result == 0; i++) { - try { - final OrderByItem item = orderByOption.getOrders().get(i); - final TypedOperand op1 = - item.getExpression().accept(new ExpressionVisitorImpl(e1, edmEntitySet)).asTypedOperand(); - final TypedOperand op2 = - item.getExpression().accept(new ExpressionVisitorImpl(e2, edmEntitySet)).asTypedOperand(); - - if (op1.isNull() || op2.isNull()) { - if (op1.isNull() && op2.isNull()) { - result = 0; // null is equals to null - } else { - result = op1.isNull() ? -1 : 1; - } - } else { - Object o1 = op1.getValue(); - Object o2 = op2.getValue(); - - if (o1.getClass() == o2.getClass() && o1 instanceof Comparable) { - result = ((Comparable) o1).compareTo(o2); - } else { - result = 0; - } - } - - result = item.isDescending() ? result * -1 : result; - } catch (ODataApplicationException e) { - throw new FilterRuntimeException(e); - } catch (ExpressionVisitException e) { - throw new FilterRuntimeException(e); - } - } - return result; - } - }); - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/TypedOperand.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/TypedOperand.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/TypedOperand.java deleted file mode 100644 index 23037b6..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/TypedOperand.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression.operand; - -import java.math.BigDecimal; -import java.util.Locale; - -import org.apache.olingo.commons.api.edm.EdmPrimitiveType; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; -import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.api.edm.EdmType; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.commons.core.edm.primitivetype.EdmByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDecimal; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt16; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt32; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt64; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSingle; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.tecsvc.processor.expression.primitive.EdmNull; - -public class TypedOperand extends VisitorOperand { - - final private EdmType type; - final private EdmProperty edmProperty; - - public TypedOperand(Object value, EdmType type) { - super(value); - this.type = type; - this.edmProperty = null; - } - - public TypedOperand(Object value, EdmType type, EdmProperty edmProperty) { - super(value); - this.type = type; - this.edmProperty = edmProperty; - } - - @Override - public TypedOperand asTypedOperand() throws ODataApplicationException { - if (!isNull() && value.getClass() != getDefaultType((EdmPrimitiveType) type)) { - return asTypedOperand((EdmPrimitiveType) type); - } - return this; - } - - @Override - public TypedOperand asTypedOperand(EdmPrimitiveType... asTypes) throws ODataApplicationException { - if (type.equals(EdmNull.getInstance())) { - return this; - } else if (isNull()) { - return new TypedOperand(null, asTypes[0]); - } - - Object newValue = null; - for (EdmPrimitiveType asType : asTypes) { - // Use BigDecimal for unlimited precision - if (asType.equals(EdmDouble.getInstance()) - || asType.equals(EdmSingle.getInstance()) - || asType.equals(EdmDecimal.getInstance())) { - - try { - newValue = new BigDecimal(value.toString()); - } catch(NumberFormatException e) { - // Nothing to do - } - } else { - // Use type conversion of EdmPrimitive types - try { - final String literal = getLiteral(value); - newValue = tryCast(literal, (EdmPrimitiveType) type); - } catch (EdmPrimitiveTypeException e) { - // Nothing to do - } - } - - if (newValue != null) { - return new TypedOperand(newValue, asType); - } - } - - throw new ODataApplicationException("Cast failed ", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - - public TypedOperand castToCommonType(VisitorOperand otherOperand) throws ODataApplicationException { - final TypedOperand other = otherOperand.asTypedOperand(); - final EdmType oType = other.getType(); - - // Make sure that the EDM type is equals, check also the java type. - // So it is possible, that there is an conversation even if the same - // EdmType is provided. - // For example consider an Edm16 (internal Integer) and Edm16(internal - // Short) - // shortInstance.equals(intInstance) will always be false! - if (type == oType && value != null && other.getValue() != null - && value.getClass() == other.getValue().getClass()) { - return this; - } else if (isNullLiteral() || other.isNullLiteral()) { - return this; - } - - if (type.equals(EdmDouble.getInstance()) || oType.equals(EdmDouble.getInstance())) { - return asTypedOperand(EdmDouble.getInstance()); - } else if (type.equals(EdmSingle.getInstance()) || oType.equals(EdmSingle.getInstance())) { - return asTypedOperand(EdmSingle.getInstance()); - } else if (type.equals(EdmDecimal.getInstance()) || oType.equals(EdmDecimal.getInstance())) { - return asTypedOperand(EdmDecimal.getInstance()); - } else if (type.equals(EdmInt64.getInstance()) || oType.equals(EdmInt64.getInstance())) { - return asTypedOperand(EdmInt64.getInstance()); - } else if (type.equals(EdmInt32.getInstance()) || oType.equals(EdmInt32.getInstance())) { - return asTypedOperand(EdmInt32.getInstance()); - } else if (type.equals(EdmInt16.getInstance()) || oType.equals(equals(EdmInt16.getInstance()))) { - return asTypedOperand(EdmInt16.getInstance()); - } else { - return asTypedOperand((EdmPrimitiveType) type); - } - } - - public EdmType getType() { - return type; - } - - public <T> T getTypedValue(Class<T> clazz) { - return clazz.cast(value); - } - - public boolean isNullLiteral() { - return type.equals(EdmNull.getInstance()); - } - - public boolean isNull() { - return isNullLiteral() || value == null; - } - - public boolean isIntegerType() { - return is(EdmByte.getInstance(), - EdmSByte.getInstance(), - EdmInt16.getInstance(), - EdmInt32.getInstance(), - EdmInt64.getInstance()); - } - - public boolean isDecimalType() { - return is(EdmSingle.getInstance(), - EdmDouble.getInstance(), - EdmDecimal.getInstance()); - } - - public boolean is(EdmPrimitiveType... types) { - if (isNullLiteral()) { - return true; - } - - for (EdmPrimitiveType type : types) { - if (type.equals(this.type)) { - return true; - } - } - - return false; - } - - @Override - public EdmProperty getEdmProperty() { - return edmProperty; - } - - private String getLiteral(Object value) throws EdmPrimitiveTypeException { - final EdmProperty edmProperty = getEdmProperty(); - String uriLiteral = null; - - if (edmProperty != null) { - uriLiteral = ((EdmPrimitiveType) type).valueToString(value, edmProperty.isNullable(), edmProperty.getMaxLength(), - edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode()); - } else { - uriLiteral = ((EdmPrimitiveType) type).valueToString(value, null, null, null, null, null); - } - - return ((EdmPrimitiveType) type).toUriLiteral(uriLiteral); - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/UntypedOperand.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/UntypedOperand.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/UntypedOperand.java deleted file mode 100644 index fa82c09..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/UntypedOperand.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression.operand; - -import java.util.Locale; - -import org.apache.olingo.commons.api.edm.EdmPrimitiveType; -import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.commons.core.edm.primitivetype.EdmByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDecimal; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDuration; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt16; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt32; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt64; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSingle; -import org.apache.olingo.commons.core.edm.primitivetype.EdmString; -import org.apache.olingo.commons.core.edm.primitivetype.EdmTime; -import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.tecsvc.processor.expression.primitive.EdmNull; - -public class UntypedOperand extends VisitorOperand { - - public UntypedOperand(final String literal) { - super(literal); - } - - @Override - public TypedOperand asTypedOperand() throws ODataApplicationException { - return determineType(); - } - - @Override - public TypedOperand asTypedOperand(final EdmPrimitiveType... types) throws ODataApplicationException { - final String literal = (String) value; - Object newValue = null; - - // First try the null literal - if ((newValue = tryCast(literal, EdmNull.getInstance())) != null) { - return new TypedOperand(newValue, EdmNull.getInstance()); - } - - // Than try the given types - for (EdmPrimitiveType type : types) { - newValue = tryCast(literal, type); - - if (newValue != null) { - 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; - Object newValue = null; - - // Null literal - if ((newValue = tryCast(literal, EdmNull.getInstance())) != null) { - return new TypedOperand(newValue, EdmNull.getInstance()); - } - - // String - if ((newValue = tryCast(literal, EdmString.getInstance())) != null) { - return new TypedOperand(newValue, EdmString.getInstance()); - } - - // Date - if ((newValue = tryCast(literal, EdmDateTimeOffset.getInstance())) != null) { - return new TypedOperand(newValue, EdmDateTimeOffset.getInstance()); - } - - if ((newValue = tryCast(literal, EdmDate.getInstance())) != null) { - return new TypedOperand(newValue, EdmDate.getInstance()); - } - - if ((newValue = tryCast(literal, EdmTimeOfDay.getInstance())) != null) { - return new TypedOperand(newValue, EdmTimeOfDay.getInstance()); - } - - if ((newValue = tryCast(literal, EdmTime.getInstance())) != null) { - return new TypedOperand(newValue, EdmTime.getInstance()); - } - - if ((newValue = tryCast(literal, EdmDuration.getInstance())) != null) { - return new TypedOperand(newValue, EdmDuration.getInstance()); - } - - // Integer - if ((newValue = tryCast(literal, EdmSByte.getInstance())) != null) { - return new TypedOperand(newValue, EdmSByte.getInstance()); - } - - if ((newValue = tryCast(literal, EdmByte.getInstance())) != null) { - return new TypedOperand(newValue, EdmByte.getInstance()); - } - - if ((newValue = tryCast(literal, EdmInt16.getInstance())) != null) { - return new TypedOperand(newValue, EdmInt16.getInstance()); - } - - if ((newValue = tryCast(literal, EdmInt32.getInstance())) != null) { - return new TypedOperand(newValue, EdmInt32.getInstance()); - } - - if ((newValue = tryCast(literal, EdmInt64.getInstance())) != null) { - return new TypedOperand(newValue, EdmInt64.getInstance()); - } - - // Decimal - if ((newValue = tryCast(literal, EdmDecimal.getInstance())) != null) { - return new TypedOperand(newValue, EdmDecimal.getInstance()); - } - - // Float - if ((newValue = tryCast(literal, EdmSingle.getInstance())) != null) { - return new TypedOperand(newValue, EdmSingle.getInstance()); - } - - if ((newValue = tryCast(literal, EdmDouble.getInstance())) != null) { - return new TypedOperand(newValue, EdmDouble.getInstance()); - } - - throw new ODataApplicationException("Could not determine type for literal " + literal, - HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ROOT); - } - - @Override - public EdmProperty getEdmProperty() { - return null; - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/VisitorOperand.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/VisitorOperand.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/VisitorOperand.java deleted file mode 100644 index 02f6fb0..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/VisitorOperand.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression.operand; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.HashMap; - -import org.apache.olingo.commons.api.edm.EdmPrimitiveType; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; -import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.api.edm.EdmType; -import org.apache.olingo.commons.core.edm.primitivetype.EdmByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDecimal; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt16; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt32; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt64; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSingle; -import org.apache.olingo.server.api.ODataApplicationException; - -public abstract class VisitorOperand { - final static private HashMap<EdmType, Class<?>> defaultTypeMapping = new HashMap<EdmType, Class<?>>(); - protected Object value; - - static { - defaultTypeMapping.put(EdmByte.getInstance(), BigInteger.class); - defaultTypeMapping.put(EdmSByte.getInstance(), BigInteger.class); - defaultTypeMapping.put(EdmInt16.getInstance(), BigInteger.class); - defaultTypeMapping.put(EdmInt32.getInstance(), BigInteger.class); - defaultTypeMapping.put(EdmInt64.getInstance(), BigInteger.class); - - defaultTypeMapping.put(EdmSingle.getInstance(), BigDecimal.class); - defaultTypeMapping.put(EdmDouble.getInstance(), BigDecimal.class); - defaultTypeMapping.put(EdmDecimal.getInstance(), BigDecimal.class); - } - - public VisitorOperand(Object value) { - this.value = value; - } - - public abstract TypedOperand asTypedOperand() throws ODataApplicationException; - - public abstract TypedOperand asTypedOperand(EdmPrimitiveType... types) throws ODataApplicationException; - - public abstract EdmProperty getEdmProperty(); - - public Object getValue() { - return value; - } - - protected Object castTo(final String value, EdmPrimitiveType type) throws EdmPrimitiveTypeException { - final EdmProperty edmProperty = getEdmProperty(); - - if (edmProperty != null) { - return type.valueOfString(value, edmProperty.isNullable(), edmProperty.getMaxLength(), - edmProperty.getPrecision(), edmProperty.getScale(), - edmProperty.isUnicode(), getDefaultType(type)); - } else { - return type.valueOfString(value, null, null, null, null, null, getDefaultType(type)); - } - } - - protected Class<?> getDefaultType(EdmPrimitiveType type) { - return defaultTypeMapping.get(type) != null ? defaultTypeMapping.get(type) : type.getDefaultType(); - } - - protected Object tryCast(final String literal, final EdmPrimitiveType type) { - try { - return castTo(type.fromUriLiteral(literal), type); - } catch (EdmPrimitiveTypeException e) { - return null; - } - } - -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/BinaryOperator.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/BinaryOperator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/BinaryOperator.java deleted file mode 100644 index f41e798..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/BinaryOperator.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression.operation; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Timestamp; -import java.util.Calendar; -import java.util.Locale; - -import org.apache.olingo.commons.api.edm.EdmType; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.commons.core.edm.primitivetype.EdmBoolean; -import org.apache.olingo.commons.core.edm.primitivetype.EdmByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDuration; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt16; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt32; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt64; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSByte; -import org.apache.olingo.commons.core.edm.primitivetype.EdmSingle; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind; -import org.apache.olingo.server.tecsvc.processor.expression.operand.TypedOperand; -import org.apache.olingo.server.tecsvc.processor.expression.operand.VisitorOperand; -import org.apache.olingo.server.tecsvc.processor.expression.primitive.EdmNull; - -public class BinaryOperator { - private static final int FACTOR_SECOND_INT = 1000; - private static final BigDecimal FACTOR_SECOND = new BigDecimal(1000); - private static final BigInteger EDM_SBYTE_MIN = BigInteger.valueOf(Byte.MIN_VALUE); - private static final BigInteger EDN_SBYTE_MAX = BigInteger.valueOf(Byte.MAX_VALUE); - private static final BigInteger EDM_BYTE_MIN = BigInteger.ZERO; - private static final BigInteger EDM_BYTE_MAX = BigInteger.valueOf(((Byte.MAX_VALUE * 2) + 1)); - private static final BigInteger EDM_INT16_MIN = BigInteger.valueOf(Short.MIN_VALUE); - private static final BigInteger EDM_INT16_MAX = BigInteger.valueOf(Short.MAX_VALUE); - private static final BigInteger EDM_INT32_MIN = BigInteger.valueOf(Integer.MIN_VALUE); - private static final BigInteger EDM_INT32_MAX = BigInteger.valueOf(Integer.MAX_VALUE); - private static final BigInteger EDM_INT64_MIN = BigInteger.valueOf(Long.MIN_VALUE); - private static final BigInteger EDM_INT64_MAX = BigInteger.valueOf(Long.MAX_VALUE); - private static final BigDecimal EDM_SINGLE_MIN = BigDecimal.valueOf(Float.MIN_VALUE); - private static final BigDecimal EDM_SINGLE_MAX = BigDecimal.valueOf(Float.MAX_VALUE); - - private static final int EQUALS = 0; - private static final int LESS_THAN = -1; - private static final int GREATER_THAN = 1; - - private TypedOperand right; - private TypedOperand left; - - public BinaryOperator(final VisitorOperand leftOperand, final VisitorOperand rightOperand) - throws ODataApplicationException { - left = leftOperand.asTypedOperand(); - right = rightOperand.asTypedOperand(); - - left = left.castToCommonType(right); - right = right.castToCommonType(left); - } - - public VisitorOperand andOperator() throws ODataApplicationException { - Boolean result = null; - if (left.is(EdmBoolean.getInstance()) && right.is(EdmBoolean.getInstance())) { - if (Boolean.TRUE.equals(left.getValue()) && Boolean.TRUE.equals(right.getValue())) { - result = true; - } else if (Boolean.FALSE.equals(left.getValue()) || Boolean.FALSE.equals(right.getValue())) { - result = false; - } - - return new TypedOperand(result, EdmBoolean.getInstance()); - } else { - throw new ODataApplicationException("Add operator needs two binary operands", - HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - public VisitorOperand orOperator() throws ODataApplicationException { - Boolean result = null; - if (left.is(EdmBoolean.getInstance()) && right.is(EdmBoolean.getInstance())) { - if (Boolean.TRUE.equals(left.getValue()) || Boolean.TRUE.equals(right.getValue())) { - result = true; - } else if (Boolean.FALSE.equals(left.getValue()) && Boolean.FALSE.equals(right.getValue())) { - result = false; - } - - return new TypedOperand(result, EdmBoolean.getInstance()); - } else { - throw new ODataApplicationException("Or operator needs two binary operands", - HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - public VisitorOperand equalsOperator() { - final boolean result = isBinaryComparisonNecessary() && binaryComparison(EQUALS); - return new TypedOperand(result, EdmBoolean.getInstance()); - } - - public VisitorOperand notEqualsOperator() { - final VisitorOperand equalsOperator = equalsOperator(); - return new TypedOperand(!(Boolean) equalsOperator.getValue(), EdmBoolean.getInstance()); - } - - private boolean isBinaryComparisonNecessary() { - // binaryComparison() need to be called, if both operand are either null or not null - return !(left.isNull() ^ right.isNull()); - } - - public VisitorOperand greaterEqualsOperator() { - final boolean result = isBinaryComparisonNecessary() && binaryComparison(GREATER_THAN, EQUALS); - return new TypedOperand(result, EdmBoolean.getInstance()); - } - - public VisitorOperand greaterThanOperator() { - final boolean result = isBinaryComparisonNecessary() && binaryComparison(GREATER_THAN); - return new TypedOperand(result, EdmBoolean.getInstance()); - } - - public VisitorOperand lessEqualsOperator() { - final boolean result = isBinaryComparisonNecessary() && binaryComparison(LESS_THAN, EQUALS); - return new TypedOperand(result, EdmBoolean.getInstance()); - } - - public VisitorOperand lessThanOperator() { - final boolean result = isBinaryComparisonNecessary() && binaryComparison(LESS_THAN); - return new TypedOperand(result, EdmBoolean.getInstance()); - } - - private boolean binaryComparison(int... expect) { - int result; - - if (left.isNull() && right.isNull()) { - result = 0; // null is equals to null - } else { - // left and right are not null! - if (left.isIntegerType()) { - result = left.getTypedValue(BigInteger.class).compareTo(right.getTypedValue(BigInteger.class)); - } else if (left.isDecimalType()) { - result = left.getTypedValue(BigDecimal.class).compareTo(right.getTypedValue(BigDecimal.class)); - } else { - result = left.getValue().equals(right.getValue()) ? 0 : 1; - } - } - for (int expectedValue : expect) { - if (expectedValue == result) { - return true; - } - } - - return false; - } - - public VisitorOperand arithmeticOperator(BinaryOperatorKind operator) throws ODataApplicationException { - if (left.isNull() || right.isNull()) { - return new TypedOperand(new Object(), EdmNull.getInstance()); - } else { - if (left.isIntegerType()) { - final BigInteger result = integerArithmeticOperation(operator); - return new TypedOperand(result, determineResultType(result, left)); - } else if (left.isDecimalType()) { - final BigDecimal result = decimalArithmeticOperation(operator); - return new TypedOperand(result, determineResultType(result, left)); - } else if (left.is(EdmDate.getInstance(), EdmDuration.getInstance(), EdmDateTimeOffset.getInstance())) { - return dateArithmeticOperation(operator); - } else { - throw new ODataApplicationException("Invalid type", HttpStatusCode.BAD_REQUEST.getStatusCode(), - Locale.ROOT); - } - } - } - - private EdmType determineResultType(final Number arithmeticResult, TypedOperand leftOperand) { - // Left and right operand have the same typed, so it is enough to check the type of the left operand - if (leftOperand.isDecimalType()) { - final BigDecimal value = (BigDecimal) arithmeticResult; - if (value.compareTo(EDM_SINGLE_MIN) >= 0 && value.compareTo(EDM_SINGLE_MAX) <= 0) { - return EdmSingle.getInstance(); - } else { - return EdmDouble.getInstance(); - } - } else { - final BigInteger value = (BigInteger) arithmeticResult; - - if (value.compareTo(EDN_SBYTE_MAX) <= 0 && value.compareTo(EDM_SBYTE_MIN) >= 0) { - return EdmSByte.getInstance(); - } - if (value.compareTo(EDM_BYTE_MAX) <= 0 && value.compareTo(EDM_BYTE_MIN) >= 0) { - return EdmByte.getInstance(); - } - if (value.compareTo(EDM_INT16_MAX) <= 0 && value.compareTo(EDM_INT16_MIN) >= 0) { - return EdmInt16.getInstance(); - } - if (value.compareTo(EDM_INT32_MAX) <= 0 && value.compareTo(EDM_INT32_MIN) >= 0) { - return EdmInt32.getInstance(); - } - if (value.compareTo(EDM_INT64_MAX) <= 0 && value.compareTo(EDM_INT64_MIN) >= 0) { - return EdmInt64.getInstance(); - } - // Choose double instead single because precision is higher (52 bits instead of 23) - return EdmDouble.getInstance(); - } - } - - private VisitorOperand dateArithmeticOperation(BinaryOperatorKind operator) throws ODataApplicationException { - VisitorOperand result = null; - - if (left.is(EdmDate.getInstance())) { - if (right.is(EdmDate.getInstance()) && operator == BinaryOperatorKind.SUB) { - long millis = left.getTypedValue(Calendar.class).getTimeInMillis() - - left.getTypedValue(Calendar.class).getTimeInMillis(); - - result = new TypedOperand(new BigDecimal(millis).divide(FACTOR_SECOND), EdmDuration.getInstance()); - } else if (right.is(EdmDuration.getInstance()) && operator == BinaryOperatorKind.ADD) { - long millis = left.getTypedValue(Calendar.class).getTimeInMillis() - + (right.getTypedValue(BigDecimal.class).longValue() * FACTOR_SECOND_INT); - - result = new TypedOperand(new Timestamp(millis), EdmDateTimeOffset.getInstance()); - } else if (right.is(EdmDuration.getInstance()) && operator == BinaryOperatorKind.SUB) { - long millis = left.getTypedValue(Calendar.class).getTimeInMillis() - - (right.getTypedValue(BigDecimal.class).longValue() * FACTOR_SECOND_INT); - - result = new TypedOperand(new Timestamp(millis), EdmDateTimeOffset.getInstance()); - } - } else if (left.is(EdmDuration.getInstance())) { - if (right.is(EdmDuration.getInstance()) && operator == BinaryOperatorKind.ADD) { - long seconds = left.getTypedValue(BigDecimal.class).longValue() - + right.getTypedValue(BigDecimal.class).longValue(); - - result = new TypedOperand(new BigDecimal(seconds), EdmDuration.getInstance()); - } else if (right.is(EdmDuration.getInstance()) && operator == BinaryOperatorKind.SUB) { - long seconds = left.getTypedValue(BigDecimal.class).longValue() - - right.getTypedValue(BigDecimal.class).longValue(); - - result = new TypedOperand(new BigDecimal(seconds), EdmDuration.getInstance()); - } - } else if (left.is(EdmDateTimeOffset.getInstance())) { - if (right.is(EdmDuration.getInstance()) && operator == BinaryOperatorKind.ADD) { - long millis = left.getTypedValue(Timestamp.class).getTime() - + (right.getTypedValue(BigDecimal.class).longValue() * FACTOR_SECOND_INT); - - result = new TypedOperand(new Timestamp(millis), EdmDateTimeOffset.getInstance()); - } else if (right.is(EdmDuration.getInstance()) && operator == BinaryOperatorKind.SUB) { - long millis = left.getTypedValue(Timestamp.class).getTime() - - (right.getTypedValue(BigDecimal.class).longValue() * FACTOR_SECOND_INT); - - result = new TypedOperand(new Timestamp(millis), EdmDateTimeOffset.getInstance()); - } else if (right.is(EdmDateTimeOffset.getInstance()) && operator == BinaryOperatorKind.SUB) { - long millis = left.getTypedValue(Timestamp.class).getTime() - - right.getTypedValue(Timestamp.class).getTime(); - - result = new TypedOperand(new BigDecimal(millis).divide(FACTOR_SECOND), EdmDuration.getInstance()); - } - } - - if (result == null) { - throw new ODataApplicationException("Invalid operation / operand", HttpStatusCode.BAD_REQUEST.getStatusCode(), - Locale.ROOT); - } else { - return result; - } - } - - private BigDecimal decimalArithmeticOperation(BinaryOperatorKind operator) throws ODataApplicationException { - final BigDecimal left = this.left.getTypedValue(BigDecimal.class); - final BigDecimal right = this.right.getTypedValue(BigDecimal.class); - - switch (operator) { - case ADD: - return left.add(right); - case DIV: - return left.divide(left); - case MUL: - return left.multiply(right); - case SUB: - return left.subtract(right); - default: - throw new ODataApplicationException("Operator not valid", HttpStatusCode.BAD_REQUEST.getStatusCode(), - Locale.ROOT); - } - } - - private BigInteger integerArithmeticOperation(BinaryOperatorKind operator) throws ODataApplicationException { - final BigInteger left = this.left.getTypedValue(BigInteger.class); - final BigInteger right = this.right.getTypedValue(BigInteger.class); - - switch (operator) { - case ADD: - return left.add(right); - case DIV: - return left.divide(right); - case MUL: - return left.multiply(right); - case SUB: - return left.subtract(right); - case MOD: - return left.mod(right); - default: - throw new ODataApplicationException("Operator not valid", HttpStatusCode.BAD_REQUEST.getStatusCode(), - Locale.ROOT); - } - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2ebdea80/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/MethodCallOperator.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/MethodCallOperator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/MethodCallOperator.java deleted file mode 100644 index ece4df4..0000000 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/MethodCallOperator.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * 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.server.tecsvc.processor.expression.operation; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; -import java.math.RoundingMode; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; - -import org.apache.olingo.commons.api.edm.EdmPrimitiveType; -import org.apache.olingo.commons.api.edm.EdmType; -import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.commons.core.edm.primitivetype.EdmBoolean; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDecimal; -import org.apache.olingo.commons.core.edm.primitivetype.EdmInt32; -import org.apache.olingo.commons.core.edm.primitivetype.EdmString; -import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay; -import org.apache.olingo.server.api.ODataApplicationException; -import org.apache.olingo.server.tecsvc.processor.expression.operand.TypedOperand; -import org.apache.olingo.server.tecsvc.processor.expression.operand.VisitorOperand; - -public class MethodCallOperator { - - final private List<VisitorOperand> parameters; - - public MethodCallOperator(List<VisitorOperand> parameters) { - this.parameters = parameters; - } - - public VisitorOperand endsWith() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).endsWith(params.get(1)); - } - }, EdmBoolean.getInstance()); - } - - public VisitorOperand indexOf() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).indexOf(params.get(1)); - } - }, EdmInt32.getInstance()); - } - - public VisitorOperand startsWith() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).startsWith(params.get(1)); - } - }, EdmBoolean.getInstance()); - } - - public VisitorOperand toLower() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).toLowerCase(); - } - }, EdmString.getInstance()); - } - - public VisitorOperand toUpper() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).toUpperCase(); - } - }, EdmString.getInstance()); - } - - public VisitorOperand trim() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).trim(); - } - }, EdmString.getInstance()); - } - - public VisitorOperand substring() throws ODataApplicationException { - final TypedOperand valueOperand = parameters.get(0).asTypedOperand(); - final TypedOperand startOperand = parameters.get(1).asTypedOperand(); - - if (valueOperand.isNull() || startOperand.isNull()) { - return new TypedOperand(null, EdmString.getInstance()); - } else if (valueOperand.is(EdmString.getInstance()) && startOperand.isIntegerType()) { - final String value = valueOperand.getTypedValue(String.class); - final BigInteger start = startOperand.getTypedValue(BigInteger.class); - int end = value.length(); - - if (parameters.size() == 3) { - final TypedOperand lengthOperand = parameters.get(2).asTypedOperand(); - - if (lengthOperand.isNull()) { - return new TypedOperand(null, EdmString.getInstance()); - } else if (lengthOperand.isIntegerType()) { - end = Math.min(start.add(lengthOperand.getTypedValue(BigInteger.class)).intValue(), value.length()); - } else { - throw new ODataApplicationException("Third substring parameter should be Edm.Int32", - HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - return new TypedOperand(value.substring(Math.min(start.intValue(), value.length()), end), - EdmString.getInstance()); - } else { - throw new ODataApplicationException("Substring has invalid parameters. First parameter should be Edm.String," - + " second parameter should be Edm.Int32", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - public VisitorOperand contains() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).contains(params.get(1)); - } - }, EdmBoolean.getInstance()); - } - - public VisitorOperand concat() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0) + params.get(1); - } - }, EdmString.getInstance()); - } - - public VisitorOperand length() throws ODataApplicationException { - return stringFunction(new StringFunction() { - @Override - public Object perform(List<String> params) { - return params.get(0).length(); - } - }, EdmInt32.getInstance()); - } - - public VisitorOperand year() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - return calendar.get(Calendar.YEAR); - } - }, EdmInt32.getInstance(), EdmDateTimeOffset.getInstance(), EdmDate.getInstance()); - } - - public VisitorOperand month() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - // Month is 0-based! - return calendar.get(Calendar.MONTH) + 1; - } - }, EdmInt32.getInstance(), EdmDateTimeOffset.getInstance(), EdmDate.getInstance()); - } - - public VisitorOperand day() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - return calendar.get(Calendar.DAY_OF_MONTH); - } - }, EdmInt32.getInstance(), EdmDateTimeOffset.getInstance(), EdmDate.getInstance()); - } - - public VisitorOperand hour() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - return calendar.get(Calendar.HOUR_OF_DAY); - } - }, EdmInt32.getInstance(), EdmDateTimeOffset.getInstance(), EdmTimeOfDay.getInstance()); - } - - public VisitorOperand minute() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - return calendar.get(Calendar.MINUTE); - } - }, EdmInt32.getInstance(), EdmDateTimeOffset.getInstance(), EdmTimeOfDay.getInstance()); - } - - public VisitorOperand second() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - return calendar.get(Calendar.SECOND); - } - }, EdmInt32.getInstance(), EdmDateTimeOffset.getInstance(), EdmTimeOfDay.getInstance()); - } - - public VisitorOperand fractionalseconds() throws ODataApplicationException { - return dateFunction(new DateFunction() { - @Override - public Object perform(Calendar calendar, TypedOperand operand) { - if (operand.getValue() instanceof Timestamp) { - return new BigDecimal(operand.getTypedValue(Timestamp.class).getNanos()).divide(BigDecimal - .valueOf(1000 * 1000 * 1000)); - } else { - return new BigDecimal(calendar.get(Calendar.MILLISECOND)).divide(BigDecimal.valueOf(1000)); - } - } - }, EdmDecimal.getInstance(), EdmDateTimeOffset.getInstance(), EdmTimeOfDay.getInstance()); - } - - public VisitorOperand round() throws ODataApplicationException { - final TypedOperand operand = parameters.get(0).asTypedOperand(); - if (operand.isNull()) { - return operand; - } else if (operand.isDecimalType()) { - return new TypedOperand(operand.getTypedValue(BigDecimal.class).round(new MathContext(1, RoundingMode.HALF_UP)), - operand.getType()); - } else { - throw new ODataApplicationException("Invalid type", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - public VisitorOperand floor() throws ODataApplicationException { - final TypedOperand operand = parameters.get(0).asTypedOperand(); - if (operand.isNull()) { - return operand; - } else if (operand.isDecimalType()) { - return new TypedOperand(operand.getTypedValue(BigDecimal.class).round(new MathContext(1, RoundingMode.FLOOR)), - operand.getType()); - } else { - throw new ODataApplicationException("Invalid type", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - public VisitorOperand ceiling() throws ODataApplicationException { - final TypedOperand operand = parameters.get(0).asTypedOperand(); - if (operand.isNull()) { - return operand; - } else if (operand.isDecimalType()) { - return new TypedOperand(operand.getTypedValue(BigDecimal.class).round(new MathContext(1, RoundingMode.CEILING)), - operand.getType()); - } else { - throw new ODataApplicationException("Invalid type", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - private interface StringFunction { - Object perform(List<String> params); - } - - private interface DateFunction { - Object perform(Calendar calendar, TypedOperand operand); - } - - private VisitorOperand dateFunction(DateFunction f, EdmType returnType, EdmPrimitiveType... expectedTypes) - throws ODataApplicationException { - final TypedOperand operand = parameters.get(0).asTypedOperand(); - - if (operand.is(expectedTypes)) { - if (!operand.isNull()) { - Calendar calendar = null; - if (operand.is(EdmDate.getInstance())) { - calendar = operand.getTypedValue(Calendar.class); - } else if (operand.is(EdmDateTimeOffset.getInstance())) { - final Timestamp timestamp = operand.getTypedValue(Timestamp.class); - calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - calendar.setTimeInMillis(timestamp.getTime()); - } else if (operand.is(EdmTimeOfDay.getInstance())) { - calendar = operand.getTypedValue(Calendar.class); - } else { - throw new ODataApplicationException("Invalid type", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - - return new TypedOperand(f.perform(calendar, operand), returnType); - } else { - return new TypedOperand(null, returnType); - } - } else { - throw new ODataApplicationException("Invalid type", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); - } - } - - private VisitorOperand stringFunction(StringFunction f, EdmType returnValue) throws ODataApplicationException { - List<String> stringParameters = getParametersAsString(); - if (stringParameters.contains(null)) { - return new TypedOperand(null, returnValue); - } else { - return new TypedOperand(f.perform(stringParameters), returnValue); - } - } - - private List<String> getParametersAsString() throws ODataApplicationException { - List<String> result = new ArrayList<String>(); - - for (VisitorOperand param : parameters) { - TypedOperand operand = param.asTypedOperand(); - if (operand.isNull()) { - result.add(null); - } else if (operand.is(EdmString.getInstance())) { - result.add(operand.getTypedValue(String.class)); - } else { - throw new ODataApplicationException("Invalid parameter. Expected Edm.String", HttpStatusCode.BAD_REQUEST - .getStatusCode(), Locale.ROOT); - } - } - - return result; - } -}
