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 */ 017package org.apache.commons.validator; 018 019import java.io.Serializable; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Iterator; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029/** 030 * Holds a set of <code>Form</code>s stored associated with a <code>Locale</code> 031 * based on the country, language, and variant specified. Instances of this 032 * class are configured with a <formset> xml element. 033 * 034 * @version $Revision$ 035 */ 036public class FormSet implements Serializable { 037 038 private static final long serialVersionUID = -8936513232763306055L; 039 040 /** Logging */ 041 private transient Log log = LogFactory.getLog(FormSet.class); 042 043 /** 044 * Whether or not the this <code>FormSet</code> was processed for replacing 045 * variables in strings with their values. 046 */ 047 private boolean processed = false; 048 049 /** Language component of <code>Locale</code> (required). */ 050 private String language = null; 051 052 /** Country component of <code>Locale</code> (optional). */ 053 private String country = null; 054 055 /** Variant component of <code>Locale</code> (optional). */ 056 private String variant = null; 057 058 /** 059 * A <code>Map</code> of <code>Form</code>s using the name field of the 060 * <code>Form</code> as the key. 061 */ 062 private final Map<String, Form> forms = new HashMap<String, Form>(); 063 064 /** 065 * A <code>Map</code> of <code>Constant</code>s using the name field of the 066 * <code>Constant</code> as the key. 067 */ 068 private final Map<String, String> constants = new HashMap<String, String>(); 069 070 /** 071 * This is the type of <code>FormSet</code>s where no locale is specified. 072 */ 073 protected final static int GLOBAL_FORMSET = 1; 074 075 /** 076 * This is the type of <code>FormSet</code>s where only language locale is 077 * specified. 078 */ 079 protected final static int LANGUAGE_FORMSET = 2; 080 081 /** 082 * This is the type of <code>FormSet</code>s where only language and country 083 * locale are specified. 084 */ 085 protected final static int COUNTRY_FORMSET = 3; 086 087 /** 088 * This is the type of <code>FormSet</code>s where full locale has been set. 089 */ 090 protected final static int VARIANT_FORMSET = 4; 091 092 /** 093 * Flag indicating if this formSet has been merged with its parent (higher 094 * rank in Locale hierarchy). 095 */ 096 private boolean merged; 097 098 /** 099 * Has this formSet been merged? 100 * 101 * @return true if it has been merged 102 * @since Validator 1.2.0 103 */ 104 protected boolean isMerged() { 105 return merged; 106 } 107 108 /** 109 * Returns the type of <code>FormSet</code>:<code>GLOBAL_FORMSET</code>, 110 * <code>LANGUAGE_FORMSET</code>,<code>COUNTRY_FORMSET</code> or <code>VARIANT_FORMSET</code> 111 * . 112 * 113 * @return The type value 114 * @since Validator 1.2.0 115 * @throws NullPointerException if there is inconsistency in the locale 116 * definition (not sure about this) 117 */ 118 protected int getType() { 119 if (getVariant() != null) { 120 if (getLanguage() == null || getCountry() == null) { 121 throw new NullPointerException( 122 "When variant is specified, country and language must be specified."); 123 } 124 return VARIANT_FORMSET; 125 } 126 else if (getCountry() != null) { 127 if (getLanguage() == null) { 128 throw new NullPointerException( 129 "When country is specified, language must be specified."); 130 } 131 return COUNTRY_FORMSET; 132 } 133 else if (getLanguage() != null) { 134 return LANGUAGE_FORMSET; 135 } 136 else { 137 return GLOBAL_FORMSET; 138 } 139 } 140 141 /** 142 * Merges the given <code>FormSet</code> into this one. If any of <code>depends</code> 143 * s <code>Forms</code> are not in this <code>FormSet</code> then, include 144 * them, else merge both <code>Forms</code>. Theoretically we should only 145 * merge a "parent" formSet. 146 * 147 * @param depends FormSet to be merged 148 * @since Validator 1.2.0 149 */ 150 protected void merge(FormSet depends) { 151 if (depends != null) { 152 Map<String, Form> pForms = getForms(); 153 Map<String, Form> dForms = depends.getForms(); 154 for (Iterator<Entry<String, Form>> it = dForms.entrySet().iterator(); it.hasNext(); ) { 155 Entry<String, Form> entry = it.next(); 156 String key = entry.getKey(); 157 Form pForm = pForms.get(key); 158 if (pForm != null) {//merge, but principal 'rules', don't overwrite 159 // anything 160 pForm.merge(entry.getValue()); 161 } 162 else {//just add 163 addForm(entry.getValue()); 164 } 165 } 166 } 167 merged = true; 168 } 169 170 /** 171 * Whether or not the this <code>FormSet</code> was processed for replacing 172 * variables in strings with their values. 173 * 174 * @return The processed value 175 */ 176 public boolean isProcessed() { 177 return processed; 178 } 179 180 /** 181 * Gets the equivalent of the language component of <code>Locale</code>. 182 * 183 * @return The language value 184 */ 185 public String getLanguage() { 186 return language; 187 } 188 189 /** 190 * Sets the equivalent of the language component of <code>Locale</code>. 191 * 192 * @param language The new language value 193 */ 194 public void setLanguage(String language) { 195 this.language = language; 196 } 197 198 /** 199 * Gets the equivalent of the country component of <code>Locale</code>. 200 * 201 * @return The country value 202 */ 203 public String getCountry() { 204 return country; 205 } 206 207 /** 208 * Sets the equivalent of the country component of <code>Locale</code>. 209 * 210 * @param country The new country value 211 */ 212 public void setCountry(String country) { 213 this.country = country; 214 } 215 216 /** 217 * Gets the equivalent of the variant component of <code>Locale</code>. 218 * 219 * @return The variant value 220 */ 221 public String getVariant() { 222 return variant; 223 } 224 225 /** 226 * Sets the equivalent of the variant component of <code>Locale</code>. 227 * 228 * @param variant The new variant value 229 */ 230 public void setVariant(String variant) { 231 this.variant = variant; 232 } 233 234 /** 235 * Add a <code>Constant</code> to the locale level. 236 * 237 * @param name The constant name 238 * @param value The constant value 239 */ 240 public void addConstant(String name, String value) { 241 242 if (constants.containsKey(name)) { 243 getLog().error("Constant '" + name + "' already exists in FormSet[" 244 + this.displayKey() + "] - ignoring."); 245 246 } else { 247 constants.put(name, value); 248 } 249 250 } 251 252 /** 253 * Add a <code>Form</code> to the <code>FormSet</code>. 254 * 255 * @param f The form 256 */ 257 public void addForm(Form f) { 258 259 String formName = f.getName(); 260 if (forms.containsKey(formName)) { 261 getLog().error("Form '" + formName + "' already exists in FormSet[" 262 + this.displayKey() + "] - ignoring."); 263 264 } else { 265 forms.put(f.getName(), f); 266 } 267 268 } 269 270 /** 271 * Retrieve a <code>Form</code> based on the form name. 272 * 273 * @param formName The form name 274 * @return The form 275 */ 276 public Form getForm(String formName) { 277 return this.forms.get(formName); 278 } 279 280 /** 281 * A <code>Map</code> of <code>Form</code>s is returned as an unmodifiable 282 * <code>Map</code> with the key based on the form name. 283 * 284 * @return The forms map 285 */ 286 public Map<String, Form> getForms() { 287 return Collections.unmodifiableMap(forms); 288 } 289 290 /** 291 * Processes all of the <code>Form</code>s. 292 * 293 * @param globalConstants Global constants 294 */ 295 synchronized void process(Map<String, String> globalConstants) { 296 for (Iterator<Form> i = forms.values().iterator(); i.hasNext(); ) { 297 Form f = i.next(); 298 f.process(globalConstants, constants, forms); 299 } 300 301 processed = true; 302 } 303 304 /** 305 * Returns a string representation of the object's key. 306 * 307 * @return A string representation of the key 308 */ 309 public String displayKey() { 310 StringBuilder results = new StringBuilder(); 311 if (language != null && language.length() > 0) { 312 results.append("language="); 313 results.append(language); 314 } 315 if (country != null && country.length() > 0) { 316 if (results.length() > 0) { 317 results.append(", "); 318 } 319 results.append("country="); 320 results.append(country); 321 } 322 if (variant != null && variant.length() > 0) { 323 if (results.length() > 0) { 324 results.append(", "); 325 } 326 results.append("variant="); 327 results.append(variant ); 328 } 329 if (results.length() == 0) { 330 results.append("default"); 331 } 332 333 return results.toString(); 334 } 335 336 /** 337 * Returns a string representation of the object. 338 * 339 * @return A string representation 340 */ 341 @Override 342 public String toString() { 343 StringBuilder results = new StringBuilder(); 344 345 results.append("FormSet: language="); 346 results.append(language); 347 results.append(" country="); 348 results.append(country); 349 results.append(" variant="); 350 results.append(variant); 351 results.append("\n"); 352 353 for (Iterator<?> i = getForms().values().iterator(); i.hasNext(); ) { 354 results.append(" "); 355 results.append(i.next()); 356 results.append("\n"); 357 } 358 359 return results.toString(); 360 } 361 362 /** 363 * Accessor method for Log instance. 364 * 365 * The Log instance variable is transient and 366 * accessing it through this method ensures it 367 * is re-initialized when this instance is 368 * de-serialized. 369 * 370 * @return The Log instance. 371 */ 372 private Log getLog() { 373 if (log == null) { 374 log = LogFactory.getLog(FormSet.class); 375 } 376 return log; 377 } 378}