1/*
2 * Copyright (C) 2010 Google Inc.
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 com.android.i18n.addressinput;
18
19import java.util.EnumMap;
20import java.util.EnumSet;
21import java.util.HashMap;
22import java.util.HashSet;
23import java.util.Map;
24
25/**
26 * Configuration Options that can be used by Address Display components for things like show/hide
27 * fields or make them readonly. By default, all the fields are visible and editable.
28 *
29 * <p>Also, provides the ability to add additional required fields, for e.g. {@link
30 * AddressField#RECIPIENT}.
31 */
32public class FormOptions {
33
34    private final String mBaseId;
35
36    private final EnumSet<AddressField> mHiddenFields;
37
38    private final EnumSet<AddressField> mReadonlyFields;
39
40    private final EnumSet<AddressField> mRequiredFields;
41
42    private final EnumMap<AddressField, String> mCustomLabels =
43            new EnumMap<AddressField, String>(AddressField.class);
44
45    private final Map<String, AddressField[]> mOverrideFieldOrder =
46            new HashMap<String, AddressField[]>();
47
48    private final EnumMap<AddressField, Integer> mMaxLengths =
49            new EnumMap<AddressField, Integer>(AddressField.class);
50
51    private final String mServerUrl;
52
53    private FormOptions(Builder builder) {
54        // copy values from builder
55        mBaseId = builder.mBaseId;
56        mHiddenFields = EnumSet.copyOf(builder.mHiddenFields);
57        mReadonlyFields = EnumSet.copyOf(builder.mReadonlyFields);
58        mRequiredFields = EnumSet.copyOf(builder.mRequiredFields);
59        mCustomLabels.putAll(builder.mCustomLabels);
60        mOverrideFieldOrder.putAll(builder.mOverrideFieldOrder);
61        mMaxLengths.putAll(builder.mMaxLengths);
62        mServerUrl = builder.mServerUrl;
63    }
64
65    /**
66     * Gets base ID of the address form. Default is "addressform".
67     */
68    String getBaseId() {
69        return mBaseId;
70    }
71
72    boolean isHidden(AddressField field) {
73        return mHiddenFields.contains(field);
74    }
75
76    boolean isReadonly(AddressField field) {
77        return mReadonlyFields.contains(field);
78    }
79
80    boolean isRequired(AddressField field) {
81        return mRequiredFields.contains(field);
82    }
83
84    EnumSet<AddressField> getRequiredFields() {
85        return mRequiredFields;
86    }
87
88    /**
89     * Gets the customized label for the {@code field}, or returns null if none.
90     */
91    String getCustomLabel(AddressField field) {
92        return mCustomLabels.get(field);
93    }
94
95    /**
96     * Gets the URL of the Address Data Server.
97     */
98    String getUrl() {
99        return mServerUrl;
100    }
101
102    /**
103     * Gets the overridden field orders with their corresponding region code. Returns null if field
104     * orders for {@code regionCode} is not specified.
105     */
106    AddressField[] getCustomFieldOrder(String regionCode) {
107        if (regionCode == null) {
108            throw new RuntimeException("regionCode cannot be null.");
109        }
110        return mOverrideFieldOrder.get(regionCode);
111    }
112
113    /**
114     * Gets the customized max length for the {@code field}, or null if none.
115     */
116    Integer getCustomMaxLength(AddressField field) {
117        return mMaxLengths.get(field);
118    }
119
120    /**
121     * Class to build the form, specifying the attributes for each field.
122     */
123    public static class Builder {
124
125        private String mBaseId = "addressform";
126
127        private final EnumSet<AddressField> mRequiredFields =
128                EnumSet.noneOf(AddressField.class);
129
130        private final EnumSet<AddressField> mHiddenFields =
131                EnumSet.noneOf(AddressField.class);
132
133        private final EnumSet<AddressField> mReadonlyFields =
134                EnumSet.noneOf(AddressField.class);
135
136        private final EnumMap<AddressField, String> mCustomLabels =
137                new EnumMap<AddressField, String>(AddressField.class);
138
139        private final Map<String, AddressField[]> mOverrideFieldOrder =
140                new HashMap<String, AddressField[]>();
141
142        private final EnumMap<AddressField, Integer> mMaxLengths =
143                new EnumMap<AddressField, Integer>(AddressField.class);
144
145        /**
146         * Uses the default server URL from CacheData.
147         */
148        private String mServerUrl = new CacheData().getUrl();
149
150        /**
151         * Sets the base ID of the address form.
152         */
153        public Builder baseId(String baseId) {
154            if (baseId == null) {
155                throw new RuntimeException("baseId cannot be null.");
156            }
157            mBaseId = baseId;
158            return this;
159        }
160
161        public Builder hide(AddressField field) {
162            if (field == null) {
163                throw new RuntimeException("AddressField field cannot be null.");
164            }
165            mHiddenFields.add(field);
166            return this;
167        }
168
169        /**
170         * Make a field read-only.
171         */
172        public Builder readonly(AddressField field) {
173            if (field == null) {
174                throw new RuntimeException("AddressField field cannot be null.");
175            }
176            mReadonlyFields.add(field);
177            return this;
178        }
179
180        /**
181         * Make a field required.
182         */
183        public Builder required(AddressField field) {
184            if (field == null) {
185                throw new RuntimeException("AddressField field cannot be null.");
186            }
187            mRequiredFields.add(field);
188            return this;
189        }
190
191        /**
192         * Customizes label for an {@code AddressField}.
193         */
194        public Builder customizeLabel(AddressField field, String label) {
195            if (field == null) {
196                throw new RuntimeException("AddressField field cannot be null.");
197            }
198            if (label == null) {
199                throw new RuntimeException("Label cannot be null.");
200            }
201            mCustomLabels.put(field, label);
202            return this;
203        }
204
205        /**
206         * Sets the field order for a region code. The order you set here will override the
207         * predefined one. For example, you can set field order for US to be first {@code
208         * AddressField#ORGANIZATION} then {@code AddressField#RECIPIENT}. Repeated address fields
209         * in {@code fields} are not allowed. Size of {@code fields} has to be larger than one.
210         * Input {@code fields} can be partial or even contain field not needed in the specified
211         * {@code regionCode}. For example, German addresses contain the following fields
212         * (in order):<br/>
213           {@link AddressField#RECIPIENT}, {@link AddressField#ORGANIZATION}, {@link
214         * AddressField#STREET_ADDRESS}, {@link AddressField#POSTAL_CODE}, {@link
215         * AddressField#LOCALITY}. <br/>
216         *
217         * <p>With the following call: <br/>
218         *
219         * customizeFieldOrder("DE", AddressField.ORGANIZATION, AddressField.RECIPIENT,
220         * AddressField.ADMIN_AREA);
221         *
222         * <p>Field order for Germany will become: <br/> {@link AddressField#ORGANIZATION}, {@link
223         * AddressField#RECIPIENT}, {@link AddressField#STREET_ADDRESS}, {@link
224         * AddressField#POSTAL_CODE}, {@link AddressField#LOCALITY}. </p>
225         *
226         * <p>Notice that:<br/> <ol> <li>{@link AddressField#ORGANIZATION} comes before {@link
227         * AddressField#RECIPIENT} after reordering.</li>
228         * <li>Fields not specified stays the same.</li>
229         * <li>{@link AddressField#ADMIN_AREA} is specified but since it is not in German address
230         * format, it is simpled neglected.</li> </ol>
231         *
232         * @param fields the overridden field order.
233         */
234        public Builder customizeFieldOrder(String regionCode, AddressField... fields) {
235            if (regionCode == null) {
236                throw new RuntimeException("regionCode cannot be null.");
237            }
238            if (fields == null) {
239                throw new RuntimeException("Fields cannot be null.");
240            }
241            if (fields.length <= 1) {
242                throw new RuntimeException("There must be more than one field.");
243            }
244            HashSet<AddressField> checkList = new HashSet<AddressField>();
245            AddressField[] f = new AddressField[fields.length];
246            int i = 0;
247            for (AddressField field : fields) {
248                // Can't contain repeated address fields.
249                if (checkList.contains(field)) {
250                    throw new RuntimeException("Address fields cannot be repeated.");
251                }
252                checkList.add(field);
253                f[i] = field;
254                i++;
255            }
256            mOverrideFieldOrder.put(regionCode, f);
257            return this;
258        }
259
260        /**
261         * Sets the URL of address data server. {@code url} cannot be null. This url will override
262         * the default address server url.
263         */
264        public Builder setUrl(String url) {
265            if (url == null) {
266                throw new RuntimeException("Can't set address server URL to null.");
267            }
268            mServerUrl = url;
269            return this;
270        }
271
272        /**
273         * Customizes max length for a {@code AddressField}.
274         */
275        public Builder customizeMaxLength(AddressField field, int maxLength) {
276            if (field == null) {
277                throw new RuntimeException("AddressField field cannot be null.");
278            }
279            mMaxLengths.put(field, maxLength);
280            return this;
281        }
282
283        public FormOptions build() {
284            return new FormOptions(this);
285        }
286    }
287}
288