Coverage Report - org.apache.commons.validator.ValidatorResources
 
Classes in this File Line Coverage Branch Coverage Complexity
ValidatorResources
90%
144/160
79%
73/92
2.852
ValidatorResources$1
7%
1/13
0%
0/2
2.852
 
 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;
 18  
 
 19  
 import java.io.IOException;
 20  
 import java.io.InputStream;
 21  
 import java.io.Serializable;
 22  
 import java.net.URL;
 23  
 import java.util.Collections;
 24  
 import java.util.Iterator;
 25  
 import java.util.Locale;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.apache.commons.collections.FastHashMap; // DEPRECATED
 29  
 import org.apache.commons.digester.Digester;
 30  
 import org.apache.commons.digester.Rule;
 31  
 import org.apache.commons.digester.xmlrules.DigesterLoader;
 32  
 import org.apache.commons.logging.Log;
 33  
 import org.apache.commons.logging.LogFactory;
 34  
 import org.xml.sax.SAXException;
 35  
 import org.xml.sax.Attributes;
 36  
 
 37  
 /**
 38  
  * <p>
 39  
  * General purpose class for storing <code>FormSet</code> objects based
 40  
  * on their associated <code>Locale</code>.  Instances of this class are usually
 41  
  * configured through a validation.xml file that is parsed in a constructor.
 42  
  * </p>
 43  
  *
 44  
  * <p><strong>Note</strong> - Classes that extend this class
 45  
  * must be Serializable so that instances may be used in distributable
 46  
  * application server environments.</p>
 47  
  *
 48  
  * <p>
 49  
  * The use of FastHashMap is deprecated and will be replaced in a future
 50  
  * release.
 51  
  * </p>
 52  
  *
 53  
  * @version $Revision: 1739361 $
 54  
  */
 55  
 //TODO mutable non-private fields
 56  0
 public class ValidatorResources implements Serializable {
 57  
 
 58  
     private static final long serialVersionUID = -8203745881446239554L;
 59  
 
 60  
     /** Name of the digester validator rules file */
 61  
     private static final String VALIDATOR_RULES = "digester-rules.xml";
 62  
 
 63  
     /**
 64  
      * The set of public identifiers, and corresponding resource names, for
 65  
      * the versions of the configuration file DTDs that we know about.  There
 66  
      * <strong>MUST</strong> be an even number of Strings in this list!
 67  
      */
 68  1
     private static final String REGISTRATIONS[] = {
 69  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN",
 70  
         "/org/apache/commons/validator/resources/validator_1_0.dtd",
 71  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0.1//EN",
 72  
         "/org/apache/commons/validator/resources/validator_1_0_1.dtd",
 73  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN",
 74  
         "/org/apache/commons/validator/resources/validator_1_1.dtd",
 75  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN",
 76  
         "/org/apache/commons/validator/resources/validator_1_1_3.dtd",
 77  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.2.0//EN",
 78  
         "/org/apache/commons/validator/resources/validator_1_2_0.dtd",
 79  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN",
 80  
         "/org/apache/commons/validator/resources/validator_1_3_0.dtd",
 81  
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.4.0//EN",
 82  
         "/org/apache/commons/validator/resources/validator_1_4_0.dtd"
 83  
     };
 84  
 
 85  112
     private transient Log log = LogFactory.getLog(ValidatorResources.class);
 86  
 
 87  
     /**
 88  
      * <code>Map</code> of <code>FormSet</code>s stored under
 89  
      * a <code>Locale</code> key (expressed as a String).
 90  
      * @deprecated Subclasses should use getFormSets() instead.
 91  
      */
 92  112
     @Deprecated
 93  
     protected FastHashMap hFormSets = new FastHashMap(); // <String, FormSet>
 94  
 
 95  
     /**
 96  
      * <code>Map</code> of global constant values with
 97  
      * the name of the constant as the key.
 98  
      * @deprecated Subclasses should use getConstants() instead.
 99  
      */
 100  112
     @Deprecated
 101  
     protected FastHashMap hConstants = new FastHashMap(); // <String, String>
 102  
 
 103  
     /**
 104  
      * <code>Map</code> of <code>ValidatorAction</code>s with
 105  
      * the name of the <code>ValidatorAction</code> as the key.
 106  
      * @deprecated Subclasses should use getActions() instead.
 107  
      */
 108  112
     @Deprecated
 109  
     protected FastHashMap hActions = new FastHashMap(); // <String, ValidatorAction>
 110  
 
 111  
     /**
 112  
      * The default locale on our server.
 113  
      */
 114  1
     protected static Locale defaultLocale = Locale.getDefault();
 115  
 
 116  
     /**
 117  
      * Create an empty ValidatorResources object.
 118  
      */
 119  
     public ValidatorResources() {
 120  4
         super();
 121  4
     }
 122  
 
 123  
     /**
 124  
      * This is the default <code>FormSet</code> (without locale). (We probably don't need
 125  
      * the defaultLocale anymore.)
 126  
      */
 127  
     protected FormSet defaultFormSet;
 128  
 
 129  
     /**
 130  
      * Create a ValidatorResources object from an InputStream.
 131  
      *
 132  
      * @param in InputStream to a validation.xml configuration file.  It's the client's
 133  
      * responsibility to close this stream.
 134  
      * @throws SAXException if the validation XML files are not valid or well
 135  
      * formed.
 136  
      * @throws IOException if an I/O error occurs processing the XML files
 137  
      * @since Validator 1.1
 138  
      */
 139  
     public ValidatorResources(InputStream in) throws IOException, SAXException {
 140  96
         this(new InputStream[]{in});
 141  95
     }
 142  
 
 143  
     /**
 144  
      * Create a ValidatorResources object from an InputStream.
 145  
      *
 146  
      * @param streams An array of InputStreams to several validation.xml
 147  
      * configuration files that will be read in order and merged into this object.
 148  
      * It's the client's responsibility to close these streams.
 149  
      * @throws SAXException if the validation XML files are not valid or well
 150  
      * formed.
 151  
      * @throws IOException if an I/O error occurs processing the XML files
 152  
      * @since Validator 1.1
 153  
      */
 154  
     public ValidatorResources(InputStream[] streams)
 155  
             throws IOException, SAXException {
 156  
 
 157  106
         super();
 158  
 
 159  106
         Digester digester = initDigester();
 160  216
         for (int i = 0; i < streams.length; i++) {
 161  111
             if (streams[i] == null) {
 162  1
                 throw new IllegalArgumentException("Stream[" + i + "] is null");
 163  
             }
 164  110
             digester.push(this);
 165  110
             digester.parse(streams[i]);
 166  
         }
 167  
 
 168  105
         this.process();
 169  105
     }
 170  
 
 171  
     /**
 172  
      * Create a ValidatorResources object from an uri
 173  
      *
 174  
      * @param uri The location of a validation.xml configuration file.
 175  
      * @throws SAXException if the validation XML files are not valid or well
 176  
      * formed.
 177  
      * @throws IOException if an I/O error occurs processing the XML files
 178  
      * @since Validator 1.2
 179  
      */
 180  
     public ValidatorResources(String uri) throws IOException, SAXException {
 181  1
         this(new String[]{uri});
 182  1
     }
 183  
 
 184  
     /**
 185  
      * Create a ValidatorResources object from several uris
 186  
      *
 187  
      * @param uris An array of uris to several validation.xml
 188  
      * configuration files that will be read in order and merged into this object.
 189  
      * @throws SAXException if the validation XML files are not valid or well
 190  
      * formed.
 191  
      * @throws IOException if an I/O error occurs processing the XML files
 192  
      * @since Validator 1.2
 193  
      */
 194  
     public ValidatorResources(String[] uris)
 195  
             throws IOException, SAXException {
 196  
 
 197  1
         super();
 198  
 
 199  1
         Digester digester = initDigester();
 200  2
         for (int i = 0; i < uris.length; i++) {
 201  1
             digester.push(this);
 202  1
             digester.parse(uris[i]);
 203  
         }
 204  
 
 205  1
         this.process();
 206  1
     }
 207  
 
 208  
     /**
 209  
      * Create a ValidatorResources object from a URL.
 210  
      *
 211  
      * @param url The URL for the validation.xml
 212  
      * configuration file that will be read into this object.
 213  
      * @throws SAXException if the validation XML file are not valid or well
 214  
      * formed.
 215  
      * @throws IOException if an I/O error occurs processing the XML files
 216  
      * @since Validator 1.3.1
 217  
      */
 218  
     public ValidatorResources(URL url)
 219  
             throws IOException, SAXException {
 220  1
         this(new URL[]{url});
 221  1
     }
 222  
 
 223  
     /**
 224  
      * Create a ValidatorResources object from several URL.
 225  
      *
 226  
      * @param urls An array of URL to several validation.xml
 227  
      * configuration files that will be read in order and merged into this object.
 228  
      * @throws SAXException if the validation XML files are not valid or well
 229  
      * formed.
 230  
      * @throws IOException if an I/O error occurs processing the XML files
 231  
      * @since Validator 1.3.1
 232  
      */
 233  
     public ValidatorResources(URL[] urls)
 234  
             throws IOException, SAXException {
 235  
 
 236  1
         super();
 237  
 
 238  1
         Digester digester = initDigester();
 239  2
         for (int i = 0; i < urls.length; i++) {
 240  1
             digester.push(this);
 241  1
             digester.parse(urls[i]);
 242  
         }
 243  
 
 244  1
         this.process();
 245  1
     }
 246  
 
 247  
     /**
 248  
      *  Initialize the digester.
 249  
      */
 250  
     private Digester initDigester() {
 251  108
         URL rulesUrl = this.getClass().getResource(VALIDATOR_RULES);
 252  108
         if (rulesUrl == null) {
 253  
             // Fix for Issue# VALIDATOR-195
 254  0
             rulesUrl = ValidatorResources.class.getResource(VALIDATOR_RULES);
 255  
         }
 256  108
         if (getLog().isDebugEnabled()) {
 257  0
             getLog().debug("Loading rules from '" + rulesUrl + "'");
 258  
         }
 259  108
         Digester digester = DigesterLoader.createDigester(rulesUrl);
 260  108
         digester.setNamespaceAware(true);
 261  108
         digester.setValidating(true);
 262  108
         digester.setUseContextClassLoader(true);
 263  
 
 264  
         // Add rules for arg0-arg3 elements
 265  108
         addOldArgRules(digester);
 266  
 
 267  
         // register DTDs
 268  864
         for (int i = 0; i < REGISTRATIONS.length; i += 2) {
 269  756
             URL url = this.getClass().getResource(REGISTRATIONS[i + 1]);
 270  756
             if (url != null) {
 271  756
                 digester.register(REGISTRATIONS[i], url.toString());
 272  
             }
 273  
         }
 274  108
         return digester;
 275  
     }
 276  
 
 277  
     private static final String ARGS_PATTERN
 278  
                = "form-validation/formset/form/field/arg";
 279  
 
 280  
     /**
 281  
      * Create a <code>Rule</code> to handle <code>arg0-arg3</code>
 282  
      * elements. This will allow validation.xml files that use the
 283  
      * versions of the DTD prior to Validator 1.2.0 to continue
 284  
      * working.
 285  
      */
 286  
     private void addOldArgRules(Digester digester) {
 287  
 
 288  
         // Create a new rule to process args elements
 289  108
         Rule rule = new Rule() {
 290  
             @Override
 291  
             public void begin(String namespace, String name,
 292  
                                Attributes attributes) throws Exception {
 293  
                 // Create the Arg
 294  0
                 Arg arg = new Arg();
 295  0
                 arg.setKey(attributes.getValue("key"));
 296  0
                 arg.setName(attributes.getValue("name"));
 297  0
                 if ("false".equalsIgnoreCase(attributes.getValue("resource"))) {
 298  0
                     arg.setResource(false);
 299  
                 }
 300  
                 try {
 301  0
                     final int length = "arg".length(); // skip the arg prefix
 302  0
                     arg.setPosition(Integer.parseInt(name.substring(length)));
 303  0
                 } catch (Exception ex) {
 304  0
                     getLog().error("Error parsing Arg position: "
 305  
                                + name + " " + arg + " " + ex);
 306  0
                 }
 307  
 
 308  
                 // Add the arg to the parent field
 309  0
                 ((Field)getDigester().peek(0)).addArg(arg);
 310  0
             }
 311  
         };
 312  
 
 313  
         // Add the rule for each of the arg elements
 314  108
         digester.addRule(ARGS_PATTERN + "0", rule);
 315  108
         digester.addRule(ARGS_PATTERN + "1", rule);
 316  108
         digester.addRule(ARGS_PATTERN + "2", rule);
 317  108
         digester.addRule(ARGS_PATTERN + "3", rule);
 318  
 
 319  108
     }
 320  
 
 321  
     /**
 322  
      * Add a <code>FormSet</code> to this <code>ValidatorResources</code>
 323  
      * object.  It will be associated with the <code>Locale</code> of the
 324  
      * <code>FormSet</code>.
 325  
      * @param fs The form set to add.
 326  
      * @since Validator 1.1
 327  
      */
 328  
     public void addFormSet(FormSet fs) {
 329  151
         String key = this.buildKey(fs);
 330  151
         if (key.length() == 0) {// there can only be one default formset
 331  111
             if (getLog().isWarnEnabled() && defaultFormSet != null) {
 332  
                 // warn the user he might not get the expected results
 333  0
                 getLog().warn("Overriding default FormSet definition.");
 334  
             }
 335  111
             defaultFormSet = fs;
 336  
         } else {
 337  40
             FormSet formset = getFormSets().get(key);
 338  40
             if (formset == null) {// it hasn't been included yet
 339  40
                 if (getLog().isDebugEnabled()) {
 340  0
                     getLog().debug("Adding FormSet '" + fs.toString() + "'.");
 341  
                 }
 342  0
             } else if (getLog().isWarnEnabled()) {// warn the user he might not
 343  
                                                 // get the expected results
 344  0
                 getLog()
 345  
                         .warn("Overriding FormSet definition. Duplicate for locale: "
 346  
                                 + key);
 347  
             }
 348  40
             getFormSets().put(key, fs);
 349  
         }
 350  151
     }
 351  
 
 352  
     /**
 353  
      * Add a global constant to the resource.
 354  
      * @param name The constant name.
 355  
      * @param value The constant value.
 356  
      */
 357  
     public void addConstant(String name, String value) {
 358  0
         if (getLog().isDebugEnabled()) {
 359  0
             getLog().debug("Adding Global Constant: " + name + "," + value);
 360  
         }
 361  
 
 362  0
         this.hConstants.put(name, value);
 363  0
     }
 364  
 
 365  
     /**
 366  
      * Add a <code>ValidatorAction</code> to the resource.  It also creates an
 367  
      * instance of the class based on the <code>ValidatorAction</code>s
 368  
      * classname and retrieves the <code>Method</code> instance and sets them
 369  
      * in the <code>ValidatorAction</code>.
 370  
      * @param va The validator action.
 371  
      */
 372  
     public void addValidatorAction(ValidatorAction va) {
 373  384
         va.init();
 374  
 
 375  384
         getActions().put(va.getName(), va);
 376  
 
 377  384
         if (getLog().isDebugEnabled()) {
 378  0
             getLog().debug("Add ValidatorAction: " + va.getName() + "," + va.getClassname());
 379  
         }
 380  384
     }
 381  
 
 382  
     /**
 383  
      * Get a <code>ValidatorAction</code> based on it's name.
 384  
      * @param key The validator action key.
 385  
      * @return The validator action.
 386  
      */
 387  
     public ValidatorAction getValidatorAction(String key) {
 388  0
         return getActions().get(key);
 389  
     }
 390  
 
 391  
     /**
 392  
      * Get an unmodifiable <code>Map</code> of the <code>ValidatorAction</code>s.
 393  
      * @return Map of validator actions.
 394  
      */
 395  
     public Map<String, ValidatorAction> getValidatorActions() {
 396  125
         return Collections.unmodifiableMap(getActions());
 397  
     }
 398  
 
 399  
     /**
 400  
      * Builds a key to store the <code>FormSet</code> under based on it's
 401  
      * language, country, and variant values.
 402  
      * @param fs The Form Set.
 403  
      * @return generated key for a formset.
 404  
      */
 405  
     protected String buildKey(FormSet fs) {
 406  151
         return
 407  
                 this.buildLocale(fs.getLanguage(), fs.getCountry(), fs.getVariant());
 408  
     }
 409  
 
 410  
     /**
 411  
      * Assembles a Locale code from the given parts.
 412  
      */
 413  
     private String buildLocale(String lang, String country, String variant) {
 414  797
         String key = ((lang != null && lang.length() > 0) ? lang : "");
 415  797
         key += ((country != null && country.length() > 0) ? "_" + country : "");
 416  797
         key += ((variant != null && variant.length() > 0) ? "_" + variant : "");
 417  797
         return key;
 418  
     }
 419  
 
 420  
     /**
 421  
      * <p>Gets a <code>Form</code> based on the name of the form and the
 422  
      * <code>Locale</code> that most closely matches the <code>Locale</code>
 423  
      * passed in.  The order of <code>Locale</code> matching is:</p>
 424  
      * <ol>
 425  
      *    <li>language + country + variant</li>
 426  
      *    <li>language + country</li>
 427  
      *    <li>language</li>
 428  
      *    <li>default locale</li>
 429  
      * </ol>
 430  
      * @param locale The Locale.
 431  
      * @param formKey The key for the Form.
 432  
      * @return The validator Form.
 433  
      * @since Validator 1.1
 434  
      */
 435  
     public Form getForm(Locale locale, String formKey) {
 436  160
         return this.getForm(locale.getLanguage(), locale.getCountry(), locale
 437  
                 .getVariant(), formKey);
 438  
     }
 439  
 
 440  
     /**
 441  
      * <p>Gets a <code>Form</code> based on the name of the form and the
 442  
      * <code>Locale</code> that most closely matches the <code>Locale</code>
 443  
      * passed in.  The order of <code>Locale</code> matching is:</p>
 444  
      * <ol>
 445  
      *    <li>language + country + variant</li>
 446  
      *    <li>language + country</li>
 447  
      *    <li>language</li>
 448  
      *    <li>default locale</li>
 449  
      * </ol>
 450  
      * @param language The locale's language.
 451  
      * @param country The locale's country.
 452  
      * @param variant The locale's language variant.
 453  
      * @param formKey The key for the Form.
 454  
      * @return The validator Form.
 455  
      * @since Validator 1.1
 456  
      */
 457  
     public Form getForm(String language, String country, String variant,
 458  
             String formKey) {
 459  
 
 460  164
         Form form = null;
 461  
 
 462  
         // Try language/country/variant
 463  164
         String key = this.buildLocale(language, country, variant);
 464  164
         if (key.length() > 0) {
 465  162
             FormSet formSet = getFormSets().get(key);
 466  162
             if (formSet != null) {
 467  25
                 form = formSet.getForm(formKey);
 468  
             }
 469  
         }
 470  164
         String localeKey  = key;
 471  
 
 472  
 
 473  
         // Try language/country
 474  164
         if (form == null) {
 475  143
             key = buildLocale(language, country, null);
 476  143
             if (key.length() > 0) {
 477  141
                 FormSet formSet = getFormSets().get(key);
 478  141
                 if (formSet != null) {
 479  5
                     form = formSet.getForm(formKey);
 480  
                 }
 481  
             }
 482  
         }
 483  
 
 484  
         // Try language
 485  164
         if (form == null) {
 486  142
             key = buildLocale(language, null, null);
 487  142
             if (key.length() > 0) {
 488  140
                 FormSet formSet = getFormSets().get(key);
 489  140
                 if (formSet != null) {
 490  4
                     form = formSet.getForm(formKey);
 491  
                 }
 492  
             }
 493  
         }
 494  
 
 495  
         // Try default formset
 496  164
         if (form == null) {
 497  142
             form = defaultFormSet.getForm(formKey);
 498  142
             key = "default";
 499  
         }
 500  
 
 501  164
         if (form == null) {
 502  6
             if (getLog().isWarnEnabled()) {
 503  6
                 getLog().warn("Form '" + formKey + "' not found for locale '" +
 504  
                          localeKey + "'");
 505  
             }
 506  
         } else {
 507  158
             if (getLog().isDebugEnabled()) {
 508  0
                 getLog().debug("Form '" + formKey + "' found in formset '" +
 509  
                           key + "' for locale '" + localeKey + "'");
 510  
             }
 511  
         }
 512  
 
 513  164
         return form;
 514  
 
 515  
     }
 516  
 
 517  
     /**
 518  
      * Process the <code>ValidatorResources</code> object. Currently sets the
 519  
      * <code>FastHashMap</code> s to the 'fast' mode and call the processes
 520  
      * all other resources. <strong>Note </strong>: The framework calls this
 521  
      * automatically when ValidatorResources is created from an XML file. If you
 522  
      * create an instance of this class by hand you <strong>must </strong> call
 523  
      * this method when finished.
 524  
      */
 525  
     public void process() {
 526  111
         hFormSets.setFast(true);
 527  111
         hConstants.setFast(true);
 528  111
         hActions.setFast(true);
 529  
 
 530  111
         this.processForms();
 531  111
     }
 532  
 
 533  
     /**
 534  
      * <p>Process the <code>Form</code> objects.  This clones the <code>Field</code>s
 535  
      * that don't exist in a <code>FormSet</code> compared to its parent
 536  
      * <code>FormSet</code>.</p>
 537  
      */
 538  
     private void processForms() {
 539  111
         if (defaultFormSet == null) {// it isn't mandatory to have a
 540  
             // default formset
 541  0
             defaultFormSet = new FormSet();
 542  
         }
 543  111
         defaultFormSet.process(getConstants());
 544  
         // Loop through FormSets and merge if necessary
 545  111
         for (Iterator<String> i = getFormSets().keySet().iterator(); i.hasNext();) {
 546  40
             String key = i.next();
 547  40
             FormSet fs = getFormSets().get(key);
 548  40
             fs.merge(getParent(fs));
 549  40
         }
 550  
 
 551  
         // Process Fully Constructed FormSets
 552  111
         for (Iterator<FormSet> i = getFormSets().values().iterator(); i.hasNext();) {
 553  40
             FormSet fs = i.next();
 554  40
             if (!fs.isProcessed()) {
 555  40
                 fs.process(getConstants());
 556  
             }
 557  40
         }
 558  111
     }
 559  
 
 560  
     /**
 561  
      * Finds the given formSet's parent. ex: A formSet with locale en_UK_TEST1
 562  
      * has a direct parent in the formSet with locale en_UK. If it doesn't
 563  
      * exist, find the formSet with locale en, if no found get the
 564  
      * defaultFormSet.
 565  
      *
 566  
      * @param fs
 567  
      *            the formSet we want to get the parent from
 568  
      * @return fs's parent
 569  
      */
 570  
     private FormSet getParent(FormSet fs) {
 571  
 
 572  40
         FormSet parent = null;
 573  40
         if (fs.getType() == FormSet.LANGUAGE_FORMSET) {
 574  10
             parent = defaultFormSet;
 575  30
         } else if (fs.getType() == FormSet.COUNTRY_FORMSET) {
 576  15
             parent = getFormSets().get(buildLocale(fs.getLanguage(),
 577  
                     null, null));
 578  15
             if (parent == null) {
 579  5
                 parent = defaultFormSet;
 580  
             }
 581  15
         } else if (fs.getType() == FormSet.VARIANT_FORMSET) {
 582  15
             parent = getFormSets().get(buildLocale(fs.getLanguage(), fs
 583  
                     .getCountry(), null));
 584  15
             if (parent == null) {
 585  10
                 parent = getFormSets().get(buildLocale(fs.getLanguage(),
 586  
                         null, null));
 587  10
                 if (parent == null) {
 588  10
                     parent = defaultFormSet;
 589  
                 }
 590  
             }
 591  
         }
 592  40
         return parent;
 593  
     }
 594  
 
 595  
     /**
 596  
      * <p>Gets a <code>FormSet</code> based on the language, country
 597  
      *    and variant.</p>
 598  
      * @param language The locale's language.
 599  
      * @param country The locale's country.
 600  
      * @param variant The locale's language variant.
 601  
      * @return The FormSet for a locale.
 602  
      * @since Validator 1.2
 603  
      */
 604  
     FormSet getFormSet(String language, String country, String variant) {
 605  
 
 606  157
         String key = buildLocale(language, country, variant);
 607  
 
 608  157
         if (key.length() == 0) {
 609  112
             return defaultFormSet;
 610  
         }
 611  
 
 612  45
         return getFormSets().get(key);
 613  
     }
 614  
 
 615  
     /**
 616  
      * Returns a Map of String locale keys to Lists of their FormSets.
 617  
      * @return Map of Form sets
 618  
      * @since Validator 1.2.0
 619  
      */
 620  
     @SuppressWarnings("unchecked") // FastHashMap is not generic
 621  
     protected Map<String, FormSet> getFormSets() {
 622  870
         return hFormSets;
 623  
     }
 624  
 
 625  
     /**
 626  
      * Returns a Map of String constant names to their String values.
 627  
      * @return Map of Constants
 628  
      * @since Validator 1.2.0
 629  
      */
 630  
     @SuppressWarnings("unchecked") // FastHashMap is not generic
 631  
     protected Map<String, String> getConstants() {
 632  151
         return hConstants;
 633  
     }
 634  
 
 635  
     /**
 636  
      * Returns a Map of String ValidatorAction names to their ValidatorAction.
 637  
      * @return Map of Validator Actions
 638  
      * @since Validator 1.2.0
 639  
      */
 640  
     @SuppressWarnings("unchecked") // FastHashMap is not generic
 641  
     protected Map<String, ValidatorAction> getActions() {
 642  509
         return hActions;
 643  
     }
 644  
 
 645  
     /**
 646  
      * Accessor method for Log instance.
 647  
      *
 648  
      * The Log instance variable is transient and
 649  
      * accessing it through this method ensures it
 650  
      * is re-initialized when this instance is
 651  
      * de-serialized.
 652  
      *
 653  
      * @return The Log instance.
 654  
      */
 655  
     private Log getLog() {
 656  813
         if (log == null) {
 657  0
             log =  LogFactory.getLog(ValidatorResources.class);
 658  
         }
 659  813
         return log;
 660  
     }
 661  
 
 662  
 }