/*
* USPostalCodeService.java
* Copyright (C) 2006 Amin Ahmad
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.ahmadsoft.postal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Provides a postal code service implementation for the United
* States of America.
*
* @author Amin Ahmad
*/
public class USPostalCodeService {
private PostalRetrievalStrategy retrievalStrategy;
private static boolean firstUse = true;
public USPostalCodeService() {
if (firstUse) {
firstUse = false;
System.out.println(
"U.S. Postal Code Service for Java version 1, Copyright (C) 2006 Amin Ahmad\n\n"+
"U.S. Postal Code Service for Java comes with ABSOLUTELY NO WARRANTY;\n"+
"This program is free software; you can redistribute it and/or modify\n"+
"it under the terms of the GNU General Public License as published by\n"+
"the Free Software Foundation; either version 2 of the License, or\n"+
"any later version."
);
}
}
/**
* Disposes an instance of this object. Subsequent use of this
* object is a logical programming error.
* @throws Exception
*/
public void dispose() throws Exception {
}
/**
* Initializes an instance of the US Postal Code Service. Initialization
* is required prior to use.
*
* @param retrievalStrategy an initialized postal retrieval strategy.
* @throws Exception if an error occurs during initialization. Renders
* this service instance unusable.
*/
public void initialize(PostalRetrievalStrategy retrievalStrategy) throws Exception {
this.retrievalStrategy = retrievalStrategy;
}
/**
* Returns a list of all recognized candidate cities for a given postal
* code. Candidate cities are catogorized as actual, acceptable, and
* unacceptable.
*
* @param postalCode the postal code.
* @return a list of all recognized candidate cities for a given postal
* code.
*/
public List getCandidates(int postalCode) {
return retrievalStrategy.getCandidates(postalCode);
}
/**
* Returns true
if the given postal code is within
* the given state, or false
otherwise. For
* example, isPostalCodeIn(85050, "AZ")
will return true
* because the 85050 postal code is within Arizona, but
* isPostalCodeIn(43202, "CA")
will return false
* because the 43202 is not in California, but rather in Ohio.
*
* @param postalCode the postal code.
* @param stateAbbr the two-digit, upper-case abbreviation for
* a state, as specified in
* United States Postal Service - Abbreviations.
* @return true
if the given postal code is within
* the given state, or false
otherwise.
*/
public boolean isPostalCodeIn(int postalCode, String stateAbbr) {
List candidates = this.retrievalStrategy.getCandidates(postalCode);
for (Iterator i=candidates.iterator();i.hasNext();) {
PostalCodeEntry entry = (PostalCodeEntry) i.next();
if (entry.getState().equals(stateAbbr)) {
return true;
}
}
return false;
}
/**
* Returns the PostalCodeEntry
for the actual city
* registered with the U.S. Post Office for this postal code. This
* is a useful operation because every postal code has an official
* city name associated with it, as well as several other names
* recognized by the post office as acceptable or unacceptable.
*
* For example, getActualFor(90064)
will return a
* PostalCodeEntry
for Los Angeles, CA, which is
* the official city for the 90064 postal code. Rancho Park, CA
* is an acceptable, but not the actual, name.
*
* @param postalCode
* @return the PostalCodeEntry
for the actual city
* registered with the U.S. Post Office for this postal code, or
* null
if there is no "actual" candidate for this postal code.
* This may occur if the postal code is not yet assigned, or is
* out of range.
*/
public PostalCodeEntry getActualFor(int postalCode) {
List candidates = this.retrievalStrategy.getCandidates(postalCode);
for (Iterator i=candidates.iterator();i.hasNext();) {
PostalCodeEntry entry = (PostalCodeEntry) i.next();
if (entry.getEntryType() == PostalCodeConstants.CITY_ACTUAL) {
return entry;
}
}
return null;
}
/**
* Performs a match using default matching options well-suited
* to common validation. Specifically, ignore capitalization is
* true
, ignore punctuation is true
,
* ignore whitespace is false
, and the minimum match
* level is PostalCodeConstants.CITY_ACCEPTABLE
.
*
* @param city the city to match.
* @param postalCode the postal code within which to match the city.
* @return the closest matches to the specified city within the specified
* postal code.
* @see #match(String, int, MatchOptions)
*/
public List match(String city, int postalCode) {
return match(city, postalCode, new MatchOptions(true, true, false, PostalCodeConstants.CITY_ACCEPTABLE));
}
/**
* Returns a list of the closest matches to the specified city
* within the specified postal code. The details of the matching
* process can be controlled by specifying match options.
*
* Note: This method does not take the state into consideration. Rather,
* use #isPostalCodeIn(int, String) to determine if a postal code
* is within a given state.
*
* @param city the city to match.
* @param postalCode the postal code within which to match the city.
* @param options parameters to control the matching process.
* @return a list of the closest matches to the specified city within
* the specfied postal code.
* @see MatchOptions
*/
public List match(String city, int postalCode, MatchOptions options) {
List results = new ArrayList();
List candidates = getCandidates(postalCode);
StringBuffer sbCity = new StringBuffer(city.length());
for (int j=0; jtrue
is capitalization should be ignored when computing
* the distance between two names, and false
otherwise. For example,
* if the value were true, then the distace between Dallas and dallas would be
* zero, but if the value were false, then the distace would be one.
*
* @return true
is capitalization should be ignored when computing
* the distance between two names, and false
otherwise.
*/
public boolean isIgnoreCapitalization() {
return ignoreCapitalization;
}
/**
* Returns true
if punctuation should be ignored when computing the
* distance between two names, and false
otherwise. The
* punctuation characters are defined as follows:
* `~!@#$%^&*()_-+=[{]}\|;:'",<.>/?
*
* @return true
if punctuation should be ignored when
* computing the distance between two names, and false
* otherwise.
*/
public boolean isIgnorePunctuation() {
return ignorePunctuation;
}
/**
* Returns true
if whitespace should be ignored when computing the
* distance between two names, and false otherwise
. If, for
* example, the value were true, then the match distance between
* LOSANGELES and LOS ANGELES would be one, and if the value were false,
* the distance would be zero.
*
* @return true
if whitespace should be ignored when computing the
* distance between two names, and false otherwise
.
*/
public boolean isIgnoreWhitespace() {
return ignoreWhitespace;
}
/**
* Returns the maximum match level for a match operation. Only cities within a postal
* code that meet the maximum match level are considered for inclusion in the results.
* Note that CITY_ACTUAL
< CITY_ACCEPTABLE
<
* CITY_UNACCEPTABLE
.
*
* @return the minimum match level for a match operation.
*/
public int getMaxMatchLevel() {
return maxMatchLevel;
}
}
/**
* The result of a matching operation.
* @author Amin Ahmad
*/
public static class MatchResult {
public int distance;
public PostalCodeEntry match;
public MatchResult(PostalCodeEntry match, int distance) {
super();
this.match = match;
this.distance = distance;
}
public String toString() {
return match + ", distance = " + distance;
}
}
}