001 // Copyright 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.hivemind.impl;
016
017 import java.io.BufferedInputStream;
018 import java.io.IOException;
019 import java.io.InputStream;
020 import java.net.URL;
021 import java.util.ArrayList;
022 import java.util.Collections;
023 import java.util.HashMap;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Locale;
027 import java.util.Map;
028 import java.util.Properties;
029
030 import org.apache.hivemind.ApplicationRuntimeException;
031 import org.apache.hivemind.Resource;
032 import org.apache.hivemind.internal.MessageFinder;
033 import org.apache.hivemind.util.Defense;
034 import org.apache.hivemind.util.IOUtils;
035 import org.apache.hivemind.util.LocalizedNameGenerator;
036
037 /**
038 * @author Howard M. Lewis Ship
039 * @since 1.1
040 */
041 public class MessageFinderImpl implements MessageFinder
042 {
043 private static final String EXTENSION = ".properties";
044
045 private static class Localization
046 {
047 private Locale _locale;
048
049 private Resource _resource;
050
051 Localization(Locale locale, Resource resource)
052 {
053 _locale = locale;
054 _resource = resource;
055 }
056
057 public Locale getLocale()
058 {
059 return _locale;
060 }
061
062 public Resource getResource()
063 {
064 return _resource;
065 }
066
067 }
068
069 private Resource _baseResource;
070
071 private String _baseName;
072
073 private Map _propertiesMap = new HashMap();
074
075 private Properties _emptyProperties = new Properties();
076
077 public MessageFinderImpl(Resource baseResource)
078 {
079 Defense.notNull(baseResource, "baseResource");
080
081 _baseResource = baseResource;
082
083 // Strip off the extension to form the base name
084 // when building new (localized) resources.
085
086 String name = _baseResource.getName();
087 int dotx = name.lastIndexOf('.');
088
089 _baseName = name.substring(0, dotx);
090 }
091
092 public String getMessage(String key, Locale locale)
093 {
094 return findProperties(locale).getProperty(key);
095 }
096
097 private synchronized Properties findProperties(Locale locale)
098 {
099 Properties result = (Properties) _propertiesMap.get(locale);
100
101 // If doesn't exist, build it (which will update the
102 // propertiesMap as a side effect.
103
104 if (result == null)
105 result = buildProperties(locale);
106
107 return result;
108 }
109
110 private Properties buildProperties(Locale locale)
111 {
112 Properties result = _emptyProperties;
113
114 List localizations = findLocalizations(locale);
115
116 Iterator i = localizations.iterator();
117 while (i.hasNext())
118 {
119 Localization l = (Localization) i.next();
120
121 result = readProperties(l.getLocale(), l.getResource(), result);
122 }
123
124 return result;
125 }
126
127 /**
128 * Returns the properties, reading them if necessary. Properties may have been previously read
129 * for this locale, in which case the cached value is returned. Also, if the resource doesn't
130 * exist, then the parent is returned as is. Updates the propertiesMap cache.
131 */
132
133 private Properties readProperties(Locale locale, Resource propertiesResource, Properties parent)
134 {
135 Properties result = (Properties) _propertiesMap.get(locale);
136
137 if (result != null)
138 return result;
139
140 URL url = propertiesResource.getResourceURL();
141
142 if (url == null)
143 result = parent;
144 else
145 result = readPropertiesFile(url, parent);
146
147 _propertiesMap.put(locale, result);
148
149 return result;
150 }
151
152 private Properties readPropertiesFile(URL url, Properties parent)
153 {
154 InputStream stream = null;
155
156 Properties result = new Properties(parent);
157
158 try
159 {
160 stream = new BufferedInputStream(url.openStream());
161
162 result.load(stream);
163
164 stream.close();
165
166 stream = null;
167 }
168 catch (IOException ex)
169 {
170 throw new ApplicationRuntimeException(ImplMessages.unableToReadMessages(url), ex);
171
172 }
173 finally
174 {
175 IOUtils.close(stream);
176 }
177
178 return result;
179 }
180
181 /**
182 * Returns a List of Localizations, in order from most generic (i.e., hivemodule.properties) to
183 * most specific (i.e., hivemodule_en_US_yokel.properties).
184 */
185
186 private List findLocalizations(Locale locale)
187 {
188 List result = new ArrayList();
189
190 LocalizedNameGenerator g = new LocalizedNameGenerator(_baseName, locale, EXTENSION);
191
192 while (g.more())
193 {
194 String name = g.next();
195
196 Localization l = new Localization(g.getCurrentLocale(), _baseResource
197 .getRelativeResource(name));
198
199 result.add(l);
200 }
201
202 Collections.reverse(result);
203
204 return result;
205 }
206 }