View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.loadbundle;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Enumeration;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.MissingResourceException;
30  import java.util.ResourceBundle;
31  import java.util.Set;
32  
33  import javax.faces.component.StateHolder;
34  import javax.faces.component.UIComponentBase;
35  import javax.faces.component.UIViewRoot;
36  import javax.faces.context.FacesContext;
37  import javax.faces.event.AbortProcessingException;
38  import javax.faces.event.FacesEvent;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  
43  /**
44   * Extended loadbundle which does its job in all life-cycle phases or even on calling LoadBundle.loadBundle() - not only when rendering happens...
45   * 
46   * A load-bundle alternative which allows to use load-bundle
47   * even on AJAX-enabled pages.
48   * <p/>
49   * A component that allows to load bundles not only on rendering, but whenever the
50   * page author needs it. By default, it loads it on every lifecycle phase
51   * except restore-state and save-state.* 
52   * <p/>
53   * The core-load-bundle only loads its message-bundle
54   * on rendering - this load-bundle does it on every life-cycle,
55   * and optionally whenever the method loadBundle is called.
56   *
57   * @JSFComponent
58   *   name = "s:loadBundle"
59   *   class = "org.apache.myfaces.custom.loadbundle.LoadBundle"
60   *   tagClass = "org.apache.myfaces.custom.loadbundle.LoadBundleTag"
61   *   tagSuperclass = "org.apache.myfaces.custom.loadbundle.AbstractLoadBundleTag"
62   *   
63   * @author Martin Marinschek
64   */
65  public abstract class AbstractLoadBundle extends UIComponentBase implements StateHolder {
66  
67      private static Log log = LogFactory.getLog(AbstractLoadBundle.class);
68  
69      public static final String COMPONENT_TYPE = "org.apache.myfaces.LoadBundle";
70      public static final String COMPONENT_FAMILY = "org.apache.myfaces.LoadBundle";
71  
72      private boolean alreadyLoaded = false;
73  
74      /**
75       * Path to the bundle-file in the class-path, e.g.: org.apache.myfaces.i18n.myprops
76       * 
77       * @JSFProperty
78       */
79      public abstract String getBasename();
80  
81      public abstract void setBasename(String basename);
82  
83      /**
84       * Variable this bundle will be stored under, e.g. mybundle. Use #{mybundle.propertykey} 
85       * or #{mybundle['propertykey']} to access the keys of the bundle.
86       * 
87       * @JSFProperty
88       */
89      public abstract String getVar();
90  
91      public abstract void setVar(String var);
92  
93      public Object processSaveState(FacesContext context)
94      {
95          //intentionally don't do anything special in this phase
96          return super.processSaveState(context);
97      }
98  
99      public void processRestoreState(FacesContext context, Object state)
100     {
101         //intentionally don't do anything special in this phase
102         super.processRestoreState(context, state);
103     }
104 
105     public void processValidators(FacesContext context)
106     {
107         loadBundle();
108         super.processValidators(context);
109     }
110 
111     public void processDecodes(FacesContext context)
112     {
113         loadBundle();
114         super.processDecodes(context);
115     }
116 
117     public void processUpdates(FacesContext context)
118     {
119         loadBundle();
120         super.processUpdates(context);
121     }
122 
123 
124   public void encodeBegin(FacesContext context) throws IOException {
125         loadBundle();
126   }
127 
128 
129   public void encodeEnd(FacesContext context) throws IOException {
130   }
131 
132   public void queueEvent(FacesEvent event)
133     {
134         super.queueEvent(new FacesEventWrapper(event, this));
135     }
136 
137     public void broadcast(FacesEvent event) throws AbortProcessingException {
138         loadBundle();
139 
140         if (event instanceof FacesEventWrapper)
141         {
142             FacesEvent originalEvent = ((FacesEventWrapper) event).getWrappedFacesEvent();
143             originalEvent.getComponent().broadcast(originalEvent);
144         }
145         else
146         {
147             super.broadcast(event);
148         }
149     }
150 
151     public void loadBundle() {
152         if(alreadyLoaded)
153             return;
154 
155         resolveBundle(getBasename());
156 
157         alreadyLoaded = true;
158     }
159 
160     /**
161      * This method is copied over from LoadBundle in core.
162      * If you change anything here, think about changing it there as well.
163      *
164      * @param resolvedBasename
165      */
166     private void resolveBundle(String resolvedBasename) {
167         //ATTENTION: read comment above before changing this!
168         FacesContext facesContext = FacesContext.getCurrentInstance();
169 
170         UIViewRoot viewRoot = facesContext.getViewRoot();
171         if (viewRoot == null)
172         {
173             throw new IllegalStateException("No view root! LoadBundle must be nested inside <f:view> action.");
174         }
175 
176         Locale locale = viewRoot.getLocale();
177         if (locale == null)
178         {
179             locale = facesContext.getApplication().getDefaultLocale();
180         }
181 
182         final ResourceBundle bundle;
183         try
184         {
185             bundle = ResourceBundle.getBundle(resolvedBasename,
186                                               locale,
187                                               Thread.currentThread().getContextClassLoader());
188 
189             facesContext.getExternalContext().getRequestMap().put(getVar(),
190                                                                   new BundleMap(bundle));
191 
192         }
193         catch (MissingResourceException e)
194         {
195             log.error("Resource bundle '" + resolvedBasename + "' could not be found.");
196         }
197         //ATTENTION: read comment above before changing this!
198     }
199 
200 
201     /**
202      * This class is copied over from LoadBundle in myfaces-api.
203      * If you change anything here, think about changing it there as well.
204      *
205      */
206     private static class BundleMap implements Map
207     {
208         //ATTENTION: read javadoc
209         private ResourceBundle _bundle;
210         private List _values;
211 
212         public BundleMap(ResourceBundle bundle)
213         {
214             _bundle = bundle;
215         }
216 
217         //Optimized methods
218 
219         public Object get(Object key)
220         {
221             try {
222                 return _bundle.getObject(key.toString());
223             } catch (Exception e) {
224                 return "MISSING: " + key + " :MISSING";
225             }
226         }
227 
228         public boolean isEmpty()
229         {
230             return !_bundle.getKeys().hasMoreElements();
231         }
232 
233         public boolean containsKey(Object key)
234         {
235             try {
236                 return _bundle.getObject(key.toString()) != null;
237             } catch (MissingResourceException e) {
238                 return false;
239             }
240         }
241 
242 
243         //Unoptimized methods
244 
245         public Collection values()
246         {
247             if (_values == null)
248             {
249                 _values = new ArrayList();
250                 for (Enumeration enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
251                 {
252                     String v = _bundle.getString((String)enumer.nextElement());
253                     _values.add(v);
254                 }
255             }
256             return _values;
257         }
258 
259         public int size()
260         {
261             return values().size();
262         }
263 
264         public boolean containsValue(Object value)
265         {
266             return values().contains(value);
267         }
268 
269         public Set entrySet()
270         {
271             Set set = new HashSet();
272             for (Enumeration enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
273             {
274                 final String k = (String)enumer.nextElement();
275                 set.add(new Map.Entry() {
276                     public Object getKey()
277                     {
278                         return k;
279                     }
280 
281                     public Object getValue()
282                     {
283                         return _bundle.getObject(k);
284                     }
285 
286                     public Object setValue(Object value)
287                     {
288                         throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
289                     }
290                 });
291             }
292             return set;
293         }
294 
295         public Set keySet()
296         {
297             Set set = new HashSet();
298             for (Enumeration enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
299             {
300                 set.add(enumer.nextElement());
301             }
302             return set;
303         }
304         //ATTENTION: read javadoc
305 
306 
307         //Unsupported methods
308 
309         public Object remove(Object key)
310         {
311             throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
312         }
313 
314         public void putAll(Map t)
315         {
316             throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
317         }
318 
319         public Object put(Object key, Object value)
320         {
321             throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
322         }
323 
324         public void clear()
325         {
326             throw new UnsupportedOperationException(this.getClass().getName() + " UnsupportedOperationException");
327         }
328         //ATTENTION: read javadoc
329     }
330 }