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.util.Locale;
20  
21  import org.springframework.beans.factory.InitializingBean;
22  import org.springframework.context.MessageSource;
23  import org.springframework.context.MessageSourceResolvable;
24  import org.springframework.context.NoSuchMessageException;
25  import org.springframework.context.support.ResourceBundleMessageSource;
26  
27  import ch.elca.el4j.util.codingsupport.Reject;
28  
29  /**
30   * Test class loading the same properties files in two or more message sources
31   * and comparing their behavior.
32   *
33   * <h4> Usage </h4>
34   * Replace the message source in the spring configuration with an instance of
35   * this class (leaving the basenames-assignment intact) and configure the
36   * types to be compared using {@link #m_types}. This class will then resolve
37   * every
38   * message it is asked to resolve from both sources, and {@code assert} they
39   * agree about the message's value. (You should therefore enable assertions ...)
40   *
41   * @svnLink $Revision: 3874 $;$Date: 2009-08-04 14:25:40 +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/CrossCheckingMessageSource.java $
42   *
43   * @author Adrian Moos (AMS)
44   */
45  public class CrossCheckingMessageSource implements MessageSource,
46  	InitializingBean {
47  	
48  	/** The message sources to be compared. */
49  	MessageSource[] m_sources;
50  	
51  	/** The types of the message sources to be compared. */
52  	String[] m_types;
53  	
54  	/**
55  	 * Like {@link ResourceBundleMessageSource#setBasenames(String[])}.
56  	 */
57  	String[] m_basenames;
58  	
59  	/**
60  	 * Sets the types of the message sources to be compared.
61  	 */
62  	public void setTypes(String[] types) {
63  		m_types = types;
64  	}
65  	
66  	/**
67  	 * Like {@link ResourceBundleMessageSource#setBasenames(String[])}.
68  	 */
69  	public void setBasenames(String[] basenames) {
70  		m_basenames = basenames;
71  	}
72  	
73  	/** Represents a lookup's result. */
74  	static interface Result {
75  		/** Simulates the return of this result. */
76  		String ret();
77  	}
78  	
79  	/** The lookup found a message. */
80  	static class Found implements Result {
81  		/** The resolved message. */
82  		String m_message;
83  		
84  		/**
85  		 * Constructor.
86  		 * @param message The resolved message
87  		 */
88  		Found(String message) { m_message = message; }
89  		
90  		/** {@inheritDoc} */
91  		public String ret() { return m_message; }
92  		
93  		/** {@inheritDoc} */
94  		public int hashCode() {
95  			return m_message.hashCode();
96  		}
97  		
98  		/** {@inheritDoc} */
99  		public boolean equals(Object o) {
100 			return o instanceof Found
101 				&& m_message.equals(((Found) o).m_message);
102 		}
103 	}
104 	
105 	/** The lookup did not find a message. */
106 	static class NotFound implements Result {
107 		/** The exception that signaled the failure. */
108 		NoSuchMessageException m_e;
109 		
110 		/**
111 		 * Constructor.
112 		 * @param e The exception signaling the failure.
113 		 */
114 		NotFound(NoSuchMessageException e) { m_e = e; }
115 		
116 		/** {@inheritDoc} */
117 		public String ret() { throw m_e; }
118 		
119 		/** {@inheritDoc} */
120 		public int hashCode() {
121 			// Checkstyle: MagicNumber off
122 			return 0xdead;
123 			// Checkstyle: MagicNumber on
124 		}
125 		
126 		/** {@inheritDoc} */
127 		public boolean equals(Object o) {
128 			return o instanceof NotFound;
129 		}
130 	}
131 	
132 	/** Asserts that all lookup results are equal. */
133 	static class Checker {
134 		/** The result of the first lookup. */
135 		Result m_first;
136 		
137 		/** Checks a result. */
138 		void process(Result r) {
139 			if (m_first == null) {
140 				m_first = r;
141 			} else {
142 				assert m_first.equals(r);
143 			}
144 		}
145 		
146 		/** Simulates a lookup's result. */
147 		String get() {
148 			return m_first.ret();
149 		}
150 	}
151 	
152 	/** {@inheritDoc} */
153 	public String getMessage(MessageSourceResolvable resolvable, Locale locale)
154 		throws NoSuchMessageException {
155 		Checker c = new Checker();
156 		for (MessageSource ms : m_sources) {
157 			try {
158 				c.process(new Found(ms.getMessage(resolvable, locale)));
159 			} catch (NoSuchMessageException e) {
160 				c.process(new NotFound(e));
161 			}
162 			
163 		}
164 		return c.get();
165 	}
166 
167 	/** {@inheritDoc} */
168 	public String getMessage(String code, Object[] args, Locale locale)
169 		throws NoSuchMessageException {
170 		Checker c = new Checker();
171 		for (MessageSource ms : m_sources) {
172 			try {
173 				c.process(new Found(ms.getMessage(code, args, locale)));
174 			} catch (NoSuchMessageException e) {
175 				c.process(new NotFound(e));
176 			}
177 			
178 		}
179 		return c.get();
180 	}
181 
182 	/** {@inheritDoc} */
183 	public String getMessage(String code, Object[] args, String defaultMessage,
184 		Locale locale) {
185 		
186 		Checker c = new Checker();
187 		for (MessageSource ms : m_sources) {
188 			try {
189 				c.process(new Found(ms.getMessage(code, args, locale)));
190 			} catch (NoSuchMessageException e) {
191 				c.process(new NotFound(e));
192 			}
193 			
194 		}
195 		return c.get();
196 	}
197 
198 	/** Creates and initializes the requested message sources. */
199 	public void afterPropertiesSet() throws Exception {
200 		m_sources = new MessageSource[m_types.length];
201 		for (int i = 0; i < m_types.length; i++) {
202 			MessageSource ms;
203 			try {
204 				ms = (MessageSource) Class.forName(m_types[i]).newInstance();
205 			} catch (Exception e) {
206 				throw new RuntimeException(e);
207 			}
208 			
209 			if (ms instanceof RewritingMessageSource) {
210 				((RewritingMessageSource) ms).setBasenames(m_basenames);
211 			} else if (ms instanceof ResourceBundleMessageSource) {
212 				((ResourceBundleMessageSource) ms).setBasenames(m_basenames);
213 			} else {
214 				Reject.ifCondition(true, "unsupported type");
215 			}
216 			m_sources[i] = ms;
217 		}
218 	}
219 
220 }