litesoft
@ 948
litesoft / trunk / GWT_Sandbox / FormEngine / src / org / litesoft / core / util / externalization / HelperE13nResolver.java
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 |
package org.litesoft.core.util.externalization; import org.litesoft.core.util.externalization.E13nData.*; import com.temp.shared.utils.*; import java.util.*; /** * 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 tamplate */ String resolveWithoutCompleting( 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 ) { Assert.notNull( "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 ) { Assert.notNull( "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 ) { Assert.notNull( "Enum key", key ); String keyName = key.name(); String prefix = ObjectUtils.getSimpleClassName( 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 = Assert.noEmpty( "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( 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(); } 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 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 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 } // 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 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 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 } } |