• 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

75.0
/src/main/java/com/stasha/info/flex/parsers/FlexStringTemplateOptions.java
1
package com.stasha.info.flex.parsers;
2

3
import java.util.Arrays;
4
import java.util.LinkedHashMap;
5
import java.util.Map;
6
import java.util.regex.Matcher;
7
import java.util.regex.Pattern;
8

9
/**
10
 *
11
 * @author stasha
12
 */
13
public class FlexStringTemplateOptions {
14

15
    /**
16
     * Pattern for parsing template options.<br />
17
     * Plurals are enclosed in square brackets [options delimited by
18
     * comma].<br />
19
     * <p>
20
     * Option pattern example:
21
     * <ul>
22
     * <li>[0=no items, 1=1 item, 2=two items, 3-7=few items, other=many
23
     * items]</li>
24
     * </ul>
25
     * </p>
26
     */
27
    private static final Pattern TEMPLATE_OPTIONS_PATTERN = Pattern.compile("\\s*((?<!\\\\),)\\s*");
1✔
28

29
    private static final Pattern PARTIAL_NUMBER_PATTERN = Pattern.compile("^\\s*(~)?(_)?([^-\\s_\\~]+)(_)?(~)?(\\s*-\\s*)?(~)?(_)?([^-\\s_\\~]+)?(_)?(~)?\\s*$");
1✔
30

31
    private static final Pattern KEY_VALUE_SPLIT_PATTERN = Pattern.compile("\\s*((?<!\\\\)=)\\s*");
1✔
32

33
    private String[] options;
34
    private Map<String, String> data;
35

36
    public Map<String, String> getData() {
NEW
37
        return data;
×
38
    }
39

40
    public FlexStringTemplateOptions() {
1✔
41
    }
1✔
42

43
    public FlexStringTemplateOptions(String options) {
1✔
44
        this.data = parse(options);
1✔
45
    }
1✔
46

47
    protected Map<String, String> parse(String str) {
48
        if (str != null) {
1✔
49
            // split template options at ","
50
            // 0=no items, 1=1 item, 2=two items, 3-7=few items, other=many items
51
            String[] p = TEMPLATE_OPTIONS_PATTERN.split(str);
1✔
52

53
            this.options = p;
1✔
54

55
            for (int i = 0; i < p.length; ++i) {
1✔
56
                String[] kv = KEY_VALUE_SPLIT_PATTERN.split(p[i], 2);
1✔
57
                if (data == null) {
1✔
58
                    data = new LinkedHashMap<>(10);
1✔
59
                }
60
                try {
61
                    data.put(kv[0], kv[1]);
1✔
62
                } catch (Exception ex) {
1✔
63
                    throw new ParseOptionsException("Failed to parse option", str);
1✔
64
                }
1✔
65
            }
66
        }
67
        return data;
1✔
68
    }
69

70
    public String getValue(Object value) {
71
        if (value instanceof String val) {
1✔
72
            return this.data.computeIfAbsent(val, k -> {
1✔
73
                return this.getValue(value, this.data);
1✔
74
            });
75
        }
76
        return this.getValue(value, this.data);
1✔
77
    }
78

79
    public String getValue(Object value, String optionsString) {
80
        return getValue(value, parse(optionsString));
×
81
    }
82

83
    public String getValue(Object value, Map<String, String> options) {
84

85
        String val = String.valueOf(value);
1✔
86
        if (options != null && !options.isEmpty()) {
1!
87
            String key = match(value, options.keySet().toArray(new String[]{}));
1✔
88
            val = options.get(key);
1✔
89
        }
90

91
        // don't unescape it here as this is called recursively
92
        // with options that can have escape that should be taken
93
        // into consideration in different interpolation places.
94
        return val;
1✔
95
    }
96

97
    public static String match(Object value, String[] options) {
98

99
        String val = String.valueOf(value);
1✔
100
        if (val.startsWith(" ") || val.endsWith(" ")) {
1!
101
            val = val.trim();
×
102
        }
103

104
        if (options.length > 0) {
1!
105
            for (int i = 0; i < options.length; ++i) {
1✔
106
                String k = options[i];
1✔
107

108
                boolean isRange = k.contains("-");
1✔
109
                boolean isPartial = k.contains("_");
1✔
110
                boolean isMath = k.contains("~");
1✔
111

112
                // single value
113
                if (!isRange) {
1✔
114
                    if (!isPartial && !isMath) {
1✔
115
                        if (val.equals(k)) {
1✔
116
                            return k;
1✔
117
                        } else if (k.equals("other")) {
1✔
118
                            return k;
1✔
119
                        } else if (isPartial) {
1!
NEW
120
                            String v = k;
×
121
                            // starts/ends with number
NEW
122
                            if (k.startsWith("_")) {
×
NEW
123
                                v = k.substring(1, k.length());
×
NEW
124
                            } else if (k.endsWith("_")) {
×
NEW
125
                                v = k.substring(0, k.length() - 1);
×
126
                            }
NEW
127
                            if (val.equals(v)) {
×
NEW
128
                                return v;
×
129
                            }
130
                        } else if (k.contains("~")) {
1!
131
                            // flor/max
132
                        }
133
                    }
134
                } else {
135
                    int _indexof = k.indexOf("-");
1✔
136
                    String key = k.substring(0, _indexof);
1✔
137
                    String vl = k.substring(_indexof + 1, k.length());
1✔
138
                    String[] kv = new String[]{key, vl};
1✔
139
                    String result = match(value, kv);
1✔
140
                    if (result != null) {
1✔
141
                        return k;
1✔
142
                    }
143
                }
144

145
                boolean fromStartsWith = false;
1✔
146
                boolean fromEndsWith = false;
1✔
147
                boolean toStartsWith = false;
1✔
148
                boolean toEndsWith = false;
1✔
149
                boolean fromMathFloor = false;
1✔
150
                boolean fromMathMax = false;
1✔
151
                boolean toMathFlor = false;
1✔
152
                boolean toMathMax = false;
1✔
153

154
                String from = k;
1✔
155
                String to = null;
1✔
156

157
                if (k.contains("_") || k.contains("-") || k.contains("~")) {
1✔
158
                    Matcher m = PARTIAL_NUMBER_PATTERN.matcher(k);
1✔
159
                    if (m.find()) {
1!
160

161
                        fromMathFloor = "~".equals(m.group(5));
1✔
162
                        fromStartsWith = "_".equals(m.group(4));
1✔
163
                        from = m.group(3);
1✔
164
                        fromEndsWith = "_".equals(m.group(2));
1✔
165
                        fromMathMax = "~".equals(m.group(1));
1✔
166

167
                        toMathFlor = "~".equals(m.group(7));
1✔
168
                        toStartsWith = ("_").equals(m.group(10));
1✔
169
                        to = m.group(9);
1✔
170
                        toEndsWith = ("_").equals(m.group(8));
1✔
171
                        toMathMax = "~".equals(m.group(11));
1✔
172

173
                    }
174
                }
175

176
                if (from != null) {
1!
177
                    // not a range
178
                    if (to == null) {
1✔
179
                        if (from.equals(val)) {
1✔
180
                            return k;
1✔
181
                        } else if (fromEndsWith && val.endsWith(from)) {
1✔
182
                            return k;
1✔
183
                        } else if (fromStartsWith && val.startsWith(from)) {
1!
184
                            return k;
×
185
                        } else if (fromMathFloor) {
1✔
186
                            if (val.equals(from)) {
1!
187
                                return k;
×
188
                            } else {
189
                                Double fr = Double.valueOf(from);
1✔
190
                                Double flor = Double.valueOf(val);
1✔
191
                                Double v = flor == 0 ? 0 : (Math.pow(10, Math.floor(Math.log10(flor))) * Math.floor(flor / Math.pow(10, Math.floor(Math.log10(flor)))));
1!
192
                                if (fr.equals(v)) {
1✔
193
                                    return k;
1✔
194
                                }
195
                            }
1✔
196
                        }
197
                    } else {
198
                        // can't combine _a-b_ and a_-_b
199
                        // only _a-_b or a_-b_
200
                        if (fromStartsWith && toEndsWith) {
1!
201
                            throw new IllegalStateException("Starts with and ends with patterns can't becombined: _a-b_ or a_-_b");
×
202
                        }
203

204
                        // _11-_15 - ends with 11, 12, 13, 14, 15
205
                        if (fromEndsWith && toEndsWith && val.length() > from.length()) {
1!
206
                            val = val.substring(val.length() - from.length(), from.length());
×
207
                        }
208

209
                        try {
210
                            Double f = Double.valueOf(from);
1✔
211
                            Double t = Double.valueOf(to);
1✔
212
                            Double dval = !(value instanceof Double) ? Double.valueOf(val) : (Double) value;
1!
213

214
                            // check if value is in range
215
                            if (dval >= f && dval <= t) {
1✔
216
                                return k;
1✔
217
                            }
218
                        } catch (NumberFormatException ex) {
×
219
                            // string can't have range
220
                            if (from.equals(value)) {
×
221
                                return k;
×
222
                            } else if (fromEndsWith && String.valueOf(value).endsWith(from)) {
×
223
                                return k;
×
224
                            } else if (fromStartsWith && String.valueOf(value).startsWith(from)) {
×
225
                                return k;
×
226
                            }
227
                        }
1✔
228
                    }
229
                }
230
            }
231
        }
232

233
        return null;
1✔
234
    }
235

236
    public boolean hasOptions() {
237
        return this.data != null && !this.data.isEmpty();
1!
238
    }
239

240
    @Override
241
    public String toString() {
242
        return Arrays.toString(options);
1✔
243
    }
244

245
}
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