1 package org.robolectric.res; 2 3 import com.google.common.annotations.VisibleForTesting; 4 import org.robolectric.util.Logger; 5 6 public class StringResources { 7 8 private static final int CODE_POINT_LENGTH = 4; 9 10 /** 11 * Processes String resource values in the same way real Android does, namely:- 12 * 1) Trim leading and trailing whitespace. 13 * 2) Converts code points. 14 * 3) Escapes 15 */ processStringResources(String inputValue)16 public static String processStringResources(String inputValue) { 17 return escape(inputValue.trim()); 18 } 19 20 /** 21 * Provides escaping of String resources as described 22 * [here](http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling). 23 * 24 * @param text Text to escape. 25 * @return Escaped text. 26 */ 27 @VisibleForTesting escape(String text)28 static String escape(String text) { 29 // unwrap double quotes 30 if (text.length() > 1 && text.charAt(0) == '"' && text.charAt(text.length() - 1) == '"') { 31 text = text.substring(1, text.length() - 1); 32 } 33 int i = 0; 34 int length = text.length(); 35 StringBuilder result = new StringBuilder(text.length()); 36 while (true) { 37 int j = text.indexOf('\\', i); 38 if (j == -1) { 39 result.append(text.substring(i)); 40 break; 41 } 42 result.append(text.substring(i, j)); 43 if (j == length - 1) { 44 // dangling backslash 45 break; 46 } 47 boolean isUnicodeEscape = false; 48 char escapeCode = text.charAt(j + 1); 49 switch (escapeCode) { 50 case '\'': 51 case '"': 52 case '\\': 53 case '?': 54 case '@': 55 case '#': 56 result.append(escapeCode); 57 break; 58 case 'n': 59 result.append('\n'); 60 break; 61 case 't': 62 result.append('\t'); 63 break; 64 case 'u': 65 isUnicodeEscape = true; 66 break; 67 default: 68 Logger.strict("Unsupported string resource escape code '%s'", escapeCode); 69 } 70 if (!isUnicodeEscape) { 71 i = j + 2; 72 } else { 73 j += 2; 74 if (length - j < CODE_POINT_LENGTH) { 75 throw new IllegalArgumentException("Too short code point: \\u" + text.substring(j)); 76 } 77 String codePoint = text.substring(j, j + CODE_POINT_LENGTH); 78 result.append(extractCodePoint(codePoint)); 79 i = j + CODE_POINT_LENGTH; 80 } 81 } 82 return result.toString(); 83 } 84 85 /** 86 * Converts code points in a given string to actual characters. This method doesn't handle code 87 * points whose char counts are 2. In other words, this method doesn't handle U+10XXXX. 88 */ extractCodePoint(String codePoint)89 private static char[] extractCodePoint(String codePoint) { 90 try { 91 return Character.toChars(Integer.valueOf(codePoint, 16)); 92 } catch (IllegalArgumentException e) { 93 // This may be caused by NumberFormatException of Integer.valueOf() or 94 // IllegalArgumentException of Character.toChars(). 95 throw new IllegalArgumentException("Invalid code point: \\u" + codePoint, e); 96 } 97 } 98 } 99