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

stasha / flex / 16

27 Apr 2025 11:53PM UTC coverage: 74.123% (+17.2%) from 56.927%
16

push

circleci

stasha
Re-factored project and directory name and added ordinals and plurals for 56 of countries

164 of 247 branches covered (66.4%)

Branch coverage included in aggregate %.

260 of 286 new or added lines in 10 files covered. (90.91%)

3 existing lines in 2 files now uncovered.

343 of 437 relevant lines covered (78.49%)

0.78 hits per line

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

76.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 FlexStringTemplateOptions() {
1✔
37
    }
1✔
38

39
    public FlexStringTemplateOptions(String options) {
1✔
40
        this.data = parse(options);
1✔
41
    }
1✔
42

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

49
            this.options = p;
1✔
50

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

66
    public String getValue(Object value) {
67
        return this.getValue(value, this.data);
1✔
68
    }
69

70
    public String getValue(Object value, String optionsString) {
NEW
71
        return getValue(value, parse(optionsString));
×
72
    }
73

74
    public String getValue(Object value, Map<String, String> options) {
75

76
        String val = String.valueOf(value);
1✔
77
        if (options != null && !options.isEmpty()) {
1!
78
            String key = match(value, options.keySet().toArray(new String[]{}));
1✔
79
            val = options.get(key);
1✔
80
        }
81

82
        // don't unescape it here as this is called recursively
83
        // with options that can have escape that should be taken
84
        // into consideration in different interpolation places.
85
        return val;
1✔
86
    }
87

88
    public static String match(Object value, String[] options) {
89
        String val = String.valueOf(value);
1✔
90
        if(val.startsWith(" ") || val.endsWith(" ")){
1!
NEW
91
            val = val.trim();
×
92
        }
93

94
        if (options.length > 0) {
1!
95
            for (int i = 0; i < options.length; ++i) {
1!
96
                String k = options[i];
1✔
97
                if (k.equals("other")) {
1✔
98
                    return k;
1✔
99
                }
100

101
                boolean fromStartsWith = false;
1✔
102
                boolean fromEndsWith = false;
1✔
103
                boolean toStartsWith = false;
1✔
104
                boolean toEndsWith = false;
1✔
105
                boolean fromMathFloor = false;
1✔
106
                boolean fromMathMax = false;
1✔
107
                boolean toMathFlor = false;
1✔
108
                boolean toMathMax = false;
1✔
109

110
                String from = k;
1✔
111
                String to = null;
1✔
112

113
                if (k.contains("_") || k.contains("-") || k.contains("~")) {
1✔
114
                    Matcher m = PARTIAL_NUMBER_PATTERN.matcher(k);
1✔
115
                    if (m.find()) {
1!
116

117
                        fromMathFloor = "~".equals(m.group(5));
1✔
118
                        fromStartsWith = "_".equals(m.group(4));
1✔
119
                        from = m.group(3);
1✔
120
                        fromEndsWith = "_".equals(m.group(2));
1✔
121
                        fromMathMax = "~".equals(m.group(1));
1✔
122

123
                        toMathFlor = "~".equals(m.group(7));
1✔
124
                        toStartsWith = ("_").equals(m.group(10));
1✔
125
                        to = m.group(9);
1✔
126
                        toEndsWith = ("_").equals(m.group(8));
1✔
127
                        toMathMax = "~".equals(m.group(11));
1✔
128

129
                    }
130
                }
131

132
                if (from != null) {
1!
133
                    // not a range
134
                    if (to == null) {
1✔
135
                        if (from.equals(val)) {
1✔
136
                            return k;
1✔
137
                        } else if (fromEndsWith && val.endsWith(from)) {
1✔
138
                            return k;
1✔
139
                        } else if (fromStartsWith && val.startsWith(from)) {
1!
NEW
140
                            return k;
×
141
                        } else if (fromMathFloor) {
1✔
142
                            if (val.equals(from)) {
1!
NEW
143
                                return k;
×
144
                            } else {
145
                                Double fr = Double.valueOf(from);
1✔
146
                                Double flor = Double.valueOf(val);
1✔
147
                                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!
148
                                if (fr.equals(v)) {
1✔
149
                                    return k;
1✔
150
                                }
151
                            }
1✔
152
                        }
153
                    } else {
154
                        // can't combine _a-b_ and a_-_b
155
                        // only _a-_b or a_-b_
156
                        if (fromStartsWith && toEndsWith) {
1!
NEW
157
                            throw new IllegalStateException("Starts with and ends with patterns can't becombined: _a-b_ or a_-_b");
×
158
                        }
159

160
                        // _11-_15 - ends with 11, 12, 13, 14, 15
161
                        if (fromEndsWith && toEndsWith && val.length() > from.length()) {
1!
NEW
162
                            val = val.substring(val.length() - from.length(), from.length());
×
163
                        }
164

165
                        try {
166
                            Double f = Double.valueOf(from);
1✔
167
                            Double t = Double.valueOf(to);
1✔
168
                            Double dval = !(value instanceof Double) ? Double.valueOf(val) : (Double) value;
1!
169

170
                            // check if value is in range
171
                            if (dval >= f && dval <= t) {
1✔
172
                                return k;
1✔
173
                            }
NEW
174
                        } catch (NumberFormatException ex) {
×
175
                            // string can't have range
NEW
176
                            if (from.equals(value)) {
×
NEW
177
                                return k;
×
NEW
178
                            } else if (fromEndsWith && String.valueOf(value).endsWith(from)) {
×
NEW
179
                                return k;
×
NEW
180
                            } else if (fromStartsWith && String.valueOf(value).startsWith(from)) {
×
NEW
181
                                return k;
×
182
                            }
183
                        }
1✔
184
                    }
185
                }
186
            }
187
        }
188

NEW
189
        return null;
×
190
    }
191

192
    public boolean hasOptions() {
193
        return this.data != null && !this.data.isEmpty();
1!
194
    }
195

196
    @Override
197
    public String toString() {
198
        return Arrays.toString(options);
1✔
199
    }
200

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