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 }
This page was automatically generated by Maven