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
17package android.text.util;
18
19import android.annotation.Nullable;
20
21/**
22 * This class stores an RFC 822-like name, address, and comment,
23 * and provides methods to convert them to quoted strings.
24 */
25public class Rfc822Token {
26    @Nullable
27    private String mName, mAddress, mComment;
28
29    /**
30     * Creates a new Rfc822Token with the specified name, address,
31     * and comment.
32     */
33    public Rfc822Token(@Nullable String name, @Nullable String address, @Nullable String comment) {
34        mName = name;
35        mAddress = address;
36        mComment = comment;
37    }
38
39    /**
40     * Returns the name part.
41     */
42    @Nullable
43    public String getName() {
44        return mName;
45    }
46
47    /**
48     * Returns the address part.
49     */
50    @Nullable
51    public String getAddress() {
52        return mAddress;
53    }
54
55    /**
56     * Returns the comment part.
57     */
58    @Nullable
59    public String getComment() {
60        return mComment;
61    }
62
63    /**
64     * Changes the name to the specified name.
65     */
66    public void setName(@Nullable String name) {
67        mName = name;
68    }
69
70    /**
71     * Changes the address to the specified address.
72     */
73    public void setAddress(@Nullable String address) {
74        mAddress = address;
75    }
76
77    /**
78     * Changes the comment to the specified comment.
79     */
80    public void setComment(@Nullable String comment) {
81        mComment = comment;
82    }
83
84    /**
85     * Returns the name (with quoting added if necessary),
86     * the comment (in parentheses), and the address (in angle brackets).
87     * This should be suitable for inclusion in an RFC 822 address list.
88     */
89    public String toString() {
90        StringBuilder sb = new StringBuilder();
91
92        if (mName != null && mName.length() != 0) {
93            sb.append(quoteNameIfNecessary(mName));
94            sb.append(' ');
95        }
96
97        if (mComment != null && mComment.length() != 0) {
98            sb.append('(');
99            sb.append(quoteComment(mComment));
100            sb.append(") ");
101        }
102
103        if (mAddress != null && mAddress.length() != 0) {
104            sb.append('<');
105            sb.append(mAddress);
106            sb.append('>');
107        }
108
109        return sb.toString();
110    }
111
112    /**
113     * Returns the name, conservatively quoting it if there are any
114     * characters that are likely to cause trouble outside of a
115     * quoted string, or returning it literally if it seems safe.
116     */
117    public static String quoteNameIfNecessary(String name) {
118        int len = name.length();
119
120        for (int i = 0; i < len; i++) {
121            char c = name.charAt(i);
122
123            if (! ((c >= 'A' && c <= 'Z') ||
124                   (c >= 'a' && c <= 'z') ||
125                   (c == ' ') ||
126                   (c >= '0' && c <= '9'))) {
127                return '"' + quoteName(name) + '"';
128            }
129        }
130
131        return name;
132    }
133
134    /**
135     * Returns the name, with internal backslashes and quotation marks
136     * preceded by backslashes.  The outer quote marks themselves are not
137     * added by this method.
138     */
139    public static String quoteName(String name) {
140        StringBuilder sb = new StringBuilder();
141
142        int len = name.length();
143        for (int i = 0; i < len; i++) {
144            char c = name.charAt(i);
145
146            if (c == '\\' || c == '"') {
147                sb.append('\\');
148            }
149
150            sb.append(c);
151        }
152
153        return sb.toString();
154    }
155
156    /**
157     * Returns the comment, with internal backslashes and parentheses
158     * preceded by backslashes.  The outer parentheses themselves are
159     * not added by this method.
160     */
161    public static String quoteComment(String comment) {
162        int len = comment.length();
163        StringBuilder sb = new StringBuilder();
164
165        for (int i = 0; i < len; i++) {
166            char c = comment.charAt(i);
167
168            if (c == '(' || c == ')' || c == '\\') {
169                sb.append('\\');
170            }
171
172            sb.append(c);
173        }
174
175        return sb.toString();
176    }
177
178    public int hashCode() {
179        int result = 17;
180        if (mName != null) result = 31 * result + mName.hashCode();
181        if (mAddress != null) result = 31 * result + mAddress.hashCode();
182        if (mComment != null) result = 31 * result + mComment.hashCode();
183        return result;
184    }
185
186    private static boolean stringEquals(String a, String b) {
187        if (a == null) {
188            return (b == null);
189        } else {
190            return (a.equals(b));
191        }
192    }
193
194    public boolean equals(Object o) {
195        if (!(o instanceof Rfc822Token)) {
196            return false;
197        }
198        Rfc822Token other = (Rfc822Token) o;
199        return (stringEquals(mName, other.mName) &&
200                stringEquals(mAddress, other.mAddress) &&
201                stringEquals(mComment, other.mComment));
202    }
203}
204
205