• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stasha / flex / 17

29 Apr 2025 11:38PM UTC coverage: 67.722% (-6.4%) from 74.123%
17

push

circleci

stasha
Added bunch of changes and various experiments

206 of 343 branches covered (60.06%)

Branch coverage included in aggregate %.

143 of 219 new or added lines in 12 files covered. (65.3%)

4 existing lines in 2 files now uncovered.

436 of 605 relevant lines covered (72.07%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.1
/src/main/java/com/stasha/info/flex/formatters/FlexFormat.java
1
package com.stasha.info.flex.formatters;
2

3
import com.stasha.info.flex.store.LRUCache;
4
import java.text.DateFormat;
5
import java.text.DecimalFormat;
6
import java.text.NumberFormat;
7
import java.text.SimpleDateFormat;
8
import java.util.Date;
9
import java.util.HashMap;
10
import java.util.Locale;
11
import java.util.Map;
12
import java.util.regex.Pattern;
13

14
/**
15
 *
16
 * @author stasha
17
 */
18
public class FlexFormat {
19

20
    private final Pattern SPLIT_GENDER_PATTERN = Pattern.compile("\\s*\\|\\s*");
1✔
21
    private static final String DIGIT_STRINGS = "0123456789"; // For char access
22

23
    private static final LRUCache<String, String> DATE_TIME_CACHE = new LRUCache(100);
1✔
24
    private Map<Integer, char[]> currencyTemplates = new HashMap<>();
1✔
25
    private Map<Double, String> currencyPreCache = new HashMap<>();
1✔
26
    private ThreadLocal<char[]> resultBuffer = ThreadLocal.withInitial(() -> new char[32]); // Max template size (~20 digits + symbols)
1✔
27

28
    private static final String DEFAULT_LOCALE = "en-US";
29
    private Locale loc;
30
    private String locale;
31
    private DateFormat sdf;
32
    private DateFormat stf;
33
    private NumberFormat nf;
34
    private NumberFormat cf;
35
    private String dateFormat;
36
    private String timeFormat;
37

38
    public FlexFormat() {
1✔
39
        setLocale(DEFAULT_LOCALE);
1✔
40
    }
1✔
41

42
    protected void setLocale(String locale) {
43
        if (locale == null || locale.isBlank() || locale.equals("fmt.locale")) {
1!
UNCOV
44
            locale = DEFAULT_LOCALE;
×
45
        }
46
        this.locale = locale;
1✔
47

48
        if (loc == null || !loc.toLanguageTag().equals(locale)) {
1!
49
            loc = Locale.forLanguageTag(locale);
1✔
50
            sdf = SimpleDateFormat.getDateInstance(DateFormat.DEFAULT, loc);
1✔
51
            stf = SimpleDateFormat.getTimeInstance(DateFormat.DEFAULT, loc);
1✔
52
            nf = NumberFormat.getInstance(loc);
1✔
53
            cf = NumberFormat.getCurrencyInstance(loc);
1✔
54
            dateFormat = ((SimpleDateFormat) sdf).toPattern();
1✔
55
            timeFormat = ((SimpleDateFormat) stf).toPattern();
1✔
56

57
            // Generate templates for 1–20 integer digits
58
            // Generate templates for 1–20 integer digits
59
            for (int digits = 1; digits <= 20; digits++) {
1✔
60
                String sample = "1" + "2".repeat(Math.max(0, digits - 1)) + ".78";
1✔
61
                double sampleNumber = Double.parseDouble(sample);
1✔
62
                String formatted = cf.format(sampleNumber);
1✔
63
                char[] elements = new char[formatted.length()];
1✔
64
                for (int i = 0; i < formatted.length(); i++) {
1✔
65
                    elements[i] = Character.isDigit(formatted.charAt(i)) ? '#' : formatted.charAt(i);
1✔
66
                }
67
                currencyTemplates.put(digits, elements);
1✔
68
            }
69

70
            // Precache 0–299,999
71
            for (int i = 0; i < 300000; i++) {
1✔
72
                double value = i + (i % 100) / 100.0; // Match test input pattern
1✔
73
                currencyPreCache.put(value, cf.format(value));
1✔
74
            }
75
        }
76
    }
1✔
77

78
    /**
79
     * Formats a value according to the specified type and locale.
80
     *
81
     * @param value The value to format (e.g., Date, Number, String).
82
     * @param formatType The format type (e.g., "fmt.date", "fmt.currency").
83
     * @param userFormat Optional custom pattern (e.g., "dd.MM.yy").
84
     * @param locale The locale tag (e.g., "en-US") or null for default.
85
     * @return The formatted string or an error message if formatting fails.
86
     */
87
    public String format(Object value, String formatType, String userFormat, String locale) {
88

89
//        System.out.println(cacheKey);
90
//        setLocale(locale);
91
//        if(1 == 1)  return String.valueOf(value);
92
        try {
93
            switch (formatType) {
1✔
94
                case "fmt.date", "fmt.time" -> {
95
                    if (value instanceof Date date) {
1!
96

97
                        String cacheKey;
98

99
                        if (formatType.equals("fmt.date")) {
1!
100
                            long normalizedTime = date.getTime() / (24 * 60 * 60 * 1000) * (24 * 60 * 60 * 1000);
1✔
101
                            cacheKey = new StringBuilder(this.locale).append(normalizedTime).append(userFormat).toString();
1✔
102
                        } else {
1✔
NEW
103
                            long normalizedDate = ((date.getTime() % (24 * 60 * 60 * 1000)) + (24 * 60 * 60 * 1000)) % (24 * 60 * 60 * 1000);
×
NEW
104
                            cacheKey = new StringBuilder(this.locale).append(normalizedDate).append(userFormat).toString();
×
105
                        }
106

107
                        return DATE_TIME_CACHE.computeIfAbsent(cacheKey, k -> {
1✔
108
                            DateFormat udf = formatType.equals("fmt.date") ? sdf : stf;
1!
109
                            String pattern = formatType.equals("fmt.date") ? dateFormat : timeFormat;
1!
110
                            if (userFormat != null && !pattern.equals(userFormat)) {
1!
111
                                udf = new SimpleDateFormat(userFormat, loc);
1✔
112
                                if (formatType.equals("fmt.date")) {
1!
113
                                    sdf = udf;
1✔
114
                                    dateFormat = userFormat;
1✔
115
                                } else {
NEW
116
                                    stf = udf;
×
NEW
117
                                    timeFormat = userFormat;
×
118
                                }
119
                            }
120
                            return udf.format(value);
1✔
121
                        });
122

123
                    }
124
                    return "argument: " + value + " can't be formatted as date";
×
125
                }
126
                case "fmt.decimal" -> {
127
                    if (userFormat != null && !userFormat.equals(((DecimalFormat) nf).toPattern())) {
1!
128
                        ((DecimalFormat) nf).applyPattern(userFormat);
×
129
                    }
130
                    return nf.format(value);
1✔
131
                }
132
                case "fmt.currency" -> {
133
                    Double v = null;
1✔
134
                    switch (value) {
1!
NEW
135
                        case String str ->
×
NEW
136
                            v = Double.valueOf(str);
×
137
                        case Integer num ->
1✔
138
                            v = (double) num;
1✔
NEW
139
                        case Double dub ->
×
NEW
140
                            v = dub;
×
141
                        default -> {
142
                        }
143
                    }
144

145
                    if (v != null) {
1!
146
                        // Check precache
147
                        String cached = currencyPreCache.get(v);
1✔
148
                        if (cached != null) {
1✔
149
                            return cached;
1✔
150
                        }
151

152
                        // Handle negative numbers
153
                        boolean isNegative = v < 0;
1!
154
                        double absNumber = Math.abs(v);
1✔
155

156
                        // Get integer part’s digit length without String.valueOf
157
                        long integerPart = (long) absNumber;
1✔
158
                        int digitLength = integerPart == 0 ? 1 : (int) (Math.log10(integerPart) + 1);
1!
159
                        if (digitLength > 20) {
1!
NEW
160
                            return cf.format(v);
×
161
                        }
162

163
                        // Get template
164
                        char[] formatTemplate = currencyTemplates.get(digitLength);
1✔
165
                        if (formatTemplate == null) {
1!
NEW
166
                            return cf.format(v);
×
167
                        }
168

169
                        // Get result buffer
170
                        char[] result = resultBuffer.get();
1✔
171
                        if (result.length < formatTemplate.length) {
1!
NEW
172
                            result = new char[formatTemplate.length];
×
NEW
173
                            resultBuffer.set(result);
×
174
                        }
175

176
                        // Extract and map digits in one pass
177
                        double fractionalPart = absNumber - integerPart;
1✔
178
                        int fracValue = (int) Math.round(fractionalPart * 100); // e.g., 0.234 → 23
1✔
179
                        int fracDigit1 = fracValue / 10;
1✔
180
                        int fracDigit2 = fracValue % 10;
1✔
181
                        long temp = integerPart;
1✔
182
                        int[] intDigits = new int[digitLength];
1✔
183
                        for (int i = digitLength - 1; i >= 0; i--) {
1✔
184
                            intDigits[i] = (int) (temp % 10);
1✔
185
                            temp /= 10;
1✔
186
                        }
187
                        int digitIndex = 0;
1✔
188
                        int resultIndex = isNegative ? 1 : 0;
1!
189
                        if (isNegative) {
1!
NEW
190
                            result[0] = '-';
×
191
                        }
192
                        for (int i = 0; i < formatTemplate.length; i++) {
1✔
193
                            if (formatTemplate[i] == '#') {
1✔
194
                                if (digitIndex < intDigits.length) {
1✔
195
                                    result[resultIndex++] = DIGIT_STRINGS.charAt(intDigits[digitIndex++]);
1✔
196
                                } else if (digitIndex == intDigits.length) {
1✔
197
                                    result[resultIndex++] = DIGIT_STRINGS.charAt(fracDigit1);
1✔
198
                                    digitIndex++;
1✔
199
                                } else if (digitIndex == intDigits.length + 1) {
1!
200
                                    result[resultIndex++] = DIGIT_STRINGS.charAt(fracDigit2);
1✔
201
                                    digitIndex++;
1✔
202
                                } else {
NEW
203
                                    result[resultIndex++] = '0';
×
204
                                }
205
                            } else {
206
                                result[resultIndex++] = formatTemplate[i];
1✔
207
                            }
208
                        }
209

210
                        // Convert to String
211
                        return new String(result, 0, resultIndex);
1✔
212
                    }
UNCOV
213
                }
×
214
                case "fmt.uppercase" -> {
215
                    return String.valueOf(value).toUpperCase(loc);
1✔
216
                }
217
                case "m", "f", "n" -> {
218
                    String val = String.valueOf(value);
1✔
219
                    if (val.contains("[")) {
1✔
220
                        String start = val.substring(0, val.lastIndexOf("["));
1✔
221
                        val = val.substring(val.lastIndexOf("[") + 1, val.length() - 1);
1✔
222
                        String[] vls = SPLIT_GENDER_PATTERN.split(val);
1✔
223
                        switch (formatType) {
1✔
224
                            case "m" -> {
225
                                return start + vls[0];
1✔
226
                            }
227
                            case "f" -> {
228
                                return start + vls[1];
1✔
229
                            }
230
                            default -> {
231
                                return start + vls[2];
1✔
232
                            }
233
                        }
234
                    }
235
                    return String.valueOf(value);
1✔
236
                }
237
                default -> {
238
                    return userFormat != null ? userFormat : String.valueOf(value);
1!
239
                }
240
            }
241
        } catch (Exception ex) {
×
242
            return "argument: " + value + " can't be formatted as " + formatType;
×
UNCOV
243
        }
×
244

NEW
245
        return String.valueOf(value);
×
246
    }
247
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc