001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.validator;
018
019 import java.io.Serializable;
020 import java.util.Collections;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.Map;
024 import org.apache.commons.logging.Log;
025 import org.apache.commons.logging.LogFactory;
026
027 /**
028 * Holds a set of <code>Form</code>s stored associated with a <code>Locale</code>
029 * based on the country, language, and variant specified. Instances of this
030 * class are configured with a <formset> xml element.
031 *
032 * @version $Revision: 493905 $ $Date: 2007-01-08 03:11:38 +0100 (Mo, 08. Jan 2007) $
033 */
034 public class FormSet implements Serializable {
035
036 /** Logging */
037 private transient Log log = LogFactory.getLog(FormSet.class);
038
039 /**
040 * Whether or not the this <code>FormSet</code> was processed for replacing
041 * variables in strings with their values.
042 */
043 private boolean processed = false;
044
045 /** Language component of <code>Locale</code> (required). */
046 private String language = null;
047
048 /** Country component of <code>Locale</code> (optional). */
049 private String country = null;
050
051 /** Variant component of <code>Locale</code> (optional). */
052 private String variant = null;
053
054 /**
055 * A <code>Map</code> of <code>Form</code>s using the name field of the
056 * <code>Form</code> as the key.
057 */
058 private Map forms = new HashMap();
059
060 /**
061 * A <code>Map</code> of <code>Constant</code>s using the name field of the
062 * <code>Constant</code> as the key.
063 */
064 private Map constants = new HashMap();
065
066 /**
067 * This is the type of <code>FormSet</code>s where no locale is specified.
068 */
069 protected final static int GLOBAL_FORMSET = 1;
070
071 /**
072 * This is the type of <code>FormSet</code>s where only language locale is
073 * specified.
074 */
075 protected final static int LANGUAGE_FORMSET = 2;
076
077 /**
078 * This is the type of <code>FormSet</code>s where only language and country
079 * locale are specified.
080 */
081 protected final static int COUNTRY_FORMSET = 3;
082
083 /**
084 * This is the type of <code>FormSet</code>s where full locale has been set.
085 */
086 protected final static int VARIANT_FORMSET = 4;
087
088 /**
089 * Flag indicating if this formSet has been merged with its parent (higher
090 * rank in Locale hierarchy).
091 */
092 private boolean merged;
093
094 /**
095 * Has this formSet been merged?
096 *
097 * @return true if it has been merged
098 * @since Validator 1.2.0
099 */
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 }