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

Quick Search    Search Deep

Source code: org/apache/myfaces/el/ELParserHelper.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.io.StringReader;
19  import java.util.List;
20  
21  import javax.faces.application.Application;
22  import javax.faces.component.UIComponent;
23  import javax.faces.context.FacesContext;
24  import javax.faces.el.EvaluationException;
25  import javax.faces.el.ReferenceSyntaxException;
26  import javax.servlet.jsp.el.ELException;
27  import javax.servlet.jsp.el.FunctionMapper;
28  import javax.servlet.jsp.el.VariableResolver;
29  
30  import org.apache.myfaces.util.StringUtils;
31  
32  import org.apache.commons.el.ArraySuffix;
33  import org.apache.commons.el.BinaryOperatorExpression;
34  import org.apache.commons.el.Coercions;
35  import org.apache.commons.el.ComplexValue;
36  import org.apache.commons.el.ConditionalExpression;
37  import org.apache.commons.el.Expression;
38  import org.apache.commons.el.ExpressionString;
39  import org.apache.commons.el.FunctionInvocation;
40  import org.apache.commons.el.Literal;
41  import org.apache.commons.el.Logger;
42  import org.apache.commons.el.NamedValue;
43  import org.apache.commons.el.PropertySuffix;
44  import org.apache.commons.el.UnaryOperatorExpression;
45  import org.apache.commons.el.ValueSuffix;
46  import org.apache.commons.el.parser.ELParser;
47  import org.apache.commons.el.parser.ParseException;
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  
51  
52  /**
53   * Utility class to implement support functionality to "morph" JSP EL into JSF
54   * EL
55   *
56   * @author Anton Koinov (latest modification by $Author: mbr $)
57   * @version $Revision: 231425 $ $Date: 2005-08-11 07:49:45 -0400 (Thu, 11 Aug 2005) $
58   */
59  public class ELParserHelper
60  {
61      static final Log           log    = LogFactory.getLog(ELParserHelper.class);
62      public static final Logger LOGGER = new Logger(System.out);
63  
64      private ELParserHelper()
65      {
66          // util class, do not instantiate
67      }
68  
69      /**
70       * Gets the parsed form of the given expression string. Returns either an
71       * Expression or ExpressionString.
72       */
73      public static Object parseExpression(String expressionString)
74      {
75          expressionString = toJspElExpression(expressionString);
76  
77          ELParser parser = new ELParser(new StringReader(expressionString));
78          try
79          {
80              Object expression = parser.ExpressionString();
81              if (!(expression instanceof Expression)
82                  && !(expression instanceof ExpressionString))
83              {
84                  throw new ReferenceSyntaxException("Invalid expression: '"
85                      + expressionString
86                      + "'. Parsed Expression of unexpected type "
87                      + expression.getClass().getName());
88              }
89  
90              replaceSuffixes(expression);
91  
92              return expression;
93          }
94          catch (ParseException e)
95          {
96              String msg = "Invalid expression: '" + expressionString + "'";
97              throw new ReferenceSyntaxException(msg, e);
98          }
99      }
100 
101     /**
102      * Convert ValueBinding syntax #{ } to JSP EL syntax ${ }
103      *
104      * @param expressionString <code>ValueBinding</code> reference expression
105      *
106      * @return JSP EL compatible expression
107      */
108     static String toJspElExpression(String expressionString)
109     {
110         StringBuffer sb = new StringBuffer(expressionString.length());
111         int remainsPos = 0;
112 
113         for (int posOpenBrace = expressionString.indexOf('{'); posOpenBrace >= 0;
114             posOpenBrace = expressionString.indexOf('{', remainsPos))
115         {
116             if (posOpenBrace > 0)
117             {
118         if( posOpenBrace-1 > remainsPos )
119           sb.append(expressionString.substring(remainsPos, posOpenBrace - 1));
120 
121                 if (expressionString.charAt(posOpenBrace - 1) == '$')
122                 {
123                     sb.append("${'${'}");
124           remainsPos = posOpenBrace+1;
125           continue;
126                 }
127                 else if (expressionString.charAt(posOpenBrace - 1) == '#')
128                 {
129 //                    // TODO: should use \\ as escape for \ always, not just when before #{
130 //                    // allow use of '\' as escape symbol for #{ (for compatibility with Sun's extended implementation)
131 //                    if (isEscaped(expressionString, posOpenBrace - 1))
132 //                    {
133 //                      escapes: {
134 //                            for (int i = sb.length() - 1; i >= 0; i--)
135 //                            {
136 //                                if (sb.charAt(i) != '\\')
137 //                                {
138 //                                    sb.setLength(
139 //                                        sb.length() - (sb.length() - i) / 2);
140 //                                    break escapes;
141 //                                }
142 //                            }
143 //                            sb.setLength(sb.length() / 2);
144 //                        }
145 //                        sb.append("#{");
146 //                    }
147 //                    else
148 //                    {
149                         sb.append("${");
150             int posCloseBrace = indexOfMatchingClosingBrace(expressionString, posOpenBrace);
151                         sb.append(expressionString.substring(posOpenBrace + 1, posCloseBrace + 1));
152             remainsPos = posCloseBrace + 1;
153             continue;
154 //                    }
155                 }else{
156           if( posOpenBrace > remainsPos )
157             sb.append( expressionString.charAt(posOpenBrace - 1) );
158                 }
159             }
160 
161       // Standalone brace
162       sb.append('{');
163       remainsPos = posOpenBrace + 1;
164         }
165 
166     sb.append(expressionString.substring(remainsPos));
167 
168         // Create a new String to shrink mem size since we are caching
169         return new String(sb.toString());
170     }
171 
172     private static int findQuote(String expressionString, int start)
173     {
174         int indexofSingleQuote = expressionString.indexOf('\'', start);
175         int indexofDoubleQuote = expressionString.indexOf('"', start);
176         return StringUtils.minIndex(indexofSingleQuote, indexofDoubleQuote);
177     }
178 
179     /**
180      * Return the index of the matching closing brace, skipping over quoted text
181      *
182      * @param expressionString string to search
183      * @param indexofOpeningBrace the location of opening brace to match
184      *
185      * @return the index of the matching closing brace
186      *
187      * @throws ReferenceSyntaxException if matching brace cannot be found
188      */
189     private static int indexOfMatchingClosingBrace(String expressionString,
190         int indexofOpeningBrace)
191     {
192         int len = expressionString.length();
193         int i = indexofOpeningBrace + 1;
194 
195         // Loop through quoted strings
196         for (;;)
197         {
198             if (i >= len)
199             {
200                 throw new ReferenceSyntaxException(
201                     "Missing closing brace. Expression: '" + expressionString
202                         + "'");
203             }
204 
205             int indexofClosingBrace = expressionString.indexOf('}', i);
206             i = StringUtils.minIndex(indexofClosingBrace, findQuote(
207                 expressionString, i));
208 
209             if (i < 0)
210             {
211                 // No delimiter found
212                 throw new ReferenceSyntaxException(
213                     "Missing closing brace. Expression: '" + expressionString
214                         + "'");
215             }
216 
217             // 1. If quoted literal, find closing quote
218             if (i != indexofClosingBrace)
219             {
220                 i = indexOfMatchingClosingQuote(expressionString, i) + 1;
221                 if (i == 0)
222                 {
223                     // Note: if no match, i==0 because -1 + 1 = 0
224                     throw new ReferenceSyntaxException(
225                         "Missing closing quote. Expression: '"
226                             + expressionString + "'");
227                 }
228             }
229             else
230             {
231                 // Closing brace
232                 return i;
233             }
234         }
235     }
236 
237     /**
238      * Returns the index of the matching closing quote, skipping over escaped
239      * quotes
240      *
241      * @param expressionString string to scan
242      * @param indexOfOpeningQuote start from this position in the string
243      * @return -1 if no match, the index of closing quote otherwise
244      */
245     private static int indexOfMatchingClosingQuote(String expressionString,
246         int indexOfOpeningQuote)
247     {
248         char quote = expressionString.charAt(indexOfOpeningQuote);
249         for (int i = expressionString.indexOf(quote, indexOfOpeningQuote + 1);
250             i >= 0; i = expressionString.indexOf(quote, i + 1))
251         {
252             if (!isEscaped(expressionString, i))
253             {
254                 return i;
255             }
256         }
257 
258         // No matching quote found
259         return -1;
260     }
261 
262     private static boolean isEscaped(String expressionString, int i)
263     {
264         int escapeCharCount = 0;
265         while ((--i >= 0) && (expressionString.charAt(i) == '\\'))
266         {
267             escapeCharCount++;
268         }
269 
270         return (escapeCharCount % 2) != 0;
271     }
272 
273     /**
274      * Replaces all <code>ValueSuffix</code>es with custom implementation
275      * ValueSuffexes that use JSF <code>PropertyResolver</code> insted of JSP
276      * EL one.
277      *
278      * @param expression <code>Expression</code> or
279      *        <code>ExpressionString</code> instance
280      * @param application <code>Application</code> instance to get
281      *        <code>PropertyResolver</code> from
282      */
283     private static void replaceSuffixes(Object expression)
284     {
285         if (expression instanceof Expression)
286         {
287             replaceSuffixes((Expression) expression);
288         }
289         else if (expression instanceof ExpressionString)
290         {
291             replaceSuffixes((ExpressionString) expression);
292         }
293         else
294         {
295             throw new IllegalStateException(
296                 "Expression element of unknown class: "
297                     + expression.getClass().getName());
298         }
299     }
300 
301     private static void replaceSuffixes(ExpressionString expressionString)
302     {
303         Object[] expressions = expressionString.getElements();
304         for (int i = 0, len = expressions.length; i < len; i++)
305         {
306             Object expression = expressions[i];
307             if (expression instanceof Expression)
308             {
309                 replaceSuffixes((Expression) expression);
310             }
311             else if (expression instanceof ExpressionString)
312             {
313                 replaceSuffixes((ExpressionString) expression);
314             }
315             else if (!(expression instanceof String))
316             {
317                 throw new IllegalStateException(
318                     "Expression element of unknown class: "
319                         + expression.getClass().getName());
320             }
321             // ignore Strings
322         }
323     }
324 
325     static void replaceSuffixes(Expression expression)
326     {
327         if (expression instanceof BinaryOperatorExpression)
328         {
329             replaceSuffixes(((BinaryOperatorExpression) expression)
330                 .getExpression());
331         }
332         else if (expression instanceof ComplexValue)
333         {
334             replaceSuffixes((ComplexValue) expression);
335         }
336         else if (expression instanceof ConditionalExpression)
337         {
338             ConditionalExpression conditionalExpression =
339                 (ConditionalExpression) expression;
340             replaceSuffixes(conditionalExpression.getTrueBranch());
341             replaceSuffixes(conditionalExpression.getFalseBranch());
342         }
343         else if (expression instanceof UnaryOperatorExpression)
344         {
345             replaceSuffixes(((UnaryOperatorExpression) expression)
346                 .getExpression());
347         }
348 
349         // ignore the remaining expression types
350         else if (!(expression instanceof FunctionInvocation
351             || expression instanceof Literal || expression instanceof NamedValue))
352         {
353             throw new IllegalStateException(
354                 "Expression element of unknown class: "
355                     + expression.getClass().getName());
356         }
357     }
358 
359     private static void replaceSuffixes(ComplexValue complexValue)
360     {
361         Application application = FacesContext.getCurrentInstance()
362             .getApplication();
363 
364         List suffixes = complexValue.getSuffixes();
365         for (int i = 0, len = suffixes.size(); i < len; i++)
366         {
367             ValueSuffix suffix = (ValueSuffix) suffixes.get(i);
368             if (suffix instanceof PropertySuffix)
369             {
370                 if (suffix instanceof MyPropertySuffix)
371                 {
372                     throw new IllegalStateException(
373                         "Suffix is MyPropertySuffix and must not be");
374                 }
375 
376                 suffixes.set(i, new MyPropertySuffix((PropertySuffix) suffix,
377                     application));
378             }
379             else if (suffix instanceof ArraySuffix)
380             {
381                 if (suffix instanceof MyArraySuffix)
382                 {
383                     throw new IllegalStateException(
384                         "Suffix is MyArraySuffix and must not be");
385                 }
386 
387                 suffixes.set(i, new MyArraySuffix((ArraySuffix) suffix,
388                     application));
389             }
390             else
391             {
392                 throw new IllegalStateException("Unknown suffix class: "
393                     + suffix.getClass().getName());
394             }
395         }
396     }
397 
398     private static Integer coerceToIntegerWrapper(Object base, Object index)
399         throws EvaluationException, ELException
400     {
401         Integer integer = Coercions.coerceToInteger(index, LOGGER);
402         if (integer != null)
403         {
404             return integer;
405         }
406         throw new ReferenceSyntaxException(
407             "Cannot convert index to int for base " + base.getClass().getName()
408                 + " and index " + index);
409     }
410 
411     /**
412      * Coerces <code>index</code> to Integer for array types, or returns
413      * <code>null</code> for non-array types.
414      *
415      * @param base Object for the base
416      * @param index Object for the index
417      * @return Integer a valid Integer index, or null if not an array type
418      *
419      * @throws ELException if exception occurs trying to coerce to Integer
420      * @throws EvaluationException if base is array type but cannot convert
421      *         index to Integer
422      */
423     public static Integer toIndex(Object base, Object index)
424         throws ELException, EvaluationException
425     {
426         if ((base instanceof List) || (base.getClass().isArray()))
427         {
428             return coerceToIntegerWrapper(base, index);
429         }
430         if (base instanceof UIComponent)
431         {
432             try
433             {
434                 return coerceToIntegerWrapper(base, index);
435             }
436             catch (Throwable t)
437             {
438                 // treat as simple property
439                 return null;
440             }
441         }
442 
443         // If not an array type
444         return null;
445     }
446 
447     /**
448      * Override ArraySuffix.evaluate() to use our property resolver
449      */
450     public static class MyArraySuffix extends ArraySuffix
451     {
452         private Application _application;
453 
454         public MyArraySuffix(ArraySuffix arraySuffix, Application application)
455         {
456             super(arraySuffix.getIndex());
457             replaceSuffixes(getIndex());
458             _application = application;
459         }
460 
461         /**
462          * Evaluates the expression in the given context, operating on the given
463          * value, using JSF property resolver.
464          */
465         public Object evaluate(Object base, VariableResolver variableResolver,
466             FunctionMapper functions, Logger logger)
467             throws ELException
468         {
469             // Check for null value
470             if (base == null)
471             {
472                 return null;
473             }
474 
475             // Evaluate the index
476             Object indexVal = getIndex().evaluate(variableResolver, functions,
477                 logger);
478             if (indexVal == null)
479             {
480                 return null;
481             }
482 
483             Integer index = toIndex(base, indexVal);
484             if (index == null)
485             {
486                 return _application.getPropertyResolver().getValue(base,
487                     indexVal);
488             }
489             else
490             {
491                 return _application.getPropertyResolver().getValue(base,
492                     index.intValue());
493             }
494         }
495     }
496 
497     public static class MyPropertySuffix extends PropertySuffix
498     {
499         private Application _application;
500 
501         public MyPropertySuffix(PropertySuffix propertySuffix,
502             Application application)
503         {
504             super(propertySuffix.getName());
505             _application = application;
506         }
507 
508         /**
509          * Evaluates the expression in the given context, operating on the given
510          * value, using JSF property resolver.
511          */
512         public Object evaluate(Object base, VariableResolver variableResolver,
513             FunctionMapper functions, Logger logger)
514             throws ELException
515         {
516             // Check for null value
517             if (base == null)
518             {
519                 return null;
520             }
521 
522             // Evaluate the index
523             String indexVal = getName();
524             if (indexVal == null)
525             {
526                 return null;
527             }
528 
529             Integer index = toIndex(base, indexVal);
530             if (index == null)
531             {
532                 return _application.getPropertyResolver().getValue(base,
533                     indexVal);
534             }
535             else
536             {
537                 return _application.getPropertyResolver().getValue(base,
538                     index.intValue());
539             }
540         }
541     }
542 }