• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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