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

Save This Page
Home » commons-validator-1.3.1-src » org.apache.commons » validator » [javadoc | source]