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 }