aboutsummaryrefslogtreecommitdiff
path: root/java/src/com/android/i18n/addressinput/AddressVerificationData.java
blob: 58f4366b8cd205a640f8011becd356fa1a4b1df3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright (C) 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.i18n.addressinput;

import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Wraps a Map of address property data to provide the AddressVerificationData API.
 */
class AddressVerificationData implements DataSource {

    private final Map<String, String> mPropertiesMap;

    private static final Pattern KEY_VALUES_PATTERN = Pattern.compile("\"([^\"]+)\":\"([^\"]*)\"");

    private static final Pattern SEPARATOR_PATTERN = Pattern.compile("\",\"");

    /**
     * Constructs from a map of address property data.  This keeps a reference to the map.  This
     * does not mutate the map. The map should not be mutated subsequent to this call.
     */
    AddressVerificationData(Map<String, String> propertiesMap) {
        mPropertiesMap = propertiesMap;
    }

    @Override
    public AddressVerificationNodeData get(String key) {
        String json = mPropertiesMap.get(key);
        if (json != null && isValidKey(key)) {
            return createNodeData(json);
        }
        return null;
    }

    /**
     * Returns a set of the keys for which verification data is provided.  The returned set is
     * immutable.
     */
    Set<String> keys() {
        Set<String> result = new HashSet<String>();
        for (String key : mPropertiesMap.keySet()) {
            if (isValidKey(key)) {
                result.add(key);
            }
        }
        return Collections.unmodifiableSet(result);
    }

    /**
     * We can be initialized with the full set of address information, but validation only uses
     * info prefixed with "data" (in particular, no info prefixed with "examples").
     */
    private boolean isValidKey(String key) {
        return key.startsWith("data");
    }

    /**
     * Returns the contents of the JSON-format string as a map.
     */
    AddressVerificationNodeData createNodeData(String json) {
        // Remove leading and trailing { and }.
        json = json.substring(1, json.length() - 1);
        Map<AddressDataKey, String> map =
                new EnumMap<AddressDataKey, String>(AddressDataKey.class);

        // our objects are very simple so we parse manually
        // - no double quotes within strings
        // - no extra spaces
        // can't use split "," since some data has commas in it.
        Matcher sm = SEPARATOR_PATTERN.matcher(json);
        int pos = 0;
        while (pos < json.length()) {
            String pair;
            if (sm.find()) {
                pair = json.substring(pos, sm.start() + 1);
                pos = sm.start() + 2;
            } else {
                pair = json.substring(pos);
                pos = json.length();
            }

            Matcher m = KEY_VALUES_PATTERN.matcher(pair);
            if (m.matches()) {
                String value = m.group(2);

                // Remove escaped backslashes.
                // Java regex doesn't handle a replacement String consisting of
                // a single backslash, and treats a replacement String consisting of
                // two backslashes as two backslashes instead of one.  So there's
                // no way to use regex to replace a match with a single backslash,
                // apparently.
                if (value.length() > 0) {
                    char[] linechars = m.group(2).toCharArray();
                    int w = 1;
                    for (int r = w; r < linechars.length; ++r) {
                        char c = linechars[r];
                        if (c == '\\' && linechars[w - 1] == '\\') {
                            // don't increment w;
                            continue;
                        }
                        linechars[w++] = c;
                    }
                    value = new String(linechars, 0, w);
                }

                AddressDataKey df = AddressDataKey.get(m.group(1));
                if (df == null) {
                    // Skip this data - it isn't used in the Android version.
                } else {
                    map.put(df, value);
                }
            } else {
                // This is a runtime data sanity check.  The data should be
                // checked when the data is built.  The JSON data string should
                // be parsable into string pairs using SEP_PAT.
                throw new RuntimeException("could not match '" + pair + "' in '" + json + "'");
            }
        }

        return new AddressVerificationNodeData(map);
    }

    @Override
    public AddressVerificationNodeData getDefaultData(String key) {
        // gets country key
        if (key.split("/").length > 1) {
            String[] parts = key.split("/");
            key = parts[0] + "/" + parts[1];
        }

        AddressVerificationNodeData data = get(key);
        if (data == null) {
            throw new RuntimeException("failed to get default data with key " + key);
        }
        return data;
    }
}