001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.tags.databinding.bundle;
020:
021: import javax.servlet.jsp.JspException;
022: import javax.servlet.jsp.tagext.TryCatchFinally;
023: import javax.servlet.http.HttpServletRequest;
024: import java.util.Locale;
025:
026: import org.apache.beehive.netui.tags.AbstractClassicTag;
027: import org.apache.beehive.netui.util.Bundle;
028: import org.apache.beehive.netui.script.common.BundleMap;
029:
030: /**
031: * <p>Declares a {@link java.util.ResourceBundle java.util.ResourceBundle} as a source for displaying internationalized
032: * messages. The declared resource bundle is accessible using the <code>${bundle...}</code> data binding context.
033: * The required <code>name</code> attribute specifies the identifier used to refer to the ResourceBundle in an expression.
034: * For example:
035: * <br/>
036: * <pre>
037: * <netui-data:declareBundle name="someMessages" bundlePath="com/foobar/resources/WebAppMessages"/>
038: * </pre>
039: * <p>
040: * This tag declares a bundle that is referenced in a data binding expression as <code>${bundle.someMessages}</code>.
041: * </p>
042: * <p>
043: * The bundle that is referenced depends on the
044: * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Locale.html" target="_blank">java.util.Locale</a>
045: * specified. The resource bundle properties files that are accessed are located
046: * in the package <code>com/foobar/resources</code> with the root properties file
047: * name of <code>WebAppMessages</code>. The naming conventions for properties can
048: * be found in Sun's Java documentation at
049: * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html#getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)" target="_blank">ResourceBundle.getBundle(String,Locale, ClassLoader)</a> .
050: * These files must be located in a classpath that is available to the web application. Often, they are stored in
051: * <code>WEB-INF/classes</code>. If the properties file contains a key called <code>helloWorld</code>, then the
052: * expression <code>${bundle.someMessages.helloWorld}</code> would look-up the message
053: * matching the Locale specified on the tag. Bundle binding expressions can be
054: * used in any data bindable <netui...> tag attribute.
055: * </p>
056: * <p>It is possible to have keys that contain multiple words separated by spaces, commas, or periods. If this is the
057: * case, then you must use slightly differente syntax to reference those keys in your data binding statement. The
058: * following list illustrates three ways to access the key <code>My helloWorld</code> from the
059: * <code>someMessages</code> property file:
060: * </p>
061: * <ul>
062: * <li><code><netui:span value="${bundle.someMessages['My helloWorld']}"/></code></li>
063: * <li><code><netui:span value='${bundle.someMessages["My helloWorld"]}'/></code></li>
064: * <li><code><netui:span value="${bundle.someMessages[\"My helloWorld\"]}"/></code></li>
065: * </ul>
066: * <p>
067: * <b>Note:</b> the name <code>default</code> is a bundle identifier that is reserved for use by the
068: * <netui-data:declareBundle> tag. If this value is used for the <code>name</code> attribute on a
069: * <netui-data:declareBundle> tag, an error will be reported in the page. The <code>default</code> bundle
070: * is reserved for use when accessing internationalized messages from the "current" Struts module's
071: * default properties file.</p>
072: * <p/>
073: * <p>
074: * This tag provides a high level of customizability for rendering internationalized messages. Specifically, the
075: * {@link Locale} for which to look-up messages can be specified on the <netui-data:declareBundle>
076: * tag. By default, the Locale for the current request is used, but this Locale can be overridden by
077: * setting the <code>language</code>, <code>country</code>, and <code>variant</code> tag attributes
078: * as necessary. See java.util.Locale for more information on the possible values for these attributes.</p>
079: * <p>The Locale can be overridden by setting these attributes in three combinations:</p>
080: * <blockquote>
081: * <ul>
082: * <li>country, language, variant</li>
083: * <li>country, language</li>
084: * <li>country</li>
085: * </ul>
086: * </blockquote>
087: * Any other combinations will throw an exception.
088: * </p>
089: * <p>The <netui-data:declareBundle> tag and the Struts <i18n:getMessage> tags have the following differences.
090: * The <netui-data:declareBundle> tag lets you customize the use of a particular resource bundle with attributes
091: * to set the country, language, and variant explicitly, but it does not write a message out to the JSP page.
092: * Writing out a message from this bundle is done inside of any of the other tags using the <code>bundle</code>
093: * JSP EL implicit object.
094: * <p/>
095: * <pre>
096: * <netui:span value="<b>${bundle.messages.messageKey}</b>"/>
097: * </pre>
098: * <p/>
099: * <p>The Struts <i18n:getMessage> tag is used to access a bundle <i>and</i> write the message out. It
100: * is roughly equivalent to doing the following:
101: * <p/>
102: * <pre>
103: * <netui-data:declareBundle bundlePath="com/foobar/resources/messages" name="messages"/>
104: * <netui:span value="${bundle.messages.messageKey}"/></pre>
105: * <p/>
106: * <p>An advantage of using the ${bundle...} data binding context, is that it lets you write into the
107: * <span>...</span> that the <netui:span> creates, or into a <neuti:checkBox> name, etc.
108: * just like using a regular String.
109: *
110: * @jsptagref.tagdescription
111: * <p>Declares a {@link java.util.ResourceBundle java.util.ResourceBundle} as a source for displaying internationalized
112: * messages. The declared resource bundle is accessible using the <code>${bundle...}</code> data binding context.
113: * The required <code>name</code> attribute specifies the identifier used to refer to the ResourceBundle in an expression.
114: * For example:
115: * <br/>
116: * <pre>
117: * <netui-data:declareBundle name="someMessages" bundlePath="com/foobar/resources/WebAppMessages"/>
118: * </pre>
119: * <p>
120: * This tag declares a bundle that is referenced in a data binding expression as <code>${bundle.someMessages}</code>.
121: * </p>
122: * <p>
123: * The bundle that is referenced depends on the
124: * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Locale.html" target="_blank">java.util.Locale</a>
125: * specified. The resource bundle properties files that are accessed are located
126: * in the package <code>com/foobar/resources</code> with the root properties file
127: * name of <code>WebAppMessages</code>. The naming conventions for properties can
128: * be found in Sun's Java documentation at
129: * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html#getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)" target="_blank">ResourceBundle.getBundle(String,Locale, ClassLoader)</a> .
130: * These files must be located in a classpath that is available to the web application. Often, they are stored in
131: * <code>WEB-INF/classes</code>. If the properties file contains a key called <code>helloWorld</code>, then the
132: * expression <code>${bundle.someMessages.helloWorld}</code> would look-up the message
133: * matching the Locale specified on the tag. Bundle binding expressions can be
134: * used in any data bindable <netui...> tag attribute.
135: * </p>
136: * <p>It is possible to have keys that contain multiple words separated by spaces,
137: * commas, or periods. If this is the case, then you must use slightly different
138: * syntax to reference those keys in your data binding statement. The following
139: * list illustrates three ways to access the key <code>My helloWorld</code> from the <code>someMessages</code>
140: * property file:
141: * </p>
142: * <ul>
143: * <li><code><netui:span value="${bundle.someMessages['My helloWorld']}"/></code></li>
144: * <li><code><netui:span value='${bundle.someMessages["My helloWorld"]}'/></code></li>
145: * <li><code><netui:span value="${bundle.someMessages[\"My helloWorld\"]}"/></code></li>
146: * </ul>
147: * <p>
148: * <b>Note:</b> the name <code>default</code> is a bundle identifier that is reserved for use by the
149: * <netui-data:declareBundle> tag. If this value is used for the <code>name</code> attribute on a
150: * <netui-data:declareBundle> tag,
151: * an error will be reported in the page. The <code>default</code> bundle is reserved for use when
152: * accessing internationalized messages from the "current" Struts module's default properties file.</p>
153: * <p/>
154: * <p>
155: * This tag provides a high level of customizability for rendering internationalized messages.
156: * Specifically, the Locale for which to look-up messages can be specified on the
157: * <netui-data:declareBundle>
158: * tag. By default, the Locale for the current request is used, but this Locale can be overridden by
159: * setting the <code>language</code>, <code>country</code>, and <code>variant</code> tag attributes
160: * as necessary. See java.util.Locale for more information on the possible values for these attributes.</p>
161: * <p>The Locale can be overridden by setting these attributes in three combinations:</p>
162: * <blockquote>
163: * <ul>
164: * <li>country, language, variant</li>
165: * <li>country, language</li>
166: * <li>country</li>
167: * </ul>
168: * </blockquote>
169: * Any other combinations will throw an exception.
170: * </p>
171: * <p>The <netui-data:declareBundle> tag and the Struts <i18n:getMessage> tags have the following differences.
172: * The <netui-data:declareBundle> tag lets you customize the use of a particular resource bundle with attributes
173: * to set the country, language, and variant explicitly, but it does not write a message out to the JSP page.
174: * Writing out a message from this bundle is done inside of any of the other tags using the <code>bundle</code>
175: * JSP EL implicit object.
176: * <p/>
177: * <pre> <netui:span value="<b>${bundle.messages.messageKey}</b>"/></pre>
178: * <p/>
179: * <p>The Struts <i18n:getMessage> tag is used to access a bundle
180: * <i>and</i> write the message out. It is roughly equivalent to doing the following:
181: * <p/>
182: * <pre>
183: * <netui-data:declareBundle bundlePath="com/foobar/resources/messages" name="messages"/>
184: * <netui:span value="${bundle.messages.messageKey}"/></pre>
185: * <p/>
186: * <p>An advantage of using the ${bundle...} data binding context, is that it lets you write into the
187: * <span>...</span> that the <netui:span> creates, or into a <neuti:checkBox> name, etc.
188: * just like using a regular String.
189: *
190: * @netui:tag name="declareBundle"
191: * description="Use this tag to declare a resource bundle that is available in the bundle databinding context"
192: */
193: public class DeclareBundle extends AbstractClassicTag implements
194: TryCatchFinally {
195:
196: private String _name = null;
197: private String _bundlePath = null;
198: private String _language = null;
199: private String _country = null;
200: private String _variant = null;
201:
202: /**
203: * Get the name of this tag. This is used to identify the type of this tag
204: * for reporting tag errors.
205: *
206: * @return a constant String representing the name of this tag.
207: */
208: public String getTagName() {
209: return "DeclareBundle";
210: }
211:
212: /**
213: * Set the language to use when looking-up resource bundle messages.
214: * The two-letter lowercase ISO-639 language code for the Locale from which to look-up resource bundle messages.
215: * This value is used to further specify the name of the .properties file from which message keys will be read.
216: *
217: * @param language the two-letter lowercase ISO-639 code for a language.
218: * @jsptagref.attributedescription
219: * Set the language to use when looking-up resource bundle messages.
220: * The two-letter lowercase ISO-639 language code for the Locale from which to look-up resource bundle messages.
221: * This value is used to further specify the name of the .properties file from which message keys will be read.
222: * @jsptagref.attributesyntaxvalue <i>string_language</i>
223: * @netui:attribute required="false" rtexprvalue="true"
224: */
225: public void setLanguage(String language) {
226: _language = language;
227: }
228:
229: /**
230: * Set the country to use when looking-up resource bundle messages. The two-letter uppercase ISO-3166
231: * country / region code for the Locale from which to look-up resource bundle messages. This value is used to
232: * further specify the name of the .properties file from which message keys will be read.
233: *
234: * @param country the two-letter uppercase ISO-3166 code for a country
235: * @jsptagref.attributedescription
236: * Set the country to use when looking-up resource bundle messages. The two-letter uppercase ISO-3166
237: * country / region code for the Locale from which to look-up resource bundle messages. This value is used to
238: * further specify the name of the .properties file from which message keys will be read.
239: * @jsptagref.attributesyntaxvalue <i>string_country</i>
240: * @netui:attribute required="false" rtexprvalue="true"
241: */
242: public void setCountry(String country) {
243: _country = country;
244: }
245:
246: /**
247: * Sets a vendor / browser specific code for further parameterizign the Locale from which to look-up resource bundle messages.
248: *
249: * @param variant the variant
250: * @jsptagref.attributedescription
251: * Sets a vendor / browser specific code for further parameterizign the Locale from which to look-up resource bundle messages.
252: * @jsptagref.attributesyntaxvalue <i>string_variant</i>
253: * @netui:attribute required="false" rtexprvalue="true"
254: */
255: public void setVariant(String variant) {
256: _variant = variant;
257: }
258:
259: /**
260: * The name inside of the ${bundle...} databinding context under which the properties in this bundle are available.
261: * The identifier <code>default</code> is an illegal value for this attribute and is reserved for use by this tag.
262: *
263: * @param name the name of the bundle
264: * @jsptagref.attributedescription
265: * The name inside of the ${bundle...} databinding context under which the properties in this bundle are available.
266: * The identifier <code>default</code> is an illegal value for this attribute and is reserved for use by this tag.
267: * @jsptagref.attributesyntaxvalue <i>string_name</i>
268: * @netui:attribute required="true"
269: */
270: public void setName(String name) {
271: _name = name;
272: }
273:
274: /**
275: * Set the path to the resource bundle's properties files. This can be a slash or dot delimited classpath.
276: * Valid values should appear as:
277: * <blockquote>
278: * <ul>
279: * <li>com/foobar/resources/WebAppProperties</li>
280: * <li>com.foobar.resources.WebAppProperties</li>
281: * </ul>
282: * </blockquote>
283: * <p>These are treated as equivalent values. The {@link java.util.ResourceBundle} class
284: * will handle appending the <code>.properties</code> file type and locale information
285: * as necessary. See {@link java.util.ResourceBundle#getBundle(String, Locale, ClassLoader)}
286: * for the <code>.properties</code> file naming conventions. These files must be available in
287: * classpath for the webapp in order to be successfully located.
288: *
289: * @param bundlePath the path to the bundle's properties files.
290: * @jsptagref.attributedescription
291: * Set the path to the resource bundle's properties files. This can be a slash or dot delimited classpath.
292: * Valid values should appear as:
293: * <blockquote>
294: * <ul>
295: * <li>com/foobar/resources/WebAppProperties</li>
296: * <li>com.foobar.resources.WebAppProperties</li>
297: * </ul>
298: * </blockquote>
299: * <p>These are treated as equivalent values. The {@link java.util.ResourceBundle} class
300: * will handle appending the <code>.properties</code> file type and locale information
301: * as necessary. See {@link java.util.ResourceBundle#getBundle(String, Locale, ClassLoader)}
302: * for the <code>.properties</code> file naming conventions. These files must be available in
303: * classpath for the webapp in order to be successfully located.
304: * @jsptagref.attributesyntaxvalue <i>string_bundlePath</i>
305: * @netui:attribute required="true"
306: */
307: public void setBundlePath(String bundlePath) {
308: _bundlePath = bundlePath;
309: }
310:
311: /**
312: * Start the JSP rendering lifecycle for this tag; it skips its body.
313: *
314: * @return {@link #SKIP_BODY}
315: */
316: public int doStartTag() {
317: return SKIP_BODY;
318: }
319:
320: /**
321: * Register a ResourceBundle that is available for the scope of this page. Errors raised
322: * during the execution of this tag will be reported in the page, which will continue
323: * rendering.
324: *
325: * @return {@link #EVAL_PAGE}
326: * @throws JspException if error conditions are encountered during this method which can not
327: * be reported in the page.
328: */
329: public int doEndTag() throws JspException {
330:
331: if (_name.length() == 0) {
332: String msg = Bundle.getErrorString(
333: "Tags_DeclareBundle_invalidName",
334: new Object[] { _name });
335: registerTagError(msg, null);
336: }
337:
338: if (_name.equals(BundleMap.DEFAULT_STRUTS_BUNDLE_NAME)) {
339: String msg = Bundle.getErrorString(
340: "Tags_DeclareBundle_defaultIsReservedWord", null);
341: registerTagError(msg, null);
342: }
343:
344: if (_bundlePath.length() == 0) {
345: String msg = Bundle.getErrorString(
346: "Tags_DeclareBundle_invalidResourcePath",
347: new Object[] { _bundlePath });
348: registerTagError(msg, null);
349: }
350:
351: Locale locale = getCurrentLocale();
352:
353: if (hasErrors()) {
354: reportErrors();
355: return EVAL_PAGE;
356: }
357:
358: BundleMap bundleMap = null;
359: Object obj = pageContext.getAttribute("bundle");
360: if (obj == null) {
361: // NetUI v2 -- in JSP 2.0 EL, the BundleMap is dropped into the PageContext so that
362: // it's available to the EL runtime.
363: bundleMap = new BundleMap((HttpServletRequest) pageContext
364: .getRequest(), pageContext.getServletContext());
365: pageContext.setAttribute("bundle", bundleMap);
366: } else if (!(obj instanceof BundleMap)) {
367: String msg = Bundle.getErrorString(
368: "Tags_DeclareBundle_wrongContextType",
369: new Object[] { obj.getClass().getName() });
370: registerTagError(msg, null);
371: if (hasErrors())
372: reportErrors();
373: return EVAL_PAGE;
374: } else
375: bundleMap = (BundleMap) obj;
376:
377: bundleMap.registerResourceBundle(_name, _bundlePath, locale);
378:
379: return EVAL_PAGE;
380: }
381:
382: public void doFinally() {
383: localRelease();
384: }
385:
386: public void doCatch(Throwable t) throws Throwable {
387: throw t;
388: }
389:
390: /**
391: * Reset all of the fields of this tag.
392: */
393: protected void localRelease() {
394: super .localRelease();
395: _name = null;
396: _bundlePath = null;
397: _language = null;
398: _country = null;
399: _variant = null;
400: }
401:
402: private Locale getCurrentLocale() throws JspException {
403: if (_language == null && _country == null && _variant == null)
404: return getUserLocale();
405:
406: if (hasErrors())
407: return null;
408:
409: if (_language != null && _country != null && _variant != null)
410: return new Locale(_language, _country, _variant);
411: else if (_language != null && _country != null)
412: return new Locale(_language, _country);
413: else if (_language != null)
414: return new Locale(_language);
415: else if (_country != null || _variant != null) {
416: String msg = Bundle.getErrorString(
417: "Tags_DeclareBundle_invalidLocaleOverride",
418: new Object[] { _language, _country, _variant });
419: registerTagError(msg, null);
420: }
421:
422: return getUserLocale();
423: }
424: }
|