001: package org.apache.velocity.tools.generic;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.text.MessageFormat;
023: import java.util.List;
024: import java.util.Locale;
025: import java.util.Map;
026: import java.util.ResourceBundle;
027:
028: /**
029: * <p>Tool for accessing ResourceBundles and formatting messages therein.</p>
030: * <p><pre>
031: * Template example(s):
032: * $text.foo -> bar
033: * $text.hello.world -> Hello World!
034: * #set( $otherText = $text.bundle('otherBundle') )
035: * $otherText.foo -> woogie
036: * $otherText.bar -> The args are {0} and {1}.
037: * $otherText.bar.insert(4) -> The args are 4 and {1}.
038: * $otherText.bar.insert(4,true) -> The args are 4 and true.
039: *
040: * Toolbox configuration example:
041: * <tool>
042: * <key>text</key>
043: * <class>org.apache.velocity.tools.generic.ResourceTool</class>
044: * <parameter name="bundles" value="resources,com.foo.moreResources"/>
045: * <parameter name="locale" value="en_US"/>
046: * </tool>
047: * </pre></p>
048: *
049: * <p>This comes in very handy when internationalizing templates.
050: * Note that the default resource bundle baseName is "resources", and
051: * the default locale is the system locale. These may both be overridden
052: * in your toolbox config as demonstrated above.
053: * </p>
054: * <p>Also, be aware that very few performance considerations have been made
055: * in this initial version. It should do fine, but if you have performance
056: * issues, please report them to dev@velocity.apache.org, so we can make
057: * improvements.
058: * </p>
059: *
060: * @author Nathan Bubna
061: * @version $Revision: 498714 $ $Date: 2006-11-27 10:49:37 -0800 (Mon, 27 Nov 2006) $
062: * @since VelocityTools 1.3
063: */
064: public class ResourceTool {
065: public static final String BUNDLES_KEY = "bundles";
066: public static final String LOCALE_KEY = "locale";
067:
068: private String[] bundles = new String[] { "resources" };
069: private Locale locale = Locale.getDefault();
070:
071: protected final void setDefaultBundle(String bundle) {
072: if (bundle == null) {
073: throw new NullPointerException(
074: "Default bundle cannot be null");
075: }
076: this .bundles = new String[] { bundle };
077: }
078:
079: protected final String getDefaultBundle() {
080: return this .bundles[0];
081: }
082:
083: protected final void setDefaultLocale(Locale locale) {
084: if (locale == null) {
085: throw new NullPointerException(
086: "Default locale cannot be null");
087: }
088: this .locale = locale;
089: }
090:
091: protected final Locale getDefaultLocale() {
092: return this .locale;
093: }
094:
095: public void configure(Map params) {
096: ValueParser parser = new ValueParser(params);
097: String[] bundles = parser.getStrings(BUNDLES_KEY);
098: if (bundles != null) {
099: this .bundles = bundles;
100: }
101:
102: Locale locale = parser.getLocale(LOCALE_KEY);
103: if (locale != null) {
104: this .locale = locale;
105: }
106: }
107:
108: public Key get(String key) {
109: return new Key(key, this .bundles, this .locale, null);
110: }
111:
112: public Key bundle(String bundle) {
113: return new Key(null, new String[] { bundle }, this .locale, null);
114: }
115:
116: public Key locale(Locale locale) {
117: return new Key(null, this .bundles, locale, null);
118: }
119:
120: public Key insert(Object[] args) {
121: return new Key(null, this .bundles, this .locale, args);
122: }
123:
124: public Key insert(List args) {
125: return insert(args.toArray());
126: }
127:
128: public Key insert(Object arg) {
129: return insert(new Object[] { arg });
130: }
131:
132: public Key insert(Object arg0, Object arg1) {
133: return insert(new Object[] { arg0, arg1 });
134: }
135:
136: /**
137: * Returns the value for the specified key in the ResourceBundle for
138: * the specified basename and locale. If no such resource can be
139: * found, no errors are thrown and {@code null} is returned.
140: */
141: public Object get(String key, String baseName, Locale locale) {
142: if (baseName == null || key == null) {
143: return null;
144: }
145: if (locale == null) {
146: locale = this .locale;
147: }
148:
149: ResourceBundle bundle = ResourceBundle.getBundle(baseName,
150: locale);
151: if (bundle != null) {
152: try {
153: return bundle.getObject(key);
154: } catch (Exception e) {
155: // do nothing
156: }
157: }
158: return null;
159: }
160:
161: /**
162: * Retrieve a resource for the specified key from the first of the
163: * specified bundles in which a matching resource is found.
164: * If no resource is found, no exception will be thrown and {@code null}
165: * will be returned.
166: */
167: public Object get(String key, String[] bundles, Locale locale) {
168: for (int i = 0; i < bundles.length; i++) {
169: Object resource = get(key, bundles[i], locale);
170: if (resource != null) {
171: return resource;
172: }
173: }
174: return null;
175: }
176:
177: /**
178: * Renders the specified resource value and arguments as a String.
179: * If there are no arguments, then the String value of the resource
180: * is returned directly. If there are arguments, then the resource
181: * is treated as a {@link MessageFormat} pattern and used to format
182: * the specified argument values.
183: */
184: public String render(Object resource, Object[] args) {
185: String value = String.valueOf(resource);
186: if (args == null) {
187: return value;
188: }
189: return MessageFormat.format(value, args);
190: }
191:
192: /**
193: * Internal class used to enable an elegant syntax for accessing
194: * resources.
195: */
196: public final class Key {
197: // these are copied and/or altered when a mutator is called
198: private String[] bundles;
199: private String key;
200: private Locale locale;
201: private Object[] args;
202:
203: // these are not copied when a mutator is called
204: private boolean cached = false;
205: private Object rawValue;
206:
207: public Key(String key, String[] bundles, Locale locale,
208: Object[] args) {
209: this .key = key;
210: this .bundles = bundles;
211: this .locale = locale;
212: this .args = args;
213: }
214:
215: // ----- mutators (these return an altered duplicate) ---
216:
217: public Key get(String key) {
218: String newKey;
219: if (this .key == null) {
220: newKey = key;
221: } else {
222: newKey = this .key + '.' + key;
223: }
224: return new Key(newKey, this .bundles, this .locale, this .args);
225: }
226:
227: public Key bundle(String bundle) {
228: String[] newBundles = new String[] { bundle };
229: return new Key(this .key, newBundles, this .locale, this .args);
230: }
231:
232: public Key locale(Locale locale) {
233: return new Key(this .key, this .bundles, locale, this .args);
234: }
235:
236: public Key insert(Object[] args) {
237: Object[] newargs;
238: if (this .args == null) {
239: // we can just use the new ones
240: newargs = args;
241: } else {
242: // create a new array to hold both the new and old args
243: newargs = new Object[this .args.length + args.length];
244: // copy the old args into the newargs array
245: System.arraycopy(this .args, 0, newargs, 0,
246: this .args.length);
247: // copy the args to be inserted into the newargs array
248: System.arraycopy(args, 0, newargs, this .args.length,
249: args.length);
250: }
251: return new Key(this .key, this .bundles, this .locale, newargs);
252: }
253:
254: public Key insert(List args) {
255: return insert(args.toArray());
256: }
257:
258: public Key insert(Object arg) {
259: return insert(new Object[] { arg });
260: }
261:
262: public Key insert(Object arg0, Object arg1) {
263: return insert(new Object[] { arg0, arg1 });
264: }
265:
266: // --- accessors (these do not return a new Key) ---
267:
268: public boolean getExists() {
269: return (getRaw() != null);
270: }
271:
272: public Object getRaw() {
273: if (!this .cached) {
274: this .rawValue = ResourceTool.this .get(this .key,
275: this .bundles, this .locale);
276: this .cached = true;
277: }
278: return this .rawValue;
279: }
280:
281: public String toString() {
282: if (this .key == null) {
283: return "";
284: }
285: if (!getExists()) {
286: return "???" + this .key + "???";
287: }
288: return ResourceTool.this.render(this.rawValue, this.args);
289: }
290: }
291:
292: }
|