• 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

89.09
/src/main/java/com/stasha/info/flex/interpolators/FlexInterpolator.java
1
package com.stasha.info.flex.interpolators;
2

3
import com.stasha.info.flex.formatters.FlexFormat;
4
import com.stasha.info.flex.parsers.FlexParsedTemplate;
5
import com.stasha.info.flex.parsers.FlexStringTemplateOptions;
6
import com.stasha.info.flex.parsers.FlexStringTemplateParser;
7
import com.stasha.info.flex.store.FlexStore;
8
import com.stasha.info.flex.store.FlexStringStore;
9
import java.util.List;
10
import java.util.regex.Pattern;
11

12
/**
13
 * Flex Interpolation implementation.
14
 *
15
 * @author stasha
16
 */
17
public class FlexInterpolator {
1✔
18

19
    private FlexFormat flexFormat;
20
    private FlexStringTemplateParser parser = new FlexStringTemplateParser();
1✔
21

22
    private static final Pattern FORMAT_TYPE_PATTERN = Pattern.compile("(fmt\\.(date|time|decimal|currency)).*");
1✔
23

24
    private static final Pattern UNESCAPE = Pattern.compile("\\\\");
1✔
25

26
    /**
27
     * Sets formatter that will be used for formatting date, time, currency or
28
     * any other type.
29
     *
30
     * @param format
31
     */
32
    public void setFlexFormat(FlexFormat format) {
33
        this.flexFormat = format;
1✔
34
    }
1✔
35

36
    /**
37
     * Retrieves string from store based on passed key and interpolates argument
38
     * at specified index.<br>
39
     * Example: Welcome {0}, you are visitor number {1}.
40
     * interpolate("welcome.msg", 1, "Jon Doe", 1234) will return string:
41
     * Welcome {0}, you are visitor number 1234.
42
     *
43
     * @param key
44
     * @param index
45
     * @param store
46
     * @param arguments
47
     * @return
48
     */
49
    public String interpolate(String key, Integer index, FlexStore<String, String> store, Object... arguments) {
50
        // retrieves string from store
51
        String str = (String) store.getOrDefault(key, key);
1✔
52
        FlexStore<String, Object> cacheStore = ((FlexStringStore) store).getCacheStore();
1✔
53
        FlexParsedTemplate parsedTpl = (FlexParsedTemplate) cacheStore.get(key);
1✔
54

55
        if (parsedTpl == null) {
1✔
56
            parsedTpl = parser.parse(str);
1✔
57
            if (parsedTpl != null) {
1!
58
                cacheStore.put(key, parsedTpl);
1✔
59
            }
60
        }
61

62
        String indexValue = parsedTpl == null ? null : parsedTpl.getValue();
1!
63
        // fast check if string contains markers for interpolation
64
        if (indexValue != null) {
1✔
65
            indexValue = indexValue.trim();
1✔
66
            Object argAtIndex = indexValue;
1✔
67

68
            try {
69
                index = index == null ? Integer.valueOf(indexValue) : index;
1✔
70
                if (index <= arguments.length - 1 && index >= 0) {
1!
71
                    argAtIndex = arguments.length > 0 ? arguments[index] : null;
1!
72
                }
73
            } catch (NumberFormatException ex) {
1✔
74
                argAtIndex = interpolate(indexValue, null, store, arguments);
1✔
75
            }
1✔
76

77
            // extract value at specified index from passed arguments
78
            FlexStringTemplateOptions options = parsedTpl.getOptions();
1✔
79
            // check if plurals are present
80
            if (options != null && options.hasOptions()) {
1✔
81
                // key under which pluralization result will be cached
82
                String cacheKey = new StringBuilder("plc_").append(argAtIndex).append(options.toString()).toString();
1✔
83

84
                // get cached value for plural
85
                String result = (String) cacheStore.getOrDefault(cacheKey, null);
1✔
86
                if (result == null) {
1!
87
                    String option = options.getValue(argAtIndex);
1✔
88
                    argAtIndex = interpolate(option, index, store, arguments);
1✔
89
                    // caching plural results
90
                    store.put(cacheKey, String.valueOf(argAtIndex));
1✔
91
                } else {
1✔
UNCOV
92
                    argAtIndex = result;
×
93
                }
94
            }
95

96
            // check if format is present
97
            String[] formatters = parsedTpl.getFormats();
1✔
98
            if (formatters != null) {
1✔
99
                for (String format : formatters) {
1✔
100

101
                    // determine what type of fomrat to use.
102
                    // user can specify custom formats for date, time like:
103
                    // fmt.date.short = dd.MM.yy
104
                    // fmt.time.hours = HH
105
                    // ...
106
                    String formatType = FORMAT_TYPE_PATTERN.matcher(format).replaceAll("$1");
1✔
107
                    List<String> op = ((List<String>) ((FlexStringStore) store).getObject(new StringBuilder(format).append("$options").toString()));
1✔
108
                    if (op != null) {
1✔
109
                        Object obj = null;
1✔
110
                        if (index != null && index <= arguments.length - 1 && index >= 0) {
1!
111
                            obj = arguments[index];
1✔
112
                        } else {
113
                            obj = argAtIndex;
1✔
114
                        }
115
                        final String mformat = FlexStringTemplateOptions.match(obj, op.toArray(new String[]{}));
1✔
116
                        if (mformat != null) {
1!
117
                            format = new StringBuilder(format).append("[").append(mformat).append("]").toString();
1✔
118
                        }
119
                    }
120

121
                    if (format.startsWith("{") && format.endsWith("}")) {
1!
122
                        String iv = format.substring(1, format.length() - 1);
1✔
123
                        try {
124
                            index = Integer.valueOf(iv);
1✔
125
                            formatType = null;
1✔
126
                        } catch (NumberFormatException ex) {
×
127
                            //noop
128
                        }
1✔
129
                    }
130

131
                    // find user specified format.
132
                    // for example user could override currency format
133
                    // with fmt.currency="${0}.fmt.decimal
134
                    String userFormat = interpolate(format, index, store, arguments);
1✔
135
                    if (formatType == null) {
1✔
136
                        formatType = userFormat;
1✔
137
                    }
138

139
                    // get user specified locale
140
                    // user can specify custom locale in localization file with
141
                    // fmt.locale=fr-FR
142
                    String locale = interpolate("fmt.locale", index, store, arguments);
1✔
143

144
                    // format argument
145
                    argAtIndex = flexFormat.format(argAtIndex, formatType, userFormat, locale);
1✔
146
                }
147
            }
148

149
            // after input string is interpolated, interpolation marker {n} 
150
            // is replaced by it's value and then new interpolation is attempted
151
            // in case string contains additional interpolation markers.
152
            // This allows arbitrary interpolation marker nesting for more
153
            // complex localization logic.
154
//            str = interpolate(new StringBuilder(str).replace(m.start(), m.end(), String.valueOf(argAtIndex)).toString(), null, store, arguments);
155
            int start = str.indexOf(parsedTpl.getStrToReplace());
1✔
156
            int end = start + parsedTpl.getStrToReplace().length();
1✔
157
            String finalStr = new StringBuilder(str).replace(start, end, String.valueOf(argAtIndex)).toString();
1✔
158
            str = interpolate(finalStr, null, store, arguments);
1✔
159
        }
160

161
        // returns fully localized string.
162
        if (str != null && str.contains("\\")) {
1!
163
            return UNESCAPE.matcher(str).replaceAll("");
1✔
164
        }
165
        return str;
1✔
166
    }
167

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