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 static com.android.i18n.addressinput.AddressField.ADMIN_AREA; 20import static com.android.i18n.addressinput.AddressField.COUNTRY; 21import static com.android.i18n.addressinput.AddressField.DEPENDENT_LOCALITY; 22import static com.android.i18n.addressinput.AddressField.LOCALITY; 23import static com.android.i18n.addressinput.AddressField.ORGANIZATION; 24import static com.android.i18n.addressinput.AddressField.POSTAL_CODE; 25import static com.android.i18n.addressinput.AddressField.RECIPIENT; 26import static com.android.i18n.addressinput.AddressField.SORTING_CODE; 27import static com.android.i18n.addressinput.AddressField.STREET_ADDRESS; 28 29import com.android.i18n.addressinput.LookupKey.ScriptType; 30 31import java.util.Collections; 32import java.util.Iterator; 33import java.util.List; 34import java.util.Map; 35 36/** 37 * Performs various consistency checks on an AddressData. This uses a {@link FieldVerifier} to check 38 * each field in the address. 39 */ 40public class StandardAddressVerifier { 41 42 protected final FieldVerifier mRootVerifier; 43 44 protected final VerifierRefiner mRefiner; 45 46 protected final Map<AddressField, List<AddressProblemType>> mProblemMap; 47 48 /** 49 * Uses the rootVerifier and {@link #DEFAULT_REFINER} to perform the standard checks on the 50 * address fields, as defined in {@link StandardChecks}. 51 */ 52 public StandardAddressVerifier(FieldVerifier rootVerifier) { 53 this(rootVerifier, DEFAULT_REFINER, StandardChecks.PROBLEM_MAP); 54 } 55 56 /** 57 * Uses the rootVerifier and the refiner to perform the standard checks on the address fields, 58 * as defined in {@link StandardChecks}. 59 */ 60 public StandardAddressVerifier(FieldVerifier rootVerifier, VerifierRefiner refiner) { 61 this(rootVerifier, refiner, StandardChecks.PROBLEM_MAP); 62 } 63 64 /** 65 * Uses the rootVerifier and {@link #DEFAULT_REFINER} to perform the given checks on the address 66 * fields. A reference to problemMap is maintained. It is not modified by this class, and should 67 * not be modified subsequent to this call. 68 */ 69 public StandardAddressVerifier(FieldVerifier rootVerifier, 70 Map<AddressField, List<AddressProblemType>> problemMap) { 71 this(rootVerifier, DEFAULT_REFINER, problemMap); 72 } 73 74 /** 75 * Uses the rootVerifier and the refiner to perform the given checks on the address fields. A 76 * reference to problemMap is maintained. It is not modified by this class, and should not be 77 * modified subsequent to this call. 78 */ 79 public StandardAddressVerifier(FieldVerifier rootVerifier, VerifierRefiner refiner, 80 Map<AddressField, List<AddressProblemType>> problemMap) { 81 mRootVerifier = rootVerifier; 82 mRefiner = refiner; 83 mProblemMap = StandardChecks.PROBLEM_MAP; 84 } 85 86 public void verify(AddressData address, AddressProblems problems) { 87 NotifyingListener listener = new NotifyingListener(this); 88 verifyAsync(address, problems, listener); 89 try { 90 listener.waitLoadingEnd(); 91 } catch (InterruptedException e) { 92 throw new RuntimeException(e); 93 } 94 } 95 96 public void verifyAsync(AddressData address, AddressProblems problems, 97 DataLoadListener listener) { 98 Thread verifier = new Thread(new Verifier(address, problems, listener)); 99 verifier.start(); 100 } 101 102 private class Verifier implements Runnable { 103 private AddressData mAddress; 104 private AddressProblems mProblems; 105 private DataLoadListener mListener; 106 107 Verifier(AddressData address, AddressProblems problems, DataLoadListener listener) { 108 mAddress = address; 109 mProblems = problems; 110 mListener = listener; 111 } 112 113 @Override 114 public void run() { 115 mListener.dataLoadingBegin(); 116 117 FieldVerifier v = mRootVerifier; 118 119 ScriptType script = null; 120 if (mAddress.getLanguageCode() != null) { 121 if (Util.isExplicitLatinScript(mAddress.getLanguageCode())) { 122 script = ScriptType.LATIN; 123 } else { 124 script = ScriptType.LOCAL; 125 } 126 } 127 128 // The first four calls refine the verifier, so must come first, and in this 129 // order. 130 verifyField(script, v, COUNTRY, mAddress.getPostalCountry(), mProblems); 131 if (mProblems.isEmpty()) { 132 v = v.refineVerifier(mAddress.getPostalCountry()); 133 verifyField(script, v, ADMIN_AREA, mAddress.getAdministrativeArea(), mProblems); 134 if (mProblems.isEmpty()) { 135 v = v.refineVerifier(mAddress.getAdministrativeArea()); 136 verifyField(script, v, LOCALITY, mAddress.getLocality(), mProblems); 137 if (mProblems.isEmpty()) { 138 v = v.refineVerifier(mAddress.getLocality()); 139 verifyField(script, v, DEPENDENT_LOCALITY, 140 mAddress.getDependentLocality(), mProblems); 141 if (mProblems.isEmpty()) { 142 v = v.refineVerifier(mAddress.getDependentLocality()); 143 } 144 } 145 } 146 } 147 148 String street = Util.joinAndSkipNulls("\n", mAddress.getAddressLine1(), 149 mAddress.getAddressLine2()); 150 151 // remaining calls don't change the field verifier 152 verifyField(script, v, POSTAL_CODE, mAddress.getPostalCode(), mProblems); 153 verifyField(script, v, STREET_ADDRESS, street, mProblems); 154 verifyField(script, v, SORTING_CODE, mAddress.getSortingCode(), mProblems); 155 verifyField(script, v, ORGANIZATION, mAddress.getOrganization(), mProblems); 156 verifyField(script, v, RECIPIENT, mAddress.getRecipient(), mProblems); 157 158 postVerify(v, mAddress, mProblems); 159 160 mListener.dataLoadingEnd(); 161 } 162 } 163 164 /** 165 * Hook to perform any final processing using the final verifier. Default does no additional 166 * verification. 167 */ 168 protected void postVerify(FieldVerifier verifier, AddressData address, 169 AddressProblems problems) { 170 } 171 172 /** 173 * Hook called by verify with each verifiable field, in order. Override to provide pre- or 174 * post-checks for all fields. 175 */ 176 protected boolean verifyField(LookupKey.ScriptType script, 177 FieldVerifier verifier, AddressField field, String value, 178 AddressProblems problems) { 179 Iterator<AddressProblemType> iter = getProblemIterator(field); 180 while (iter.hasNext()) { 181 AddressProblemType prob = iter.next(); 182 if (!verifyProblemField(script, verifier, prob, field, value, problems)) { 183 return false; 184 } 185 } 186 return true; 187 } 188 189 /** 190 * Hook for on-the-fly modification of the problem list. Override to change the problems to 191 * check for a particular field. Generally, changing the problemMap passed to the constructor 192 * is a better approach. 193 */ 194 protected Iterator<AddressProblemType> getProblemIterator(AddressField field) { 195 List<AddressProblemType> list = mProblemMap.get(field); 196 if (list == null) { 197 list = Collections.emptyList(); 198 } 199 return list.iterator(); 200 } 201 202 /** 203 * Hook for adding special checks for particular problems and/or fields. 204 */ 205 protected boolean verifyProblemField(LookupKey.ScriptType script, 206 FieldVerifier verifier, AddressProblemType problem, AddressField field, 207 String datum, AddressProblems problems) { 208 return verifier.check(script, problem, field, datum, problems); 209 } 210 211 /** 212 * This gets called with the hierarchical fields COUNTRY, ADMIN_AREA, LOCALITY, 213 * DEPENDENT_LOCALITY in order, returning the refined verifier at each step. 214 * 215 * <p>The default implementation is stateless, and delegates to the verifier to do the 216 * refinement. 217 */ 218 public static class VerifierRefiner { 219 220 /** 221 * Refines the verifier. This delegates to the verifier to perform the refinement. 222 */ 223 public FieldVerifier refineVerifier(FieldVerifier v, AddressField field, 224 String subkey) { 225 return v.refineVerifier(subkey); 226 } 227 228 /** 229 * Returns a clean version of the refiner. Since this implementation is stateless, returns 230 * this. 231 */ 232 public VerifierRefiner newInstance() { 233 return this; 234 } 235 } 236 237 private static final VerifierRefiner DEFAULT_REFINER = new VerifierRefiner(); 238} 239