PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://github.com/android/platform_frameworks_base
Java | 314 lines | 214 code | 40 blank | 60 comment | 107 complexity | 1bc13ed944b46c2bd0ad9fc8fa1be350 MD5 | raw file
Possible License(s): BitTorrent-1.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, CC0-1.0
  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 == '\\') {
  74. if (i + 1 < cursor) {
  75. name.append(text.charAt(i + 1));
  76. }
  77. i += 2;
  78. } else {
  79. name.append(c);
  80. i++;
  81. }
  82. }
  83. } else if (c == '(') {
  84. int level = 1;
  85. i++;
  86. while (i < cursor && level > 0) {
  87. c = text.charAt(i);
  88. if (c == ')') {
  89. if (level > 1) {
  90. comment.append(c);
  91. }
  92. level--;
  93. i++;
  94. } else if (c == '(') {
  95. comment.append(c);
  96. level++;
  97. i++;
  98. } else if (c == '\\') {
  99. if (i + 1 < cursor) {
  100. comment.append(text.charAt(i + 1));
  101. }
  102. i += 2;
  103. } else {
  104. comment.append(c);
  105. i++;
  106. }
  107. }
  108. } else if (c == '<') {
  109. i++;
  110. while (i < cursor) {
  111. c = text.charAt(i);
  112. if (c == '>') {
  113. i++;
  114. break;
  115. } else {
  116. address.append(c);
  117. i++;
  118. }
  119. }
  120. } else if (c == ' ') {
  121. name.append('\0');
  122. i++;
  123. } else {
  124. name.append(c);
  125. i++;
  126. }
  127. }
  128. crunch(name);
  129. if (address.length() > 0) {
  130. out.add(new Rfc822Token(name.toString(),
  131. address.toString(),
  132. comment.toString()));
  133. } else if (name.length() > 0) {
  134. out.add(new Rfc822Token(null,
  135. name.toString(),
  136. comment.toString()));
  137. }
  138. }
  139. /**
  140. * This method will try to take a string like
  141. * "Foo Bar (something) &lt;foo\@google.com&gt;,
  142. * blah\@google.com (something)"
  143. * and convert it into one or more Rfc822Tokens.
  144. * It does *not* decode MIME encoded-words; charset conversion
  145. * must already have taken place if necessary.
  146. * It will try to be tolerant of broken syntax instead of
  147. * returning an error.
  148. */
  149. public static Rfc822Token[] tokenize(CharSequence text) {
  150. ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>();
  151. tokenize(text, out);
  152. return out.toArray(new Rfc822Token[out.size()]);
  153. }
  154. private static void crunch(StringBuilder sb) {
  155. int i = 0;
  156. int len = sb.length();
  157. while (i < len) {
  158. char c = sb.charAt(i);
  159. if (c == '\0') {
  160. if (i == 0 || i == len - 1 ||
  161. sb.charAt(i - 1) == ' ' ||
  162. sb.charAt(i - 1) == '\0' ||
  163. sb.charAt(i + 1) == ' ' ||
  164. sb.charAt(i + 1) == '\0') {
  165. sb.deleteCharAt(i);
  166. len--;
  167. } else {
  168. i++;
  169. }
  170. } else {
  171. i++;
  172. }
  173. }
  174. for (i = 0; i < len; i++) {
  175. if (sb.charAt(i) == '\0') {
  176. sb.setCharAt(i, ' ');
  177. }
  178. }
  179. }
  180. /**
  181. * {@inheritDoc}
  182. */
  183. public int findTokenStart(CharSequence text, int cursor) {
  184. /*
  185. * It's hard to search backward, so search forward until
  186. * we reach the cursor.
  187. */
  188. int best = 0;
  189. int i = 0;
  190. while (i < cursor) {
  191. i = findTokenEnd(text, i);
  192. if (i < cursor) {
  193. i++; // Skip terminating punctuation
  194. while (i < cursor && text.charAt(i) == ' ') {
  195. i++;
  196. }
  197. if (i < cursor) {
  198. best = i;
  199. }
  200. }
  201. }
  202. return best;
  203. }
  204. /**
  205. * {@inheritDoc}
  206. */
  207. public int findTokenEnd(CharSequence text, int cursor) {
  208. int len = text.length();
  209. int i = cursor;
  210. while (i < len) {
  211. char c = text.charAt(i);
  212. if (c == ',' || c == ';') {
  213. return i;
  214. } else if (c == '"') {
  215. i++;
  216. while (i < len) {
  217. c = text.charAt(i);
  218. if (c == '"') {
  219. i++;
  220. break;
  221. } else if (c == '\\' && i + 1 < len) {
  222. i += 2;
  223. } else {
  224. i++;
  225. }
  226. }
  227. } else if (c == '(') {
  228. int level = 1;
  229. i++;
  230. while (i < len && level > 0) {
  231. c = text.charAt(i);
  232. if (c == ')') {
  233. level--;
  234. i++;
  235. } else if (c == '(') {
  236. level++;
  237. i++;
  238. } else if (c == '\\' && i + 1 < len) {
  239. i += 2;
  240. } else {
  241. i++;
  242. }
  243. }
  244. } else if (c == '<') {
  245. i++;
  246. while (i < len) {
  247. c = text.charAt(i);
  248. if (c == '>') {
  249. i++;
  250. break;
  251. } else {
  252. i++;
  253. }
  254. }
  255. } else {
  256. i++;
  257. }
  258. }
  259. return i;
  260. }
  261. /**
  262. * Terminates the specified address with a comma and space.
  263. * This assumes that the specified text already has valid syntax.
  264. * The Adapter subclass's convertToString() method must make that
  265. * guarantee.
  266. */
  267. public CharSequence terminateToken(CharSequence text) {
  268. return text + ", ";
  269. }
  270. }