/** * Copyright 2010 Wealthfront 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.kaching.platform.common.values; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.Integer.parseInt; import static java.lang.String.format; import com.kaching.platform.common.AbstractIdentifier; /** * A U.S. Social Security number, as issued to an individual by the Social * Security Administration. */ public class Ssn extends AbstractIdentifier<String> { private static final long serialVersionUID = -7832881252708349697L; public Ssn(String ssn) { super(validate(ssn.replaceAll("-", ""))); } /** * Performs simple validation. Note that many fraudulent SSNs cannot easily * be detected using only publicly available information. The following * numbers are invalid: * <ol> * <li>Numbers with an area number between 734 and 749, or above 772</li> * <li>Numbers with all zeros in any digit group</li> * <li>Numbers of the form 666-xx-####</li> * <li>Numbers from 987-65-4320 to 987-65-4329</li> * </ol> * @throws IllegalArgumentException if the SSN is invalid * @return the untouched SSN, so it can be passed to the super class */ private static String validate(String ssn) { // Careful, checks must not leak the SSN in stack traces! checkArgument(ssn.length() == 9); try { parseInt(ssn); } catch (NumberFormatException e) { throw new IllegalArgumentException(); } String firstPart = firstPartOf(ssn); String secondPart = secondPartOf(ssn); String thirdPart = thirdPartOf(ssn); for (int i = 734; i <= 749; i++) { checkArgument(!firstPart.equals(String.valueOf(i))); } checkArgument(parseInt(firstPart) <= 772); checkArgument(!firstPart.equals("000")); checkArgument(!secondPart.equals("00")); checkArgument(!thirdPart.equals("0000")); checkArgument(!firstPart.equals("666")); for (int i = 0; i <= 9; i++) { checkArgument(!ssn.equals("98765432" + i)); } return ssn; } /** * Returns the first three digits, assigned by the geographical region. */ public String getAreaNumber() { return firstPartOf(getId()); } /** * Returns the middle two digits. The group numbers range from 01 to 99. */ public String getGroupNumber() { return secondPartOf(getId()); } /** * Returns the last four digits. They represent a straight numerical * sequence of digits from 0001 to 9999 within the group. */ public String getSerialNumber() { return thirdPartOf(getId()); } @Override public String toString() { return format("***-**-%s", getSerialNumber()); } private static String firstPartOf(String ssn) { return ssn.substring(0, 3); } private static String secondPartOf(String ssn) { return ssn.substring(3, 5); } private static String thirdPartOf(String ssn) { return ssn.substring(5, 9); } }