KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > myfaces > el > ValueBindingImpl


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 JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
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 JavaDoc;
32 import javax.servlet.jsp.el.FunctionMapper JavaDoc;
33 import javax.servlet.jsp.el.VariableResolver JavaDoc;
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: matze $)
54  * @author Anton Koinov
55  * @version $Revision: 1.54 $ $Date: 2004/10/13 11:51:00 $
56  *
57  * $Log: ValueBindingImpl.java,v $
58  * Revision 1.54 2004/10/13 11:51:00 matze
59  * renamed packages to org.apache
60  *
61  * Revision 1.53 2004/10/01 12:01:05 dave0000
62  * remove unneeded checks
63  *
64  * Revision 1.52 2004/10/01 11:54:29 dave0000
65  * add detailed error messages for "base is null"
66  *
67  * Revision 1.51 2004/09/28 19:11:49 dave0000
68  * uppercase static final prop
69  * remove redundant code
70  *
71  * Revision 1.50 2004/09/28 18:29:48 dave0000
72  * fix for bug 1034332: ValueBinding.getExpressionString() not implemented
73  *
74  * Revision 1.49 2004/09/20 14:35:48 dave0000
75  * bug 1030875:
76  * getType() should return null if type cannot be determined
77  * isReadOnly() should return false if read-only cannot be determined
78  *
79  * Revision 1.48 2004/07/27 06:28:34 dave0000
80  * fix issue with getType of literal expressions (and other improvements)
81  *
82  * Revision 1.47 2004/07/07 08:34:58 mwessendorf
83  * removed unused import-statements
84  *
85  * Revision 1.46 2004/07/07 00:25:07 o_rossmueller
86  * tidy up config/confignew package (moved confignew classes to package config)
87  *
88  * Revision 1.45 2004/07/06 23:46:01 o_rossmueller
89  * tidy up config/confignew package
90  *
91  * Revision 1.44 2004/07/01 22:05:12 mwessendorf
92  * ASF switch
93  *
94  * Revision 1.43 2004/06/28 22:12:13 o_rossmueller
95  * fix #978654: do not coerce null
96  *
97  * Revision 1.42 2004/06/16 23:02:24 o_rossmueller
98  * merged confignew_branch
99  *
100  * Revision 1.41.2.1 2004/06/16 02:07:23 o_rossmueller
101  * get navigation rules from RuntimeConfig
102  * refactored all remaining usages of MyFacesFactoryFinder to use RuntimeConfig
103  *
104  * Revision 1.41 2004/05/31 02:28:37 dave0000
105  * Fix for: [955111] RestoreState of outputText with mixed ValueBinding attribute
106  *
107  * Revision 1.40 2004/05/11 04:24:10 dave0000
108  * Bug 943166: add value coercion to ManagedBeanConfigurator
109  *
110  * Revision 1.39 2004/05/10 05:30:14 dave0000
111  * Fix issue with setting Managed Bean to a wrong scope
112  *
113  * Revision 1.38 2004/04/26 05:54:59 dave0000
114  * Add coercion to ValueBinding (and related changes)
115  *
116  * Revision 1.37 2004/04/08 05:16:45 dave0000
117  * change to always use JSF PropertyResolver (was using JSP PR sometimes)
118  *
119  * Revision 1.36 2004/04/07 09:46:38 tinytoony
120  * changed exception handling to show root cause
121  *
122  * Revision 1.35 2004/04/07 03:21:19 dave0000
123  * fix issue with trim()ing of expression strings
124  * prepare for PropertyResolver integration
125  *
126  * Revision 1.34 2004/04/07 01:52:55 dave0000
127  * fix set "root" variable - was setting in the wrong scope if new var
128  *
129  * Revision 1.33 2004/04/07 01:40:13 dave0000
130  * fix set "root" variable bug
131  *
132  * Revision 1.32 2004/03/30 07:40:08 dave0000
133  * implement mixed string-reference expressions
134  *
135  */

136 public class ValueBindingImpl extends ValueBinding implements StateHolder
137 {
138     //~ Static fields/initializers --------------------------------------------
139

140     static final Log log = LogFactory.getLog(ValueBindingImpl.class);
141     
142     /**
143      * To implement function support, subclass and use a static
144      * initialization block to assign your own function mapper
145      */

146     protected static FunctionMapper JavaDoc s_functionMapper = new FunctionMapper JavaDoc()
147         {
148             public Method JavaDoc resolveFunction(String JavaDoc prefix, String JavaDoc localName)
149             {
150                 throw new ReferenceSyntaxException(
151                     "Functions not supported in expressions. Function: "
152                     + prefix + ":" + localName);
153             }
154         };
155     
156     private static final BiLevelCacheMap s_expressionCache =
157         new BiLevelCacheMap(90)
158         {
159             protected Object JavaDoc newInstance(Object JavaDoc key)
160             {
161                 return ELParserHelper.parseExpression((String JavaDoc) key);
162             }
163         };
164
165     //~ Instance fields -------------------------------------------------------
166

167     protected Application _application;
168     protected String JavaDoc _expressionString;
169     protected Object JavaDoc _expression;
170     
171     /**
172      * RuntimeConfig is instantiated once per servlet and never changes--we can
173      * safely cache it
174      */

175     private RuntimeConfig _runtimeConfig;
176
177     //~ Constructors ----------------------------------------------------------
178

179     public ValueBindingImpl(Application application, String JavaDoc expression)
180     {
181         if (application == null)
182         {
183             throw new NullPointerException JavaDoc("application");
184         }
185         
186         // Do not trim(), we support mixed string-bindings
187
if ((expression == null) || (expression.length() == 0))
188         {
189             throw new ReferenceSyntaxException("Expression: empty or null");
190         }
191         _application = application;
192         _expressionString = expression;
193
194         _expression = s_expressionCache.get(expression);
195     }
196
197     //~ Methods ---------------------------------------------------------------
198

199     public String JavaDoc getExpressionString()
200     {
201         return _expressionString;
202     }
203     
204     public boolean isReadOnly(FacesContext facesContext)
205     {
206         try
207         {
208             Object JavaDoc base_ = resolveToBaseAndProperty(facesContext);
209             if (base_ instanceof String JavaDoc)
210             {
211                 return VariableResolverImpl.s_standardImplicitObjects
212                     .containsKey(base_);
213             }
214             
215             Object JavaDoc[] baseAndProperty = (Object JavaDoc[]) base_;
216             Object JavaDoc base = baseAndProperty[0];
217             Object JavaDoc property = baseAndProperty[1];
218             
219             Integer JavaDoc index = ELParserHelper.toIndex(base, property);
220             return (index == null)
221                 ? _application.getPropertyResolver().isReadOnly(base, property)
222                 : _application.getPropertyResolver()
223                     .isReadOnly(base, index.intValue());
224         }
225         catch (NotVariableReferenceException e)
226         {
227             // if it is not a variable reference (e.g., a constant literal),
228
// we cannot write to it but can read it
229
return true;
230         }
231         catch (Exception JavaDoc e)
232         {
233             // Cannot determine read-only, return false (is this what the spec requires?)
234
return false;
235         }
236     }
237
238     public Class JavaDoc getType(FacesContext facesContext)
239     {
240         try
241         {
242             Object JavaDoc base_ = resolveToBaseAndProperty(facesContext);
243             if (base_ instanceof String JavaDoc)
244             {
245                 String JavaDoc name = (String JavaDoc) base_;
246                 
247                 // Check if it is a ManagedBean
248
// WARNING: must do this check first to avoid instantiating
249
// the MB in resolveVariable()
250
ManagedBean mbConfig =
251                     getRuntimeConfig(facesContext).getManagedBean(name);
252                 if (mbConfig != null)
253                 {
254                     // Note: if MB Class is not set, will return
255
// <code>null</code>, which is a valid return value
256
return mbConfig.getManagedBeanClass();
257                 }
258
259                 Object JavaDoc val = _application.getVariableResolver()
260                     .resolveVariable(facesContext, name);
261                 
262                 // Note: if there is no ManagedBean or variable with this name
263
// in any scope,then we will create a new one and thus
264
// any Object is allowed.
265
return (val != null) ? val.getClass() : Object JavaDoc.class;
266             }
267             else
268             {
269                 Object JavaDoc[] baseAndProperty = (Object JavaDoc[]) base_;
270                 Object JavaDoc base = baseAndProperty[0];
271                 Object JavaDoc property = baseAndProperty[1];
272                 
273                 Integer JavaDoc index = ELParserHelper.toIndex(base, property);
274                 return (index == null)
275                     ? _application.getPropertyResolver().getType(base, property)
276                     : _application.getPropertyResolver()
277                         .getType(base, index.intValue());
278             }
279         }
280         catch (NotVariableReferenceException e)
281         {
282             // It is not a value reference, then it probably is an expression
283
// that evaluates to a literal. Get the value and then it's class
284
// Note: we could hadle this case in a more performance efficient manner--
285
// but this case is so rare, that for months no-one detected
286
// the error before this code was added.
287
try
288             {
289                 return getValue(facesContext).getClass();
290             }
291             catch (Exception JavaDoc e1)
292             {
293                 // Cannot determine type, return null per JSF spec
294
return null;
295             }
296         }
297         catch (Exception JavaDoc e)
298         {
299             // Cannot determine type, return null per JSF spec
300
return null;
301         }
302     }
303
304     public void setValue(FacesContext facesContext, Object JavaDoc newValue)
305             throws PropertyNotFoundException
306     {
307         try
308         {
309             Object JavaDoc base_ = resolveToBaseAndProperty(facesContext);
310             if (base_ instanceof String JavaDoc)
311             {
312                 String JavaDoc name = (String JavaDoc) base_;
313                 if (VariableResolverImpl.s_standardImplicitObjects
314                     .containsKey(name))
315                 {
316                     String JavaDoc errorMessage =
317                         "Cannot set value of implicit object '"
318                         + name + "' for expression '" + _expressionString + "'";
319                     log.error(errorMessage);
320                     throw new ReferenceSyntaxException(errorMessage);
321                 }
322
323                 // Note: will be coerced later
324
setValueInScope(facesContext, name, newValue);
325             }
326             else
327             {
328                 Object JavaDoc[] baseAndProperty = (Object JavaDoc[]) base_;
329                 Object JavaDoc base = baseAndProperty[0];
330                 Object JavaDoc property = baseAndProperty[1];
331                 PropertyResolver propertyResolver =
332                     _application.getPropertyResolver();
333
334                 Integer JavaDoc index = ELParserHelper.toIndex(base, property);
335                 if (index == null)
336                 {
337                     Class JavaDoc clazz = propertyResolver.getType(base, property);
338                     propertyResolver.setValue(
339                         base, property, coerce(newValue, clazz));
340                 }
341                 else
342                 {
343                     int indexVal = index.intValue();
344                     Class JavaDoc clazz = propertyResolver.getType(base, indexVal);
345                     propertyResolver.setValue(
346                         base, indexVal, coerce(newValue, clazz));
347                 }
348             }
349         }
350         catch (IndexOutOfBoundsException JavaDoc e)
351         {
352             // ArrayIndexOutOfBoundsException also here
353
throw new PropertyNotFoundException(
354                 "Expression: '" + _expressionString + "'", e);
355         }
356         catch (Exception JavaDoc e)
357         {
358             if (newValue == null)
359             {
360                 log.error("Cannot set value for expression '"
361                     + _expressionString + "' to null.", e);
362             }
363             else
364             {
365                 log.error("Cannot set value for expression '"
366                     + _expressionString + "' to a new value of type "
367                     + newValue.getClass().getName(), e);
368             }
369             throw new EvaluationException(
370                 "Expression: '" + _expressionString + "'", e);
371         }
372     }
373     
374     private void setValueInScope(
375         FacesContext facesContext, String JavaDoc name, Object JavaDoc newValue)
376     throws ELException JavaDoc
377     {
378         ExternalContext externalContext = facesContext.getExternalContext();
379          
380         // Request context
381
Map JavaDoc scopeMap = externalContext.getRequestMap();
382         Object JavaDoc obj = scopeMap.get(name);
383         if (obj != null)
384         {
385             scopeMap.put(name, coerce(newValue, obj.getClass()));
386             return;
387         }
388
389         // Session context
390
scopeMap = externalContext.getSessionMap();
391         obj = scopeMap.get(name);
392         if (obj != null)
393         {
394             scopeMap.put(name, coerce(newValue, obj.getClass()));
395             return;
396         }
397
398         // Application context
399
scopeMap = externalContext.getApplicationMap();
400         obj = scopeMap.get(name);
401         if (obj != null)
402         {
403             scopeMap.put(name, coerce(newValue, obj.getClass()));
404             return;
405         }
406         
407         // Check for ManagedBean
408
ManagedBean mbConfig =
409             getRuntimeConfig(facesContext).getManagedBean(name);
410         if (mbConfig != null)
411         {
412             String JavaDoc scopeName = mbConfig.getManagedBeanScope();
413             
414             // find the scope handler object
415
// Note: this does not handle user-extended _scope values
416
Scope scope =
417                 (Scope) VariableResolverImpl.s_standardScopes.get(scopeName);
418             if (scope != null)
419             {
420                 scope.put(externalContext, name,
421                     coerce(newValue, mbConfig.getManagedBeanClass()));
422                 return;
423             }
424             
425             log.error("Managed bean '" + name + "' has illegal scope: "
426                 + scopeName);
427             externalContext.getRequestMap().put(name,
428                 coerce(newValue, mbConfig.getManagedBeanClass()));
429             return;
430         }
431         
432         // unknown target class, put newValue into request scope without coercion
433
externalContext.getRequestMap().put(name, newValue);
434     }
435
436     public Object JavaDoc getValue(FacesContext facesContext)
437     throws PropertyNotFoundException
438     {
439         try
440         {
441             return _expression instanceof Expression
442                 ? ((Expression) _expression).evaluate(
443                     new ELVariableResolver(facesContext),
444                     s_functionMapper, ELParserHelper.LOGGER)
445                 : ((ExpressionString) _expression).evaluate(
446                     new ELVariableResolver(facesContext),
447                     s_functionMapper, ELParserHelper.LOGGER);
448         }
449         catch (IndexOutOfBoundsException JavaDoc e)
450         {
451             // ArrayIndexOutOfBoundsException also here
452
throw new PropertyNotFoundException(
453                 "Expression: '" + _expressionString + "'", e);
454         }
455         catch (Exception JavaDoc e)
456         {
457             log.error("Cannot get value for expression '" + _expressionString
458                 + "'", e);
459
460             if (e instanceof ELException JavaDoc)
461             {
462                 log.error("Root cause for exception : ",
463                     ((ELException JavaDoc) e).getRootCause());
464             }
465
466             throw new EvaluationException(
467                 "Expression: '" + _expressionString + "'", e);
468         }
469     }
470     
471     protected Object JavaDoc resolveToBaseAndProperty(FacesContext facesContext)
472         throws ELException JavaDoc, NotVariableReferenceException
473     {
474         if (facesContext == null)
475         {
476             throw new NullPointerException JavaDoc("facesContext");
477         }
478         
479         VariableResolver JavaDoc variableResolver =
480             new ELVariableResolver(facesContext);
481         Object JavaDoc expression = _expression;
482         
483         while (expression instanceof ConditionalExpression)
484         {
485             ConditionalExpression conditionalExpression =
486                 ((ConditionalExpression) expression);
487             // first, evaluate the condition (and coerce the result to a
488
// boolean value)
489
boolean condition =
490               Coercions.coerceToBoolean(
491                   conditionalExpression.getCondition().evaluate(
492                       variableResolver, s_functionMapper,
493                       ELParserHelper.LOGGER),
494                       ELParserHelper.LOGGER)
495                   .booleanValue();
496
497             // then, use this boolean to branch appropriately
498
expression = condition ? conditionalExpression.getTrueBranch()
499                 : conditionalExpression.getFalseBranch();
500         }
501
502         if (expression instanceof NamedValue)
503         {
504             return ((NamedValue) expression).getName();
505         }
506     
507         if (!(expression instanceof ComplexValue)) {
508             // all other cases are not variable references
509
throw new NotVariableReferenceException(
510                 "Parsed Expression of unsupported type for this operation. Expression class: "
511                     + _expression.getClass().getName() + ". Expression: '"
512                     + _expressionString + "'");
513         }
514         
515         ComplexValue complexValue = (ComplexValue) expression;
516         
517         // resolve the prefix
518
Object JavaDoc base = complexValue.getPrefix()
519             .evaluate(variableResolver, s_functionMapper,
520                 ELParserHelper.LOGGER);
521         if (base == null)
522         {
523             throw new PropertyNotFoundException("Base is null: "
524                 + complexValue.getPrefix().getExpressionString());
525         }
526
527         // Resolve and apply the suffixes
528
List JavaDoc suffixes = complexValue.getSuffixes();
529         int max = suffixes.size() - 1;
530         for (int i = 0; i < max; i++)
531         {
532             ValueSuffix suffix = (ValueSuffix) suffixes.get(i);
533             base = suffix.evaluate(base, variableResolver, s_functionMapper,
534                 ELParserHelper.LOGGER);
535             if (base == null)
536             {
537                 throw new PropertyNotFoundException("Base is null: "
538                     + suffix.getExpressionString());
539             }
540         }
541         
542         // Resolve the last suffix
543
ArraySuffix arraySuffix = (ArraySuffix) suffixes.get(max);
544         Expression arraySuffixIndex = arraySuffix.getIndex();
545         
546         Object JavaDoc index;
547         if (arraySuffixIndex != null)
548         {
549             index = arraySuffixIndex.evaluate(
550                     variableResolver, s_functionMapper,
551                     ELParserHelper.LOGGER);
552             if (index == null)
553             {
554                 throw new PropertyNotFoundException("Index is null: "
555                     + arraySuffixIndex.getExpressionString());
556             }
557         }
558         else
559         {
560             index = ((PropertySuffix) arraySuffix).getName();
561         }
562         
563         return new Object JavaDoc[] {base, index};
564     }
565
566     private Object JavaDoc coerce(Object JavaDoc value, Class JavaDoc clazz) throws ELException JavaDoc
567     {
568         return (value == null) ? null
569             : (clazz == null) ? value :
570                 Coercions.coerce(value, clazz, ELParserHelper.LOGGER);
571     }
572     
573     protected RuntimeConfig getRuntimeConfig(FacesContext facesContext)
574     {
575         if (_runtimeConfig == null)
576         {
577             _runtimeConfig = RuntimeConfig.getCurrentInstance(facesContext.getExternalContext());
578         }
579         return _runtimeConfig;
580     }
581     
582     public String JavaDoc toString()
583     {
584         return _expressionString;
585     }
586
587     //~ State Holder ------------------------------------------------------
588

589     private boolean _transient = false;
590
591     /**
592      * Empty constructor, so that new instances can be created when restoring
593      * state.
594      */

595     public ValueBindingImpl()
596     {
597         _application = null;
598         _expressionString = null;
599         _expression = null;
600     }
601
602     public Object JavaDoc saveState(FacesContext facesContext)
603     {
604         return _expressionString;
605     }
606
607     public void restoreState(FacesContext facesContext, Object JavaDoc obj)
608     {
609         _application = facesContext.getApplication();
610         _expressionString = (String JavaDoc) obj;
611         _expression = s_expressionCache.get(_expressionString);
612     }
613
614     public boolean isTransient()
615     {
616         return _transient;
617     }
618
619     public void setTransient(boolean flag)
620     {
621         _transient = flag;
622     }
623     
624     //~ Internal classes ------------------------------------------------------
625

626     public static class ELVariableResolver implements VariableResolver JavaDoc {
627         private final FacesContext _facesContext;
628         
629         public ELVariableResolver(FacesContext facesContext)
630         {
631             _facesContext = facesContext;
632         }
633         
634         public Object JavaDoc resolveVariable(String JavaDoc pName)
635             throws ELException JavaDoc
636         {
637             return _facesContext.getApplication().getVariableResolver()
638                 .resolveVariable(_facesContext, pName);
639         }
640     }
641     
642     public static final class NotVariableReferenceException
643         extends ReferenceSyntaxException
644     {
645         public NotVariableReferenceException(String JavaDoc message)
646         {
647             super(message);
648         }
649     }
650 }
651
Popular Tags