View Javadoc
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 }