1 /*
2  * Copyright (C) 2009 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 package com.android.providers.contacts;
17 
18 import android.content.ContentValues;
19 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
20 import android.text.TextUtils;
21 
22 import java.util.Locale;
23 
24 /**
25  * Split and join {@link StructuredPostal} fields.
26  */
27 public class PostalSplitter {
28     private static final String JAPANESE_LANGUAGE = Locale.JAPANESE.getLanguage().toLowerCase();
29 
30     private final Locale mLocale;
31 
32     public static class Postal {
33         public String street;
34         public String pobox;
35         public String neighborhood;
36         public String city;
37         public String region;
38         public String postcode;
39         public String country;
40 
fromValues(ContentValues values)41         public void fromValues(ContentValues values) {
42             street = values.getAsString(StructuredPostal.STREET);
43             pobox = values.getAsString(StructuredPostal.POBOX);
44             neighborhood = values.getAsString(StructuredPostal.NEIGHBORHOOD);
45             city = values.getAsString(StructuredPostal.CITY);
46             region = values.getAsString(StructuredPostal.REGION);
47             postcode = values.getAsString(StructuredPostal.POSTCODE);
48             country = values.getAsString(StructuredPostal.COUNTRY);
49         }
50 
toValues(ContentValues values)51         public void toValues(ContentValues values) {
52             values.put(StructuredPostal.STREET, street);
53             values.put(StructuredPostal.POBOX, pobox);
54             values.put(StructuredPostal.NEIGHBORHOOD, neighborhood);
55             values.put(StructuredPostal.CITY, city);
56             values.put(StructuredPostal.REGION, region);
57             values.put(StructuredPostal.POSTCODE, postcode);
58             values.put(StructuredPostal.COUNTRY, country);
59         }
60     }
61 
PostalSplitter(Locale locale)62     public PostalSplitter(Locale locale) {
63         mLocale = locale;
64     }
65 
66     /**
67      * Parses {@link StructuredPostal#FORMATTED_ADDRESS} and returns parsed
68      * components in the {@link Postal} object.
69      */
split(Postal postal, String formattedAddress)70     public void split(Postal postal, String formattedAddress) {
71         if (!TextUtils.isEmpty(formattedAddress)) {
72             postal.street = formattedAddress;
73         }
74     }
75 
76     private static final String NEWLINE = "\n";
77     private static final String COMMA = ",";
78     private static final String SPACE = " ";
79 
80     /**
81      * Flattens the given {@link Postal} into a single field, usually for
82      * storage in {@link StructuredPostal#FORMATTED_ADDRESS}.
83      */
join(Postal postal)84     public String join(Postal postal) {
85         final String[] values = new String[] {
86                 postal.street, postal.pobox, postal.neighborhood, postal.city,
87                 postal.region, postal.postcode, postal.country
88         };
89         // TODO: split off to handle various locales
90         if (mLocale != null &&
91                 JAPANESE_LANGUAGE.equals(mLocale.getLanguage()) &&
92                 !arePrintableAsciiOnly(values)) {
93             return joinJaJp(postal);
94         } else {
95             return joinEnUs(postal);
96         }
97     }
98 
joinJaJp(final Postal postal)99     private String joinJaJp(final Postal postal) {
100         final boolean hasStreet = !TextUtils.isEmpty(postal.street);
101         final boolean hasPobox = !TextUtils.isEmpty(postal.pobox);
102         final boolean hasNeighborhood = !TextUtils.isEmpty(postal.neighborhood);
103         final boolean hasCity = !TextUtils.isEmpty(postal.city);
104         final boolean hasRegion = !TextUtils.isEmpty(postal.region);
105         final boolean hasPostcode = !TextUtils.isEmpty(postal.postcode);
106         final boolean hasCountry = !TextUtils.isEmpty(postal.country);
107 
108         // First block: [country][ ][postcode]
109         // Second block: [region][ ][city][ ][neighborhood]
110         // Third block: [street][ ][pobox]
111 
112         final StringBuilder builder = new StringBuilder();
113 
114         final boolean hasFirstBlock = hasCountry || hasPostcode;
115         final boolean hasSecondBlock = hasRegion || hasCity || hasNeighborhood;
116         final boolean hasThirdBlock = hasStreet || hasPobox;
117 
118         if (hasFirstBlock) {
119             if (hasCountry) {
120                 builder.append(postal.country);
121             }
122             if (hasPostcode) {
123                 if (hasCountry) builder.append(SPACE);
124                 builder.append(postal.postcode);
125             }
126         }
127 
128         if (hasSecondBlock) {
129             if (hasFirstBlock) {
130                 builder.append(NEWLINE);
131             }
132             if (hasRegion) {
133                 builder.append(postal.region);
134             }
135             if (hasCity) {
136                 if (hasRegion) builder.append(SPACE);
137                 builder.append(postal.city);
138             }
139             if (hasNeighborhood) {
140                 if (hasRegion || hasCity) builder.append(SPACE);
141                 builder.append(postal.neighborhood);
142             }
143         }
144 
145         if (hasThirdBlock) {
146             if (hasFirstBlock || hasSecondBlock) {
147                 builder.append(NEWLINE);
148             }
149             if (hasStreet) {
150                 builder.append(postal.street);
151             }
152             if (hasPobox) {
153                 if (hasStreet) builder.append(SPACE);
154                 builder.append(postal.pobox);
155             }
156         }
157 
158         if (builder.length() > 0) {
159             return builder.toString();
160         } else {
161             return null;
162         }
163     }
164 
joinEnUs(final Postal postal)165     private String joinEnUs(final Postal postal) {
166         final boolean hasStreet = !TextUtils.isEmpty(postal.street);
167         final boolean hasPobox = !TextUtils.isEmpty(postal.pobox);
168         final boolean hasNeighborhood = !TextUtils.isEmpty(postal.neighborhood);
169         final boolean hasCity = !TextUtils.isEmpty(postal.city);
170         final boolean hasRegion = !TextUtils.isEmpty(postal.region);
171         final boolean hasPostcode = !TextUtils.isEmpty(postal.postcode);
172         final boolean hasCountry = !TextUtils.isEmpty(postal.country);
173 
174         // First block: [street][\n][pobox][\n][neighborhood]
175         // Second block: [city][, ][region][ ][postcode]
176         // Third block: [country]
177 
178         final StringBuilder builder = new StringBuilder();
179 
180         final boolean hasFirstBlock = hasStreet || hasPobox || hasNeighborhood;
181         final boolean hasSecondBlock = hasCity || hasRegion || hasPostcode;
182         final boolean hasThirdBlock = hasCountry;
183 
184         if (hasFirstBlock) {
185             if (hasStreet) {
186                 builder.append(postal.street);
187             }
188             if (hasPobox) {
189                 if (hasStreet) builder.append(NEWLINE);
190                 builder.append(postal.pobox);
191             }
192             if (hasNeighborhood) {
193                 if (hasStreet || hasPobox) builder.append(NEWLINE);
194                 builder.append(postal.neighborhood);
195             }
196         }
197 
198         if (hasSecondBlock) {
199             if (hasFirstBlock) {
200                 builder.append(NEWLINE);
201             }
202             if (hasCity) {
203                 builder.append(postal.city);
204             }
205             if (hasRegion) {
206                 if (hasCity) builder.append(COMMA + SPACE);
207                 builder.append(postal.region);
208             }
209             if (hasPostcode) {
210                 if (hasCity || hasRegion) builder.append(SPACE);
211                 builder.append(postal.postcode);
212             }
213         }
214 
215         if (hasThirdBlock) {
216             if (hasFirstBlock || hasSecondBlock) {
217                 builder.append(NEWLINE);
218             }
219             if (hasCountry) {
220                 builder.append(postal.country);
221             }
222         }
223 
224         if (builder.length() > 0) {
225             return builder.toString();
226         } else {
227             return null;
228         }
229     }
230 
arePrintableAsciiOnly(final String[] values)231     private static boolean arePrintableAsciiOnly(final String[] values) {
232         if (values == null) {
233             return true;
234         }
235         for (final String value : values) {
236             if (TextUtils.isEmpty(value)) {
237                 continue;
238             }
239             if (!TextUtils.isPrintableAsciiOnly(value)) {
240                 return false;
241             }
242         }
243         return true;
244     }
245 }
246