You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2015/02/20 14:34:14 UTC
[3/4] olingo-odata4 git commit: [OLINGO-545] OrderBy and Filter
System Query Option evaluation added to TecScenario
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/0e6c9a11/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
new file mode 100644
index 0000000..23037b6
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/TypedOperand.java
@@ -0,0 +1,199 @@
+/*
+ * 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/0e6c9a11/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
new file mode 100644
index 0000000..fa82c09
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/UntypedOperand.java
@@ -0,0 +1,155 @@
+/*
+ * 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/0e6c9a11/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
new file mode 100644
index 0000000..02f6fb0
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operand/VisitorOperand.java
@@ -0,0 +1,93 @@
+/*
+ * 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/0e6c9a11/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
new file mode 100644
index 0000000..f41e798
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/BinaryOperator.java
@@ -0,0 +1,318 @@
+/*
+ * 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/0e6c9a11/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
new file mode 100644
index 0000000..ece4df4
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/MethodCallOperator.java
@@ -0,0 +1,334 @@
+/*
+ * 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;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/0e6c9a11/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/UnaryOperator.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/UnaryOperator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/UnaryOperator.java
new file mode 100644
index 0000000..b0b5435
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/operation/UnaryOperator.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util.Locale;
+
+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.EdmDuration;
+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 UnaryOperator {
+ final private TypedOperand operand;
+
+ public UnaryOperator(VisitorOperand operand) throws ODataApplicationException {
+ this.operand = operand.asTypedOperand();
+ }
+
+ public VisitorOperand minusOperation() throws ODataApplicationException {
+ if (operand.isNull()) {
+ return operand;
+ } else if (operand.isIntegerType()) {
+ return new TypedOperand(operand.getTypedValue(BigInteger.class).negate(), operand.getType());
+ } else if (operand.isDecimalType() || operand.is(EdmDuration.getInstance())) {
+ return new TypedOperand(operand.getTypedValue(BigDecimal.class).negate(), operand.getType());
+ } else {
+ throw new ODataApplicationException("Unsupported type", HttpStatusCode.BAD_REQUEST.getStatusCode(),
+ Locale.ROOT);
+ }
+ }
+
+ public VisitorOperand notOperation() throws ODataApplicationException {
+ if (operand.isNull()) {
+ return operand;
+ } else if (operand.is(EdmBoolean.getInstance())) {
+ return new TypedOperand(!operand.getTypedValue(Boolean.class), operand.getType());
+ } else {
+ throw new ODataApplicationException("Unsupported type", HttpStatusCode.BAD_REQUEST.getStatusCode(),
+ Locale.ROOT);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/0e6c9a11/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/primitive/EdmNull.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/primitive/EdmNull.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/primitive/EdmNull.java
new file mode 100644
index 0000000..02b624d
--- /dev/null
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/expression/primitive/EdmNull.java
@@ -0,0 +1,58 @@
+/*
+ * 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.primitive;
+
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.core.edm.primitivetype.SingletonPrimitiveType;
+
+
+public final class EdmNull extends SingletonPrimitiveType {
+
+ private static final EdmNull instance = new EdmNull();
+
+ public static EdmNull getInstance() {
+ return instance;
+ }
+
+ @Override
+ public Class<?> getDefaultType() {
+ return Object.class;
+ }
+
+ @Override
+ protected <T> T internalValueOfString(String value, Boolean isNullable, Integer maxLength, Integer precision,
+ Integer scale, Boolean isUnicode, Class<T> returnType) throws EdmPrimitiveTypeException {
+ if (!value.equals("null")) {
+ throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content.");
+ }
+
+ if (returnType.isAssignableFrom(Object.class)) {
+ return returnType.cast(new Object());
+ } else {
+ throw new ClassCastException("unsupported return type " + returnType.getSimpleName());
+ }
+ }
+
+ @Override
+ protected <T> String internalValueToString(T value, Boolean isNullable, Integer maxLength, Integer precision,
+ Integer scale, Boolean isUnicode) throws EdmPrimitiveTypeException {
+ return "null";
+ }
+
+}