View Javadoc

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.Serializable;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.Map;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  /***
28   * Holds a set of <code>Form</code>s stored associated with a <code>Locale</code>
29   * based on the country, language, and variant specified. Instances of this
30   * class are configured with a &lt;formset&gt; xml element.
31   *
32   * @version $Revision: 478334 $ $Date: 2006-11-22 21:31:54 +0000 (Wed, 22 Nov 2006) $
33   */
34  public class FormSet implements Serializable {
35  
36      /*** Logging */
37      private transient Log log = LogFactory.getLog(FormSet.class);
38  
39      /***
40       * Whether or not the this <code>FormSet</code> was processed for replacing
41       * variables in strings with their values.
42       */
43      private boolean processed = false;
44  
45      /*** Language component of <code>Locale</code> (required). */
46      private String language = null;
47  
48      /*** Country component of <code>Locale</code> (optional). */
49      private String country = null;
50  
51      /*** Variant component of <code>Locale</code> (optional). */
52      private String variant = null;
53  
54      /***
55       * A <code>Map</code> of <code>Form</code>s using the name field of the
56       * <code>Form</code> as the key.
57       */
58      private Map forms = new HashMap();
59  
60      /***
61       * A <code>Map</code> of <code>Constant</code>s using the name field of the
62       * <code>Constant</code> as the key.
63       */
64      private Map constants = new HashMap();
65  
66      /***
67       * This is the type of <code>FormSet</code>s where no locale is specified.
68       */
69      protected final static int GLOBAL_FORMSET = 1;
70  
71      /***
72       * This is the type of <code>FormSet</code>s where only language locale is
73       * specified.
74       */
75      protected final static int LANGUAGE_FORMSET = 2;
76  
77      /***
78       * This is the type of <code>FormSet</code>s where only language and country
79       * locale are specified.
80       */
81      protected final static int COUNTRY_FORMSET = 3;
82  
83      /***
84       * This is the type of <code>FormSet</code>s where full locale has been set.
85       */
86      protected final static int VARIANT_FORMSET = 4;
87  
88      /***
89       * Flag indicating if this formSet has been merged with its parent (higher
90       * rank in Locale hierarchy).
91       */
92      private boolean merged;
93  
94      /***
95       * Has this formSet been merged?
96       *
97       * @return   true if it has been merged
98       * @since    Validator 1.2.0
99       */
100     protected boolean isMerged() {
101         return merged;
102     }
103 
104     /***
105      * Returns the type of <code>FormSet</code>:<code>GLOBAL_FORMSET</code>,
106      * <code>LANGUAGE_FORMSET</code>,<code>COUNTRY_FORMSET</code> or <code>VARIANT_FORMSET</code>
107      * .
108      *
109      * @return                       The type value
110      * @since                        Validator 1.2.0
111      * @throws NullPointerException  if there is inconsistency in the locale
112      *      definition (not sure about this)
113      */
114     protected int getType() {
115         if (getVariant() != null) {
116             if (getLanguage() == null || getCountry() == null) {
117                 throw new NullPointerException(
118                     "When variant is specified, country and language must be specified.");
119             }
120             return VARIANT_FORMSET;
121         }
122         else if (getCountry() != null) {
123             if (getLanguage() == null) {
124                 throw new NullPointerException(
125                     "When country is specified, language must be specified.");
126             }
127             return COUNTRY_FORMSET;
128         }
129         else if (getLanguage() != null) {
130             return LANGUAGE_FORMSET;
131         }
132         else {
133             return GLOBAL_FORMSET;
134         }
135     }
136 
137     /***
138      * Merges the given <code>FormSet</code> into this one. If any of <code>depends</code>
139      * s <code>Forms</code> are not in this <code>FormSet</code> then, include
140      * them, else merge both <code>Forms</code>. Theoretically we should only
141      * merge a "parent" formSet.
142      *
143      * @param depends  FormSet to be merged
144      * @since          Validator 1.2.0
145      */
146     protected void merge(FormSet depends) {
147         if (depends != null) {
148             Map pForms = getForms();
149             Map dForms = depends.getForms();
150             for (Iterator it = dForms.keySet().iterator(); it.hasNext(); ) {
151                 Object key = it.next();
152                 Form pForm = (Form) pForms.get(key);
153                 if (pForm != null) {//merge, but principal 'rules', don't overwrite
154                     // anything
155                     pForm.merge((Form) dForms.get(key));
156                 }
157                 else {//just add
158                     addForm((Form) dForms.get(key));
159                 }
160             }
161         }
162         merged = true;
163     }
164 
165     /***
166      * Whether or not the this <code>FormSet</code> was processed for replacing
167      * variables in strings with their values.
168      *
169      * @return   The processed value
170      */
171     public boolean isProcessed() {
172         return processed;
173     }
174 
175     /***
176      * Gets the equivalent of the language component of <code>Locale</code>.
177      *
178      * @return   The language value
179      */
180     public String getLanguage() {
181         return language;
182     }
183 
184     /***
185      * Sets the equivalent of the language component of <code>Locale</code>.
186      *
187      * @param language  The new language value
188      */
189     public void setLanguage(String language) {
190         this.language = language;
191     }
192 
193     /***
194      * Gets the equivalent of the country component of <code>Locale</code>.
195      *
196      * @return   The country value
197      */
198     public String getCountry() {
199         return country;
200     }
201 
202     /***
203      * Sets the equivalent of the country component of <code>Locale</code>.
204      *
205      * @param country  The new country value
206      */
207     public void setCountry(String country) {
208         this.country = country;
209     }
210 
211     /***
212      * Gets the equivalent of the variant component of <code>Locale</code>.
213      *
214      * @return   The variant value
215      */
216     public String getVariant() {
217         return variant;
218     }
219 
220     /***
221      * Sets the equivalent of the variant component of <code>Locale</code>.
222      *
223      * @param variant  The new variant value
224      */
225     public void setVariant(String variant) {
226         this.variant = variant;
227     }
228 
229     /***
230      * Add a <code>Constant</code> to the locale level.
231      *
232      * @param name   The constant name
233      * @param value  The constant value
234      */
235     public void addConstant(String name, String value) {
236 
237         if (constants.containsKey(name)) {
238             getLog().error("Constant '" + name +  "' already exists in FormSet["
239                       + this.displayKey() + "] - ignoring.");
240                        
241         } else {
242             constants.put(name, value);
243         }
244 
245     }
246 
247     /***
248      * Add a <code>Form</code> to the <code>FormSet</code>.
249      *
250      * @param f  The form
251      */
252     public void addForm(Form f) {
253 
254         String formName = f.getName();
255         if (forms.containsKey(formName)) {
256             getLog().error("Form '" + formName + "' already exists in FormSet[" 
257                       + this.displayKey() + "] - ignoring.");
258                        
259         } else {
260             forms.put(f.getName(), f);
261         }
262 
263     }
264 
265     /***
266      * Retrieve a <code>Form</code> based on the form name.
267      *
268      * @param formName  The form name
269      * @return          The form
270      */
271     public Form getForm(String formName) {
272         return (Form) this.forms.get(formName);
273     }
274 
275     /***
276      * A <code>Map</code> of <code>Form</code>s is returned as an unmodifiable
277      * <code>Map</code> with the key based on the form name.
278      *
279      * @return   The forms map
280      */
281     public Map getForms() {
282         return Collections.unmodifiableMap(forms);
283     }
284 
285     /***
286      * Processes all of the <code>Form</code>s.
287      *
288      * @param globalConstants  Global constants
289      */
290     synchronized void process(Map globalConstants) {
291         for (Iterator i = forms.values().iterator(); i.hasNext(); ) {
292             Form f = (Form) i.next();
293             f.process(globalConstants, constants, forms);
294         }
295 
296         processed = true;
297     }
298 
299     /***
300      * Returns a string representation of the object's key.
301      *
302      * @return   A string representation of the key
303      */
304     public String displayKey() {
305         StringBuffer results = new StringBuffer();
306         if (language != null && language.length() > 0) {
307             results.append("language=");
308             results.append(language);
309         }
310         if (country != null && country.length() > 0) {
311             if (results.length() > 0) {
312                results.append(", ");
313             }
314             results.append("country=");
315             results.append(country);
316         }
317         if (variant != null && variant.length() > 0) {
318             if (results.length() > 0) {
319                results.append(", ");
320             }
321             results.append("variant=");
322             results.append(variant );
323         }
324         if (results.length() == 0) {
325            results.append("default");
326         }
327 
328         return results.toString();
329     }
330 
331     /***
332      * Returns a string representation of the object.
333      *
334      * @return   A string representation
335      */
336     public String toString() {
337         StringBuffer results = new StringBuffer();
338 
339         results.append("FormSet: language=");
340         results.append(language);
341         results.append("  country=");
342         results.append(country);
343         results.append("  variant=");
344         results.append(variant);
345         results.append("\n");
346 
347         for (Iterator i = getForms().values().iterator(); i.hasNext(); ) {
348             results.append("   ");
349             results.append(i.next());
350             results.append("\n");
351         }
352 
353         return results.toString();
354     }
355 
356     /***
357      * Accessor method for Log instance.
358      *
359      * The Log instance variable is transient and
360      * accessing it through this method ensures it
361      * is re-initialized when this instance is
362      * de-serialized.
363      *
364      * @return The Log instance.
365      */
366     private Log getLog() {
367         if (log == null) {
368             log =  LogFactory.getLog(FormSet.class);
369         }
370         return log;
371     }
372 }