Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/myfaces/el/ValueBindingImpl.java


1   /*
2    * Copyright 2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.myfaces.el;
17  
18  import java.lang.reflect.Method;
19  import java.util.List;
20  import java.util.Map;
21  
22  import javax.faces.application.Application;
23  import javax.faces.component.StateHolder;
24  import javax.faces.context.ExternalContext;
25  import javax.faces.context.FacesContext;
26  import javax.faces.el.EvaluationException;
27  import javax.faces.el.PropertyNotFoundException;
28  import javax.faces.el.PropertyResolver;
29  import javax.faces.el.ReferenceSyntaxException;
30  import javax.faces.el.ValueBinding;
31  import javax.servlet.jsp.el.ELException;
32  import javax.servlet.jsp.el.FunctionMapper;
33  import javax.servlet.jsp.el.VariableResolver;
34  
35  import org.apache.myfaces.config.RuntimeConfig;
36  import org.apache.myfaces.config.element.ManagedBean;
37  import org.apache.myfaces.util.BiLevelCacheMap;
38  
39  import org.apache.commons.el.ArraySuffix;
40  import org.apache.commons.el.Coercions;
41  import org.apache.commons.el.ComplexValue;
42  import org.apache.commons.el.ConditionalExpression;
43  import org.apache.commons.el.Expression;
44  import org.apache.commons.el.ExpressionString;
45  import org.apache.commons.el.NamedValue;
46  import org.apache.commons.el.PropertySuffix;
47  import org.apache.commons.el.ValueSuffix;
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  
51  
52  /**
53   * @author Manfred Geiler (latest modification by $Author: oros $)
54   * @author Anton Koinov
55   * @version $Revision: 293105 $ $Date: 2005-10-02 08:45:03 -0400 (Sun, 02 Oct 2005) $
56   */
57  public class ValueBindingImpl extends ValueBinding implements StateHolder
58  {
59      //~ Static fields/initializers --------------------------------------------
60  
61      static final Log log = LogFactory.getLog(ValueBindingImpl.class);
62  
63      /**
64       * To implement function support, subclass and use a static
65       * initialization block to assign your own function mapper
66       */
67      protected static FunctionMapper s_functionMapper = new FunctionMapper()
68          {
69              public Method resolveFunction(String prefix, String localName)
70              {
71                  throw new ReferenceSyntaxException(
72                      "Functions not supported in expressions. Function: "
73                      + prefix + ":" + localName);
74              }
75          };
76  
77      private static final BiLevelCacheMap s_expressionCache =
78          new BiLevelCacheMap(90)
79          {
80              protected Object newInstance(Object key)
81              {
82                  return ELParserHelper.parseExpression((String) key);
83              }
84          };
85  
86      //~ Instance fields -------------------------------------------------------
87  
88      protected Application _application;
89      protected String      _expressionString;
90      protected Object      _expression;
91  
92      /**
93       * RuntimeConfig is instantiated once per servlet and never changes--we can
94       * safely cache it
95       */
96      private RuntimeConfig   _runtimeConfig;
97  
98      //~ Constructors ----------------------------------------------------------
99  
100     public ValueBindingImpl(Application application, String expression)
101     {
102         if (application == null)
103         {
104             throw new NullPointerException("application");
105         }
106 
107         // Do not trim(), we support mixed string-bindings
108         if ((expression == null) || (expression.length() == 0))
109         {
110             throw new ReferenceSyntaxException("Expression: empty or null");
111         }
112         _application = application;
113         _expressionString  = expression;
114 
115         _expression = s_expressionCache.get(expression);
116     }
117 
118     //~ Methods ---------------------------------------------------------------
119 
120     public String getExpressionString()
121     {
122         return _expressionString;
123     }
124 
125     public boolean isReadOnly(FacesContext facesContext)
126     {
127         if (facesContext == null) {
128             throw new NullPointerException("facesContext");
129         }
130         try
131         {
132             Object base_ = resolveToBaseAndProperty(facesContext);
133             if (base_ instanceof String)
134             {
135                 return VariableResolverImpl.s_standardImplicitObjects
136                     .containsKey(base_);
137             }
138 
139             Object[] baseAndProperty = (Object[]) base_;
140             Object base      = baseAndProperty[0];
141             Object property  = baseAndProperty[1];
142 
143             Integer index = ELParserHelper.toIndex(base, property);
144             return (index == null)
145                 ? _application.getPropertyResolver().isReadOnly(base, property)
146                 : _application.getPropertyResolver()
147                     .isReadOnly(base, index.intValue());
148         }
149         catch (NotVariableReferenceException e)
150         {
151             // if it is not a variable reference (e.g., a constant literal),
152             // we cannot write to it but can read it
153             return true;
154         }
155         catch (Exception e)
156         {
157             // Cannot determine read-only, return false (is this what the spec requires?)
158             return false;
159         }
160     }
161 
162     public Class getType(FacesContext facesContext)
163     {
164         if (facesContext == null) {
165             throw new NullPointerException("facesContext");
166         }
167         try
168         {
169             Object base_ = resolveToBaseAndProperty(facesContext);
170             if (base_ instanceof String)
171             {
172                 String name = (String) base_;
173 
174                 // Check if it is a ManagedBean
175                 // WARNING: must do this check first to avoid instantiating
176                 //          the MB in resolveVariable()
177                 ManagedBean mbConfig =
178                     getRuntimeConfig(facesContext).getManagedBean(name);
179                 if (mbConfig != null)
180                 {
181                     // Note: if MB Class is not set, will return
182                     //       <code>null</code>, which is a valid return value
183                     return mbConfig.getManagedBeanClass();
184                 }
185 
186                 Object val = _application.getVariableResolver()
187                     .resolveVariable(facesContext, name);
188 
189                 // Note: if there is no ManagedBean or variable with this name
190                 //       in any scope,then we will create a new one and thus
191                 //       any Object is allowed.
192                 return (val != null) ? val.getClass() : Object.class;
193             }
194             else
195             {
196                 Object[] baseAndProperty = (Object[]) base_;
197                 Object base      = baseAndProperty[0];
198                 Object property  = baseAndProperty[1];
199 
200                 Integer index = ELParserHelper.toIndex(base, property);
201                 return (index == null)
202                     ? _application.getPropertyResolver().getType(base, property)
203                     : _application.getPropertyResolver()
204                         .getType(base, index.intValue());
205             }
206         }
207         catch (NotVariableReferenceException e)
208         {
209             // It is not a value reference, then it probably is an expression
210             // that evaluates to a literal. Get the value and then it's class
211             // Note: we could hadle this case in a more performance efficient manner--
212             //       but this case is so rare, that for months no-one detected
213             //       the error before this code was added.
214             try
215             {
216                 return getValue(facesContext).getClass();
217             }
218             catch (Exception e1)
219             {
220                 // Cannot determine type, return null per JSF spec
221                 return null;
222             }
223         }
224         catch (PropertyNotFoundException e) {
225             throw e;
226         }
227         catch (Exception e)
228         {
229             // Cannot determine type, return null per JSF spec
230             return null;
231         }
232     }
233 
234     public void setValue(FacesContext facesContext, Object newValue)
235             throws EvaluationException, PropertyNotFoundException
236     {
237         if (facesContext == null) {
238             throw new NullPointerException("facesContext");
239         }
240         try
241         {
242             Object base_ = resolveToBaseAndProperty(facesContext);
243             if (base_ instanceof String)
244             {
245                 String name = (String) base_;
246                 if (VariableResolverImpl.s_standardImplicitObjects
247                     .containsKey(name))
248                 {
249                     String errorMessage =
250                         "Cannot set value of implicit object '"
251                         + name + "' for expression '" + _expressionString + "'";
252                     throw new ReferenceSyntaxException(errorMessage);
253                 }
254 
255                 // Note: will be coerced later
256                 setValueInScope(facesContext, name, newValue);
257             }
258             else
259             {
260                 Object[] baseAndProperty = (Object[]) base_;
261                 Object base      = baseAndProperty[0];
262                 Object property  = baseAndProperty[1];
263                 PropertyResolver propertyResolver =
264                     _application.getPropertyResolver();
265 
266                 Integer index = ELParserHelper.toIndex(base, property);
267                 if (index == null)
268                 {
269                     propertyResolver.setValue(
270                         base, property, newValue);
271                 }
272                 else
273                 {
274                     int indexVal = index.intValue();
275                     propertyResolver.setValue(
276                         base, indexVal, newValue);
277                 }
278             }
279         }
280         catch (IndexOutOfBoundsException e)
281         {
282             // ArrayIndexOutOfBoundsException also here
283             throw new PropertyNotFoundException(
284                 "Expression: '" + _expressionString + "'", e);
285         }
286         catch (PropertyNotFoundException e)
287         {
288           throw e;
289         }
290         catch (Exception e)
291         {
292             String msg;
293             if (newValue == null)
294             {
295                 msg = "Cannot set value for expression '"
296                     + _expressionString + "' to null.";
297             }
298             else
299             {
300                 msg = "Cannot set value for expression '"
301                     + _expressionString + "' to a new value of type "
302                     + newValue.getClass().getName();
303             }
304             throw new EvaluationException(msg, e);
305         }
306     }
307 
308     private void setValueInScope(
309         FacesContext facesContext, String name, Object newValue)
310     throws ELException
311     {
312         ExternalContext externalContext = facesContext.getExternalContext();
313 
314         // Request context
315         Map scopeMap = externalContext.getRequestMap();
316         Object obj = scopeMap.get(name);
317         if (obj != null)
318         {
319             scopeMap.put(name, newValue);
320             return;
321         }
322 
323         // Session context
324         scopeMap = externalContext.getSessionMap();
325         obj = scopeMap.get(name);
326         if (obj != null)
327         {
328             scopeMap.put(name, newValue);
329             return;
330         }
331 
332         // Application context
333         scopeMap = externalContext.getApplicationMap();
334         obj = scopeMap.get(name);
335         if (obj != null)
336         {
337             scopeMap.put(name, newValue);
338             return;
339         }
340 
341         // Check for ManagedBean
342         ManagedBean mbConfig =
343             getRuntimeConfig(facesContext).getManagedBean(name);
344         if (mbConfig != null)
345         {
346             String scopeName = mbConfig.getManagedBeanScope();
347 
348             // find the scope handler object
349             // Note: this does not handle user-extended _scope values
350             Scope scope =
351                 (Scope) VariableResolverImpl.s_standardScopes.get(scopeName);
352             if (scope != null)
353             {
354                 scope.put(externalContext, name,
355                     newValue);
356                 return;
357             }
358 
359             log.error("Managed bean '" + name + "' has illegal scope: "
360                 + scopeName);
361             externalContext.getRequestMap().put(name,
362                 newValue);
363             return;
364         }
365 
366         // unknown target class, put newValue into request scope without coercion
367         externalContext.getRequestMap().put(name, newValue);
368     }
369 
370     public Object getValue(FacesContext facesContext)
371     throws EvaluationException, PropertyNotFoundException
372     {
373         if (facesContext == null) {
374             throw new NullPointerException("facesContext");
375         }
376         try
377         {
378             return _expression instanceof Expression
379                 ? ((Expression) _expression).evaluate(
380                     new ELVariableResolver(facesContext),
381                     s_functionMapper, ELParserHelper.LOGGER)
382                 : ((ExpressionString) _expression).evaluate(
383                     new ELVariableResolver(facesContext),
384                     s_functionMapper, ELParserHelper.LOGGER);
385         }
386         catch (PropertyNotFoundException e) {
387             throw e;
388         }
389         catch (IndexOutOfBoundsException e)
390         {
391             // ArrayIndexOutOfBoundsException also here
392             throw new PropertyNotFoundException(
393                 "Expression: '" + _expressionString + "'", e);
394         }
395         catch (Exception e)
396         {
397             throw new EvaluationException(
398                     "Cannot get value for expression '" + _expressionString
399                     + "'", e);
400         }
401     }
402 
403     protected Object resolveToBaseAndProperty(FacesContext facesContext)
404         throws ELException, NotVariableReferenceException
405     {
406         if (facesContext == null)
407         {
408             throw new NullPointerException("facesContext");
409         }
410 
411         VariableResolver variableResolver =
412             new ELVariableResolver(facesContext);
413         Object expression = _expression;
414 
415         while (expression instanceof ConditionalExpression)
416         {
417             ConditionalExpression conditionalExpression =
418                 ((ConditionalExpression) expression);
419             // first, evaluate the condition (and coerce the result to a
420             // boolean value)
421             boolean condition =
422               Coercions.coerceToBoolean(
423                   conditionalExpression.getCondition().evaluate(
424                       variableResolver, s_functionMapper,
425                       ELParserHelper.LOGGER),
426                       ELParserHelper.LOGGER)
427                   .booleanValue();
428 
429             // then, use this boolean to branch appropriately
430             expression = condition ? conditionalExpression.getTrueBranch()
431                 : conditionalExpression.getFalseBranch();
432         }
433 
434         if (expression instanceof NamedValue)
435         {
436             return ((NamedValue) expression).getName();
437         }
438 
439         if (!(expression instanceof ComplexValue)) {
440             // all other cases are not variable references
441             throw new NotVariableReferenceException(
442                 "Parsed Expression of unsupported type for this operation. Expression class: "
443                     + _expression.getClass().getName() + ". Expression: '"
444                     + _expressionString + "'");
445         }
446 
447         ComplexValue complexValue = (ComplexValue) expression;
448 
449         // resolve the prefix
450         Object base = complexValue.getPrefix()
451             .evaluate(variableResolver, s_functionMapper,
452                 ELParserHelper.LOGGER);
453         if (base == null)
454         {
455             throw new PropertyNotFoundException("Base is null: "
456                 + complexValue.getPrefix().getExpressionString());
457         }
458 
459         // Resolve and apply the suffixes
460         List suffixes = complexValue.getSuffixes();
461         int max = suffixes.size() - 1;
462         for (int i = 0; i < max; i++)
463         {
464             ValueSuffix suffix = (ValueSuffix) suffixes.get(i);
465             base = suffix.evaluate(base, variableResolver, s_functionMapper,
466                 ELParserHelper.LOGGER);
467             if (base == null)
468             {
469                 throw new PropertyNotFoundException("Base is null: "
470                     + suffix.getExpressionString());
471             }
472         }
473 
474         // Resolve the last suffix
475         ArraySuffix arraySuffix = (ArraySuffix) suffixes.get(max);
476         Expression arraySuffixIndex = arraySuffix.getIndex();
477 
478         Object index;
479         if (arraySuffixIndex != null)
480         {
481             index = arraySuffixIndex.evaluate(
482                     variableResolver, s_functionMapper,
483                     ELParserHelper.LOGGER);
484             if (index == null)
485             {
486                 throw new PropertyNotFoundException("Index is null: "
487                     + arraySuffixIndex.getExpressionString());
488             }
489         }
490         else
491         {
492             index = ((PropertySuffix) arraySuffix).getName();
493         }
494 
495         return new Object[] {base, index};
496     }
497 
498     private Object coerce(Object value, Class clazz) throws ELException
499     {
500         return (value == null) ? null
501             : (clazz == null) ? value :
502                 Coercions.coerce(value, clazz, ELParserHelper.LOGGER);
503     }
504 
505     protected RuntimeConfig getRuntimeConfig(FacesContext facesContext)
506     {
507         if (_runtimeConfig == null)
508         {
509             _runtimeConfig = RuntimeConfig.getCurrentInstance(facesContext.getExternalContext());
510         }
511         return _runtimeConfig;
512     }
513 
514     public String toString()
515     {
516         return _expressionString;
517     }
518 
519     //~ State Holder ------------------------------------------------------
520 
521     private boolean _transient = false;
522 
523     /**
524      * Empty constructor, so that new instances can be created when restoring
525      * state.
526      */
527     public ValueBindingImpl()
528     {
529         _application = null;
530         _expressionString = null;
531         _expression = null;
532     }
533 
534     public Object saveState(FacesContext facesContext)
535     {
536         return _expressionString;
537     }
538 
539     public void restoreState(FacesContext facesContext, Object obj)
540     {
541         _application = facesContext.getApplication();
542         _expressionString  = (String) obj;
543         _expression = s_expressionCache.get(_expressionString);
544     }
545 
546     public boolean isTransient()
547     {
548         return _transient;
549     }
550 
551     public void setTransient(boolean flag)
552     {
553         _transient = flag;
554     }
555 
556     //~ Internal classes ------------------------------------------------------
557 
558     public static class ELVariableResolver implements VariableResolver {
559         private final FacesContext _facesContext;
560 
561         public ELVariableResolver(FacesContext facesContext)
562         {
563             _facesContext = facesContext;
564         }
565 
566         public Object resolveVariable(String pName)
567             throws ELException
568         {
569             return _facesContext.getApplication().getVariableResolver()
570                 .resolveVariable(_facesContext, pName);
571         }
572     }
573 
574     public static final class NotVariableReferenceException
575         extends ReferenceSyntaxException
576     {
577         private static final long serialVersionUID = 818254526596948605L;
578 
579         public NotVariableReferenceException(String message)
580         {
581             super(message);
582         }
583     }
584 }