Rfc822Tokenizer.java revision 78a5b8106a4768f7a67e5a29c58f23cd95f6c47c
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text.util;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.MultiAutoCompleteTextView;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
2278a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghoshimport java.util.Collection;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This class works as a Tokenizer for MultiAutoCompleteTextView for
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * address list fields, and also provides a method for converting
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * a string of addresses (such as might be typed into such a field)
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * into a series of Rfc822Tokens.
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
3178a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This constructor will try to take a string like
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "Foo Bar (something) <foo\@google.com>,
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * blah\@google.com (something)"
3678a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * and convert it into one or more Rfc822Tokens, output into the supplied
3778a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * collection.
3878a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     *
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * It does *not* decode MIME encoded-words; charset conversion
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * must already have taken place if necessary.
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * It will try to be tolerant of broken syntax instead of
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * returning an error.
4378a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     *
4478a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * @hide
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4678a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder name = new StringBuilder();
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder address = new StringBuilder();
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder comment = new StringBuilder();
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int cursor = text.length();
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < cursor) {
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = text.charAt(i);
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ',' || c == ';') {
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor && text.charAt(i) == ' ') {
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                crunch(name);
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (address.length() > 0) {
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.add(new Rfc822Token(name.toString(),
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            address.toString(),
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            comment.toString()));
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (name.length() > 0) {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.add(new Rfc822Token(null,
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            name.toString(),
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            comment.toString()));
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                name.setLength(0);
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                address.setLength(0);
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                address.setLength(0);
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '"') {
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor) {
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '"') {
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '\\') {
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        name.append(text.charAt(i + 1));
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        name.append(c);
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '(') {
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int level = 1;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor && level > 0) {
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == ')') {
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (level > 1) {
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            comment.append(c);
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        level--;
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '(') {
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        comment.append(c);
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        level++;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '\\') {
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        comment.append(text.charAt(i + 1));
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        comment.append(c);
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '<') {
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor) {
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '>') {
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        address.append(c);
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == ' ') {
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                name.append('\0');
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                name.append(c);
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        crunch(name);
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (address.length() > 0) {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.add(new Rfc822Token(name.toString(),
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    address.toString(),
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    comment.toString()));
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (name.length() > 0) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.add(new Rfc822Token(null,
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    name.toString(),
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    comment.toString()));
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15678a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    }
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15878a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    /**
15978a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * This method will try to take a string like
16078a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * "Foo Bar (something) &lt;foo\@google.com&gt;,
16178a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * blah\@google.com (something)"
16278a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * and convert it into one or more Rfc822Tokens.
16378a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * It does *not* decode MIME encoded-words; charset conversion
16478a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * must already have taken place if necessary.
16578a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * It will try to be tolerant of broken syntax instead of
16678a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * returning an error.
16778a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     */
16878a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    public static Rfc822Token[] tokenize(CharSequence text) {
16978a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh        ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>();
17078a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh        tokenize(text, out);
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return out.toArray(new Rfc822Token[out.size()]);
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void crunch(StringBuilder sb) {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = sb.length();
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < len) {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = sb.charAt(i);
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '\0') {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (i == 0 || i == len - 1 ||
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i - 1) == ' ' ||
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i - 1) == '\0' ||
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i + 1) == ' ' ||
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i + 1) == '\0') {
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.deleteCharAt(i);
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    len--;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (i = 0; i < len; i++) {
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (sb.charAt(i) == '\0') {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.setCharAt(i, ' ');
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@inheritDoc}
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int findTokenStart(CharSequence text, int cursor) {
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * It's hard to search backward, so search forward until
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * we reach the cursor.
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int best = 0;
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < cursor) {
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            i = findTokenEnd(text, i);
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i < cursor) {
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++; // Skip terminating punctuation
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor && text.charAt(i) == ' ') {
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (i < cursor) {
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    best = i;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return best;
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@inheritDoc}
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int findTokenEnd(CharSequence text, int cursor) {
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = cursor;
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < len) {
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = text.charAt(i);
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ',' || c == ';') {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '"') {
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < len) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '"') {
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '\\') {
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '(') {
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int level = 1;
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < len && level > 0) {
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == ')') {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        level--;
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '(') {
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        level++;
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '\\') {
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '<') {
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < len) {
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '>') {
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return i;
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Terminates the specified address with a comma and space.
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This assumes that the specified text already has valid syntax.
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The Adapter subclass's convertToString() method must make that
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * guarantee.
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public CharSequence terminateToken(CharSequence text) {
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return text + ", ";
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
312