Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.validator; import org.apache.commons.collections4.FastHashMap; import org.apache.commons.digester.Digester; import org.apache.commons.digester.DigesterService; import org.apache.commons.digester.Rule; import org.osgi.framework.Bundle; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.URL; import java.util.Collections; import java.util.Iterator; import java.util.Locale; import java.util.Map; /** * <p> * General purpose class for storing <code>FormSet</code> objects based * on their associated <code>Locale</code>. Instances of this class are usually * configured through a validation.xml file that is parsed in a constructor. * </p> * <p> * <p><strong>Note</strong> - Classes that extend this class * must be Serializable so that instances may be used in distributable * application server environments.</p> * <p> * <p> * The use of FastHashMap is deprecated and will be replaced in a future * release. * </p> * * @version $Revision: 1739361 $ */ //TODO mutable non-private fields public class ValidatorResources implements Serializable { private static final long serialVersionUID = -8203745881446239554L; /** * Name of the digester validator rules file */ private static final String VALIDATOR_RULES = "digester-rules.xml"; /** * The set of public identifiers, and corresponding resource names, for * the versions of the configuration file DTDs that we know about. There * <strong>MUST</strong> be an even number of Strings in this list! */ private static final String REGISTRATIONS[] = { "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN", "/org/apache/commons/validator/resources/validator_1_0.dtd", "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0.1//EN", "/org/apache/commons/validator/resources/validator_1_0_1.dtd", "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN", "/org/apache/commons/validator/resources/validator_1_1.dtd", "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN", "/org/apache/commons/validator/resources/validator_1_1_3.dtd", "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.2.0//EN", "/org/apache/commons/validator/resources/validator_1_2_0.dtd", "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN", "/org/apache/commons/validator/resources/validator_1_3_0.dtd", "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.4.0//EN", "/org/apache/commons/validator/resources/validator_1_4_0.dtd" }; /** * <code>Map</code> of <code>FormSet</code>s stored under * a <code>Locale</code> key (expressed as a String). * * @deprecated Subclasses should use getFormSets() instead. */ @Deprecated protected FastHashMap hFormSets = new FastHashMap(); // <String, FormSet> /** * <code>Map</code> of global constant values with * the name of the constant as the key. * * @deprecated Subclasses should use getConstants() instead. */ @Deprecated protected FastHashMap hConstants = new FastHashMap(); // <String, String> /** * <code>Map</code> of <code>ValidatorAction</code>s with * the name of the <code>ValidatorAction</code> as the key. * * @deprecated Subclasses should use getActions() instead. */ @Deprecated protected FastHashMap hActions = new FastHashMap(); // <String, ValidatorAction> /** * The default locale on our server. */ protected static Locale defaultLocale = Locale.getDefault(); /** * Create an empty ValidatorResources object. */ public ValidatorResources() { super(); } /** * This is the default <code>FormSet</code> (without locale). (We probably don't need * the defaultLocale anymore.) */ protected FormSet defaultFormSet; public ValidatorResources(Bundle bundle, DigesterService service, InputStream in) throws IOException, SAXException { this(bundle, service, new InputStream[] { in }); } public ValidatorResources(Bundle bundle, DigesterService service, InputStream[] streams) throws IOException, SAXException { super(); Digester digester = initDigester(bundle, service); for (int i = 0; i < streams.length; i++) { if (streams[i] == null) { throw new IllegalArgumentException("Stream[" + i + "] is null"); } digester.push(this); digester.parse(streams[i]); } this.process(); } private Digester initDigester(Bundle bundle, DigesterService service) { URL rulesUrl = this.getClass().getResource(VALIDATOR_RULES); if (rulesUrl == null) { // Fix for Issue# VALIDATOR-195 rulesUrl = ValidatorResources.class.getResource(VALIDATOR_RULES); } Digester digester = service.createDigester(bundle, rulesUrl); digester.setNamespaceAware(true); digester.setValidating(true); digester.setUseContextClassLoader(true); // Add rules for arg0-arg3 elements addOldArgRules(digester); // register DTDs for (int i = 0; i < REGISTRATIONS.length; i += 2) { URL url = this.getClass().getResource(REGISTRATIONS[i + 1]); if (url != null) { digester.register(REGISTRATIONS[i], url.toString()); } } return digester; } private static final String ARGS_PATTERN = "form-validation/formset/form/field/arg"; /** * Create a <code>Rule</code> to handle <code>arg0-arg3</code> * elements. This will allow validation.xml files that use the * versions of the DTD prior to Validator 1.2.0 to continue * working. */ private void addOldArgRules(Digester digester) { // Create a new rule to process args elements Rule rule = new Rule() { @Override public void begin(String namespace, String name, Attributes attributes) throws Exception { // Create the Arg Arg arg = new Arg(); arg.setKey(attributes.getValue("key")); arg.setName(attributes.getValue("name")); if ("false".equalsIgnoreCase(attributes.getValue("resource"))) { arg.setResource(false); } try { final int length = "arg".length(); // skip the arg prefix arg.setPosition(Integer.parseInt(name.substring(length))); } catch (Exception ex) { } // Add the arg to the parent field ((Field) getDigester().peek(0)).addArg(arg); } }; // Add the rule for each of the arg elements digester.addRule(ARGS_PATTERN + "0", rule); digester.addRule(ARGS_PATTERN + "1", rule); digester.addRule(ARGS_PATTERN + "2", rule); digester.addRule(ARGS_PATTERN + "3", rule); } /** * Add a <code>FormSet</code> to this <code>ValidatorResources</code> * object. It will be associated with the <code>Locale</code> of the * <code>FormSet</code>. * * @param fs The form set to add. * @since Validator 1.1 */ public void addFormSet(FormSet fs) { String key = this.buildKey(fs); if (key.length() == 0) {// there can only be one default formset defaultFormSet = fs; } else { getFormSets().put(key, fs); } } /** * Add a global constant to the resource. * * @param name The constant name. * @param value The constant value. */ public void addConstant(String name, String value) { this.hConstants.put(name, value); } /** * Add a <code>ValidatorAction</code> to the resource. It also creates an * instance of the class based on the <code>ValidatorAction</code>s * classname and retrieves the <code>Method</code> instance and sets them * in the <code>ValidatorAction</code>. * * @param va The validator action. */ public void addValidatorAction(ValidatorAction va) { va.init(); getActions().put(va.getName(), va); } /** * Get a <code>ValidatorAction</code> based on it's name. * * @param key The validator action key. * @return The validator action. */ public ValidatorAction getValidatorAction(String key) { return getActions().get(key); } /** * Get an unmodifiable <code>Map</code> of the <code>ValidatorAction</code>s. * * @return Map of validator actions. */ public Map<String, ValidatorAction> getValidatorActions() { return Collections.unmodifiableMap(getActions()); } /** * Builds a key to store the <code>FormSet</code> under based on it's * language, country, and variant values. * * @param fs The Form Set. * @return generated key for a formset. */ protected String buildKey(FormSet fs) { return this.buildLocale(fs.getLanguage(), fs.getCountry(), fs.getVariant()); } /** * Assembles a Locale code from the given parts. */ private String buildLocale(String lang, String country, String variant) { String key = ((lang != null && lang.length() > 0) ? lang : ""); key += ((country != null && country.length() > 0) ? "_" + country : ""); key += ((variant != null && variant.length() > 0) ? "_" + variant : ""); return key; } /** * <p>Gets a <code>Form</code> based on the name of the form and the * <code>Locale</code> that most closely matches the <code>Locale</code> * passed in. The order of <code>Locale</code> matching is:</p> * <ol> * <li>language + country + variant</li> * <li>language + country</li> * <li>language</li> * <li>default locale</li> * </ol> * * @param locale The Locale. * @param formKey The key for the Form. * @return The validator Form. * @since Validator 1.1 */ public Form getForm(Locale locale, String formKey) { return this.getForm(locale.getLanguage(), locale.getCountry(), locale.getVariant(), formKey); } /** * <p>Gets a <code>Form</code> based on the name of the form and the * <code>Locale</code> that most closely matches the <code>Locale</code> * passed in. The order of <code>Locale</code> matching is:</p> * <ol> * <li>language + country + variant</li> * <li>language + country</li> * <li>language</li> * <li>default locale</li> * </ol> * * @param language The locale's language. * @param country The locale's country. * @param variant The locale's language variant. * @param formKey The key for the Form. * @return The validator Form. * @since Validator 1.1 */ public Form getForm(String language, String country, String variant, String formKey) { Form form = null; // Try language/country/variant String key = this.buildLocale(language, country, variant); if (key.length() > 0) { FormSet formSet = getFormSets().get(key); if (formSet != null) { form = formSet.getForm(formKey); } } String localeKey = key; // Try language/country if (form == null) { key = buildLocale(language, country, null); if (key.length() > 0) { FormSet formSet = getFormSets().get(key); if (formSet != null) { form = formSet.getForm(formKey); } } } // Try language if (form == null) { key = buildLocale(language, null, null); if (key.length() > 0) { FormSet formSet = getFormSets().get(key); if (formSet != null) { form = formSet.getForm(formKey); } } } // Try default formset if (form == null) { form = defaultFormSet.getForm(formKey); } return form; } /** * Process the <code>ValidatorResources</code> object. Currently sets the * <code>FastHashMap</code> s to the 'fast' mode and call the processes * all other resources. <strong>Note </strong>: The framework calls this * automatically when ValidatorResources is created from an XML file. If you * create an instance of this class by hand you <strong>must </strong> call * this method when finished. */ public void process() { hFormSets.setFast(true); hConstants.setFast(true); hActions.setFast(true); this.processForms(); } /** * <p>Process the <code>Form</code> objects. This clones the <code>Field</code>s * that don't exist in a <code>FormSet</code> compared to its parent * <code>FormSet</code>.</p> */ private void processForms() { if (defaultFormSet == null) {// it isn't mandatory to have a // default formset defaultFormSet = new FormSet(); } defaultFormSet.process(getConstants()); // Loop through FormSets and merge if necessary for (Iterator<String> i = getFormSets().keySet().iterator(); i.hasNext();) { String key = i.next(); FormSet fs = getFormSets().get(key); fs.merge(getParent(fs)); } // Process Fully Constructed FormSets for (Iterator<FormSet> i = getFormSets().values().iterator(); i.hasNext();) { FormSet fs = i.next(); if (!fs.isProcessed()) { fs.process(getConstants()); } } } /** * Finds the given formSet's parent. ex: A formSet with locale en_UK_TEST1 * has a direct parent in the formSet with locale en_UK. If it doesn't * exist, find the formSet with locale en, if no found get the * defaultFormSet. * * @param fs the formSet we want to get the parent from * @return fs's parent */ private FormSet getParent(FormSet fs) { FormSet parent = null; if (fs.getType() == FormSet.LANGUAGE_FORMSET) { parent = defaultFormSet; } else if (fs.getType() == FormSet.COUNTRY_FORMSET) { parent = getFormSets().get(buildLocale(fs.getLanguage(), null, null)); if (parent == null) { parent = defaultFormSet; } } else if (fs.getType() == FormSet.VARIANT_FORMSET) { parent = getFormSets().get(buildLocale(fs.getLanguage(), fs.getCountry(), null)); if (parent == null) { parent = getFormSets().get(buildLocale(fs.getLanguage(), null, null)); if (parent == null) { parent = defaultFormSet; } } } return parent; } /** * <p>Gets a <code>FormSet</code> based on the language, country * and variant.</p> * * @param language The locale's language. * @param country The locale's country. * @param variant The locale's language variant. * @return The FormSet for a locale. * @since Validator 1.2 */ FormSet getFormSet(String language, String country, String variant) { String key = buildLocale(language, country, variant); if (key.length() == 0) { return defaultFormSet; } return getFormSets().get(key); } /** * Returns a Map of String locale keys to Lists of their FormSets. * * @return Map of Form sets * @since Validator 1.2.0 */ @SuppressWarnings("unchecked") // FastHashMap is not generic protected Map<String, FormSet> getFormSets() { return hFormSets; } /** * Returns a Map of String constant names to their String values. * * @return Map of Constants * @since Validator 1.2.0 */ @SuppressWarnings("unchecked") // FastHashMap is not generic protected Map<String, String> getConstants() { return hConstants; } /** * Returns a Map of String ValidatorAction names to their ValidatorAction. * * @return Map of Validator Actions * @since Validator 1.2.0 */ @SuppressWarnings("unchecked") // FastHashMap is not generic protected Map<String, ValidatorAction> getActions() { return hActions; } }