View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/validator/src/share/org/apache/commons/validator/ValidatorResources.java,v 1.30.2.2 2004/06/22 02:24:38 husted Exp $
3    * $Revision: 1.30.2.2 $
4    * $Date: 2004/06/22 02:24:38 $
5    *
6    * ====================================================================
7    * Copyright 2001-2004 The Apache Software Foundation
8    *
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   *
13   *     http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   */
21  
22  package org.apache.commons.validator;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.Serializable;
27  import java.net.URL;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  
35  import org.apache.commons.collections.FastHashMap; // DEPRECATED
36  import org.apache.commons.digester.Digester;
37  import org.apache.commons.digester.xmlrules.DigesterLoader;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  import org.xml.sax.SAXException;
42  
43  /***
44   * <p>
45   * General purpose class for storing <code>FormSet</code> objects based
46   * on their associated <code>Locale</code>.  Instances of this class are usually
47   * configured through a validation.xml file that is parsed in a constructor.
48   * </p>
49   *
50   * <p><strong>Note</strong> - Classes that extend this class
51   * must be Serializable so that instances may be used in distributable
52   * application server environments.</p>
53   *
54   * <p>
55   * The use of FastHashMap is deprecated and will be replaced in a future
56   * release.
57   * </p>
58   */
59  public class ValidatorResources implements Serializable {
60  
61      /***
62       * The set of public identifiers, and corresponding resource names, for
63       * the versions of the configuration file DTDs that we know about.  There
64       * <strong>MUST</strong> be an even number of Strings in this list!
65       */
66      private static final String registrations[] = {
67          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN",
68          "/org/apache/commons/validator/resources/validator_1_0.dtd",
69          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0.1//EN",
70          "/org/apache/commons/validator/resources/validator_1_0_1.dtd",
71          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN",
72          "/org/apache/commons/validator/resources/validator_1_1.dtd",
73          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN",
74          "/org/apache/commons/validator/resources/validator_1_1_3.dtd"
75      };
76  
77      /***
78       * Logger.
79       * @deprecated Subclasses should use their own logging instance.
80        */
81      protected static Log log = LogFactory.getLog(ValidatorResources.class);
82  
83      /***
84       * <code>FastHashMap</code> of <code>FormSet</code>s stored under
85       * a <code>Locale</code> key.
86       */
87      protected FastHashMap hFormSets = new FastHashMap();
88  
89      /***
90       * <code>FastHashMap</code> of global constant values with
91       * the name of the constant as the key.
92       */
93      protected FastHashMap hConstants = new FastHashMap();
94  
95      /***
96       * <code>FastHashMap</code> of <code>ValidatorAction</code>s with
97       * the name of the <code>ValidatorAction</code> as the key.
98       */
99      protected FastHashMap hActions = new FastHashMap();
100 
101     /***
102      * The default locale on our server.
103      */
104     protected static Locale defaultLocale = Locale.getDefault();
105 
106     /***
107      * Create an empty ValidatorResources object.
108      */
109     public ValidatorResources() {
110         super();
111     }
112 
113     /***
114      * Create a ValidatorResources object from an InputStream.
115      *
116      * @param in InputStream to a validation.xml configuration file.  It's the client's
117      * responsibility to close this stream.
118      * @throws IOException
119      * @throws SAXException if the validation XML files are not valid or well
120      * formed.
121      * @since Validator 1.1
122      */
123     public ValidatorResources(InputStream in) throws IOException, SAXException {
124         this(new InputStream[]{in});
125     }
126 
127     /***
128      * Create a ValidatorResources object from an InputStream.
129      *
130      * @param streams An array of InputStreams to several validation.xml
131      * configuration files that will be read in order and merged into this object.
132      * It's the client's responsibility to close these streams.
133      * @throws IOException
134      * @throws SAXException if the validation XML files are not valid or well
135      * formed.
136      * @since Validator 1.1
137      */
138     public ValidatorResources(InputStream[] streams)
139             throws IOException, SAXException {
140 
141         super();
142 
143         URL rulesUrl = this.getClass().getResource("digester-rules.xml");
144         Digester digester = DigesterLoader.createDigester(rulesUrl);
145         digester.setNamespaceAware(true);
146         digester.setValidating(true);
147         digester.setUseContextClassLoader(true);
148 
149         // register DTDs
150         for (int i = 0; i < registrations.length; i += 2) {
151             URL url = this.getClass().getResource(registrations[i + 1]);
152             if (url != null) {
153                 digester.register(registrations[i], url.toString());
154             }
155         }
156 
157         for (int i = 0; i < streams.length; i++) {
158             digester.push(this);
159             digester.parse(streams[i]);
160         }
161 
162         this.process();
163     }
164 
165     /***
166      * Add a <code>FormSet</code> to this <code>ValidatorResources</code>
167      * object.  It will be associated with the <code>Locale</code> of the
168      * <code>FormSet</code>.
169      * @deprecated Use addFormSet() instead.
170      */
171     public void put(FormSet fs) {
172         this.addFormSet(fs);
173     }
174 
175     /***
176      * Add a <code>FormSet</code> to this <code>ValidatorResources</code>
177      * object.  It will be associated with the <code>Locale</code> of the
178      * <code>FormSet</code>.
179      * @since Validator 1.1
180      */
181     public void addFormSet(FormSet fs) {
182         String key = this.buildKey(fs);
183         List formsets = (List) hFormSets.get(key);
184 
185         if (formsets == null) {
186             formsets = new ArrayList();
187             hFormSets.put(key, formsets);
188         }
189 
190         if (!formsets.contains(fs)) {
191             if (log.isDebugEnabled()) {
192                 log.debug("Adding FormSet '" + fs.toString() + "'.");
193             }
194             formsets.add(fs);
195         }
196     }
197 
198     /***
199      * Add a global constant to the resource.
200      * @deprecated Use addConstant(String, String) instead.
201      */
202     public void addConstant(Constant c) {
203         this.addConstantParam(c.getName(), c.getValue());
204     }
205 
206     /***
207      * Add a global constant to the resource.
208      * @deprecated Use addConstant(String, String) instead.
209      */
210     public void addConstantParam(String name, String value) {
211         if (name != null
212                 && name.length() > 0
213                 && value != null
214                 && value.length() > 0) {
215 
216             if (log.isDebugEnabled()) {
217                 log.debug("Adding Global Constant: " + name + "," + value);
218             }
219 
220             this.hConstants.put(name, value);
221         }
222     }
223 
224     /***
225      * Add a global constant to the resource.
226      */
227     public void addConstant(String name, String value) {
228         if (log.isDebugEnabled()) {
229             log.debug("Adding Global Constant: " + name + "," + value);
230         }
231 
232         this.hConstants.put(name, value);
233     }
234 
235     /***
236      * Add a <code>ValidatorAction</code> to the resource.  It also creates an
237      * instance of the class based on the <code>ValidatorAction</code>s
238      * classname and retrieves the <code>Method</code> instance and sets them
239      * in the <code>ValidatorAction</code>.
240      */
241     public void addValidatorAction(ValidatorAction va) {
242         va.init();
243 
244         this.hActions.put(va.getName(), va);
245 
246         if (log.isDebugEnabled()) {
247             log.debug("Add ValidatorAction: " + va.getName() + "," + va.getClassname());
248         }
249     }
250 
251     /***
252      * Get a <code>ValidatorAction</code> based on it's name.
253      */
254     public ValidatorAction getValidatorAction(String key) {
255         return (ValidatorAction) hActions.get(key);
256     }
257 
258     /***
259      * Get an unmodifiable <code>Map</code> of the <code>ValidatorAction</code>s.
260      */
261     public Map getValidatorActions() {
262         return Collections.unmodifiableMap(hActions);
263     }
264 
265     /***
266      * Builds a key to store the <code>FormSet</code> under based on it's
267      * language, country, and variant values.
268      */
269     protected String buildKey(FormSet fs) {
270         String locale =
271                 this.buildLocale(fs.getLanguage(), fs.getCountry(), fs.getVariant());
272 
273         if (locale.length() == 0) {
274             locale = defaultLocale.toString();
275         }
276 
277         return locale;
278     }
279 
280     /***
281      * Assembles a Locale code from the given parts.
282      */
283     private String buildLocale(String lang, String country, String variant) {
284         String key = ((lang != null && lang.length() > 0) ? lang : "");
285         key += ((country != null && country.length() > 0) ? "_" + country : "");
286         key += ((variant != null && variant.length() > 0) ? "_" + variant : "");
287         return key;
288     }
289 
290     /***
291      * <p>Gets a <code>Form</code> based on the name of the form and the <code>Locale</code> that
292      * most closely matches the <code>Locale</code> passed in.  The order of <code>Locale</code>
293      * matching is:</p>
294      * <ol>
295      *    <li>language + country + variant</li>
296      *    <li>language + country</li>
297      *    <li>language</li>
298      *    <li>default locale</li>
299      * </ol>
300      * @deprecated Use getForm() instead.
301      */
302     public Form get(Locale locale, Object formKey) {
303         String key = (formKey == null) ? null : formKey.toString();
304         return this.getForm(locale, key);
305     }
306 
307     /***
308      * <p>Gets a <code>Form</code> based on the name of the form and the
309      * <code>Locale</code> that most closely matches the <code>Locale</code>
310      * passed in.  The order of <code>Locale</code> matching is:</p>
311      * <ol>
312      *    <li>language + country + variant</li>
313      *    <li>language + country</li>
314      *    <li>language</li>
315      *    <li>default locale</li>
316      * </ol>
317      * @since Validator 1.1
318      */
319     public Form getForm(Locale locale, String formKey) {
320         return this.getForm(
321                 locale.getLanguage(),
322                 locale.getCountry(),
323                 locale.getVariant(),
324                 formKey);
325     }
326 
327     /***
328      * <p>Gets a <code>Form</code> based on the name of the form and the
329      * <code>Locale</code> that most closely matches the <code>Locale</code>
330      * passed in.  The order of <code>Locale</code> matching is:</p>
331      * <ol>
332      *    <li>language + country + variant</li>
333      *    <li>language + country</li>
334      *    <li>language</li>
335      *    <li>default locale</li>
336      * </ol>
337      * @deprecated Use getForm() instead.
338      */
339     public Form get(
340             String language,
341             String country,
342             String variant,
343             Object formKey) {
344 
345         String key = (formKey == null) ? null : formKey.toString();
346         return this.getForm(language, country, variant, key);
347     }
348 
349     /***
350      * <p>Gets a <code>Form</code> based on the name of the form and the
351      * <code>Locale</code> that most closely matches the <code>Locale</code>
352      * passed in.  The order of <code>Locale</code> matching is:</p>
353      * <ol>
354      *    <li>language + country + variant</li>
355      *    <li>language + country</li>
356      *    <li>language</li>
357      *    <li>default locale</li>
358      * </ol>
359      * @since Validator 1.1
360      */
361     public Form getForm(
362             String language,
363             String country,
364             String variant,
365             String formKey) {
366 
367         String key = this.buildLocale(language, country, variant);
368 
369         List v = (List) hFormSets.get(key);
370 
371         if (v == null) {
372             key = (language != null && language.length() > 0) ? language : "";
373             key += (country != null && country.length() > 0) ? "_" + country : "";
374             v = (List) hFormSets.get(key);
375         }
376 
377         if (v == null) {
378             key = (language != null && language.length() > 0) ? language : "";
379             v = (List) hFormSets.get(key);
380         }
381 
382         if (v == null) {
383             key = defaultLocale.toString();
384             v = (List) hFormSets.get(key);
385         }
386 
387         if (v == null) {
388             return null;
389         }
390 
391         Iterator formsets = v.iterator();
392         while (formsets.hasNext()) {
393             FormSet set = (FormSet) formsets.next();
394 
395             if ((set != null) && (set.getForm(formKey) != null)) {
396                 return set.getForm(formKey);
397             }
398 
399         }
400         return null;
401     }
402 
403     /***
404      * Process the <code>ValidatorResources</code> object.  Currently sets the
405      * <code>FastHashMap</code>s to the 'fast' mode and call the processes all
406      * other resources.  <strong>Note</strong>: The framework calls this automatically
407      * when ValidatorResources is created from an XML file.  If you create an instance
408      * of this class by hand you <strong>must</strong> call this method when finished.
409      */
410     public void process() {
411         hFormSets.setFast(true);
412         hConstants.setFast(true);
413         hActions.setFast(true);
414 
415         this.internalProcessForms();
416     }
417 
418     /***
419      * <p>Process the <code>Form</code> objects.  This clones the <code>Field</code>s
420      * that don't exist in a <code>FormSet</code> compared to the default
421      * <code>FormSet</code>.</p>
422      * @deprecated This is an internal method that client classes need not call directly.
423      */
424     public void processForms() {
425         this.internalProcessForms();
426     }
427 
428     /***
429      * <p>Process the <code>Form</code> objects.  This clones the <code>Field</code>s
430      * that don't exist in a <code>FormSet</code> compared to the default
431      * <code>FormSet</code>.</p>
432      * TODO When processForms() is removed from the public interface, rename this
433      * private method back to "processForms".
434      */
435     private void internalProcessForms() {
436         //hFormSets.put(buildKey(fs), fs);
437         String defaultKey = defaultLocale.toString();
438 
439         // Loop through FormSets
440         for (Iterator i = hFormSets.keySet().iterator(); i.hasNext();) {
441             String key = (String) i.next();
442             // Skip default FormSet
443             if (key.equals(defaultKey)) {
444                 continue;
445             }
446             List formsets = (List) hFormSets.get(key);
447             Iterator formsetsIterator = formsets.iterator();
448             while (formsetsIterator.hasNext()) {
449                 FormSet fs = (FormSet) formsetsIterator.next();
450 
451                 // Loop through Forms and copy/clone fields from default locale
452                 for (Iterator x = fs.getForms().keySet().iterator(); x.hasNext();) {
453                     String formKey = (String) x.next();
454                     Form form = (Form) fs.getForms().get(formKey);
455                     // Create a new Form object so the order from the default is
456                     // maintained (very noticable in the JavaScript).
457                     Form newForm = new Form();
458                     newForm.setName(form.getName());
459 
460                     // Loop through the default locale form's fields
461                     // If they don't exist in the current locale's form, then clone them.
462                     Form defaultForm = get(defaultLocale, formKey);
463 
464                     Iterator defaultFields = defaultForm.getFields().iterator();
465                     while (defaultFields.hasNext()) {
466                         Field defaultField = (Field) defaultFields.next();
467                         String fieldKey = defaultField.getKey();
468 
469                         if (form.containsField(fieldKey)) {
470                             newForm.addField(form.getField(fieldKey));
471 
472                         } else {
473                             Field field =
474                                     getClosestLocaleField(fs, formKey, fieldKey);
475 
476                             newForm.addField((Field) field.clone());
477                         }
478                     }
479 
480                     fs.addForm(newForm);
481                 }
482             }
483         }
484 
485         // Process Fully Constructed FormSets
486         for (Iterator i = hFormSets.values().iterator(); i.hasNext();) {
487             List formsets = (List) i.next();
488             Iterator formsetsIterator = formsets.iterator();
489             while (formsetsIterator.hasNext()) {
490                 FormSet fs = (FormSet) formsetsIterator.next();
491 
492                 if (!fs.isProcessed()) {
493                     fs.process(hConstants);
494                 }
495             }
496         }
497     }
498 
499     /***
500      * Retrieves the closest matching <code>Field</code> based
501      * on <code>FormSet</code>'s locale.  This is used when
502      * constructing a clone, field by field, of partial
503      * <code>FormSet</code>.
504      */
505     protected Field getClosestLocaleField(
506             FormSet fs,
507             String formKey,
508             String fieldKey) {
509 
510         Field field = null;
511         String language = fs.getLanguage();
512         String country = fs.getCountry();
513         String variant = fs.getVariant();
514 
515         if (!GenericValidator.isBlankOrNull(language)
516                 && !GenericValidator.isBlankOrNull(country)
517                 && !GenericValidator.isBlankOrNull(variant)) {
518 
519             Form form = this.getForm(language, country, variant, formKey);
520             field = form.getField(fieldKey);
521         }
522 
523         if (field == null) {
524             if (!GenericValidator.isBlankOrNull(language)
525                     && !GenericValidator.isBlankOrNull(country)) {
526 
527                 Form form = this.getForm(language, country, null, formKey);
528                 field = form.getField(fieldKey);
529             }
530         }
531 
532         if (field == null) {
533             if (!GenericValidator.isBlankOrNull(language)) {
534                 Form form = this.getForm(language, null, null, formKey);
535                 field = form.getField(fieldKey);
536             }
537         }
538 
539         if (field == null) {
540             Form form = this.getForm(defaultLocale, formKey);
541             field = form.getField(fieldKey);
542         }
543 
544         return field;
545     }
546 
547 }