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     *
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4578a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder name = new StringBuilder();
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder address = new StringBuilder();
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder comment = new StringBuilder();
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int cursor = text.length();
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < cursor) {
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = text.charAt(i);
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ',' || c == ';') {
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor && text.charAt(i) == ' ') {
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                crunch(name);
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (address.length() > 0) {
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.add(new Rfc822Token(name.toString(),
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            address.toString(),
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            comment.toString()));
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (name.length() > 0) {
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.add(new Rfc822Token(null,
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            name.toString(),
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            comment.toString()));
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                name.setLength(0);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                address.setLength(0);
7799c7dea7546bf0bd9ec1c03d300b11d93d9306ddErik                comment.setLength(0);
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '"') {
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor) {
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '"') {
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '\\') {
889694f9056a86081aadc1d7caab82d340847b3884Henrik Hall                        if (i + 1 < cursor) {
899694f9056a86081aadc1d7caab82d340847b3884Henrik Hall                            name.append(text.charAt(i + 1));
909694f9056a86081aadc1d7caab82d340847b3884Henrik Hall                        }
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        name.append(c);
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '(') {
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int level = 1;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor && level > 0) {
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == ')') {
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (level > 1) {
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            comment.append(c);
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        level--;
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '(') {
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        comment.append(c);
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        level++;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (c == '\\') {
1169694f9056a86081aadc1d7caab82d340847b3884Henrik Hall                        if (i + 1 < cursor) {
1179694f9056a86081aadc1d7caab82d340847b3884Henrik Hall                            comment.append(text.charAt(i + 1));
1189694f9056a86081aadc1d7caab82d340847b3884Henrik Hall                        }
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        comment.append(c);
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '<') {
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor) {
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '>') {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        address.append(c);
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == ' ') {
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                name.append('\0');
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                name.append(c);
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        crunch(name);
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (address.length() > 0) {
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.add(new Rfc822Token(name.toString(),
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    address.toString(),
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    comment.toString()));
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (name.length() > 0) {
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.add(new Rfc822Token(null,
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    name.toString(),
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    comment.toString()));
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15978a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16178a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    /**
16278a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * This method will try to take a string like
16378a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * "Foo Bar (something) &lt;foo\@google.com&gt;,
16478a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * blah\@google.com (something)"
16578a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * and convert it into one or more Rfc822Tokens.
16678a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * It does *not* decode MIME encoded-words; charset conversion
16778a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * must already have taken place if necessary.
16878a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * It will try to be tolerant of broken syntax instead of
16978a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     * returning an error.
17078a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh     */
17178a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh    public static Rfc822Token[] tokenize(CharSequence text) {
17278a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh        ArrayList<Rfc822Token> out = new ArrayList<Rfc822Token>();
17378a5b8106a4768f7a67e5a29c58f23cd95f6c47cDebajit Ghosh        tokenize(text, out);
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return out.toArray(new Rfc822Token[out.size()]);
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void crunch(StringBuilder sb) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = sb.length();
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < len) {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = sb.charAt(i);
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '\0') {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (i == 0 || i == len - 1 ||
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i - 1) == ' ' ||
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i - 1) == '\0' ||
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i + 1) == ' ' ||
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.charAt(i + 1) == '\0') {
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.deleteCharAt(i);
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    len--;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (i = 0; i < len; i++) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (sb.charAt(i) == '\0') {
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.setCharAt(i, ' ');
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@inheritDoc}
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int findTokenStart(CharSequence text, int cursor) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * It's hard to search backward, so search forward until
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * we reach the cursor.
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int best = 0;
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < cursor) {
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            i = findTokenEnd(text, i);
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i < cursor) {
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++; // Skip terminating punctuation
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < cursor && text.charAt(i) == ' ') {
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (i < cursor) {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    best = i;
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return best;
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@inheritDoc}
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int findTokenEnd(CharSequence text, int cursor) {
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = cursor;
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i < len) {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = text.charAt(i);
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ',' || c == ';') {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '"') {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < len) {
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '"') {
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
259114f98a75334813fe116da3d95567db8984d45b9Mattias Niklewski                    } else if (c == '\\' && i + 1 < len) {
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '(') {
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int level = 1;
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < len && level > 0) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    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                        level++;
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
278114f98a75334813fe116da3d95567db8984d45b9Mattias Niklewski                    } else if (c == '\\' && i + 1 < len) {
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += 2;
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '<') {
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i < len) {
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    c = text.charAt(i);
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (c == '>') {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i++;
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return i;
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Terminates the specified address with a comma and space.
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This assumes that the specified text already has valid syntax.
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The Adapter subclass's convertToString() method must make that
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * guarantee.
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public CharSequence terminateToken(CharSequence text) {
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return text + ", ";
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
315