Clover coverage report - Code Coverage for tapestry release 4.0-beta-2
Coverage timestamp: Sat Jul 9 2005 22:02:17 EDT
file stats: LOC: 347   Methods: 16
NCLOC: 219   Classes: 1
30 day Evaluation License registered to hlship@comcast.net Your 30 day evaluation period has expired. Please visit http://www.cenqua.com to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
ComponentMessagesSourceImpl.java 91.7% 97.7% 93.8% 96.1%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.services.impl;
 16   
 17    import java.io.BufferedInputStream;
 18    import java.io.IOException;
 19    import java.io.InputStream;
 20    import java.net.URL;
 21    import java.util.ArrayList;
 22    import java.util.Collections;
 23    import java.util.HashMap;
 24    import java.util.Iterator;
 25    import java.util.List;
 26    import java.util.Locale;
 27    import java.util.Map;
 28    import java.util.Properties;
 29   
 30    import org.apache.hivemind.ApplicationRuntimeException;
 31    import org.apache.hivemind.Messages;
 32    import org.apache.hivemind.Resource;
 33    import org.apache.hivemind.util.Defense;
 34    import org.apache.hivemind.util.LocalizedNameGenerator;
 35    import org.apache.tapestry.IComponent;
 36    import org.apache.tapestry.INamespace;
 37    import org.apache.tapestry.event.ResetEventListener;
 38    import org.apache.tapestry.services.ComponentMessagesSource;
 39    import org.apache.tapestry.services.ComponentPropertySource;
 40    import org.apache.tapestry.util.text.LocalizedProperties;
 41   
 42    /**
 43    * Service used to access localized properties for a component.
 44    *
 45    * @author Howard Lewis Ship
 46    * @since 2.0.4
 47    */
 48   
 49    public class ComponentMessagesSourceImpl implements ComponentMessagesSource, ResetEventListener
 50    {
 51    private Properties _emptyProperties = new Properties();
 52   
 53    private static final String SUFFIX = ".properties";
 54   
 55    /**
 56    * The name of the component/application/etc property that will be used to determine the
 57    * encoding to use when loading the messages
 58    */
 59   
 60    public static final String MESSAGES_ENCODING_PROPERTY_NAME = "org.apache.tapestry.messages-encoding";
 61   
 62    /**
 63    * Map of Maps. The outer map is keyed on component specification location (a{@link Resource}.
 64    * This inner map is keyed on locale and the value is a {@link Properties}.
 65    */
 66   
 67    private Map _componentCache = new HashMap();
 68   
 69    private ComponentPropertySource _componentPropertySource;
 70   
 71    /**
 72    * Returns an instance of {@link Properties}containing the properly localized messages for the
 73    * component, in the {@link Locale}identified by the component's containing page.
 74    */
 75   
 76  29 protected synchronized Properties getLocalizedProperties(IComponent component)
 77    {
 78  29 Defense.notNull(component, "component");
 79   
 80  29 Resource specificationLocation = component.getSpecification().getSpecificationLocation();
 81  29 Locale locale = component.getPage().getLocale();
 82   
 83  29 Map propertiesMap = findPropertiesMapForResource(specificationLocation);
 84   
 85  29 Properties result = (Properties) propertiesMap.get(locale);
 86   
 87  29 if (result == null)
 88    {
 89   
 90    // Not found, create it now.
 91   
 92  28 result = assembleComponentProperties(
 93    component,
 94    specificationLocation,
 95    propertiesMap,
 96    locale);
 97   
 98  28 propertiesMap.put(locale, result);
 99    }
 100   
 101  29 return result;
 102    }
 103   
 104  57 private Map findPropertiesMapForResource(Resource resource)
 105    {
 106  57 Map result = (Map) _componentCache.get(resource);
 107   
 108  57 if (result == null)
 109    {
 110  46 result = new HashMap();
 111  46 _componentCache.put(resource, result);
 112    }
 113   
 114  57 return result;
 115    }
 116   
 117  28 private Properties getNamespaceProperties(IComponent component, Locale locale)
 118    {
 119  28 INamespace namespace = component.getNamespace();
 120   
 121  28 Resource namespaceLocation = namespace.getSpecificationLocation();
 122   
 123  28 Map propertiesMap = findPropertiesMapForResource(namespaceLocation);
 124   
 125  28 Properties result = (Properties) propertiesMap.get(locale);
 126   
 127  28 if (result == null)
 128    {
 129  28 result = assembleNamespaceProperties(namespace, propertiesMap, locale);
 130   
 131  28 propertiesMap.put(locale, result);
 132    }
 133   
 134  28 return result;
 135    }
 136   
 137  28 private Properties assembleComponentProperties(IComponent component,
 138    Resource baseResourceLocation, Map propertiesMap, Locale locale)
 139    {
 140  28 List localizations = findLocalizationsForResource(baseResourceLocation, locale);
 141   
 142  28 Properties parent = getNamespaceProperties(component, locale);
 143   
 144  28 Iterator i = localizations.iterator();
 145   
 146  28 while (i.hasNext())
 147    {
 148  74 ResourceLocalization rl = (ResourceLocalization) i.next();
 149   
 150  74 Locale l = rl.getLocale();
 151   
 152  74 Properties properties = (Properties) propertiesMap.get(l);
 153   
 154  74 if (properties == null)
 155    {
 156  67 properties = readComponentProperties(component, l, rl.getResource(), parent);
 157   
 158  67 propertiesMap.put(l, properties);
 159    }
 160   
 161  74 parent = properties;
 162    }
 163   
 164  28 return parent;
 165    }
 166   
 167  28 private Properties assembleNamespaceProperties(INamespace namespace, Map propertiesMap,
 168    Locale locale)
 169    {
 170  28 List localizations = findLocalizationsForResource(
 171    namespace.getSpecificationLocation(),
 172    locale);
 173   
 174    // Build them back up in reverse order.
 175   
 176  28 Properties parent = _emptyProperties;
 177   
 178  28 Iterator i = localizations.iterator();
 179   
 180  28 while (i.hasNext())
 181    {
 182  74 ResourceLocalization rl = (ResourceLocalization) i.next();
 183   
 184  74 Locale l = rl.getLocale();
 185   
 186  74 Properties properties = (Properties) propertiesMap.get(l);
 187   
 188  74 if (properties == null)
 189    {
 190  67 properties = readNamespaceProperties(namespace, l, rl.getResource(), parent);
 191   
 192  67 propertiesMap.put(l, properties);
 193    }
 194   
 195  74 parent = properties;
 196    }
 197   
 198  28 return parent;
 199   
 200    }
 201   
 202    /**
 203    * Finds the localizations of the provided resource. Returns a List of
 204    * {@link ResourceLocalization}(each pairing a locale with a localized resource). The list is
 205    * ordered from most general (i.e., "foo.properties") to most specific (i.e.,
 206    * "foo_en_US_yokel.properties").
 207    */
 208   
 209  56 private List findLocalizationsForResource(Resource resource, Locale locale)
 210    {
 211  56 List result = new ArrayList();
 212   
 213  56 String baseName = extractBaseName(resource);
 214   
 215  56 LocalizedNameGenerator g = new LocalizedNameGenerator(baseName, locale, SUFFIX);
 216   
 217  56 while (g.more())
 218    {
 219  148 String localizedName = g.next();
 220  148 Locale l = g.getCurrentLocale();
 221  148 Resource localizedResource = resource.getRelativeResource(localizedName);
 222   
 223  148 result.add(new ResourceLocalization(l, localizedResource));
 224    }
 225   
 226  56 Collections.reverse(result);
 227   
 228  56 return result;
 229    }
 230   
 231  56 private String extractBaseName(Resource baseResourceLocation)
 232    {
 233  56 String fileName = baseResourceLocation.getName();
 234  56 int dotx = fileName.lastIndexOf('.');
 235   
 236  56 return fileName.substring(0, dotx);
 237    }
 238   
 239  67 private Properties readComponentProperties(IComponent component, Locale locale,
 240    Resource propertiesResource, Properties parent)
 241    {
 242  67 String encoding = getComponentMessagesEncoding(component, locale);
 243   
 244  67 return readPropertiesResource(propertiesResource.getResourceURL(), encoding, parent);
 245    }
 246   
 247  67 private Properties readNamespaceProperties(INamespace namespace, Locale locale,
 248    Resource propertiesResource, Properties parent)
 249    {
 250  67 String encoding = getNamespaceMessagesEncoding(namespace, locale);
 251   
 252  67 return readPropertiesResource(propertiesResource.getResourceURL(), encoding, parent);
 253    }
 254   
 255  134 private Properties readPropertiesResource(URL resourceURL, String encoding, Properties parent)
 256    {
 257  134 if (resourceURL == null)
 258  60 return parent;
 259   
 260  74 Properties result = new Properties(parent);
 261   
 262  74 LocalizedProperties wrapper = new LocalizedProperties(result);
 263   
 264  74 InputStream input = null;
 265   
 266  74 try
 267    {
 268  74 input = new BufferedInputStream(resourceURL.openStream());
 269   
 270  74 if (encoding == null)
 271  70 wrapper.load(input);
 272    else
 273  4 wrapper.load(input, encoding);
 274   
 275  74 input.close();
 276    }
 277    catch (IOException ex)
 278    {
 279  0 throw new ApplicationRuntimeException(ImplMessages.unableToLoadProperties(
 280    resourceURL,
 281    ex), ex);
 282    }
 283    finally
 284    {
 285  74 close(input);
 286    }
 287   
 288  74 return result;
 289    }
 290   
 291  74 private void close(InputStream is)
 292    {
 293  74 if (is != null)
 294  74 try
 295    {
 296  74 is.close();
 297    }
 298    catch (IOException ex)
 299    {
 300    // Ignore.
 301    }
 302    }
 303   
 304    /**
 305    * Clears the cache of read properties files.
 306    */
 307   
 308  0 public synchronized void resetEventDidOccur()
 309    {
 310  0 _componentCache.clear();
 311    }
 312   
 313  29 public Messages getMessages(IComponent component)
 314    {
 315  29 return new ComponentMessages(component.getPage().getLocale(),
 316    getLocalizedProperties(component));
 317    }
 318   
 319  67 private String getComponentMessagesEncoding(IComponent component, Locale locale)
 320    {
 321  67 String encoding = _componentPropertySource.getLocalizedComponentProperty(
 322    component,
 323    locale,
 324    MESSAGES_ENCODING_PROPERTY_NAME);
 325   
 326  67 if (encoding == null)
 327  65 encoding = _componentPropertySource.getLocalizedComponentProperty(
 328    component,
 329    locale,
 330    TemplateSourceImpl.TEMPLATE_ENCODING_PROPERTY_NAME);
 331   
 332  67 return encoding;
 333    }
 334   
 335  67 private String getNamespaceMessagesEncoding(INamespace namespace, Locale locale)
 336    {
 337  67 return _componentPropertySource.getLocalizedNamespaceProperty(
 338    namespace,
 339    locale,
 340    MESSAGES_ENCODING_PROPERTY_NAME);
 341    }
 342   
 343  23 public void setComponentPropertySource(ComponentPropertySource componentPropertySource)
 344    {
 345  23 _componentPropertySource = componentPropertySource;
 346    }
 347    }