diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparator.java index c49d56942..53015ae8c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * Copyright 2013-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,10 @@ import org.springframework.util.StringUtils; import java.util.Comparator; /** - * Sorts by ascending specificity of pattern, based on counting wildcards (with * taking - * precedence over ?). Hence * > foo* > ??? > fo? > foo. + * Sorts by descending specificity of pattern, based on counting wildcards (with ? being + * considered more specific than *). This means that more specific patterns will be + * considered greater than less specific patterns. Hence foo > fo? > ??? > foo* + * > * * * For more complex comparisons, any string containing at least one * token will be * considered more generic than any string that has no * token. If both strings have at @@ -39,8 +41,8 @@ import java.util.Comparator; * * If the strings contain neither * nor ? tokens then alphabetic comparison will be used. * - * Hence * > foo* > *f* > *foo* > ??? > ?o? > foo?? > bar?? > fo? - * > foo > bar + * Hence bar > foo > fo? > bar?? > foo?? > ?0? > ??? > *foo* > *f* + * > foo* > * * * @see Comparator * @author Michael Minella @@ -61,31 +63,31 @@ public class DefaultStateTransitionComparator implements Comparator 0 && arg1AsteriskCount == 0) { - return 1; + return -1; } if (arg0AsteriskCount == 0 && arg1AsteriskCount > 0) { - return -1; + return 1; } if (arg0AsteriskCount > 0 && arg1AsteriskCount > 0) { if (arg0AsteriskCount < arg1AsteriskCount) { - return 1; + return -1; } if (arg0AsteriskCount > arg1AsteriskCount) { - return -1; + return 1; } } int arg0WildcardCount = StringUtils.countOccurrencesOf(arg0Pattern, "?"); int arg1WildcardCount = StringUtils.countOccurrencesOf(arg1Pattern, "?"); if (arg0WildcardCount > arg1WildcardCount) { - return 1; - } - if (arg0WildcardCount < arg1WildcardCount) { return -1; } - if (arg0Pattern.length() != arg1Pattern.length() && (arg0AsteriskCount > 0 || arg0WildcardCount > 0)) { - return Integer.compare(arg1Pattern.length(), arg0Pattern.length()); + if (arg0WildcardCount < arg1WildcardCount) { + return 1; } - return arg0.getPattern().compareTo(arg1Pattern); + if (arg0Pattern.length() != arg1Pattern.length() && (arg0AsteriskCount > 0 || arg0WildcardCount > 0)) { + return Integer.compare(arg0Pattern.length(), arg1Pattern.length()); + } + return arg1.getPattern().compareTo(arg0Pattern); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java index f66ce004c..1818d017f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2023 the original author or authors. + * Copyright 2006-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -313,7 +313,7 @@ public class SimpleFlow implements Flow, InitializingBean { set = new LinkedHashSet<>(); } else { - set = new TreeSet<>(stateTransitionComparator); + set = new TreeSet<>(stateTransitionComparator).descendingSet(); } transitionMap.put(name, set); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparatorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparatorTests.java index d71e4813b..45e323f6c 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparatorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/flow/support/DefaultStateTransitionComparatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,96 +39,96 @@ class DefaultStateTransitionComparatorTests { void testSimpleOrderingMoreGeneral() { StateTransition generic = StateTransition.createStateTransition(state, "CONTIN???LE", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CONTINUABLE", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testSimpleOrderingMostGeneral() { StateTransition generic = StateTransition.createStateTransition(state, "*", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CONTINUABLE", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testSubstringAndWildcard() { StateTransition generic = StateTransition.createStateTransition(state, "CONTIN*", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CONTINUABLE", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testSimpleOrderingMostToNextGeneral() { StateTransition generic = StateTransition.createStateTransition(state, "*", "start"); StateTransition specific = StateTransition.createStateTransition(state, "C?", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testSimpleOrderingAdjacent() { StateTransition generic = StateTransition.createStateTransition(state, "CON*", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CON?", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testOrderByNumberOfGenericWildcards() { StateTransition generic = StateTransition.createStateTransition(state, "*", "start"); StateTransition specific = StateTransition.createStateTransition(state, "**", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testOrderByNumberOfSpecificWildcards() { StateTransition generic = StateTransition.createStateTransition(state, "CONTI??ABLE", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CONTI?UABLE", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testOrderByLengthWithAsteriskEquality() { StateTransition generic = StateTransition.createStateTransition(state, "CON*", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CONTINUABLE*", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testOrderByLengthWithWildcardEquality() { StateTransition generic = StateTransition.createStateTransition(state, "CON??", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CONTINUABLE??", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testOrderByAlphaWithAsteriskEquality() { StateTransition generic = StateTransition.createStateTransition(state, "DOG**", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CAT**", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testOrderByAlphaWithWildcardEquality() { StateTransition generic = StateTransition.createStateTransition(state, "DOG??", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CAT??", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } @Test void testPriorityOrderingWithAlphabeticComparison() { StateTransition generic = StateTransition.createStateTransition(state, "DOG", "start"); StateTransition specific = StateTransition.createStateTransition(state, "CAT", "start"); - assertEquals(1, comparator.compare(generic, specific)); - assertEquals(-1, comparator.compare(specific, generic)); + assertEquals(1, comparator.compare(specific, generic)); + assertEquals(-1, comparator.compare(generic, specific)); } }