This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch camel-4.4.x in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.4.x by this push: new 9df80f9f47d CAMEL-21109: Make comparing numbers and floating numbers in typeCoerce work better with better support for float type also. (#15281) 9df80f9f47d is described below commit 9df80f9f47dddcc7f4c55144b75da00a7b2d4b4a Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Aug 23 06:59:27 2024 +0200 CAMEL-21109: Make comparing numbers and floating numbers in typeCoerce work better with better support for float type also. (#15281) --- .../camel/converter/TypeCoerceCompareTest.java | 330 +++++++++++++++++++++ .../org/apache/camel/support/ObjectHelper.java | 29 +- 2 files changed, 355 insertions(+), 4 deletions(-) diff --git a/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java b/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java new file mode 100644 index 00000000000..8d0695fafa7 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java @@ -0,0 +1,330 @@ +/* + * 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.camel.converter; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.TypeConverter; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.support.ObjectHelper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TypeCoerceCompareTest extends ContextTestSupport { + + @Test + public void testCompareStringString() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", "7") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", "7.5") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", "40") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7", "40") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", "40") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", "7.5") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", "7.5") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.0", "7") == 0); + } + + @Test + public void testCompareStringInteger() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 7) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 40) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7", 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.0", 7) == 0); + } + + @Test + public void testCompareStringLong() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 7L) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 40L) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7", 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.0", 7L) == 0); + } + + @Test + public void testCompareStringDouble() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 7d) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 7.5d) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 40d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7", 40d) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 40d) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 7.5d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 7.5d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.0", 7d) == 0); + } + + @Test + public void testCompareStringFloat() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 7f) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 7.5f) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "40", 40f) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7", 40f) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 40f) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 7.5f) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.5", 7.5f) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, "7.0", 7f) == 0); + } + + @Test + public void testCompareIntegerString() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, "7") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, "7.5") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, "40") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, "40") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, "7.5") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, "7") == 0); + } + + @Test + public void testCompareLongString() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, "7") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, "7.5") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, "40") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, "40") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, "7.5") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, "7") == 0); + } + + @Test + public void testCompareDoubleString() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40d, "7") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40.5d, "7.5") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40d, "40") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7d, "40.5") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5d, "40") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5d, "7.5") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5d, "7.5") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.0d, "7") == 0); + } + + @Test + public void testCompareFloatString() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40f, "7") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40.5f, "7.5") > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40f, "40") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7f, "40.5") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5f, "40") < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5f, "7.5") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5f, "7.5") == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.0f, "7") == 0); + } + + @Test + public void testCompareIntegerInteger() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 7) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 40) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 7) == 0); + } + + @Test + public void testCompareLongLong() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 7L) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 40L) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8L, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 7L) == 0); + } + + @Test + public void testCompareIntegerLong() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 7L) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 40L) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 7L) == 0); + } + + @Test + public void testCompareLongInteger() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 7) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 40) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8L, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 7) == 0); + } + + @Test + public void testCompareDoubleInteger() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40.5d, 7) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40d, 40) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5d, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8d, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7d, 7) == 0); + } + + @Test + public void testCompareIntegerDouble() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 7.5d) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 40.0d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 40d) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8, 40d) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 7d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 7.0d) == 0); + } + + @Test + public void testCompareDoubleLong() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40.5d, 7L) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40d, 40L) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5d, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8d, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7d, 7L) == 0); + } + + @Test + public void testCompareLongDouble() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 7.5d) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 40d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 40.5d) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8L, 40d) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 7d) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 7.0d) == 0); + } + + @Test + public void testCompareFloatInteger() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40.5f, 7) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40f, 40) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5f, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8f, 40) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7f, 7) == 0); + } + + @Test + public void testCompareIntegerFloat() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 7.5f) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40, 40f) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 40.0f) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8, 40f) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, 7.0f) == 0); + } + + @Test + public void testCompareFloatLong() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40.5f, 7L) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40f, 40L) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7.5f, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8f, 40L) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7f, 7L) == 0); + } + + @Test + public void testCompareLongFloat() { + TypeConverter tc = context.getTypeConverter(); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 7.5f) > 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 40L, 40.0f) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 40f) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 8L, 40f) < 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 7f) == 0); + assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 7.0f) == 0); + } + + @Test + public void testPredicate() throws Exception { + getMockEndpoint("mock:match").expectedBodiesReceived("40", "8", "7.5", 41f, 8f, 7.5f); + getMockEndpoint("mock:other").expectedBodiesReceived("6", "1", 5f, 2f); + + // string + template.sendBody("direct:start", "40"); + template.sendBody("direct:start", "8"); + template.sendBody("direct:start", "7.5"); + template.sendBody("direct:start", "6"); + template.sendBody("direct:start", "1"); + + // float + template.sendBody("direct:start", 41f); + template.sendBody("direct:start", 8f); + template.sendBody("direct:start", 7.5f); + template.sendBody("direct:start", 5f); + template.sendBody("direct:start", 2f); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testPredicate2() throws Exception { + getMockEndpoint("mock:match2").expectedBodiesReceived("40", "8", "7.0", 41f, 8f, 7.0f); + getMockEndpoint("mock:other2").expectedBodiesReceived("6", "1", 5f, 2f); + + // string + template.sendBody("direct:start2", "40"); + template.sendBody("direct:start2", "8"); + template.sendBody("direct:start2", "7.0"); + template.sendBody("direct:start2", "6"); + template.sendBody("direct:start2", "1"); + + // float + template.sendBody("direct:start2", 41f); + template.sendBody("direct:start2", 8f); + template.sendBody("direct:start2", 7.0f); + template.sendBody("direct:start2", 5f); + template.sendBody("direct:start2", 2f); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .setProperty("left", simple("${body}")) + .setProperty("right", simple("7.5")) + .choice() + .when(simple("${exchangeProperty.left} >= ${exchangeProperty.right}")) + .to("mock:match") + .otherwise() + .to("mock:other") + .end(); + + from("direct:start2") + .setProperty("left", simple("${body}")) + .setProperty("right", simple("7")) + .choice() + .when(simple("${exchangeProperty.left} >= ${exchangeProperty.right}")) + .to("mock:match2") + .otherwise() + .to("mock:other2") + .end(); + } + }; + } +} diff --git a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java index 3c0d30cb902..3b4cc9fb02f 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java @@ -264,12 +264,25 @@ public final class ObjectHelper { if (leftValue instanceof String && rightValue instanceof String) { String leftNum = (String) leftValue; String rightNum = (String) rightValue; - if (isNumber(leftNum) && isNumber(rightNum)) { - // favour to use numeric comparison - Long num1 = Long.parseLong(leftNum); - Long num2 = Long.parseLong(rightNum); + // prioritize non-floating numbers first + Long num1 = isNumber(leftNum) ? Long.parseLong(leftNum) : null; + Long num2 = isNumber(rightNum) ? Long.parseLong(rightNum) : null; + Double dec1 = num1 == null && isFloatingNumber(leftNum) ? Double.parseDouble(leftNum) : null; + Double dec2 = num2 == null && isFloatingNumber(rightNum) ? Double.parseDouble(rightNum) : null; + if (num1 != null && num2 != null) { return num1.compareTo(num2); + } else if (dec1 != null && dec2 != null) { + return dec1.compareTo(dec2); } + // okay mixed but we need to convert to floating + if (num1 != null && dec2 != null) { + dec1 = Double.parseDouble(leftNum); + return dec1.compareTo(dec2); + } else if (num2 != null && dec1 != null) { + dec2 = Double.parseDouble(rightNum); + return dec1.compareTo(dec2); + } + // fallback to string comparison return leftNum.compareTo(rightNum); } else if (leftValue instanceof Integer && rightValue instanceof Integer) { Integer leftNum = (Integer) leftValue; @@ -283,6 +296,10 @@ public final class ObjectHelper { Double leftNum = (Double) leftValue; Double rightNum = (Double) rightValue; return leftNum.compareTo(rightNum); + } else if (leftValue instanceof Float && rightValue instanceof Float) { + Float leftNum = (Float) leftValue; + Float rightNum = (Float) rightValue; + return leftNum.compareTo(rightNum); } else if ((rightValue instanceof Integer || rightValue instanceof Long) && leftValue instanceof String && isNumber((String) leftValue)) { if (rightValue instanceof Integer) { @@ -309,6 +326,10 @@ public final class ObjectHelper { Double leftNum = Double.valueOf((String) leftValue); Double rightNum = (Double) rightValue; return leftNum.compareTo(rightNum); + } else if (rightValue instanceof Float rightNum && leftValue instanceof String + && isFloatingNumber((String) leftValue)) { + Float leftNum = Float.valueOf((String) leftValue); + return leftNum.compareTo(rightNum); } else if (rightValue instanceof Boolean && leftValue instanceof String) { Boolean leftBool = Boolean.valueOf((String) leftValue); Boolean rightBool = (Boolean) rightValue;