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 if (dotx < 1) {
089 _baseName = name;
090 } else {
091 _baseName = name.substring(0, dotx);
092 }
093 }
094
095 public String getMessage(String key, Locale locale)
096 {
097 return findProperties(locale).getProperty(key);
098 }
099
100 private synchronized Properties findProperties(Locale locale)
101 {
102 Properties result = (Properties) _propertiesMap.get(locale);
103
104 // If doesn't exist, build it (which will update the
105 // propertiesMap as a side effect.
106
107 if (result == null)
108 result = buildProperties(locale);
109
110 return result;
111 }
112
113 private Properties buildProperties(Locale locale)
114 {
115 Properties result = _emptyProperties;
116
117 List localizations = findLocalizations(locale);
118
119 Iterator i = localizations.iterator();
120 while (i.hasNext())
121 {
122 Localization l = (Localization) i.next();
123
124 result = readProperties(l.getLocale(), l.getResource(), result);
125 }
126
127 return result;
128 }
129
130 /**
131 * Returns the properties, reading them if necessary. Properties may have been previously read
132 * for this locale, in which case the cached value is returned. Also, if the resource doesn't
133 * exist, then the parent is returned as is. Updates the propertiesMap cache.
134 */
135
136 private Properties readProperties(Locale locale, Resource propertiesResource, Properties parent)
137 {
138 Properties result = (Properties) _propertiesMap.get(locale);
139
140 if (result != null)
141 return result;
142
143 URL url = propertiesResource.getResourceURL();
144
145 if (url == null)
146 result = parent;
147 else
148 result = readPropertiesFile(url, parent);
149
150 _propertiesMap.put(locale, result);
151
152 return result;
153 }
154
155 private Properties readPropertiesFile(URL url, Properties parent)
156 {
157 InputStream stream = null;
158
159 Properties result = new Properties(parent);
160
161 try
162 {
163 stream = new BufferedInputStream(url.openStream());
164
165 result.load(stream);
166
167 stream.close();
168
169 stream = null;
170 }
171 catch (IOException ex)
172 {
173 throw new ApplicationRuntimeException(ImplMessages.unableToReadMessages(url), ex);
174
175 }
176 finally
177 {
178 IOUtils.close(stream);
179 }
180
181 return result;
182 }
183
184 /**
185 * Returns a List of Localizations, in order from most generic (i.e., hivemodule.properties) to
186 * most specific (i.e., hivemodule_en_US_yokel.properties).
187 */
188
189 private List findLocalizations(Locale locale)
190 {
191 List result = new ArrayList();
192
193 LocalizedNameGenerator g = new LocalizedNameGenerator(_baseName, locale, EXTENSION);
194
195 while (g.more())
196 {
197 String name = g.next();
198
199 Localization l = new Localization(g.getCurrentLocale(), _baseResource
200 .getRelativeResource(name));
201
202 result.add(l);
203 }
204
205 Collections.reverse(result);
206
207 return result;
208 }
209 }