MultiMatchStrategy.java

1
/*
2
 * Copyright 2004 the original author or authors.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.ancientprogramming.fixedformat4j.io.read;
17
18
import com.ancientprogramming.fixedformat4j.exception.FixedFormatException;
19
20
import java.util.List;
21
import java.util.stream.Collectors;
22
23
import static java.lang.String.format;
24
25
/**
26
 * Strategy that decides which mappings to use when more than one
27
 * {@link RecordMapping} matches the same line.
28
 *
29
 * <p>The {@code matched} list passed to {@link #resolve} is ordered "most detailed first,
30
 * then registration order" — depth equals the number of positions declared by a
31
 * {@link LinePattern}, and {@link LinePattern#matchAll()} therefore appears last among
32
 * matches.</p>
33
 *
34
 * <p>Three built-in strategies are provided as static factory methods:
35
 * {@link #firstMatch()}, {@link #throwOnAmbiguity()}, and {@link #allMatches()}.</p>
36
 *
37
 * <p>Implement this interface to define custom multi-match logic — for example, choosing
38
 * the mapping whose {@code @Record} length most closely matches the actual line length.</p>
39
 *
40
 * @author Jacob von Eyben - <a href="https://eybenconsult.com">https://eybenconsult.com</a>
41
 * @since 1.8.0
42
 */
43
@FunctionalInterface
44
public interface MultiMatchStrategy {
45
46
  /**
47
   * Resolves which mappings should be used to parse a line that matched more than one pattern.
48
   * Not invoked when only one mapping matches — single matches bypass this strategy entirely.
49
   *
50
   * @param matched    all mappings whose pattern matched the line; size is always &gt; 1
51
   * @param lineNumber the 1-based line number
52
   * @return the subset of {@code matched} to use for parsing; may be empty to skip the line;
53
   *         never {@code null}
54
   */
55
  List<RecordMapping<?>> resolve(List<RecordMapping<?>> matched, long lineNumber);
56
57
  /**
58
   * Returns a strategy that uses the most detailed matching mapping and ignores the rest.
59
   * Ties (mappings with equal depth) are broken by registration order. This is the default
60
   * strategy used by {@link FixedFormatReader}.
61
   *
62
   * @return a first-match strategy; never {@code null}
63
   */
64
  static MultiMatchStrategy firstMatch() {
65 6 1. firstMatch : replaced return value with null for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::firstMatch → KILLED
2. lambda$firstMatch$0 : Substituted 0 with 1 → KILLED
3. lambda$firstMatch$0 : replaced call to java/util/List::subList with receiver → KILLED
4. lambda$firstMatch$0 : replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$firstMatch$0 → KILLED
5. lambda$firstMatch$0 : Substituted 1 with 0 → KILLED
6. lambda$firstMatch$0 : removed call to java/util/List::subList → KILLED
    return (matched, lineNumber) -> matched.subList(0, 1);
66
  }
67
68
  /**
69
   * Returns a strategy that throws {@link FixedFormatException} if more than one pattern
70
   * matches, including the line number and all matching class names in the exception message.
71
   *
72
   * @return a throw-on-ambiguity strategy; never {@code null}
73
   */
74
  static MultiMatchStrategy throwOnAmbiguity() {
75 1 1. throwOnAmbiguity : replaced return value with null for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::throwOnAmbiguity → KILLED
    return (matched, lineNumber) -> {
76 6 1. lambda$throwOnAmbiguity$2 : changed conditional boundary → SURVIVED
2. lambda$throwOnAmbiguity$2 : removed conditional - replaced comparison check with true → SURVIVED
3. lambda$throwOnAmbiguity$2 : Substituted 1 with 0 → SURVIVED
4. lambda$throwOnAmbiguity$2 : removed conditional - replaced comparison check with false → KILLED
5. lambda$throwOnAmbiguity$2 : negated conditional → KILLED
6. lambda$throwOnAmbiguity$2 : removed call to java/util/List::size → KILLED
      if (matched.size() > 1) {
77 1 1. lambda$throwOnAmbiguity$2 : removed call to java/util/List::stream → KILLED
        String classes = matched.stream()
78 5 1. lambda$throwOnAmbiguity$2 : removed call to java/util/stream/Stream::map → KILLED
2. lambda$throwOnAmbiguity$1 : removed call to com/ancientprogramming/fixedformat4j/io/read/RecordMapping::getRecordClass → KILLED
3. lambda$throwOnAmbiguity$2 : replaced call to java/util/stream/Stream::map with receiver → KILLED
4. lambda$throwOnAmbiguity$1 : replaced return value with "" for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$throwOnAmbiguity$1 → KILLED
5. lambda$throwOnAmbiguity$1 : removed call to java/lang/Class::getSimpleName → KILLED
            .map(m -> m.getRecordClass().getSimpleName())
79 2 1. lambda$throwOnAmbiguity$2 : removed call to java/util/stream/Collectors::joining → KILLED
2. lambda$throwOnAmbiguity$2 : removed call to java/util/stream/Stream::collect → KILLED
            .collect(Collectors.joining(", "));
80 2 1. lambda$throwOnAmbiguity$2 : Substituted 2 with 3 → SURVIVED
2. lambda$throwOnAmbiguity$2 : Substituted 0 with 1 → KILLED
        throw new FixedFormatException(
81 5 1. lambda$throwOnAmbiguity$2 : replaced call to java/lang/String::format with argument → KILLED
2. lambda$throwOnAmbiguity$2 : Substituted 1 with 0 → KILLED
3. lambda$throwOnAmbiguity$2 : removed call to com/ancientprogramming/fixedformat4j/exception/FixedFormatException::<init> → KILLED
4. lambda$throwOnAmbiguity$2 : removed call to java/lang/Long::valueOf → KILLED
5. lambda$throwOnAmbiguity$2 : removed call to java/lang/String::format → KILLED
            format("Line %d matched multiple patterns: %s", lineNumber, classes));
82
      }
83 1 1. lambda$throwOnAmbiguity$2 : replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$throwOnAmbiguity$2 → NO_COVERAGE
      return matched;
84
    };
85
  }
86
87
  /**
88
   * Returns a strategy that loads the line once per matching mapping and emits one record
89
   * object per match, ordered most detailed first with registration order as the tiebreaker.
90
   *
91
   * @return an all-matches strategy; never {@code null}
92
   */
93
  static MultiMatchStrategy allMatches() {
94 2 1. lambda$allMatches$3 : replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$allMatches$3 → KILLED
2. allMatches : replaced return value with null for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::allMatches → KILLED
    return (matched, lineNumber) -> matched;
95
  }
96
}

Mutations

65

1.1
Location : firstMatch
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderBuilder.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderBuilder]/[method:builderIsFluentReturningItself()]
replaced return value with null for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::firstMatch → KILLED

2.2
Location : lambda$firstMatch$0
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchFirstMatchUsesFirstRegisteredMappingOnly()]
Substituted 0 with 1 → KILLED

3.3
Location : lambda$firstMatch$0
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchFirstMatchUsesFirstRegisteredMappingOnly()]
replaced call to java/util/List::subList with receiver → KILLED

4.4
Location : lambda$firstMatch$0
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchFirstMatchUsesFirstRegisteredMappingOnly()]
replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$firstMatch$0 → KILLED

5.5
Location : lambda$firstMatch$0
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchFirstMatchUsesFirstRegisteredMappingOnly()]
Substituted 1 with 0 → KILLED

6.6
Location : lambda$firstMatch$0
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchFirstMatchUsesFirstRegisteredMappingOnly()]
removed call to java/util/List::subList → KILLED

75

1.1
Location : throwOnAmbiguity
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
replaced return value with null for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::throwOnAmbiguity → KILLED

76

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed conditional - replaced comparison check with false → KILLED

2.2
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
negated conditional → KILLED

3.3
Location : lambda$throwOnAmbiguity$2
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

4.4
Location : lambda$throwOnAmbiguity$2
Killed by : none
removed conditional - replaced comparison check with true → SURVIVED Covering tests

5.5
Location : lambda$throwOnAmbiguity$2
Killed by : none
Substituted 1 with 0 → SURVIVED Covering tests

6.6
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed call to java/util/List::size → KILLED

77

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed call to java/util/List::stream → KILLED

78

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed call to java/util/stream/Stream::map → KILLED

2.2
Location : lambda$throwOnAmbiguity$1
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed call to com/ancientprogramming/fixedformat4j/io/read/RecordMapping::getRecordClass → KILLED

3.3
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
replaced call to java/util/stream/Stream::map with receiver → KILLED

4.4
Location : lambda$throwOnAmbiguity$1
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
replaced return value with "" for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$throwOnAmbiguity$1 → KILLED

5.5
Location : lambda$throwOnAmbiguity$1
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
removed call to java/lang/Class::getSimpleName → KILLED

79

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed call to java/util/stream/Collectors::joining → KILLED

2.2
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
removed call to java/util/stream/Stream::collect → KILLED

80

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : none
Substituted 2 with 3 → SURVIVED
Covering tests

2.2
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
Substituted 0 with 1 → KILLED

81

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
replaced call to java/lang/String::format with argument → KILLED

2.2
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
Substituted 1 with 0 → KILLED

3.3
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchThrowOnAmbiguityThrowsWhenTwoPatternsMatch()]
removed call to com/ancientprogramming/fixedformat4j/exception/FixedFormatException::<init> → KILLED

4.4
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
removed call to java/lang/Long::valueOf → KILLED

5.5
Location : lambda$throwOnAmbiguity$2
Killed by : com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestFixedFormatReaderMultiMatch]/[method:throwsOnAmbiguityWhenTwoPatternsMatch()]
removed call to java/lang/String::format → KILLED

83

1.1
Location : lambda$throwOnAmbiguity$2
Killed by : none
replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$throwOnAmbiguity$2 → NO_COVERAGE

94

1.1
Location : lambda$allMatches$3
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchAllMatchesEmitsOneRecordPerMatchingMapping()]
replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::lambda$allMatches$3 → KILLED

2.2
Location : allMatches
Killed by : com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.io.TestStrategiesAndHandlers]/[method:multiMatchAllMatchesEmitsOneRecordPerMatchingMapping()]
replaced return value with null for com/ancientprogramming/fixedformat4j/io/read/MultiMatchStrategy::allMatches → KILLED

Active mutators

Tests examined


Report generated by PIT 1.23.1 support