blob: c81253777be967c343af44c83a13bedb35affb92 [file] [log] [blame]
/*
* 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 static com.android.i18n.addressinput.AddressField.ADMIN_AREA;
import static com.android.i18n.addressinput.AddressField.COUNTRY;
import static com.android.i18n.addressinput.AddressField.DEPENDENT_LOCALITY;
import static com.android.i18n.addressinput.AddressField.LOCALITY;
import static com.android.i18n.addressinput.AddressField.ORGANIZATION;
import static com.android.i18n.addressinput.AddressField.POSTAL_CODE;
import static com.android.i18n.addressinput.AddressField.RECIPIENT;
import static com.android.i18n.addressinput.AddressField.SORTING_CODE;
import static com.android.i18n.addressinput.AddressField.STREET_ADDRESS;
import com.android.i18n.addressinput.LookupKey.ScriptType;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Performs various consistency checks on an AddressData. This uses a {@link FieldVerifier} to check
* each field in the address.
*/
public class StandardAddressVerifier {
protected final FieldVerifier rootVerifier;
protected final VerifierRefiner refiner;
protected final Map<AddressField, List<AddressProblemType>> problemMap;
/**
* Uses the rootVerifier and {@link #DEFAULT_REFINER} to perform the standard checks on the
* address fields, as defined in {@link StandardChecks}.
*/
public StandardAddressVerifier(FieldVerifier rootVerifier) {
this(rootVerifier, DEFAULT_REFINER, StandardChecks.PROBLEM_MAP);
}
/**
* Uses the rootVerifier and the refiner to perform the standard checks on the address fields,
* as defined in {@link StandardChecks}.
*/
public StandardAddressVerifier(FieldVerifier rootVerifier, VerifierRefiner refiner) {
this(rootVerifier, refiner, StandardChecks.PROBLEM_MAP);
}
/**
* Uses the rootVerifier and {@link #DEFAULT_REFINER} to perform the given checks on the address
* fields. A reference to problemMap is maintained. It is not modified by this class, and should
* not be modified subsequent to this call.
*/
public StandardAddressVerifier(FieldVerifier rootVerifier,
Map<AddressField, List<AddressProblemType>> problemMap) {
this(rootVerifier, DEFAULT_REFINER, problemMap);
}
/**
* Uses the rootVerifier and the refiner to perform the given checks on the address fields. A
* reference to problemMap is maintained. It is not modified by this class, and should not be
* modified subsequent to this call.
*/
public StandardAddressVerifier(FieldVerifier rootVerifier, VerifierRefiner refiner,
Map<AddressField, List<AddressProblemType>> problemMap) {
this.rootVerifier = rootVerifier;
this.refiner = refiner;
this.problemMap = StandardChecks.PROBLEM_MAP;
}
public void verify(AddressData address, AddressProblems problems) {
FieldVerifier v = rootVerifier.refineVerifier(address.getPostalCountry());
VerifierRefiner r = refiner.newInstance();
ScriptType script = null;
//TODO: Remove this hack.
// Hack for now making all Script types LOCAL
if (address.getLanguageCode() != null) {
if (Util.isExplicitLatinScript(address.getLanguageCode())) {
script = ScriptType.LATIN;
} else {
script = ScriptType.LOCAL;
}
}
// The first four calls refine the verifier, so must come first, and in this
// order.
verifyField(script, v, COUNTRY, address.getPostalCountry(), problems);
verifyField(script, v, ADMIN_AREA, address.getAdministrativeArea(), problems);
verifyField(script, v, LOCALITY, address.getLocality(), problems);
verifyField(script, v, DEPENDENT_LOCALITY, address.getDependentLocality(), problems);
String street = Util.joinAndSkipNulls("\n", address.getAddressLine1(),
address.getAddressLine2());
if (address.getAdministrativeArea() != null) {
v = v.refineVerifier(address.getAdministrativeArea());
}
// remaining calls don't change the field verifier
verifyField(script, v, POSTAL_CODE, address.getPostalCode(), problems);
verifyField(script, v, STREET_ADDRESS, street, problems);
verifyField(script, v, SORTING_CODE, address.getSortingCode(), problems);
verifyField(script, v, ORGANIZATION, address.getOrganization(), problems);
verifyField(script, v, RECIPIENT, address.getRecipient(), problems);
postVerify(v, address, problems);
}
/**
* Hook to perform any final processing using the final verifier. Default does no additional
* verification.
*/
protected void postVerify(FieldVerifier verifier, AddressData address,
AddressProblems problems) {
}
/**
* Hook called by verify with each verifiable field, in order. Override to provide pre- or
* post-checks for all fields.
*/
protected boolean verifyField(LookupKey.ScriptType script,
FieldVerifier verifier, AddressField field, String value,
AddressProblems problems) {
Iterator<AddressProblemType> iter = getProblemIterator(field);
while (iter.hasNext()) {
AddressProblemType prob = iter.next();
if (!verifyProblemField(script, verifier, prob, field, value, problems)) {
return false;
}
}
return true;
}
/**
* Hook for on-the-fly modification of the problem list. Override to change the problems to
* check for a particular field. Generally, changing the problemMap passed to the constructor
* is a better approach.
*/
protected Iterator<AddressProblemType> getProblemIterator(AddressField field) {
List<AddressProblemType> list = problemMap.get(field);
if (list == null) {
list = Collections.emptyList();
}
return list.iterator();
}
/**
* Hook for adding special checks for particular problems and/or fields.
*/
protected boolean verifyProblemField(LookupKey.ScriptType script,
FieldVerifier verifier, AddressProblemType problem, AddressField field,
String datum, AddressProblems problems) {
return verifier.check(script, problem, field, datum, problems);
}
/**
* This gets called with the hierarchical fields COUNTRY, ADMIN_AREA, LOCALITY,
* DEPENDENT_LOCALITY in order, returning the refined verifier at each step.
*
* <p>The default implementation is stateless, and delegates to the verifier to do the
* refinement.
*/
public static class VerifierRefiner {
/**
* Refines the verifier. This delegates to the verifier to perform the refinement.
*/
public FieldVerifier refineVerifier(FieldVerifier v, AddressField field,
String subkey) {
return v.refineVerifier(subkey);
}
/**
* Returns a clean version of the refiner. Since this implementation is stateless, returns
* this.
*/
public VerifierRefiner newInstance() {
return this;
}
}
private static final VerifierRefiner DEFAULT_REFINER = new VerifierRefiner();
}