litesoft
@ 939
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 269 |
package org.litesoft.core.util.externalization; import org.litesoft.commonfoundation.typeutils.*; import java.util.*; import org.litesoft.commonfoundation.typeutils.Objects; 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 = Objects.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 = Strings.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 " "; // 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
Revision | Author | Commited | Message |
---|---|---|---|
939 Diff | GeorgeS | Mon 02 Jun, 2014 21:30:31 +0000 | Extracting commonfoundation |
917 Diff | GeorgeS | Sun 08 Dec, 2013 20:49:56 +0000 | 1.7 prep & VersionedStaticContentFilter upgrade to new “/ver” model! |
821 Diff | GeorgeS | Sun 19 Aug, 2012 00:08:41 +0000 | |
811 Diff | GeorgeS | Sat 18 Aug, 2012 13:45:18 +0000 | |
804 Diff | GeorgeS | Wed 15 Aug, 2012 12:48:51 +0000 | |
784 Diff | GeorgeS | Thu 19 Jul, 2012 03:57:21 +0000 | |
730 Diff | GeorgeS | Sun 17 Jun, 2012 17:45:19 +0000 | |
728 Diff | GeorgeS | Sat 16 Jun, 2012 23:39:49 +0000 | |
726 Diff | GeorgeS | Thu 14 Jun, 2012 13:33:38 +0000 | |
725 | GeorgeS | Mon 11 Jun, 2012 00:50:44 +0000 |