View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2006 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.services.i18n;
18  
19  import java.io.IOException;
20  import java.io.InputStreamReader;
21  import java.io.Reader;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Locale;
26  import java.util.Map;
27  
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  import org.springframework.context.MessageSource;
31  import org.springframework.context.MessageSourceResolvable;
32  import org.springframework.context.NoSuchMessageException;
33  import org.springframework.core.io.ClassPathResource;
34  import org.springframework.core.io.Resource;
35  import org.springframework.util.StringUtils;
36  
37  import ch.elca.el4j.util.codingsupport.Reject;
38  
39  /**
40   * A MessageSource based on {@link MessageRewriter MessageRewriters}.
41   *
42   * @svnLink $Revision: 3883 $;$Date: 2009-08-04 15:35:01 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/core/src/main/java/ch/elca/el4j/services/i18n/RewritingMessageSource.java $
43   *
44   * @author Adrian Moos (AMS)
45   */
46  public class RewritingMessageSource implements MessageSource {
47  	/** The base names. See {@link #setBasenames(String[])} */
48  	protected String[] m_basenames;
49  	
50  	/** A list of additional rules to be used by every locale. */
51  	protected MessageRewriter.Rule[] m_additionalRules;
52  	
53  	/** The already loaded rewriters. */
54  	protected Map<Locale, MessageRewriter> m_rewriters
55  		= new HashMap<Locale, MessageRewriter>();
56  	
57  	/** The logger. (duh!) */
58  	protected static final Logger s_logger
59  		= LoggerFactory.getLogger(RewritingMessageSource.class);
60  	
61  	/**
62  	 * Like {@link
63  	 * org.springframework.context.support.ResourceBundleMessageSource#setBasenames(String[])}.
64  	 */
65  	public void setBasenames(String... basenames) {
66  		m_basenames = basenames;
67  	}
68  	
69  	/**
70  	 * Sets {@link #m_additionalRules}.
71  	 */
72  	public void setAdditionalRules(MessageRewriter.Rule... rules) {
73  		m_additionalRules = rules;
74  	}
75  
76  	/**
77  	 * Assembles the filename consisting of the given parts, separated by
78  	 * '_'.
79  	 * @param parts the parts to assembles. A part may be null, in which case
80  	 *              it is omitted.
81  	 * @return the assembled filename
82  	 */
83  	protected String assembleFileName(String... parts) {
84  		StringBuilder r = new StringBuilder();
85  		for (String p : parts) {
86  			if (StringUtils.hasText(p)) {
87  				r.append(p).append('_');
88  			}
89  		}
90  		return r.substring(0, r.length() - 1);
91  	}
92  	
93  	
94  	/**
95  	 *  Creates a new MessageRewriter for the given locale.
96  	 * @param loc .
97  	 * @return the MessageRewriter created
98  	 */
99  	// Checkstyle: EmptyBlock off
100 	protected MessageRewriter createRewriter(Locale loc) {
101 		ArrayList<Reader> ruleSources = new ArrayList<Reader>();
102 		for (String bn : m_basenames) {
103 			String filename = assembleFileName(
104 				bn,
105 				loc.getLanguage(),
106 				loc.getCountry(),
107 				loc.getVariant()
108 			);
109 			
110 			String fn = filename;
111 			do {
112 				Resource r = new ClassPathResource(
113 					fn.replace('.', '/') + ".properties"
114 				);
115 				try {
116 					Reader reader = new InputStreamReader(r.getInputStream());
117 					if (s_logger.isDebugEnabled()) {
118 						s_logger.debug("found: " + r);
119 					}
120 					ruleSources.add(reader);
121 				} catch (IOException e) {
122 					// nothing to do
123 				}
124 
125 				int i = fn.lastIndexOf('_');
126 				if (i == -1) { break; }
127 				fn = fn.substring(0, i);
128 			} while (true);
129 		}
130 		Collections.reverse(ruleSources);
131 		MessageRewriter rewriter = new MessageRewriter(ruleSources);
132 		rewriter.add(m_additionalRules);
133 		return rewriter;
134 	}
135 	// Checkstyle: EmptyBlock on
136 
137 	
138 	/**
139 	 * Fetches the rewriter for a given locale, creating it if needed.
140 	 * @param loc .
141 	 * @return the rewriter
142 	 **/
143 	private MessageRewriter getRewriter(Locale loc) {
144 		MessageRewriter r = m_rewriters.get(loc);
145 		if (r == null) {
146 			r = createRewriter(loc);
147 			m_rewriters.put(loc, r);
148 		}
149 		return r;
150 	}
151 	
152 	/** {@inheritDoc} */
153 	public String getMessage(MessageSourceResolvable resolvable, Locale locale)
154 		throws NoSuchMessageException {
155 		
156 		NoSuchMessageException ex = null;
157 		for (String key : resolvable.getCodes()) {
158 			try {
159 				return getMessage(key, resolvable.getArguments(), locale);
160 			} catch (NoSuchMessageException e) {
161 				s_logger.info(e.toString());
162 				if (ex == null) {
163 					ex = e;
164 				}
165 			}
166 		}
167 		Reject.ifNull(
168 			ex, "a non-empty list of codes is required to resolve a message"
169 		);
170 		if (resolvable.getDefaultMessage() != null) {
171 			return resolvable.getDefaultMessage();
172 		} else {
173 			throw ex;
174 		}
175 	}
176 
177 	/** {@inheritDoc} */
178 	public String getMessage(String code, Object[] args, Locale locale)
179 		throws NoSuchMessageException {
180 		
181 		// All message sources provided by spring resolve
182 		// MessageSourceResolvables passed in args, see
183 		// AbstractMessageSource.resolveArguments
184 		// This is not prescribed by the interface, but silently assumed in
185 		// various places, so we do it, too.
186 		Object[] resolvedArgs;
187 		if (args == null) {
188 			resolvedArgs = null;
189 		} else {
190 			resolvedArgs = new Object[args.length];
191 			for (int i = 0; i < args.length; i++) {
192 				if (args[i] instanceof MessageSourceResolvable) {
193 					resolvedArgs[i] = getMessage(
194 						(MessageSourceResolvable) args[i], locale);
195 				} else {
196 					resolvedArgs[i] = args[i];
197 				}
198 			}
199 		}
200 		
201 		return getRewriter(locale).resolve(code, resolvedArgs);
202 	}
203 
204 	/** {@inheritDoc} */
205 	public String getMessage(String code, Object[] args,
206 		String defaultMessage, Locale locale) {
207 		
208 		try {
209 			return getMessage(code, args, locale);
210 		} catch (NoSuchMessageException e) {
211 			return defaultMessage;
212 		}
213 	}
214 }