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 */
16package com.android.providers.contacts;
17
18import android.content.ContentValues;
19import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
20import android.text.TextUtils;
21
22import java.util.Locale;
23
24/**
25 * Split and join {@link StructuredPostal} fields.
26 */
27public 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
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
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
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     */
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     */
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
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
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
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