PageRenderTime 73ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/core/java/android/text/util/Rfc822Tokenizer.java

https://github.com/cvpcs/android_frameworks_base.gem
Java | 310 lines | 210 code | 40 blank | 60 comment | 105 complexity | aa7aa4923055e8e3e887971d8e064e01 MD5 | raw file
  1. /*
  2. * Copyright (C) 2008 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.text.util;
  17. import android.widget.MultiAutoCompleteTextView;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. /**
  21. * This class works as a Tokenizer for MultiAutoCompleteTextView for
  22. * address list fields, and also provides a method for converting
  23. * a string of addresses (such as might be typed into such a field)
  24. * into a series of Rfc822Tokens.
  25. */
  26. public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
  27. /**
  28. * This constructor will try to take a string like
  29. * "Foo Bar (something) <foo\@google.com>,
  30. * blah\@google.com (something)"
  31. * and convert it into one or more Rfc822Tokens, output into the supplied
  32. * collection.
  33. *
  34. * It does *not* decode MIME encoded-words; charset conversion
  35. * must already have taken place if necessary.
  36. * It will try to be tolerant of broken syntax instead of
  37. * returning an error.
  38. *
  39. */
  40. public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
  41. StringBuilder name = new StringBuilder();
  42. StringBuilder address = new StringBuilder();
  43. StringBuilder comment = new StringBuilder();
  44. int i = 0;
  45. int cursor = text.length();
  46. while (i < cursor) {
  47. char c = text.charAt(i);
  48. if (c == ',' || c == ';') {
  49. i++;
  50. while (i < cursor && text.charAt(i) == ' ') {
  51. i++;
  52. }
  53. crunch(name);
  54. if (address.length() > 0) {
  55. out.add(new Rfc822Token(name.toString(),
  56. address.toString(),
  57. comment.toString()));
  58. } else if (name.length() > 0) {
  59. out.add(new Rfc822Token(null,
  60. name.toString(),
  61. comment.toString()));
  62. }
  63. name.setLength(0);
  64. address.setLength(0);
  65. comment.setLength(0);
  66. } else if (c == '"') {
  67. i++;
  68. while (i < cursor) {
  69. c = text.charAt(i);
  70. if (c == '"') {
  71. i++;
  72. break;
  73. } else if (c == '\\' && i + 1 < cursor) {
  74. name.append(text.charAt(i + 1));
  75. i += 2;
  76. } else {
  77. name.append(c);
  78. i++;
  79. }
  80. }
  81. } else if (c == '(') {
  82. int level = 1;
  83. i++;
  84. while (i < cursor && level > 0) {
  85. c = text.charAt(i);
  86. if (c == ')') {
  87. if (level > 1) {
  88. comment.append(c);
  89. }
  90. level--;
  91. i++;
  92. } else if (c == '(') {
  93. comment.append(c);
  94. level++;
  95. i++;
  96. } else if (c == '\\' && i + 1 < cursor) {
  97. comment.append(text.charAt(i + 1));
  98. i += 2;
  99. } else {
  100. comment.append(c);
  101. i++;
  102. }
  103. }
  104. } else if (c == '<') {
  105. i++;
  106. while (i < cursor) {
  107. c = text.charAt(i);
  108. if (c == '>') {
  109. i++;
  110. break;
  111. } else {
  112. address.append(c);
  113. i++;
  114. }
  115. }
  116. } else if (c == ' ') {
  117. name.append('\0');
  118. i++;
  119. } else {
  120. name.append(c);
  121. i++;
  122. }
  123. }
  124. crunch(name);
  125. if (address.length() > 0) {
  126. out.add(new Rfc822Token(name.toString(),
  127. address.toString(),
  128. comment.toString()));
  129. } else if (name.length() > 0) {
  130. out.add(new Rfc822Token(null,
  131. name.toString(),
  132. comment.toString()));
  133. }
  134. }
  135. /**
  136. * This method will try to take a string like
  137. * "Foo Bar (something) &lt;foo\@google.com&gt;,
  138. * blah\@google.com (something)"
  139. * and convert it into one or more Rfc822Tokens.
  140. * It does *not* decode MIME encoded-words; charset conversion
  141. * must already have taken place if necessary.
  142. * It will try to be tolerant of broken syntax instead of
  143. * returning an error.
  144. */
  145. public static Rfc822Token[] tokenize(CharSequence text) {
  146. ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>();
  147. tokenize(text, out);
  148. return out.toArray(new Rfc822Token[out.size()]);
  149. }
  150. private static void crunch(StringBuilder sb) {
  151. int i = 0;
  152. int len = sb.length();
  153. while (i < len) {
  154. char c = sb.charAt(i);
  155. if (c == '\0') {
  156. if (i == 0 || i == len - 1 ||
  157. sb.charAt(i - 1) == ' ' ||
  158. sb.charAt(i - 1) == '\0' ||
  159. sb.charAt(i + 1) == ' ' ||
  160. sb.charAt(i + 1) == '\0') {
  161. sb.deleteCharAt(i);
  162. len--;
  163. } else {
  164. i++;
  165. }
  166. } else {
  167. i++;
  168. }
  169. }
  170. for (i = 0; i < len; i++) {
  171. if (sb.charAt(i) == '\0') {
  172. sb.setCharAt(i, ' ');
  173. }
  174. }
  175. }
  176. /**
  177. * {@inheritDoc}
  178. */
  179. public int findTokenStart(CharSequence text, int cursor) {
  180. /*
  181. * It's hard to search backward, so search forward until
  182. * we reach the cursor.
  183. */
  184. int best = 0;
  185. int i = 0;
  186. while (i < cursor) {
  187. i = findTokenEnd(text, i);
  188. if (i < cursor) {
  189. i++; // Skip terminating punctuation
  190. while (i < cursor && text.charAt(i) == ' ') {
  191. i++;
  192. }
  193. if (i < cursor) {
  194. best = i;
  195. }
  196. }
  197. }
  198. return best;
  199. }
  200. /**
  201. * {@inheritDoc}
  202. */
  203. public int findTokenEnd(CharSequence text, int cursor) {
  204. int len = text.length();
  205. int i = cursor;
  206. while (i < len) {
  207. char c = text.charAt(i);
  208. if (c == ',' || c == ';') {
  209. return i;
  210. } else if (c == '"') {
  211. i++;
  212. while (i < len) {
  213. c = text.charAt(i);
  214. if (c == '"') {
  215. i++;
  216. break;
  217. } else if (c == '\\') {
  218. i += 2;
  219. } else {
  220. i++;
  221. }
  222. }
  223. } else if (c == '(') {
  224. int level = 1;
  225. i++;
  226. while (i < len && level > 0) {
  227. c = text.charAt(i);
  228. if (c == ')') {
  229. level--;
  230. i++;
  231. } else if (c == '(') {
  232. level++;
  233. i++;
  234. } else if (c == '\\') {
  235. i += 2;
  236. } else {
  237. i++;
  238. }
  239. }
  240. } else if (c == '<') {
  241. i++;
  242. while (i < len) {
  243. c = text.charAt(i);
  244. if (c == '>') {
  245. i++;
  246. break;
  247. } else {
  248. i++;
  249. }
  250. }
  251. } else {
  252. i++;
  253. }
  254. }
  255. return i;
  256. }
  257. /**
  258. * Terminates the specified address with a comma and space.
  259. * This assumes that the specified text already has valid syntax.
  260. * The Adapter subclass's convertToString() method must make that
  261. * guarantee.
  262. */
  263. public CharSequence terminateToken(CharSequence text) {
  264. return text + ", ";
  265. }
  266. }