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 */
39490556a764a879cd0eaff358e90705cc1335c92eErik@Deprecated
40823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpublic class Rfc822Validator implements AutoCompleteTextView.Validator {
41823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /*
42823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we
43823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * want to make sure we will keep accepting email addresses with TLD's
44823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * that don't exist at the time of this writing, so this regexp relaxes
45823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * that constraint by accepting any kind of top level domain, not just
46823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * ".com", ".fr", etc...
47823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
48823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private static final Pattern EMAIL_ADDRESS_PATTERN =
49b4f5e0e5755938f2be9a2f1a1a6609b018c530adPaul Westbrook            Pattern.compile("[^\\s@]+@([^\\s@\\.]+\\.)+[a-zA-z][a-zA-Z][a-zA-Z]*");
50823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
51823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private String mDomain;
52490556a764a879cd0eaff358e90705cc1335c92eErik    private boolean mRemoveInvalid = false;
53823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
54823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
55823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * Constructs a new validator that uses the specified domain name as
56823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * the default when none is specified.
57823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
58823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public Rfc822Validator(String domain) {
59823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        mDomain = domain;
60823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
61823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
62823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
63823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * {@inheritDoc}
64823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
65823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public boolean isValid(CharSequence text) {
66823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
67823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
68823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return tokens.length == 1 &&
69823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang               EMAIL_ADDRESS_PATTERN.
70823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                   matcher(tokens[0].getAddress()).matches();
71823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
72823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
73823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
74490556a764a879cd0eaff358e90705cc1335c92eErik     * Specify if the validator should remove invalid tokens instead of trying
75490556a764a879cd0eaff358e90705cc1335c92eErik     * to fix them. This can be used to strip results of incorrectly formatted
76490556a764a879cd0eaff358e90705cc1335c92eErik     * tokens.
77490556a764a879cd0eaff358e90705cc1335c92eErik     *
78490556a764a879cd0eaff358e90705cc1335c92eErik     * @param remove true to remove tokens with the wrong format, false to
79490556a764a879cd0eaff358e90705cc1335c92eErik     *            attempt to fix them
80490556a764a879cd0eaff358e90705cc1335c92eErik     */
81490556a764a879cd0eaff358e90705cc1335c92eErik    public void setRemoveInvalid(boolean remove) {
82490556a764a879cd0eaff358e90705cc1335c92eErik        mRemoveInvalid = remove;
83490556a764a879cd0eaff358e90705cc1335c92eErik    }
84490556a764a879cd0eaff358e90705cc1335c92eErik
85490556a764a879cd0eaff358e90705cc1335c92eErik    /**
86823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * @return a string in which all the characters that are illegal for the username
87823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * or the domain name part of the email address have been removed.
88823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
89823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private String removeIllegalCharacters(String s) {
90823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        StringBuilder result = new StringBuilder();
91823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        int length = s.length();
92823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        for (int i = 0; i < length; i++) {
93823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            char c = s.charAt(i);
94823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
95823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            /*
96823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * An RFC822 atom can contain any ASCII printing character
97823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * except for periods and any of the following punctuation.
98823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * A local-part can contain multiple atoms, concatenated by
99823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             * periods, so do allow periods here.
100823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang             */
101823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
102823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (c <= ' ' || c > '~') {
103823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                continue;
104823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
105823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
106823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (c == '(' || c == ')' || c == '<' || c == '>' ||
107823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                c == '@' || c == ',' || c == ';' || c == ':' ||
108823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                c == '\\' || c == '"' || c == '[' || c == ']') {
109823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                continue;
110823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
111823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
112823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            result.append(c);
113823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        }
114823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return result.toString();
115823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
116823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
117823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
118823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * {@inheritDoc}
119823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
120823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public CharSequence fixText(CharSequence cs) {
121823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Return an empty string if the email address only contains spaces, \n or \t
122823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        if (TextUtils.getTrimmedLength(cs) == 0) return "";
123823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
124823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
125823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        StringBuilder sb = new StringBuilder();
126823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
127823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        for (int i = 0; i < tokens.length; i++) {
128823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            String text = tokens[i].getAddress();
129490556a764a879cd0eaff358e90705cc1335c92eErik
130490556a764a879cd0eaff358e90705cc1335c92eErik            if (mRemoveInvalid && !isValid(text)) {
131490556a764a879cd0eaff358e90705cc1335c92eErik                continue;
132490556a764a879cd0eaff358e90705cc1335c92eErik            }
133823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            int index = text.indexOf('@');
134823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (index < 0) {
135490556a764a879cd0eaff358e90705cc1335c92eErik                // append the domain of the account if it exists
136490556a764a879cd0eaff358e90705cc1335c92eErik                if (mDomain != null) {
137490556a764a879cd0eaff358e90705cc1335c92eErik                    tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
138490556a764a879cd0eaff358e90705cc1335c92eErik                }
139823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            } else {
140823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                // Otherwise, remove the illegal characters on both sides of the '@'
141823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                String fix = removeIllegalCharacters(text.substring(0, index));
142490556a764a879cd0eaff358e90705cc1335c92eErik                if (TextUtils.isEmpty(fix)) {
143490556a764a879cd0eaff358e90705cc1335c92eErik                    // if the address is empty after removing invalid chars
144490556a764a879cd0eaff358e90705cc1335c92eErik                    // don't use it
145490556a764a879cd0eaff358e90705cc1335c92eErik                    continue;
146490556a764a879cd0eaff358e90705cc1335c92eErik                }
147823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                String domain = removeIllegalCharacters(text.substring(index + 1));
148490556a764a879cd0eaff358e90705cc1335c92eErik                boolean emptyDomain = domain.length() == 0;
149490556a764a879cd0eaff358e90705cc1335c92eErik                if (!emptyDomain || mDomain != null) {
150490556a764a879cd0eaff358e90705cc1335c92eErik                    tokens[i].setAddress(fix + "@" + (!emptyDomain ? domain : mDomain));
151490556a764a879cd0eaff358e90705cc1335c92eErik                }
152823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
153823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
154823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            sb.append(tokens[i].toString());
155823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            if (i + 1 < tokens.length) {
156823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                sb.append(", ");
157823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang            }
158823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        }
159823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
160823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return sb;
161823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
162823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang}
163