1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.validator.routines; 18 19 import java.io.Serializable; 20 import java.util.Arrays; 21 import java.util.Locale; 22 23 import org.apache.commons.validator.routines.checkdigit.ISINCheckDigit; 24 25 /** 26 * <b>ISIN</b> (International Securities Identifying Number) validation. 27 * 28 * <p> 29 * ISIN Numbers are 12 character alphanumeric codes used to identify Securities. 30 * </p> 31 * 32 * <p> 33 * ISINs consist of two alphabetic characters, 34 * which are the ISO 3166-1 alpha-2 code for the issuing country, 35 * nine alpha-numeric characters (the National Securities Identifying Number, or NSIN, which identifies the security), 36 * and one numerical check digit. 37 * They are 12 characters in length. 38 * </p> 39 * 40 * <p> 41 * See <a href="http://en.wikipedia.org/wiki/ISIN">Wikipedia - ISIN</a> 42 * for more details. 43 * </p> 44 * 45 * @since 1.7 46 */ 47 public class ISINValidator implements Serializable { 48 49 private static final long serialVersionUID = -5964391439144260936L; 50 51 private static final String ISIN_REGEX = "([A-Z]{2}[A-Z0-9]{9}[0-9])"; 52 53 private static final CodeValidator VALIDATOR = new CodeValidator(ISIN_REGEX, 12, ISINCheckDigit.ISIN_CHECK_DIGIT); 54 55 /** ISIN Code Validator (no countryCode check) */ 56 private static final ISINValidator ISIN_VALIDATOR_FALSE = new ISINValidator(false); 57 58 /** ISIN Code Validator (with countryCode check) */ 59 private static final ISINValidator ISIN_VALIDATOR_TRUE = new ISINValidator(true); 60 61 private static final String [] CCODES = Locale.getISOCountries(); 62 63 private static final String [] SPECIALS = { 64 "EZ", // http://www.anna-web.org/standards/isin-iso-6166/ 65 "XS", // https://www.isin.org/isin/ 66 }; 67 68 static { 69 Arrays.sort(CCODES); // we cannot assume the codes are sorted 70 Arrays.sort(SPECIALS); // Just in case ... 71 } 72 73 /** 74 * Return a singleton instance of the ISIN validator 75 * @param checkCountryCode whether to check the country-code prefix or not 76 * @return A singleton instance of the appropriate ISIN validator. 77 */ 78 public static ISINValidator getInstance(final boolean checkCountryCode) { 79 return checkCountryCode ? ISIN_VALIDATOR_TRUE : ISIN_VALIDATOR_FALSE; 80 } 81 82 private final boolean checkCountryCode; 83 84 private ISINValidator(final boolean checkCountryCode) { 85 this.checkCountryCode = checkCountryCode; 86 } 87 88 private boolean checkCode(final String code) { 89 return Arrays.binarySearch(CCODES, code) >= 0 90 || 91 Arrays.binarySearch(SPECIALS, code) >= 0 92 ; 93 } 94 95 /** 96 * Check the code is a valid ISIN code after any transformation 97 * by the validate routine. 98 * @param code The code to validate. 99 * @return {@code true} if a valid ISIN 100 * code, otherwise {@code false}. 101 */ 102 public boolean isValid(final String code) { 103 final boolean valid = VALIDATOR.isValid(code); 104 if (valid && checkCountryCode) { 105 return checkCode(code.substring(0,2)); 106 } 107 return valid; 108 } 109 110 /** 111 * Check the code is valid ISIN code. 112 * 113 * @param code The code to validate. 114 * @return A valid ISIN code if valid, otherwise <code>null</code>. 115 */ 116 public Object validate(final String code) { 117 final Object validate = VALIDATOR.validate(code); 118 if (validate != null && checkCountryCode) { 119 return checkCode(code.substring(0,2)) ? validate : null; 120 } 121 return validate; 122 } 123 124 }