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.el;
20  
21  import javax.el.ELContext;
22  import javax.el.ELException;
23  import javax.el.ELResolver;
24  import javax.el.PropertyNotFoundException;
25  import javax.el.PropertyNotWritableException;
26  import javax.faces.context.ExternalContext;
27  import javax.faces.context.FacesContext;
28  import javax.faces.context.Flash;
29  import java.beans.FeatureDescriptor;
30  import java.util.ArrayList;
31  import java.util.Iterator;
32  
33  /**
34   * Resolver for Flash object 
35   * 
36   * @author Leonardo Uribe (latest modification by $Author$)
37   * @version $Revision$ $Date$
38   */
39  public class FlashELResolver extends ELResolver
40  {
41  
42      private final static String FLASH = "flash";
43  
44      private final static String KEEP = "keep";
45  
46      private final static String NOW = "now";
47  
48      public FlashELResolver()
49      {
50          super();
51      }
52  
53      @Override
54      public void setValue(ELContext context, Object base, Object property,
55              Object value) throws NullPointerException,
56              PropertyNotFoundException, PropertyNotWritableException,
57              ELException
58      {
59          if (property == null)
60          {
61              throw new PropertyNotFoundException();
62          }
63          if (!(property instanceof String))
64          {
65              return;
66          }
67  
68          String strProperty = property.toString();
69  
70          if (FLASH.equals(strProperty))
71          {
72              throw new PropertyNotWritableException();
73          }
74          else if (base instanceof Flash)
75          {
76              context.setPropertyResolved(true);
77              try
78              {
79                  ((Flash) base).put(strProperty, value);
80              }
81              catch (UnsupportedOperationException e)
82              {
83                  throw new PropertyNotWritableException(e);
84              }
85          }
86      }
87  
88      @Override
89      public boolean isReadOnly(ELContext context, Object base, Object property)
90              throws NullPointerException, PropertyNotFoundException, ELException
91      {
92  
93          if (property == null)
94          {
95              throw new PropertyNotFoundException();
96          }
97          if (!(property instanceof String))
98          {
99              return false;
100         }
101 
102         String strProperty = property.toString();
103 
104         if (FLASH.equals(strProperty))
105         {
106             context.setPropertyResolved(true);
107             return true;
108         }
109         else if (base instanceof Flash)
110         {
111             context.setPropertyResolved(true);
112         }
113 
114         return false;
115     }
116 
117     @Override
118     public Object getValue(ELContext elContext, Object base, Object property)
119             throws NullPointerException, PropertyNotFoundException, ELException
120     {
121 
122         if (property == null)
123         {
124             throw new PropertyNotFoundException();
125         }
126         if (!(property instanceof String))
127         {
128             return null;
129         }
130 
131         String strProperty = property.toString();
132 
133         if (base == null)
134         {
135             if (FLASH.equals(strProperty))
136             {
137                 FacesContext facesContext = facesContext(elContext);
138                 if (facesContext == null)
139                 {
140                     return null;
141                 }
142                 ExternalContext externalContext = facesContext.getExternalContext();
143                 if (externalContext == null)
144                 {
145                     return null;
146                 }
147 
148                 //Access to flash object
149                 elContext.setPropertyResolved(true);
150                 Flash flash = externalContext.getFlash();
151                 //This is just to make sure after this point
152                 //we are not in "keep" promotion.
153                 setDoKeepPromotion(false, facesContext);
154                 
155                 // Note that after this object is returned, Flash.get() and Flash.put()
156                 // methods are called from javax.el.MapELResolver, since 
157                 // Flash is instance of Map.
158                 return flash;
159             }
160         }
161         else if (base instanceof Flash)
162         {
163             FacesContext facesContext = facesContext(elContext);
164             if (facesContext == null)
165             {
166                 return null;
167             }
168             ExternalContext externalContext = facesContext.getExternalContext();
169             if (externalContext == null)
170             {
171                 return null;
172             }
173             Flash flash = (Flash) base;
174             if (KEEP.equals(strProperty))
175             {
176                 setDoKeepPromotion(true, facesContext);
177                 // Since we returned a Flash instance getValue will 
178                 // be called again but this time the property name
179                 // to be resolved will be called, so we can do keep
180                 // promotion.
181                 elContext.setPropertyResolved(true);
182                 return base;
183             }
184             else if (NOW.equals(strProperty))
185             {
186                 //Prevent invalid syntax #{flash.keep.now.someKey}
187                 if (!isDoKeepPromotion(facesContext))
188                 {
189                     // According to the javadoc of Flash.putNow() and 
190                     // Flash.keep(), this is an alias to requestMap, used
191                     // as a "buffer" to promote vars to flash scope using
192                     // "keep" method
193                     elContext.setPropertyResolved(true);
194                     return externalContext.getRequestMap();
195                 }
196             }
197             else if (isDoKeepPromotion(facesContext))
198             {
199                 //Resolve property calling get or keep
200                 elContext.setPropertyResolved(true);
201                 //promote it to flash scope
202                 flash.keep(strProperty);
203                 //Obtain the value on requestMap if any
204                 Object value = externalContext.getRequestMap().get(strProperty);
205                 return value;
206             }
207             else
208             {
209                 //Just get the value
210                 elContext.setPropertyResolved(true);
211                 return flash.get(strProperty);
212             }
213         }
214         return null;
215     }
216     
217     /**
218      * This var indicate if we are inside a keep operation
219      * or not. We go into keep status in two cases:
220      * 
221      * - A direct call to Flash.keep(String key)
222      * - A lookup to keep map using a value expression #{flash.keep.someKey}.
223      *   This occur when the ELResolver try to get the keep object.
224      *   
225      * Note that when "keep" is resolved by FlashELResolver,
226      * we need a way to comunicate that the current lookup is 
227      * for keep promotion.
228      * 
229      * This var do the job.
230      */
231     private static final String KEEP_STATUS_KEY = "org.apache.myfaces.el.FlashELResolver.KEEP_STATUS";
232 
233     private static boolean isDoKeepPromotion(FacesContext facesContext)
234     {
235         Boolean doKeepPromotion = (Boolean) facesContext.getAttributes().get(KEEP_STATUS_KEY);
236 
237         if (doKeepPromotion == null)
238         {
239             doKeepPromotion = false;
240         }
241 
242         return doKeepPromotion;
243     }
244 
245     private static void setDoKeepPromotion(boolean value, FacesContext facesContext)
246     {
247         facesContext.getAttributes().put(KEEP_STATUS_KEY, Boolean.valueOf(value));
248     }
249     
250     // get the FacesContext from the ELContext
251     protected FacesContext facesContext(ELContext context)
252     {
253         return (FacesContext) context.getContext(FacesContext.class);
254     }
255 
256     protected ExternalContext externalContext(ELContext context)
257     {
258         return facesContext(context).getExternalContext();
259     }
260 
261     @Override
262     public Class<?> getType(ELContext context, Object base, Object property)
263             throws NullPointerException, PropertyNotFoundException, ELException
264     {
265 
266         if (property == null)
267         {
268             throw new PropertyNotFoundException();
269         }
270         if (!(property instanceof String))
271         {
272             return null;
273         }
274 
275         String strProperty = property.toString();
276 
277         if (FLASH.equals(strProperty))
278         {
279             context.setPropertyResolved(true);
280         }
281         else if (base instanceof Flash)
282         {
283             context.setPropertyResolved(true);
284             Object obj = ((Flash) base).get(property);
285             return (obj != null) ? obj.getClass() : null;
286         }
287 
288         return null;
289     }
290 
291     @Override
292     public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
293             Object base)
294     {
295         ArrayList<FeatureDescriptor> descriptors = new ArrayList<FeatureDescriptor>(1);
296 
297         descriptors.add(makeDescriptor(FLASH,
298                 "Represents the current flash scope", Object.class));
299 
300         if (base instanceof Flash)
301         {
302             Iterator itr = ((Flash) base).keySet().iterator();
303             Object key;
304             FeatureDescriptor desc;
305             while (itr.hasNext())
306             {
307                 key = itr.next();
308                 desc = makeDescriptor(key.toString(), key.toString(), key.getClass());
309                 descriptors.add(desc);
310             }
311         }
312         return descriptors.iterator();
313     }
314 
315     protected FeatureDescriptor makeDescriptor(String name, String description,
316             Class<?> elResolverType)
317     {
318         FeatureDescriptor fd = new FeatureDescriptor();
319         fd.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME, Boolean.TRUE);
320         fd.setValue(ELResolver.TYPE, elResolverType);
321         fd.setName(name);
322         fd.setDisplayName(name);
323         fd.setShortDescription(description);
324         fd.setExpert(false);
325         fd.setHidden(false);
326         fd.setPreferred(true);
327         return fd;
328     }
329 
330     @Override
331     public Class<?> getCommonPropertyType(ELContext context, Object base)
332     {
333         if (base == null)
334         {
335             return null;
336         }
337 
338         if (base instanceof Flash)
339         {
340             return Object.class;
341         }
342         else if (FLASH.equals(base.toString()))
343         {
344             return Object.class;
345         }
346 
347         return null;
348     }
349 
350 }