001/*
002 * File   : $Source$
003 * Date   : $Date$
004 * Version: $Revision$
005 *
006 * This library is part of OpenCms -
007 * the Open Source Content Management System
008 *
009 * Copyright (C) 2002 - 2009 Alkacon Software (http://www.alkacon.com)
010 *
011 * This library is free software; you can redistribute it and/or
012 * modify it under the terms of the GNU Lesser General Public
013 * License as published by the Free Software Foundation; either
014 * version 2.1 of the License, or (at your option) any later version.
015 *
016 * This library is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 * Lesser General Public License for more details.
020 *
021 * For further information about Alkacon Software, please see the
022 * company website: http://www.alkacon.com
023 *
024 * For further information about OpenCms, please see the
025 * project website: http://www.opencms.org
026 *
027 * You should have received a copy of the GNU Lesser General Public
028 * License along with this library; if not, write to the Free Software
029 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
030 */
031
032package org.opencms.jsp;
033
034import org.opencms.i18n.CmsResourceBundleLoader;
035
036import java.util.Locale;
037import java.util.MissingResourceException;
038import java.util.ResourceBundle;
039
040import javax.servlet.ServletResponse;
041import javax.servlet.jsp.PageContext;
042import javax.servlet.jsp.jstl.fmt.LocalizationContext;
043
044import org.apache.taglibs.standard.tag.common.fmt.BundleSupport;
045import org.apache.taglibs.standard.tag.common.fmt.SetLocaleSupport;
046import org.apache.taglibs.standard.tag.el.fmt.BundleTag;
047
048/**
049 * Provides tag access to OpenCms resource bundles.<p>
050 *
051 * This replaces the <code>&lt;fmt:bundle basename=""&gt;</code> tag which is not capable of using OpenCms resource bundles.<p>
052 *
053 * You can use <code>&lt;fmt:message key=""&gt;</code> tags inside the <code>&lt;cms:bundle basename=""&gt;</code> tag as usual.
054 *
055 * @since 8.5.2
056 */
057public class CmsJspTagBundle extends BundleTag {
058
059    /** Serial version UID required for safe serialisation. */
060    private static final long serialVersionUID = 7592250223728101278L;
061
062    /** The basename attribute value. */
063    private String m_basename;
064
065    /** The localization to use. */
066    private LocalizationContext m_locCtxt;
067
068    /**
069     * Empty constructor.<p>
070     */
071    public CmsJspTagBundle() {
072
073        super();
074        m_basename = null;
075        m_locCtxt = null;
076    }
077
078    /**
079     * Returns the initialized localization context.<p>
080     * @param pc the current page context
081     * @param basename the bas name of the bundle
082     *
083     * @return the initialized localization context
084     */
085    public static LocalizationContext getLocalizationContext(PageContext pc, String basename) {
086
087        LocalizationContext locCtxt = null;
088        ResourceBundle bundle = null;
089
090        if ((basename == null) || basename.equals("")) {
091            return new LocalizationContext();
092        }
093
094        // Try preferred locales
095        Locale pref = getLocale(pc, javax.servlet.jsp.jstl.core.Config.FMT_LOCALE);
096        if (pref != null) {
097            // Preferred locale is application-based
098            bundle = findMatch(basename, pref);
099            if (bundle != null) {
100                locCtxt = new LocalizationContext(bundle, pref);
101            }
102        }
103
104        if (locCtxt == null) {
105            // No match found with preferred locales, try using fallback locale
106            locCtxt = BundleSupport.getLocalizationContext(pc, basename);
107        } else {
108            // set response locale
109            if (locCtxt.getLocale() != null) {
110                setResponseLocale(pc, locCtxt.getLocale());
111            }
112        }
113
114        return locCtxt;
115    }
116
117    /**
118     * Returns the locale specified by the named scoped attribute or context
119     * configuration parameter.
120     *
121     * <p> The named scoped attribute is searched in the page, request,
122     * session (if valid), and application scope(s) (in this order). If no such
123     * attribute exists in any of the scopes, the locale is taken from the
124     * named context configuration parameter.
125     *
126     * @param pageContext the page in which to search for the named scoped
127     * attribute or context configuration parameter
128     * @param name the name of the scoped attribute or context configuration
129     * parameter
130     *
131     * @return the locale specified by the named scoped attribute or context
132     * configuration parameter, or <tt>null</tt> if no scoped attribute or
133     * configuration parameter with the given name exists
134     */
135    static Locale getLocale(PageContext pageContext, String name) {
136
137        Locale loc = null;
138
139        Object obj = javax.servlet.jsp.jstl.core.Config.find(pageContext, name);
140        if (obj != null) {
141            if (obj instanceof Locale) {
142                loc = (Locale)obj;
143            } else {
144                loc = SetLocaleSupport.parseLocale((String)obj);
145            }
146        }
147
148        return loc;
149    }
150
151    /**
152     * Stores the given locale in the response object of the given page
153     * context, and stores the locale's associated charset in the
154     * javax.servlet.jsp.jstl.fmt.request.charset session attribute, which
155     * may be used by the <requestEncoding> action in a page invoked by a
156     * form included in the response to set the request charset to the same as
157     * the response charset (this makes it possible for the container to
158     * decode the form parameter values properly, since browsers typically
159     * encode form field values using the response's charset).
160     *
161     * @param pc the page context whose response object is assigned
162     * the given locale
163     * @param locale the response locale
164     */
165    static void setResponseLocale(PageContext pc, Locale locale) {
166
167        // set response locale
168        ServletResponse response = pc.getResponse();
169        response.setLocale(locale);
170
171        // get response character encoding and store it in session attribute
172        if (pc.getSession() != null) {
173            try {
174                pc.setAttribute(
175                    "javax.servlet.jsp.jstl.fmt.request.charset",
176                    response.getCharacterEncoding(),
177                    PageContext.SESSION_SCOPE);
178            } catch (IllegalStateException ex) {
179                // invalidated session ignored
180            }
181        }
182    }
183
184    /**
185     * Gets the resource bundle with the given base name and preferred locale.
186     *
187     * @param basename the resource bundle base name
188     * @param pref the preferred locale
189     */
190    private static ResourceBundle findMatch(String basename, Locale pref) {
191
192        ResourceBundle match = null;
193        try {
194            ResourceBundle bundle = CmsResourceBundleLoader.getBundle(basename, pref);
195            match = bundle;
196        } catch (MissingResourceException mre) {
197            // ignored
198        }
199        return match;
200    }
201
202    /**
203     * Internal action method.<p>
204     *
205     * @return EVAL_BODY_BUFFERED
206     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
207     */
208    @Override
209    public int doStartTag() {
210
211        m_locCtxt = getLocalizationContext(pageContext, getBasename());
212        return EVAL_BODY_BUFFERED;
213    }
214
215    /**
216     * Returns the basename attribute value.<p>
217     *
218     * @return the basename attribute value
219     */
220    public String getBasename() {
221
222        return m_basename;
223    }
224
225    /**
226     * Returns the localization context to use.<p>
227     *
228     * @see org.apache.taglibs.standard.tag.common.fmt.BundleSupport#getLocalizationContext()
229     */
230    @Override
231    public LocalizationContext getLocalizationContext() {
232
233        // TODO: Auto-generated method stub
234        return m_locCtxt;
235    }
236
237    /**
238     * Sets the basename attribute value.<p>
239     *
240     * @param bn the basename attribute value
241     *
242     * @see org.apache.taglibs.standard.tag.el.fmt.BundleTag#setBasename(java.lang.String)
243     */
244    @Override
245    public void setBasename(String bn) {
246
247        m_basename = bn;
248    }
249
250}