Subversion Repository Public Repository

litesoft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
package org.litesoft.core.util.externalization;

import java.util.*;

import org.litesoft.core.typeutils.*;
import org.litesoft.core.util.*;
import org.litesoft.core.util.externalization.E13nData.*;

/**
 * Since there can be many different implementations of E13nResolver, the
 * ability to chain them is not supported as all three "flavors" of the resolve
 * methods will ALWAYS resolve the request. To overcome this chaining problem an
 * implementation of E13nResolver can also implement the NotCompleting interface
 * listed below, and delegate all of the normal three E13nResolver methods to
 * this class, and all the hard work will be done here, by limiting the actual
 * resolving to the NotCompleting method: resolveWithoutCompleting.
 * <p/>
 * The delegation to this class and the use of resolveWithoutCompleting(),
 * allows for an E13nResolver implementation (that implements NonCompleting) to
 * chain to other E13nResolver implementation(s) (that also implement
 * NonCompleting).
 *
 * @author georgs
 */
public class HelperE13nResolver
{
    private static final int MAX_DEPTH_FLAG_AS_CYCLE = 5;

    public interface NonCompleting
    {
        /**
         * @param key !empty
         *
         * @return null or raw template
         */
        String resolveWithoutCompleting( String key );

        /**
         * @param key !empty
         *
         * @return the fully prefixed key
         */
        String makeFullKey( String key );
    }

    /**
     * Code to simplify the validation and conversion of a E13nResolver into a
     * NonCompleting for the purposes of chaining E13nResolver(s)
     *
     * @param proxied !null
     *
     * @return proxied cast to a NonCompleting
     */
    public static NonCompleting validateProxy( E13nResolver proxied )
    {
        Objects.assertNotNull( "Proxied E13nResolver", proxied );
        if ( proxied instanceof NonCompleting )
        {
            return (NonCompleting) proxied;
        }
        throw new IllegalArgumentException( "Proxied E13nResolver not a NonCompleting instance: " + proxied.getClass().getName() );
    }

    /**
     * Delegation method for E13nResolver: String resolve(E13nData data);
     *
     * @param data     !null
     * @param resolver !null
     */
    public static String resolveDataWith( E13nData data, NonCompleting resolver )
    {
        Objects.assertNotNull( "E13nData data", data );
        String key = data.getTemplateIdCode();
        return processResolvedWithSubKeys( key, resolver.resolveWithoutCompleting( key ), resolver, data.getTemplateSubstitutionNamedValues() );
    }

    /**
     * Delegation method for E13nResolver: String resolve(Enum<?> key);
     *
     * @param key      !null
     * @param resolver !null
     */
    public static String resolveEnumWith( Enum<?> key, NonCompleting resolver )
    {
        Objects.assertNotNull( "Enum key", key );
        String keyName = key.name();
        String prefix = UtilsCommon.justClassNameOf( key );
        String value = resolver.resolveWithoutCompleting( prefix + E13nResolver.PREFIX_SEP + keyName );
        if ( value == null )
        {
            value = resolver.resolveWithoutCompleting( keyName );
        }
        return processResolvedWithSubKeys( keyName, value, resolver, Collections.<String, SubstitutionValue>emptyMap() );
    }

    /**
     * Delegation method for E13nResolver: String resolve(String key);
     *
     * @param key      !null
     * @param resolver !null
     */
    public static String resolveStringWith( String key, NonCompleting resolver )
    {
        key = UtilsCommon.assertNotNullNotEmpty( "String key", key );
        return processResolvedWithSubKeys( key, resolver.resolveWithoutCompleting( key ), resolver, Collections.<String, SubstitutionValue>emptyMap() );
    }

    /**
     * Common code from all three of the above delegation methods that handles
     * the primary key resolved value and then delegates to the sub-key
     * processor.
     * <p/>
     * When a primary key is not found, it is resolved to a "boxed" form, e.g. a
     * key of "Type" that is not found will return "[Type]". However, if there
     * are override substitution values, then the "boxed" form adds the override
     * values, and attempts resolve the sub-keys, e.g.
     * "[Type:Name={Name},Why={Why}]".
     * <p/>
     * Since unresolved sub-keys are left wrapped and the unresolved primary
     * keys are wrapped with '[' & ']' characters, it is hoped that unresolved
     * keys will be easy to recognize in the resulting text displayed.
     *
     * @param key       key looked up (!null)
     * @param value     value found without completing for the key (null OK)
     * @param overrides key/value pairs to resolve sub-keys from first (before
     *                  delegating to the resolver)
     *
     * @return resolved string (!null)
     */
    private static String processResolvedWithSubKeys( String key, String value, NonCompleting resolver, Map<String, SubstitutionValue> overrides )
    {
        if ( value == null )
        {
            StringBuilder sb = new StringBuilder().append( '[' ).append( resolver.makeFullKey( key ) );
            char prefix = ':';
            for ( String name : overrides.keySet() )
            {
                sb.append( prefix ).append( name ).append( "='" ).append( E13nResolver.INIT ).append( name ).append( E13nResolver.FINI ).append( "'" );
                prefix = ',';
            }
            value = sb.append( ']' ).toString();
            System.out.println( "Unable to resolve: " + value );
        }
        return processSubKeys( 0, value, resolver, overrides );
    }

    /**
     * Process the resolved 'value' for any sub-keys recursively.
     *
     * @param depth     used to track levels of recursion for giving up!
     * @param value     value found without completing for the key OR the "boxed"
     *                  primary key (!null)
     * @param overrides key/value pairs to resolve sub-keys from first (before
     *                  delegating to the resolver)
     *
     * @return resolved string (!null)
     */
    private static String processSubKeys( int depth, String value, NonCompleting resolver, Map<String, SubstitutionValue> overrides )
    {
        int finiAt = value.indexOf( E13nResolver.FINI );
        if ( finiAt == -1 )
        {
            return value; // Happy case, no sub-keys
        }
        StringBuilder sb = new StringBuilder();
        do
        {
            // extract each sub-key
            String left = value.substring( 0, ++finiAt );
            value = value.substring( finiAt );
            int initAt = left.indexOf( E13nResolver.INIT );
            if ( initAt == -1 )
            {
                sb.append( left ); // Dangling "FINI" (No "INIT")
            }
            else
            {
                sb.append( left.substring( 0, initAt ) );
                String wrappedKey = left.substring( initAt );
                // process the potential "wrapped" sub-key
                String substitutionText = wrappedResolveWith( depth, wrappedKey, resolver, overrides );
                if ( substitutionText != null )
                {
                    sb.append( substitutionText ); // Success - resolved the key!
                }
                else
                {
                    sb.append( wrappedKey ); // Couldn't resolve - simply add the wrapped key (not a key?)
                }
            }
        }
        while ( -1 != (finiAt = value.indexOf( E13nResolver.FINI )) );
        return sb.append( value ).toString();
    }

    /**
     * Handle the two special case sub-keys, and if not one of them, then unwrap
     * it and try to resolve it.
     *
     * @param depth      used to track levels of recursion for giving up!
     * @param wrappedKey wrapped key to looked up (!null) (wrapped means still
     *                   surrounded by the sub-key indicators - see: E13nResolver INIT
     *                   & FINI)
     * @param resolver   NonCompleting resolver to get the raw resolved value from
     * @param overrides  key/value pairs to resolve sub-keys from first (before
     *                   delegating to the resolver)
     *
     * @return resolved string (!null)
     */
    private static String wrappedResolveWith( int depth, String wrappedKey, NonCompleting resolver, Map<String, SubstitutionValue> overrides )
    {
        if ( E13nResolver.DONT_SHOW_SUBSTITUTION_ID.equals( wrappedKey ) )
        { // Special sub-key for Empty String
            return ""; // Empty String
        }
        if ( E13nResolver.SPACE_SUBSTITUTION_ID.equals( wrappedKey ) )
        { // Special sub-key for Space
            return " "; // Space
        }
        if ( E13nResolver.NBSP_SUBSTITUTION_ID.equals( wrappedKey ) )
        { // Special sub-key for nbsp
            return "&nbsp;"; // Non-Blanking Space
        }
        // Unwrap & attempt to resolve
        return unwrappedResolveWith( depth, wrappedKey.substring( 1, wrappedKey.length() - 1 ), resolver, overrides );
    }

    /**
     * Resolve the unwrapped sub-key using one of the following three kay/value
     * types:
     * <p/>
     * <li>override of User Data - just return the override 'value'</li>
     * <p/>
     * <li>override of non-User Data (possible key) - treat the override 'value'
     * as an unwrapped key and resolve</li>
     * <p/>
     * <li>non-override key - revolve (w/o completing) the key, if !null then
     * recursively process for sub-keys</li>
     *
     * @param depth     used to track levels of recursion for giving up!
     * @param key       key to looked up (!null but may be "")
     * @param resolver  NonCompleting resolver to get the raw resolved value from
     * @param overrides key/value pairs to resolve sub-keys from first (before
     *                  delegating to the resolver)
     *
     * @return resolved string (!null)
     */
    private static String unwrappedResolveWith( int depth, String key, NonCompleting resolver, Map<String, SubstitutionValue> overrides )
    {
        if ( depth > MAX_DEPTH_FLAG_AS_CYCLE )
        {
            return "[?" + key + " - Cycle?]";
        }
        SubstitutionValue substitutionValue = overrides.get( key );
        if ( substitutionValue != null )
        {
            String value = substitutionValue.getValue();
            if ( substitutionValue.isUserData() )
            {
                return value; // User Value : No further processing
            }
            // !User Data : Assume Value is a Key
            return unwrappedResolveWith( depth + 1, value, resolver, overrides ); // Recurse
        }
        String value = resolver.resolveWithoutCompleting( key ); // No Override! value may have Sub-Keys
        return (value == null) ? null : processSubKeys( depth + 1, value, resolver, overrides ); // Recurse
    }
}

Commits for litesoft/trunk/Java/core/Anywhere/src/org/litesoft/core/util/externalization/HelperE13nResolver.java

Diff revisions: vs.
Revision Author Commited Message
804 Diff Diff GeorgeS picture GeorgeS Wed 15 Aug, 2012 12:48:51 +0000
784 Diff Diff GeorgeS picture GeorgeS Thu 19 Jul, 2012 03:57:21 +0000
730 Diff Diff GeorgeS picture GeorgeS Sun 17 Jun, 2012 17:45:19 +0000
728 Diff Diff GeorgeS picture GeorgeS Sat 16 Jun, 2012 23:39:49 +0000
726 Diff Diff GeorgeS picture GeorgeS Thu 14 Jun, 2012 13:33:38 +0000
725 GeorgeS picture GeorgeS Mon 11 Jun, 2012 00:50:44 +0000