KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > blandware > atleap > webapp > struts > HeritableI18nFactorySet


1 /*
2  * Copyright 2004 Blandware (http://www.blandware.com)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package com.blandware.atleap.webapp.struts;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.apache.struts.taglib.tiles.ComponentConstants;
21 import org.apache.struts.tiles.DefinitionsFactoryException;
22 import org.apache.struts.tiles.FactoryNotFoundException;
23 import org.apache.struts.tiles.NoSuchDefinitionException;
24 import org.apache.struts.tiles.xmlDefinition.DefinitionsFactory;
25 import org.apache.struts.tiles.xmlDefinition.FactorySet;
26 import org.apache.struts.tiles.xmlDefinition.XmlDefinitionsSet;
27 import org.apache.struts.tiles.xmlDefinition.XmlParser;
28 import org.xml.sax.SAXException JavaDoc;
29
30 import javax.servlet.ServletContext JavaDoc;
31 import javax.servlet.ServletRequest JavaDoc;
32 import javax.servlet.http.HttpServletRequest JavaDoc;
33 import javax.servlet.http.HttpSession JavaDoc;
34 import java.io.FileNotFoundException JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.InputStream JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.HashMap JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Locale JavaDoc;
42 import java.util.Map JavaDoc;
43 import java.util.StringTokenizer JavaDoc;
44
45 /**
46  * <p>Definitions factory.
47  * This implementation allows to have a set of definition factories.
48  * There is a main factory and one factory for each file associated to a Locale.
49  * </p><p>
50  * To retrieve a definition, we first search for the appropriate factory using
51  * the Locale found in session context. If no factory is found, use the
52  * default one. Then we ask the factory for the definition.
53  * </p><p>
54  * A definition factory file is loaded using main filename extended with locale code
55  * (ex : <code>templateDefinitions_fr.xml</code>). If no file is found under this name, use default file.</p>
56  * <p>This class contains the code from <code>org.apache.struts.tiles.xmlDefinition.I18nFactorySet</code> class</p>
57  * <p><a HREF="HeritableI18nFactorySet.java.htm"><i>View Source</i></a></p>
58  * <p/>
59  *
60  * @author Andrey Grebnev <a HREF="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
61  * @version $Revision: 1.7 $ $Date: 2005/08/04 17:25:20 $
62  */

63 public class HeritableI18nFactorySet extends FactorySet {
64
65     /**
66      * Commons Logging instance.
67      */

68     protected static Log log = LogFactory.getLog(HeritableI18nFactorySet.class);
69
70     /**
71      * Config file parameter name.
72      */

73     public static final String JavaDoc DEFINITIONS_CONFIG_PARAMETER_NAME =
74             "definitions-config";
75
76     /**
77      * Config file parameter name.
78      */

79     public static final String JavaDoc PARSER_DETAILS_PARAMETER_NAME =
80             "definitions-parser-details";
81
82     /**
83      * Config file parameter name.
84      */

85     public static final String JavaDoc PARSER_VALIDATE_PARAMETER_NAME =
86             "definitions-parser-validate";
87
88     /**
89      * Possible definition filenames.
90      */

91     public static final String JavaDoc DEFAULT_DEFINITION_FILENAMES[] =
92             {
93                 "/WEB-INF/tileDefinitions.xml",
94                 "/WEB-INF/componentDefinitions.xml",
95                 "/WEB-INF/instanceDefinitions.xml"};
96
97     /**
98      * Maximum length of one branch of the resource search path tree.
99      * Used in getBundle().
100      */

101     private static final int MAX_BUNDLES_SEARCHED = 2;
102
103     /**
104      * Default filenames extension.
105      */

106     public static final String JavaDoc FILENAME_EXTENSION = ".xml";
107
108     /**
109      * Default factory.
110      */

111     protected DefinitionsFactory defaultFactory = null;
112
113     /**
114      * XML parser used.
115      * Attribute is transient to allow serialization. In this implementaiton,
116      * xmlParser is created each time we need it ;-(.
117      */

118     protected transient XmlParser xmlParser;
119
120     /**
121      * Do we want validating parser. Default is <code>false</code>.
122      * Can be set from servlet config file.
123      */

124     protected boolean isValidatingParser = false;
125
126     /**
127      * Parser detail level. Default is 0.
128      * Can be set from servlet config file.
129      */

130     protected int parserDetailLevel = 0;
131
132     /**
133      * Names of files containing instances descriptions.
134      */

135     private List JavaDoc filenames = null;
136
137     /**
138      * Collection of already loaded definitions set, referenced by their suffix.
139      */

140     private Map JavaDoc loaded = null;
141
142     /**
143      * Parameterless Constructor.
144      * Method {@link #initFactory} must be called prior to any use of created factory.
145      */

146     public HeritableI18nFactorySet() {
147         super();
148     }
149
150     /**
151      * Constructor.
152      * Inits the factory by reading appropriate configuration file.
153      *
154      * @param servletContext Servlet context.
155      * @param properties Map containing all properties.
156      * @throws org.apache.struts.tiles.FactoryNotFoundException
157      * Can't find factory configuration file.
158      */

159     public HeritableI18nFactorySet(ServletContext JavaDoc servletContext, Map JavaDoc properties)
160             throws DefinitionsFactoryException {
161
162         initFactory(servletContext, properties);
163     }
164
165     /**
166      * Initialization method.
167      * Inits the factory by reading appropriate configuration file.
168      * This method is called exactly once immediately after factory creation in
169      * case of internal creation (by DefinitionUtil).
170      *
171      * @param servletContext Servlet Context passed to newly created factory.
172      * @param properties Map of name/property passed to newly created factory. Map can contains
173      * more properties than requested.
174      * @throws DefinitionsFactoryException An error occur during initialization.
175      */

176     public void initFactory(ServletContext JavaDoc servletContext, Map JavaDoc properties)
177             throws DefinitionsFactoryException {
178
179         // Set some property values
180
String JavaDoc value = (String JavaDoc) properties.get(PARSER_VALIDATE_PARAMETER_NAME);
181         if ( value != null ) {
182             isValidatingParser = Boolean.valueOf(value).booleanValue();
183         }
184
185         value = (String JavaDoc) properties.get(PARSER_DETAILS_PARAMETER_NAME);
186         if ( value != null ) {
187             try {
188                 parserDetailLevel = Integer.valueOf(value).intValue();
189
190             } catch ( NumberFormatException JavaDoc ex ) {
191                 log.error("Bad format for parameter '"
192                         + PARSER_DETAILS_PARAMETER_NAME
193                         + "'. Integer expected.");
194             }
195         }
196
197         // Init factory with appropriate configuration file
198
// Try to use provided filename, if any.
199
// If no filenames are provided, try to use default ones.
200
String JavaDoc filename = (String JavaDoc) properties.get(DEFINITIONS_CONFIG_PARAMETER_NAME);
201         if ( filename != null ) { // Use provided filename
202
try {
203                 initFactory(servletContext, filename);
204                 if ( log.isDebugEnabled() ) {
205                     log.debug("Factory initialized from file '" + filename + "'.");
206                 }
207
208             } catch ( FileNotFoundException JavaDoc ex ) { // A filename is specified, throw appropriate error.
209
log.error(ex.getMessage() + " : Can't find file '" + filename + "'");
210                 throw new FactoryNotFoundException(ex.getMessage() + " : Can't find file '" + filename + "'");
211             }
212
213         } else { // try each default file names
214
for ( int i = 0; i < DEFAULT_DEFINITION_FILENAMES.length; i++ ) {
215                 filename = DEFAULT_DEFINITION_FILENAMES[i];
216                 try {
217                     initFactory(servletContext, filename);
218                     if ( log.isInfoEnabled() ) {
219                         log.info("Factory initialized from file '" + filename + "'.");
220                     }
221                 } catch ( FileNotFoundException JavaDoc ex ) {
222                     // Do nothing
223
}
224             }
225         }
226
227     }
228
229     /**
230      * Initialization method.
231      * Inits the factory by reading a configuration files with given names.
232      * This method is called exactly once immediately after factory creation in
233      * case of internal creation (by DefinitionUtil).
234      *
235      * @param servletContext Servlet Context passed to newly created factory.
236      * @param proposedFilename File names, comma separated, to use as base file names.
237      * @throws DefinitionsFactoryException An error occur during initialization.
238      */

239     protected void initFactory(ServletContext JavaDoc servletContext,
240                                String JavaDoc proposedFilename)
241             throws DefinitionsFactoryException, FileNotFoundException JavaDoc {
242
243         // Init list of filenames
244
StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(proposedFilename, ",");
245         this.filenames = new ArrayList JavaDoc(tokenizer.countTokens());
246         while ( tokenizer.hasMoreTokens() ) {
247             this.filenames.add(tokenizer.nextToken().trim());
248         }
249
250         loaded = new HashMap JavaDoc();
251         defaultFactory = createDefaultFactory(servletContext);
252         if ( log.isDebugEnabled() ) {
253             log.debug("default factory:" + defaultFactory);
254         }
255     }
256
257     /**
258      * Gets default factory.
259      *
260      * @return Default factory
261      */

262     protected DefinitionsFactory getDefaultFactory() {
263         return defaultFactory;
264     }
265
266     /**
267      * Creates default factory .
268      * Creates InstancesMapper for specified Locale.
269      * If creation failes, uses default mapper and logs error message.
270      *
271      * @param servletContext Current servlet context. Used to open file.
272      * @return Created default definition factory.
273      * @throws DefinitionsFactoryException If an error occur while creating factory.
274      * @throws FileNotFoundException if factory can't be loaded from filenames.
275      */

276     protected DefinitionsFactory createDefaultFactory(ServletContext JavaDoc servletContext)
277             throws DefinitionsFactoryException, FileNotFoundException JavaDoc {
278
279         XmlDefinitionsSet rootXmlConfig = parseXmlFiles(servletContext, "", null);
280         if ( rootXmlConfig == null ) {
281             throw new FileNotFoundException JavaDoc();
282         }
283
284         rootXmlConfig.resolveInheritances();
285
286         if ( log.isDebugEnabled() ) {
287             log.debug(rootXmlConfig);
288         }
289
290         DefinitionsFactory factory = new HeritableDefinitionsFactory(rootXmlConfig);
291         if ( log.isDebugEnabled() ) {
292             log.debug("factory loaded : " + factory);
293         }
294
295         return factory;
296     }
297
298     /**
299      * Extracts key that will be used to get the sub factory.
300      *
301      * @param name Name of requested definition
302      * @param request Current servlet request.
303      * @param servletContext Current servlet context.
304      * @return the key or <code>null</code> if not found.
305      */

306     protected Object JavaDoc getDefinitionsFactoryKey(String JavaDoc name,
307                                               ServletRequest JavaDoc request,
308                                               ServletContext JavaDoc servletContext) {
309
310         Locale JavaDoc locale = null;
311         try {
312             HttpSession JavaDoc session = ((HttpServletRequest JavaDoc) request).getSession(false);
313             if ( session != null ) {
314                 locale = (Locale JavaDoc) session.getAttribute(ComponentConstants.LOCALE_KEY);
315             }
316
317         } catch ( ClassCastException JavaDoc ex ) {
318             log.error("HeritableI18nFactorySet.getDefinitionsFactoryKey");
319             ex.printStackTrace();
320         }
321
322         return locale;
323     }
324
325     /**
326      * Creates a factory for specified key.
327      * If creation failes, returns default factory and logs an error message.
328      *
329      * @param key The key for which to create factory
330      * @param request Servlet request.
331      * @param servletContext Servlet context.
332      * @return Definition factory for specified key.
333      * @throws DefinitionsFactoryException If an error occur while creating factory.
334      */

335     protected DefinitionsFactory createFactory(Object JavaDoc key,
336                                                ServletRequest JavaDoc request,
337                                                ServletContext JavaDoc servletContext)
338             throws DefinitionsFactoryException {
339
340         if ( key == null ) {
341             return getDefaultFactory();
342         }
343
344         // Build possible postfixes
345
List JavaDoc possiblePostfixes = calculatePostfixes("", (Locale JavaDoc) key);
346
347         // Search last postfix corresponding to a config file to load.
348
// First check if something is loaded for this postfix.
349
// If not, try to load its config.
350
XmlDefinitionsSet lastXmlFile = null;
351         DefinitionsFactory factory = null;
352         String JavaDoc curPostfix = null;
353         int i = 0;
354
355         for ( i = possiblePostfixes.size() - 1; i >= 0; i-- ) {
356             curPostfix = (String JavaDoc) possiblePostfixes.get(i);
357
358             // Already loaded ?
359
factory = (DefinitionsFactory) loaded.get(curPostfix);
360             if ( factory != null ) { // yes, stop search
361
return factory;
362             }
363
364             // Try to load it. If success, stop search
365
lastXmlFile = parseXmlFiles(servletContext, curPostfix, null);
366             if ( lastXmlFile != null ) {
367                 break;
368             }
369         }
370
371         // Have we found a description file ?
372
// If no, return default one
373
if ( lastXmlFile == null ) {
374             return getDefaultFactory();
375         }
376
377         // We found something. Need to load base and intermediate files
378
String JavaDoc lastPostfix = curPostfix;
379         XmlDefinitionsSet rootXmlConfig = parseXmlFiles(servletContext, "", null);
380         for ( int j = 0; j < i; j++ ) {
381             curPostfix = (String JavaDoc) possiblePostfixes.get(j);
382             parseXmlFiles(servletContext, curPostfix, rootXmlConfig);
383         }
384
385         rootXmlConfig.extend(lastXmlFile);
386         rootXmlConfig.resolveInheritances();
387
388         factory = new HeritableDefinitionsFactory(rootXmlConfig);
389         loaded.put(lastPostfix, factory);
390
391         if ( log.isDebugEnabled() ) {
392             log.debug("factory loaded : " + factory);
393         }
394
395         // return last available found !
396
return factory;
397     }
398
399     /**
400      * Calculates the postfixes along the search path from the base bundle to the
401      * bundle specified by baseName and locale.
402      * Method copied from java.util.ResourceBundle
403      *
404      * @param baseName the base bundle name
405      * @param locale the locale
406      */

407     private static List JavaDoc calculatePostfixes(String JavaDoc baseName, Locale JavaDoc locale) {
408         final List JavaDoc result = new ArrayList JavaDoc(MAX_BUNDLES_SEARCHED);
409         final String JavaDoc language = locale.getLanguage();
410         final int languageLength = language.length();
411         final String JavaDoc country = locale.getCountry();
412         final int countryLength = country.length();
413         final String JavaDoc variant = locale.getVariant();
414         final int variantLength = variant.length();
415
416         if ( languageLength + countryLength + variantLength == 0 ) {
417             //The locale is "", "", "".
418
return result;
419         }
420
421         final StringBuffer JavaDoc temp = new StringBuffer JavaDoc(baseName);
422         temp.append('_');
423         temp.append(language);
424
425         if ( languageLength > 0 ) {
426             result.add(temp.toString());
427         }
428
429         if ( countryLength + variantLength == 0 ) {
430             return result;
431         }
432
433         temp.append('_');
434         temp.append(country);
435
436         if ( countryLength > 0 ) {
437             result.add(temp.toString());
438         }
439
440         if ( variantLength == 0 ) {
441             return result;
442         } else {
443             temp.append('_');
444             temp.append(variant);
445             result.add(temp.toString());
446             return result;
447         }
448     }
449
450     /**
451      * Parses files associated to postix if they exist.
452      * For each name in filenames, append postfix before file extension,
453      * then try to load the corresponding file.
454      * If file doesn't exist, try next one. Each file description is added to
455      * the XmlDefinitionsSet description.
456      * The XmlDefinitionsSet description is created only if there is a definition file.
457      * Inheritance is not resolved in the returned XmlDefinitionsSet.
458      * If no description file can be opened and no definiion set is provided, return <code>null</code>.
459      *
460      * @param postfix Postfix to add to each description file.
461      * @param xmlDefinitions Definitions set to which definitions will be added. If <code>null</code>, a definitions
462      * set is created on request.
463      * @return XmlDefinitionsSet The definitions set created or passed as parameter.
464      * @throws DefinitionsFactoryException On errors parsing file.
465      */

466     private XmlDefinitionsSet parseXmlFiles(ServletContext JavaDoc servletContext,
467                                             String JavaDoc postfix,
468                                             XmlDefinitionsSet xmlDefinitions)
469             throws DefinitionsFactoryException {
470
471         if ( postfix != null && postfix.length() == 0 ) {
472             postfix = null;
473         }
474
475         // Iterate throw each file name in list
476
Iterator JavaDoc i = filenames.iterator();
477         while ( i.hasNext() ) {
478             String JavaDoc filename = concatPostfix((String JavaDoc) i.next(), postfix);
479             xmlDefinitions = parseXmlFile(servletContext, filename, xmlDefinitions);
480         }
481
482         return xmlDefinitions;
483     }
484
485     /**
486      * Parses specified xml file and adds definition to specified definitions set.
487      * This method is used to load several description files in one instances list.
488      * If filename exists and definition set is <code>null</code>, create a new set. Otherwise, return
489      * passed definition set (can be <code>null</code>).
490      *
491      * @param servletContext Current servlet context. Used to open file.
492      * @param filename Name of file to parse.
493      * @param xmlDefinitions Definitions set to which definitions will be added. If null, a definitions
494      * set is created on request.
495      * @return XmlDefinitionsSet The definitions set created or passed as parameter.
496      * @throws DefinitionsFactoryException On errors parsing file.
497      */

498     private XmlDefinitionsSet parseXmlFile(ServletContext JavaDoc servletContext,
499                                            String JavaDoc filename,
500                                            XmlDefinitionsSet xmlDefinitions)
501             throws DefinitionsFactoryException {
502
503         try {
504             InputStream JavaDoc input = servletContext.getResourceAsStream(filename);
505             // Try to load using real path.
506
// This allow to load config file under websphere 3.5.x
507
// Patch proposed Houston, Stephen (LIT) on 5 Apr 2002
508
if ( null == input ) {
509                 try {
510                     input =
511                             new java.io.FileInputStream JavaDoc(servletContext.getRealPath(filename));
512                 } catch ( Exception JavaDoc e ) {
513                 }
514             }
515
516             // If still nothing found, this mean no config file is associated
517
if ( input == null ) {
518                 if ( log.isDebugEnabled() ) {
519                     log.debug("Can't open file '" + filename + "'");
520                 }
521                 return xmlDefinitions;
522             }
523
524             // Check if parser already exist.
525
// Doesn't seem to work yet.
526
//if( xmlParser == null )
527
if ( true ) {
528                 xmlParser = new XmlParser();
529                 xmlParser.setValidating(isValidatingParser);
530             }
531
532             // Check if definition set already exist.
533
if ( xmlDefinitions == null ) {
534                 xmlDefinitions = new XmlDefinitionsSet();
535             }
536
537             xmlParser.parse(input, xmlDefinitions);
538
539         } catch ( SAXException JavaDoc ex ) {
540             if ( log.isDebugEnabled() ) {
541                 log.debug("Error while parsing file '" + filename + "'.");
542                 ex.printStackTrace();
543             }
544             throw new DefinitionsFactoryException("Error while parsing file '" + filename + "'. " + ex.getMessage(),
545                     ex);
546
547         } catch ( IOException JavaDoc ex ) {
548             throw new DefinitionsFactoryException("IO Error while parsing file '" + filename + "'. " + ex.getMessage(),
549                     ex);
550         }
551
552         return xmlDefinitions;
553     }
554
555     /**
556      * Concats postfix to the name. Takes care of existing filename extension.
557      * Transforms the given name "name.ext" to have "name" + "postfix" + "ext".
558      * If there is no ext, returns "name" + "postfix".
559      *
560      * @param name Filename.
561      * @param postfix Postfix to add.
562      * @return Concatenated filename.
563      */

564     private String JavaDoc concatPostfix(String JavaDoc name, String JavaDoc postfix) {
565         if ( postfix == null ) {
566             return name;
567         }
568
569         // Search file name extension.
570
// Take care of Unix files starting with .
571
int dotIndex = name.lastIndexOf(".");
572         int lastNameStart = name.lastIndexOf(java.io.File.pathSeparator);
573         if ( dotIndex < 1 || dotIndex < lastNameStart ) {
574             return name + postfix;
575         }
576
577         String JavaDoc ext = name.substring(dotIndex);
578         name = name.substring(0, dotIndex);
579         return name + postfix + ext;
580     }
581
582     /**
583      * Returns String representation.
584      *
585      * @return String representation.
586      */

587     public String JavaDoc toString() {
588         StringBuffer JavaDoc buff = new StringBuffer JavaDoc("I18nFactorySet : \n");
589         buff.append("--- default factory ---\n");
590         buff.append(defaultFactory.toString());
591         buff.append("\n--- other factories ---\n");
592         Iterator JavaDoc i = factories.values().iterator();
593         while ( i.hasNext() ) {
594             buff.append(i.next().toString()).append("---------- \n");
595         }
596         return buff.toString();
597     }
598
599
600     /* Additional new methods */
601
602     /**
603      * Gets all definitions
604      *
605      * @param request Current servlet request.
606      * @param servletContext Current servlet context.
607      * @throws DefinitionsFactoryException General exception
608      */

609     public Map JavaDoc getDefinitions(ServletRequest JavaDoc request, ServletContext JavaDoc servletContext)
610             throws NoSuchDefinitionException, DefinitionsFactoryException {
611         if ( factories == null ) {
612             throw new FactoryNotFoundException("No definitions factory defined");
613         }
614
615         Object JavaDoc key = getDefinitionsFactoryKey(null, request, servletContext);
616         HeritableDefinitionsFactory factory = (HeritableDefinitionsFactory) getFactory(key, request, servletContext);
617         return factory.getDefinitions();
618     }
619
620
621 }
622
Popular Tags