• 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

92.16
/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> objectStore = ((FlexStringStore) store).getObjectStore();
1✔
53
        FlexParsedTemplate parsedTpl = (FlexParsedTemplate) objectStore.get(key);
1✔
54

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

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

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

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

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

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

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

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

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

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

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

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

157
        // returns fully localized string.
158
        if (str != null && str.contains("\\")) {
1!
159
            return UNESCAPE.matcher(str).replaceAll("");
1✔
160
        }
161
        return str;
1✔
162
    }
163

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