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