001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.junit;
016    
017    import org.apache.hivemind.Location;
018    import org.apache.hivemind.Messages;
019    import org.apache.hivemind.Resource;
020    import org.apache.hivemind.impl.DefaultClassResolver;
021    import org.apache.hivemind.service.ClassFactory;
022    import org.apache.hivemind.service.impl.ClassFactoryImpl;
023    import org.apache.hivemind.util.ClasspathResource;
024    import org.apache.tapestry.IComponent;
025    import org.apache.tapestry.INamespace;
026    import org.apache.tapestry.IPage;
027    import org.apache.tapestry.engine.Namespace;
028    import org.apache.tapestry.enhance.EnhancementOperationImpl;
029    import org.apache.tapestry.enhance.InjectMessagesWorker;
030    import org.apache.tapestry.enhance.InjectSpecificationWorker;
031    import org.apache.tapestry.html.BasePage;
032    import org.apache.tapestry.resolver.ComponentResourceResolverImpl;
033    import org.apache.tapestry.services.ComponentMessagesSource;
034    import org.apache.tapestry.services.ComponentPropertySource;
035    import org.apache.tapestry.services.impl.ClasspathResourceFactoryImpl;
036    import org.apache.tapestry.services.impl.ComponentMessagesSourceImpl;
037    import org.apache.tapestry.spec.ComponentSpecification;
038    import org.apache.tapestry.spec.IComponentSpecification;
039    import org.apache.tapestry.spec.ILibrarySpecification;
040    import org.apache.tapestry.spec.LibrarySpecification;
041    import org.testng.annotations.Test;
042    
043    import java.util.Calendar;
044    import java.util.Date;
045    import java.util.GregorianCalendar;
046    import java.util.Locale;
047    
048    /**
049     * Tests the class {@link org.apache.tapestry.services.impl.ComponentMessagesSourceImpl}.
050     * <p>
051     * TODO: Add tests realted to messages encoding (which can be specified as meta-data on the
052     * component specification or, ultimately, in the namespace (library specification).
053     *
054     * @author Howard Lewis Ship
055     * @since 2.0.4
056     */
057    @Test
058    public class TestComponentMessages extends TapestryTestCase
059    {
060        private Location _locationFixture = new Location()
061        {
062            public Resource getResource()
063            {
064                return null;
065            }
066    
067            public int getLineNumber()
068            {
069                return 0;
070            }
071    
072            public int getColumnNumber()
073            {
074                return 0;
075            }
076        };
077    
078        private static class NullComponentPropertySource implements ComponentPropertySource
079        {
080    
081            public String getComponentProperty(IComponent component, String propertyName)
082            {
083                return null;
084            }
085    
086            public String getLocalizedComponentProperty(IComponent component, Locale locale,
087                                                        String propertyName)
088            {
089                return null;
090            }
091    
092            public String getNamespaceProperty(INamespace namespace, String propertyName)
093            {
094                return null;
095            }
096    
097            public String getLocalizedNamespaceProperty(INamespace namespace, Locale locale,
098                                                        String propertyName)
099            {
100                return null;
101            }
102        }
103    
104        private void check(Messages messages, String key, String expected)
105        {
106            String actual = messages.getMessage(key);
107    
108            assertEquals(actual, expected);
109        }
110    
111        private static final String MOCK1 = "/org/apache/tapestry/junit/MockPage1.page";
112    
113        private IComponentSpecification newSpec(String path)
114        {
115            Resource resource = new ClasspathResource(new DefaultClassResolver(), path);
116    
117            IComponentSpecification spec = new ComponentSpecification();
118            spec.setSpecificationLocation(resource);
119    
120            return spec;
121        }
122    
123        private ILibrarySpecification newLibrarySpec()
124        {
125            Resource resource = new ClasspathResource(new DefaultClassResolver(), "/org/apache/tapestry/junit/Library.library");
126    
127            ILibrarySpecification spec = new LibrarySpecification();
128            spec.setSpecificationLocation(resource);
129    
130            return spec;
131        }
132    
133        /**
134         * Mocking up the page is too hard ... the relationship between the component messagess source
135         * and the page is too varied and complex. Instead, we use the tools to create the page itself,
136         * much as the full framework would do at runtime.
137         */
138    
139        private IPage newPage(IComponentSpecification specification, ComponentMessagesSource source, Locale locale)
140        {
141            ClassFactory classFactory = new ClassFactoryImpl();
142    
143            EnhancementOperationImpl op =
144              new EnhancementOperationImpl(new DefaultClassResolver(), specification, BasePage.class, classFactory, null);
145    
146            InjectMessagesWorker injectMessages = new InjectMessagesWorker();
147            injectMessages.setComponentMessagesSource(source);
148    
149            injectMessages.performEnhancement(op, specification);
150    
151            new InjectSpecificationWorker().performEnhancement(op, specification);
152    
153            IPage result = (IPage) op.getConstructor().newInstance();
154    
155            result.setLocale(locale);
156            result.setPage(result);
157    
158            return result;
159        }
160    
161        private Messages createFullMessages(String location, Locale locale)
162        {
163            ComponentMessagesSourceImpl source = new ComponentMessagesSourceImpl();
164            source.setClasspathResourceFactory(new ClasspathResourceFactoryImpl(new DefaultClassResolver()));
165            source.setComponentPropertySource(new NullComponentPropertySource());
166            source.setComponentResourceResolver(new ComponentResourceResolverImpl());
167    
168            IComponentSpecification spec = newSpec(location);
169            spec.setLocation(_locationFixture);
170    
171            IPage page = newPage(spec, source, locale);
172    
173            Resource resource = new ClasspathResource(new DefaultClassResolver(), "/org/apache/tapestry/junit/Application.application");
174            ILibrarySpecification pspec = new LibrarySpecification();
175            pspec.setSpecificationLocation(resource);
176    
177            INamespace parentNamespace = new Namespace(null, null, pspec, null);
178            INamespace namespace = new Namespace(null, parentNamespace, newLibrarySpec(), null);
179    
180            page.setNamespace(namespace);
181    
182            return source.getMessages(page);
183        }
184    
185        private Messages createMessages(String location, Locale locale)
186        {
187            ComponentMessagesSourceImpl source = new ComponentMessagesSourceImpl();
188            source.setClasspathResourceFactory(new ClasspathResourceFactoryImpl(new DefaultClassResolver()));
189            source.setComponentPropertySource(new NullComponentPropertySource());
190            source.setComponentResourceResolver(new ComponentResourceResolverImpl());
191    
192            IComponentSpecification spec = newSpec(location);
193            spec.setLocation(_locationFixture);
194    
195            IPage page = newPage(spec, source, locale);
196    
197            INamespace namespace = new Namespace(null, null, newLibrarySpec(), null);
198    
199            page.setNamespace(namespace);
200    
201            return source.getMessages(page);
202        }
203    
204        private Messages createMessages(String location, Locale locale, String propname)
205        {
206            ComponentMessagesSourceImpl source = new ComponentMessagesSourceImpl();
207            source.setClasspathResourceFactory(new ClasspathResourceFactoryImpl(new DefaultClassResolver()));
208            source.setComponentPropertySource(new NullComponentPropertySource());
209            source.setComponentResourceResolver(new ComponentResourceResolverImpl());
210    
211            IComponentSpecification spec = newSpec(location);
212            spec.setLocation(_locationFixture);
213    
214            IPage page = newPage(spec, source, locale);
215    
216            ILibrarySpecification lspec = newLibrarySpec();
217            lspec.setProperty(ComponentMessagesSourceImpl.NAMESPACE_PROPERTIES_NAME, propname);
218    
219            INamespace namespace = new Namespace(null, null, lspec, null);
220    
221            page.setNamespace(namespace);
222    
223            return source.getMessages(page);
224        }
225    
226        public void test_Only_In_Base()
227        {
228            Messages messages = createMessages(MOCK1, new Locale("en", "US"));
229    
230            check(messages, "only-in-base", "BASE1");
231        }
232    
233        public void test_Specification_Properties_File_Change()
234        {
235            Messages msgs = createMessages(MOCK1, new Locale("en", "US"), "override");
236    
237            check(msgs, "standard-property", "Whispering wind");
238        }
239    
240        public void test_Specification_Properties_File_Classpath_Change()
241        {
242            Messages msgs = createMessages(MOCK1, new Locale("en", "US"), "org.apache.tapestry.junit.mock.app.impl.classpath");
243    
244            check(msgs, "standard-property", "Here!");
245        }
246    
247        /** @since 4.0 */
248        public void test_Only_In_Namespace()
249        {
250            Messages messages = createMessages(MOCK1, new Locale("en", "US"));
251    
252            check(messages, "only-in-namespace", "LIBRARY_BASE.only-in-namespace");
253        }
254    
255        /** @since 4.0 */
256        public void test_Localized_In_Namespace()
257        {
258            Messages messages = createMessages(MOCK1, new Locale("fr"));
259    
260            check(messages, "localized-in-namespace", "LIBRARY_FR.localized-in-namespace");
261        }
262    
263        /** @since 4.0 */
264        public void test_Component_Overrides_Namespace()
265        {
266            Messages messages = createMessages(MOCK1, new Locale("en", "US"));
267    
268            check(messages, "component-overrides-namespace", "MOCKPAGE1_BASE.component-overrides-namespace");
269        }
270    
271        /** @since 4.0 */
272        public void testLocalizedComponentOverridesLocalizedNamespace()
273        {
274            Messages messages = createMessages(MOCK1, new Locale("fr"));
275    
276            check(
277              messages,
278              "localized-component-overrides-namespace",
279              "MOCKPAGE1_FR.localized-component-overrides-namespace");
280        }
281    
282        public void testMissingKey()
283        {
284            Messages messages = createMessages(MOCK1, new Locale("en", "GB"));
285    
286            check(messages, "non-existant-key", "[NON-EXISTANT-KEY]");
287        }
288    
289        public void testOverwrittenInLanguage()
290        {
291            Messages messages = createMessages(MOCK1, new Locale("en", "US"));
292    
293            check(messages, "overwritten-in-language", "LANGUAGE1_en");
294    
295            messages = createMessages(MOCK1, new Locale("fr", ""));
296    
297            check(messages, "overwritten-in-language", "LANGUAGE1_fr");
298        }
299    
300        public void testOverwrittenInCountry()
301        {
302            Messages messages = createMessages(MOCK1, new Locale("en", "US"));
303    
304            check(messages, "overwritten-in-country", "COUNTRY1_en_US");
305    
306            messages = createMessages(MOCK1, new Locale("fr", "CD"));
307    
308            check(messages, "overwritten-in-country", "COUNTRY1_fr_CD");
309        }
310    
311        public void testOverwrittenInVariant()
312        {
313            Messages messages = createMessages(MOCK1, new Locale("en", "US", "Tapestry"));
314    
315            check(messages, "overwritten-in-variant", "VARIANT1_en_US_Tapestry");
316    
317            messages = createMessages(MOCK1, new Locale("fr", "CD", "Foo"));
318    
319            check(messages, "overwritten-in-variant", "VARIANT1_fr_CD_Foo");
320        }
321    
322        private static final String MOCK2 = "/org/apache/tapestry/junit/MockPage2.page";
323    
324        /**
325         * Tests that the code that locates properties files can deal with the base path (i.e.,
326         * Foo.properties) doesn't exist.
327         */
328    
329        public void testMissingBase()
330        {
331            Messages messages = createMessages(MOCK2, new Locale("en", "US"));
332    
333            check(messages, "language-key", "LANGUAGE1");
334        }
335    
336        /**
337         * Tests that naming and search works correctly for locales that specify language and variant,
338         * but no country.
339         */
340    
341        public void testMissingCountry()
342        {
343            Messages messages = createMessages(MOCK2, new Locale("en", "", "Tapestry"));
344    
345            check(messages, "overwritten-in-variant", "VARIANT1_en__Tapestry");
346        }
347    
348        public void testDateFormatting()
349        {
350            Messages messages = createMessages(MOCK1, Locale.ENGLISH);
351    
352            Calendar c = new GregorianCalendar(1966, Calendar.DECEMBER, 24);
353    
354            Date d = c.getTime();
355    
356            assertEquals("A formatted date: 12/24/66", messages.format("using-date-format", d));
357        }
358    
359        public void testDateFormatLocalization()
360        {
361            Messages messages = createMessages(MOCK1, Locale.FRENCH);
362    
363            Calendar c = new GregorianCalendar(1966, Calendar.DECEMBER, 24);
364    
365            Date d = c.getTime();
366    
367            // French formatting puts the day before the month.
368    
369            assertEquals("A formatted date: 24/12/66", messages.format("using-date-format", d));
370    
371        }
372    
373        public void testMultipleLocalesWithNamespace()
374        {
375            ComponentMessagesSourceImpl source = new ComponentMessagesSourceImpl();
376            source.setClasspathResourceFactory(new ClasspathResourceFactoryImpl(new DefaultClassResolver()));
377            source.setComponentPropertySource(new NullComponentPropertySource());
378            source.setComponentResourceResolver(new ComponentResourceResolverImpl());
379    
380            IComponentSpecification spec = newSpec(MOCK1);
381            spec.setLocation(_locationFixture);
382    
383            INamespace namespace = new Namespace(null, null, newLibrarySpec(), null);
384    
385            IPage page = newPage(spec, source, new Locale("fr"));
386            page.setNamespace(namespace);
387    
388            assertEquals("multilocale_fr", source.getMessages(page).getMessage("multilocale"));
389    
390            page = newPage(spec, source, new Locale("en"));
391            page.setNamespace(namespace);
392    
393            assertEquals("multilocale", source.getMessages(page).getMessage("multilocale"));
394        }
395    
396        public void test_Component_Property_From_Application()
397        {
398            Messages messages = createFullMessages(MOCK1, new Locale("en", "US"));
399    
400            check(messages, "inherited.app.name", "Mock Wonder");
401        }
402    
403        public void test_Component_Property_From_Library_Overrides_Application()
404        {
405            Messages messages = createFullMessages(MOCK1, new Locale("en", "US"));
406    
407            check(messages, "library-overrides-application", "LIBRARY.override");
408        }
409    }