Rfc822Validator.java revision b4f5e0e5755938f2be9a2f1a1a6609b018c530ad
1823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang/*
2823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Copyright (C) 2008 The Android Open Source Project
3823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
4823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Licensed under the Apache License, Version 2.0 (the "License");
5823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * you may not use this file except in compliance with the License.
6823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * You may obtain a copy of the License at
7823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
8823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *      http://www.apache.org/licenses/LICENSE-2.0
9823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
10823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Unless required by applicable law or agreed to in writing, software
11823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * distributed under the License is distributed on an "AS IS" BASIS,
12823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * See the License for the specific language governing permissions and
14823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * limitations under the License.
15823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */
16823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
17823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpackage com.android.common;
18823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
19823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.text.TextUtils;
20823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.text.util.Rfc822Token;
21823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.text.util.Rfc822Tokenizer;
22823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.widget.AutoCompleteTextView;
23823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
24823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport java.util.regex.Pattern;
25823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
26823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang/**
27823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * This class works as a Validator for AutoCompleteTextView for
28823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * email addresses.  If a token does not appear to be a valid address,
29823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * it is trimmed of characters that cannot legitimately appear in one
30823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * and has the specified domain name added.  It is meant for use with
31823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * {@link Rfc822Token} and {@link Rfc822Tokenizer}.
32823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
33823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * @deprecated In the future make sure we don't quietly alter the user's
34823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *             text in ways they did not intend.  Meanwhile, hide this
35823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *             class from the public API because it does not even have
36823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *             a full understanding of the syntax it claims to correct.
37823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * @hide
38823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */
39823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpublic class Rfc822Validator implements AutoCompleteTextView.Validator {
40823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /*
41823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we
42823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * want to make sure we will keep accepting email addresses with TLD's
43823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * that don't exist at the time of this writing, so this regexp relaxes
44823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * that constraint by accepting any kind of top level domain, not just
45823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * ".com", ".fr", etc...
46823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
47823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private static final Pattern EMAIL_ADDRESS_PATTERN =
48b4f5e0e5755938f2be9a2f1a1a6609b018c530adPaul Westbrook            Pattern.compile("[^\\s@]+@([^\\s@\\.]+\\.)+[a-zA-z][a-zA-Z][a-zA-Z]*");
49823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
50823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private String mDomain;
51823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
52823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
53823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * Constructs a new validator that uses the specified domain name as
54823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * the default when none is specified.
55823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
56823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public Rfc822Validator(String domain) {
57823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        mDomain = domain;
58823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
59823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
60823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
61823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * {@inheritDoc}
62823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
63823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public boolean isValid(CharSequence text) {
64823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
65823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
66823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return tokens.length == 1 &&
67823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang               EMAIL_ADDRESS_PATTERN.
68823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                   matcher(tokens[0].getAddress()).matches();
69823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
70823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
71823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
72823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * @return a string in which all the characters that are illegal for the username
73823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * or the domain name part of the email address have been removed.
74823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
75823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private String removeIllegalCharacters(String s) {
76823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        StringBuilder result = new StringBuilder();
77823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        int length = s.length();
78823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        for (int i = 0; i < length; i++) {
79823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            char c = s.charAt(i);
80823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
81823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            /*
82823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * An RFC822 atom can contain any ASCII printing character
83823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * except for periods and any of the following punctuation.
84823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * A local-part can contain multiple atoms, concatenated by
85823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * periods, so do allow periods here.
86823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             */
87823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
88823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (c <= ' ' || c > '~') {
89823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                continue;
90823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
91823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
92823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (c == '(' || c == ')' || c == '<' || c == '>' ||
93823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                c == '@' || c == ',' || c == ';' || c == ':' ||
94823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                c == '\\' || c == '"' || c == '[' || c == ']') {
95823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                continue;
96823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
97823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
98823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            result.append(c);
99823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        }
100823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return result.toString();
101823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
102823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
103823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
104823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * {@inheritDoc}
105823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
106823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public CharSequence fixText(CharSequence cs) {
107823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Return an empty string if the email address only contains spaces, \n or \t
108823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        if (TextUtils.getTrimmedLength(cs) == 0) return "";
109823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
110823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
111823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        StringBuilder sb = new StringBuilder();
112823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
113823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        for (int i = 0; i < tokens.length; i++) {
114823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            String text = tokens[i].getAddress();
115823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            int index = text.indexOf('@');
116823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (index < 0) {
117823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                // If there is no @, just append the domain of the account
118823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
119823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            } else {
120823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                // Otherwise, remove the illegal characters on both sides of the '@'
121823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                String fix = removeIllegalCharacters(text.substring(0, index));
122823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                String domain = removeIllegalCharacters(text.substring(index + 1));
123823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                tokens[i].setAddress(fix + "@" + (domain.length() != 0 ? domain : mDomain));
124823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
125823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
126823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            sb.append(tokens[i].toString());
127823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (i + 1 < tokens.length) {
128823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                sb.append(", ");
129823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
130823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        }
131823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
132823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return sb;
133823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
134823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang}
135