AbstractPatternFormatter.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.format.impl;
17
18
import com.ancientprogramming.fixedformat4j.format.AbstractFixedFormatter;
19
import com.ancientprogramming.fixedformat4j.format.FormatInstructions;
20
21
import java.util.concurrent.ConcurrentHashMap;
22
23
/**
24
 * Base class for pattern-based date/time formatters.
25
 *
26
 * <p>Overrides {@link #stripPadding} to restore padding characters that belong
27
 * to the date/time value rather than to the field padding. This is necessary when
28
 * {@code paddingChar} equals a character that can also appear within a formatted date
29
 * (e.g. {@code '0'} with a pattern like {@code yyyyMMddHHmmss} where seconds may be
30
 * {@code 00}).
31
 *
32
 * <p>After stripping padding, if the result is shorter than the formatted output length
33
 * for the pattern, it is re-padded to that length so that the underlying date parser
34
 * receives a well-formed string.
35
 *
36
 * <p>The formatted length for each pattern is computed once per (formatter subclass, pattern)
37
 * pair and cached in a {@link ClassValue}-backed map. Values are automatically eligible for GC
38
 * when the formatter's defining classloader is collected, preventing classloader leaks in
39
 * hot-reload and multi-classloader environments.
40
 *
41
 * @param <T> the date/time type handled by this formatter
42
 * @author Jacob von Eyben - <a href="https://eybenconsult.com">https://eybenconsult.com</a>
43
 * @since 1.6.1
44
 */
45
public abstract class AbstractPatternFormatter<T> extends AbstractFixedFormatter<T> {
46
47
  // Keyed by formatter Class; values are collected when the formatter class's classloader is GC'd.
48
  private static final ClassValue<ConcurrentHashMap<String, Integer>> PATTERN_LENGTH_CACHE =
49
      new ClassValue<ConcurrentHashMap<String, Integer>>() {
50
        @Override
51
        protected ConcurrentHashMap<String, Integer> computeValue(Class<?> type) {
52 2 1. computeValue : replaced return value with null for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter$1::computeValue → SURVIVED
2. computeValue : removed call to java/util/concurrent/ConcurrentHashMap::<init> → SURVIVED
          return new ConcurrentHashMap<>();
53
        }
54
      };
55
56
  /**
57
   * Strips padding, then re-applies it when the padding character overlaps with
58
   * characters that are significant in the date/time pattern.
59
   *
60
   * <p>For example, with padding {@code '0'} and pattern {@code yyyyMMddHHmmss},
61
   * a field value of {@code "00"} (seconds) would otherwise be stripped to an empty
62
   * string, making the date un-parseable. This override detects that case and
63
   * restores the stripped characters before returning.
64
   *
65
   * <p>{@inheritDoc}
66
   */
67
  @Override
68
  protected String stripPadding(String value, FormatInstructions instructions) {
69 4 1. stripPadding : removed call to com/ancientprogramming/fixedformat4j/annotation/Align::remove → KILLED
2. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getAlignment → KILLED
3. stripPadding : replaced call to com/ancientprogramming/fixedformat4j/annotation/Align::remove with argument → KILLED
4. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getPaddingChar → KILLED
    String stripped = instructions.getAlignment().remove(value, instructions.getPaddingChar());
70 4 1. stripPadding : removed conditional - replaced equality check with false → KILLED
2. stripPadding : negated conditional → KILLED
3. stripPadding : removed conditional - replaced equality check with true → KILLED
4. stripPadding : removed call to java/lang/String::isEmpty → KILLED
    if (stripped.isEmpty()) {
71 1 1. stripPadding : replaced return value with "" for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::stripPadding → SURVIVED
      return stripped;
72
    }
73 2 1. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getFixedFormatPatternData → KILLED
2. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/data/FixedFormatPatternData::getPattern → KILLED
    String pattern = instructions.getFixedFormatPatternData().getPattern();
74 1 1. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::formattedLengthForPattern → KILLED
    int formattedLength = formattedLengthForPattern(pattern);
75 5 1. stripPadding : removed call to java/lang/String::length → SURVIVED
2. stripPadding : removed conditional - replaced comparison check with true → SURVIVED
3. stripPadding : changed conditional boundary → SURVIVED
4. stripPadding : negated conditional → KILLED
5. stripPadding : removed conditional - replaced comparison check with false → KILLED
    if (stripped.length() < formattedLength) {
76 5 1. stripPadding : replaced return value with "" for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::stripPadding → KILLED
2. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getAlignment → KILLED
3. stripPadding : removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getPaddingChar → KILLED
4. stripPadding : replaced call to com/ancientprogramming/fixedformat4j/annotation/Align::apply with argument → KILLED
5. stripPadding : removed call to com/ancientprogramming/fixedformat4j/annotation/Align::apply → KILLED
      return instructions.getAlignment().apply(stripped, formattedLength, instructions.getPaddingChar());
77
    }
78 1 1. stripPadding : replaced return value with "" for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::stripPadding → KILLED
    return stripped;
79
  }
80
81
  protected final int formattedLengthForPattern(String pattern) {
82 3 1. formattedLengthForPattern : removed call to java/lang/ClassValue::get → KILLED
2. formattedLengthForPattern : replaced int return with 0 for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::formattedLengthForPattern → KILLED
3. formattedLengthForPattern : removed call to java/lang/Object::getClass → KILLED
    return PATTERN_LENGTH_CACHE.get(getClass())
83 3 1. formattedLengthForPattern : removed call to java/lang/Integer::intValue → KILLED
2. formattedLengthForPattern : removed call to java/util/concurrent/ConcurrentHashMap::computeIfAbsent → KILLED
3. formattedLengthForPattern : replaced call to java/util/concurrent/ConcurrentHashMap::computeIfAbsent with argument → KILLED
        .computeIfAbsent(pattern, this::computeFormattedLengthForPattern);
84
  }
85
86
  /**
87
   * Returns the number of characters that this formatter's pattern produces when applied
88
   * to any date/time value. Used to restore padding characters that are part of the
89
   * date/time value rather than field padding.
90
   *
91
   * <p>This method is called at most once per (formatter type, pattern) pair; results are
92
   * cached by {@link #formattedLengthForPattern}.
93
   *
94
   * @param pattern the date/time pattern string
95
   * @return the fixed character length of the formatted output
96
   */
97
  protected abstract int computeFormattedLengthForPattern(String pattern);
98
}

Mutations

52

1.1
Location : computeValue
Killed by : none
replaced return value with null for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter$1::computeValue → SURVIVED
Covering tests

2.2
Location : computeValue
Killed by : none
removed call to java/util/concurrent/ConcurrentHashMap::<init> → SURVIVED Covering tests

69

1.1
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
removed call to com/ancientprogramming/fixedformat4j/annotation/Align::remove → KILLED

2.2
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getAlignment → KILLED

3.3
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
replaced call to com/ancientprogramming/fixedformat4j/annotation/Align::remove with argument → KILLED

4.4
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getPaddingChar → KILLED

70

1.1
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
negated conditional → KILLED

3.3
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed conditional - replaced equality check with true → KILLED

4.4
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:allZeroFieldWithZeroPaddingParsesToNull()]
removed call to java/lang/String::isEmpty → KILLED

71

1.1
Location : stripPadding
Killed by : none
replaced return value with "" for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::stripPadding → SURVIVED
Covering tests

73

1.1
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getFixedFormatPatternData → KILLED

2.2
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
removed call to com/ancientprogramming/fixedformat4j/format/data/FixedFormatPatternData::getPattern → KILLED

74

1.1
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed call to com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::formattedLengthForPattern → KILLED

75

1.1
Location : stripPadding
Killed by : none
removed call to java/lang/String::length → SURVIVED
Covering tests

2.2
Location : stripPadding
Killed by : none
removed conditional - replaced comparison check with true → SURVIVED Covering tests

3.3
Location : stripPadding
Killed by : none
changed conditional boundary → SURVIVED Covering tests

4.4
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
negated conditional → KILLED

5.5
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed conditional - replaced comparison check with false → KILLED

76

1.1
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
replaced return value with "" for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::stripPadding → KILLED

2.2
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getAlignment → KILLED

3.3
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed call to com/ancientprogramming/fixedformat4j/format/FormatInstructions::getPaddingChar → KILLED

4.4
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
replaced call to com/ancientprogramming/fixedformat4j/annotation/Align::apply with argument → KILLED

5.5
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed call to com/ancientprogramming/fixedformat4j/annotation/Align::apply → KILLED

78

1.1
Location : stripPadding
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
replaced return value with "" for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::stripPadding → KILLED

82

1.1
Location : formattedLengthForPattern
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
removed call to java/lang/ClassValue::get → KILLED

2.2
Location : formattedLengthForPattern
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
replaced int return with 0 for com/ancientprogramming/fixedformat4j/format/impl/AbstractPatternFormatter::formattedLengthForPattern → KILLED

3.3
Location : formattedLengthForPattern
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
removed call to java/lang/Object::getClass → KILLED

83

1.1
Location : formattedLengthForPattern
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateTimeFormatter]/[method:leftAlignedZeroPaddingDoesNotStripZeroValuedTimeComponents()]
removed call to java/lang/Integer::intValue → KILLED

2.2
Location : formattedLengthForPattern
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
removed call to java/util/concurrent/ConcurrentHashMap::computeIfAbsent → KILLED

3.3
Location : formattedLengthForPattern
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestLocalDateFormatter]/[method:rightAlignedZeroPaddingRestoresLeadingZerosInDateComponents()]
replaced call to java/util/concurrent/ConcurrentHashMap::computeIfAbsent with argument → KILLED

Active mutators

Tests examined


Report generated by PIT 1.23.1 support