KickJava   Java API By Example, From Geeks To Geeks.

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


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 JavaDoc;
19 import java.util.List JavaDoc;
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 JavaDoc;
27 import javax.servlet.jsp.el.FunctionMapper JavaDoc;
28 import javax.servlet.jsp.el.VariableResolver JavaDoc;
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: svieujot $)
57  * @version $Revision: 1.7 $ $Date: 2005/04/11 17:39:36 $
58  *
59  * $Log: ELParserHelper.java,v $
60  * Revision 1.7 2005/04/11 17:39:36 svieujot
61  * ELParserHelper : Fix a String IndexOutOfBoundsException & several bugs in toJspELExpression.
62  * Add a JUnit Test.
63  *
64  * Revision 1.6 2004/10/13 11:51:00 matze
65  * renamed packages to org.apache
66  *
67  * Revision 1.5 2004/09/28 19:11:50 dave0000
68  * uppercase static final prop
69  * remove redundant code
70  *
71  * Revision 1.4 2004/07/01 22:05:11 mwessendorf
72  * ASF switch
73  *
74  * Revision 1.3 2004/05/10 05:30:13 dave0000
75  * Fix issue with setting Managed Bean to a wrong scope
76  *
77  * Revision 1.2 2004/04/08 23:22:18 dave0000
78  * remove assert statements
79  *
80  */

81 public class ELParserHelper
82 {
83     static final Log log = LogFactory.getLog(ELParserHelper.class);
84     public static final Logger LOGGER = new Logger(System.out);
85
86     private ELParserHelper()
87     {
88         // util class, do not instantiate
89
}
90
91     /**
92      * Gets the parsed form of the given expression string. Returns either an
93      * Expression or ExpressionString.
94      */

95     public static Object JavaDoc parseExpression(String JavaDoc expressionString)
96     {
97         expressionString = toJspElExpression(expressionString);
98
99         ELParser parser = new ELParser(new StringReader JavaDoc(expressionString));
100         try
101         {
102             Object JavaDoc expression = parser.ExpressionString();
103             if (!(expression instanceof Expression)
104                 && !(expression instanceof ExpressionString))
105             {
106                 throw new ReferenceSyntaxException("Invalid expression: '"
107                     + expressionString
108                     + "'. Parsed Expression of unexpected type "
109                     + expression.getClass().getName());
110             }
111
112             replaceSuffixes(expression);
113
114             return expression;
115         }
116         catch (ParseException e)
117         {
118             String JavaDoc msg = "Invalid expression: '" + expressionString + "'";
119             log.debug(msg, e);
120             throw new ReferenceSyntaxException(msg, e);
121         }
122     }
123
124     /**
125      * Convert ValueBinding syntax #{ } to JSP EL syntax ${ }
126      *
127      * @param expressionString <code>ValueBinding</code> reference expression
128      *
129      * @return JSP EL compatible expression
130      */

131     static String JavaDoc toJspElExpression(String JavaDoc expressionString)
132     {
133         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(expressionString.length());
134         int remainsPos = 0;
135
136         for (int posOpenBrace = expressionString.indexOf('{'); posOpenBrace >= 0;
137             posOpenBrace = expressionString.indexOf('{', remainsPos))
138         {
139             if (posOpenBrace > 0)
140             {
141                 if( posOpenBrace-1 > remainsPos )
142                     sb.append(expressionString.substring(remainsPos, posOpenBrace - 1));
143
144                 if (expressionString.charAt(posOpenBrace - 1) == '$')
145                 {
146                     sb.append("${'${'}");
147                     remainsPos = posOpenBrace+1;
148                     continue;
149                 }
150                 else if (expressionString.charAt(posOpenBrace - 1) == '#')
151                 {
152 // // TODO: should use \\ as escape for \ always, not just when before #{
153
// // allow use of '\' as escape symbol for #{ (for compatibility with Sun's extended implementation)
154
// if (isEscaped(expressionString, posOpenBrace - 1))
155
// {
156
// escapes: {
157
// for (int i = sb.length() - 1; i >= 0; i--)
158
// {
159
// if (sb.charAt(i) != '\\')
160
// {
161
// sb.setLength(
162
// sb.length() - (sb.length() - i) / 2);
163
// break escapes;
164
// }
165
// }
166
// sb.setLength(sb.length() / 2);
167
// }
168
// sb.append("#{");
169
// }
170
// else
171
// {
172
sb.append("${");
173                         int posCloseBrace = indexOfMatchingClosingBrace(expressionString, posOpenBrace);
174                         sb.append(expressionString.substring(posOpenBrace + 1, posCloseBrace + 1));
175                         remainsPos = posCloseBrace + 1;
176                         continue;
177 // }
178
}else{
179                     if( posOpenBrace > remainsPos )
180                         sb.append( expressionString.charAt(posOpenBrace - 1) );
181                 }
182             }
183
184             // Standalone brace
185
sb.append('{');
186             remainsPos = posOpenBrace + 1;
187         }
188
189         sb.append(expressionString.substring(remainsPos));
190         
191         // Create a new String to shrink mem size since we are caching
192
return new String JavaDoc(sb.toString());
193     }
194
195     private static int findQuote(String JavaDoc expressionString, int start)
196     {
197         int indexofSingleQuote = expressionString.indexOf('\'', start);
198         int indexofDoubleQuote = expressionString.indexOf('"', start);
199         return StringUtils.minIndex(indexofSingleQuote, indexofDoubleQuote);
200     }
201
202     /**
203      * Return the index of the matching closing brace, skipping over quoted text
204      *
205      * @param expressionString string to search
206      * @param indexofOpeningBrace the location of opening brace to match
207      *
208      * @return the index of the matching closing brace
209      *
210      * @throws ReferenceSyntaxException if matching brace cannot be found
211      */

212     private static int indexOfMatchingClosingBrace(String JavaDoc expressionString,
213         int indexofOpeningBrace)
214     {
215         int len = expressionString.length();
216         int i = indexofOpeningBrace + 1;
217
218         // Loop through quoted strings
219
for (;;)
220         {
221             if (i >= len)
222             {
223                 throw new ReferenceSyntaxException(
224                     "Missing closing brace. Expression: '" + expressionString
225                         + "'");
226             }
227
228             int indexofClosingBrace = expressionString.indexOf('}', i);
229             i = StringUtils.minIndex(indexofClosingBrace, findQuote(
230                 expressionString, i));
231
232             if (i < 0)
233             {
234                 // No delimiter found
235
throw new ReferenceSyntaxException(
236                     "Missing closing brace. Expression: '" + expressionString
237                         + "'");
238             }
239
240             // 1. If quoted literal, find closing quote
241
if (i != indexofClosingBrace)
242             {
243                 i = indexOfMatchingClosingQuote(expressionString, i) + 1;
244                 if (i == 0)
245                 {
246                     // Note: if no match, i==0 because -1 + 1 = 0
247
throw new ReferenceSyntaxException(
248                         "Missing closing quote. Expression: '"
249                             + expressionString + "'");
250                 }
251             }
252             else
253             {
254                 // Closing brace
255
return i;
256             }
257         }
258     }
259
260     /**
261      * Returns the index of the matching closing quote, skipping over escaped
262      * quotes
263      *
264      * @param expressionString string to scan
265      * @param indexOfOpeningQuote start from this position in the string
266      * @return -1 if no match, the index of closing quote otherwise
267      */

268     private static int indexOfMatchingClosingQuote(String JavaDoc expressionString,
269         int indexOfOpeningQuote)
270     {
271         char quote = expressionString.charAt(indexOfOpeningQuote);
272         for (int i = expressionString.indexOf(quote, indexOfOpeningQuote + 1);
273             i >= 0; i = expressionString.indexOf(quote, i + 1))
274         {
275             if (!isEscaped(expressionString, i))
276             {
277                 return i;
278             }
279         }
280
281         // No matching quote found
282
return -1;
283     }
284
285     private static boolean isEscaped(String JavaDoc expressionString, int i)
286     {
287         int escapeCharCount = 0;
288         while ((--i >= 0) && (expressionString.charAt(i) == '\\'))
289         {
290             escapeCharCount++;
291         }
292
293         return (escapeCharCount % 2) != 0;
294     }
295
296     /**
297      * Replaces all <code>ValueSuffix</code>es with custom implementation
298      * ValueSuffexes that use JSF <code>PropertyResolver</code> insted of JSP
299      * EL one.
300      *
301      * @param expression <code>Expression</code> or
302      * <code>ExpressionString</code> instance
303      * @param application <code>Application</code> instance to get
304      * <code>PropertyResolver</code> from
305      */

306     private static void replaceSuffixes(Object JavaDoc expression)
307     {
308         if (expression instanceof Expression)
309         {
310             replaceSuffixes((Expression) expression);
311         }
312         else if (expression instanceof ExpressionString)
313         {
314             replaceSuffixes((ExpressionString) expression);
315         }
316         else
317         {
318             throw new IllegalStateException JavaDoc(
319                 "Expression element of unknown class: "
320                     + expression.getClass().getName());
321         }
322     }
323
324     private static void replaceSuffixes(ExpressionString expressionString)
325     {
326         Object JavaDoc[] expressions = expressionString.getElements();
327         for (int i = 0, len = expressions.length; i < len; i++)
328         {
329             Object JavaDoc expression = expressions[i];
330             if (expression instanceof Expression)
331             {
332                 replaceSuffixes((Expression) expression);
333             }
334             else if (expression instanceof ExpressionString)
335             {
336                 replaceSuffixes((ExpressionString) expression);
337             }
338             else if (!(expression instanceof String JavaDoc))
339             {
340                 throw new IllegalStateException JavaDoc(
341                     "Expression element of unknown class: "
342                         + expression.getClass().getName());
343             }
344             // ignore Strings
345
}
346     }
347
348     static void replaceSuffixes(Expression expression)
349     {
350         if (expression instanceof BinaryOperatorExpression)
351         {
352             replaceSuffixes(((BinaryOperatorExpression) expression)
353                 .getExpression());
354         }
355         else if (expression instanceof ComplexValue)
356         {
357             replaceSuffixes((ComplexValue) expression);
358         }
359         else if (expression instanceof ConditionalExpression)
360         {
361             ConditionalExpression conditionalExpression =
362                 (ConditionalExpression) expression;
363             replaceSuffixes(conditionalExpression.getTrueBranch());
364             replaceSuffixes(conditionalExpression.getFalseBranch());
365         }
366         else if (expression instanceof UnaryOperatorExpression)
367         {
368             replaceSuffixes(((UnaryOperatorExpression) expression)
369                 .getExpression());
370         }
371
372         // ignore the remaining expression types
373
else if (!(expression instanceof FunctionInvocation
374             || expression instanceof Literal || expression instanceof NamedValue))
375         {
376             throw new IllegalStateException JavaDoc(
377                 "Expression element of unknown class: "
378                     + expression.getClass().getName());
379         }
380     }
381
382     private static void replaceSuffixes(ComplexValue complexValue)
383     {
384         Application application = FacesContext.getCurrentInstance()
385             .getApplication();
386
387         List JavaDoc suffixes = complexValue.getSuffixes();
388         for (int i = 0, len = suffixes.size(); i < len; i++)
389         {
390             ValueSuffix suffix = (ValueSuffix) suffixes.get(i);
391             if (suffix instanceof PropertySuffix)
392             {
393                 if (suffix instanceof MyPropertySuffix)
394                 {
395                     throw new IllegalStateException JavaDoc(
396                         "Suffix is MyPropertySuffix and must not be");
397                 }
398
399                 suffixes.set(i, new MyPropertySuffix((PropertySuffix) suffix,
400                     application));
401             }
402             else if (suffix instanceof ArraySuffix)
403             {
404                 if (suffix instanceof MyArraySuffix)
405                 {
406                     throw new IllegalStateException JavaDoc(
407                         "Suffix is MyArraySuffix and must not be");
408                 }
409
410                 suffixes.set(i, new MyArraySuffix((ArraySuffix) suffix,
411                     application));
412             }
413             else
414             {
415                 throw new IllegalStateException JavaDoc("Unknown suffix class: "
416                     + suffix.getClass().getName());
417             }
418         }
419     }
420
421     private static Integer JavaDoc coerceToIntegerWrapper(Object JavaDoc base, Object JavaDoc index)
422         throws EvaluationException, ELException JavaDoc
423     {
424         Integer JavaDoc integer = Coercions.coerceToInteger(index, LOGGER);
425         if (integer != null)
426         {
427             return integer;
428         }
429         throw new ReferenceSyntaxException(
430             "Cannot convert index to int for base " + base.getClass().getName()
431                 + " and index " + index);
432     }
433
434     /**
435      * Coerces <code>index</code> to Integer for array types, or returns
436      * <code>null</code> for non-array types.
437      *
438      * @param base Object for the base
439      * @param index Object for the index
440      * @return Integer a valid Integer index, or null if not an array type
441      *
442      * @throws ELException if exception occurs trying to coerce to Integer
443      * @throws EvaluationException if base is array type but cannot convert
444      * index to Integer
445      */

446     public static Integer JavaDoc toIndex(Object JavaDoc base, Object JavaDoc index)
447         throws ELException JavaDoc, EvaluationException
448     {
449         if ((base instanceof List JavaDoc) || (base.getClass().isArray()))
450         {
451             return coerceToIntegerWrapper(base, index);
452         }
453         if (base instanceof UIComponent)
454         {
455             try
456             {
457                 return coerceToIntegerWrapper(base, index);
458             }
459             catch (Throwable JavaDoc t)
460             {
461                 // treat as simple property
462
return null;
463             }
464         }
465
466         // If not an array type
467
return null;
468     }
469
470     /**
471      * Override ArraySuffix.evaluate() to use our property resolver
472      */

473     public static class MyArraySuffix extends ArraySuffix
474     {
475         private Application _application;
476
477         public MyArraySuffix(ArraySuffix arraySuffix, Application application)
478         {
479             super(arraySuffix.getIndex());
480             replaceSuffixes(getIndex());
481             _application = application;
482         }
483
484         /**
485          * Evaluates the expression in the given context, operating on the given
486          * value, using JSF property resolver.
487          */

488         public Object JavaDoc evaluate(Object JavaDoc base, VariableResolver JavaDoc variableResolver,
489             FunctionMapper JavaDoc functions, Logger logger)
490             throws ELException JavaDoc
491         {
492             // Check for null value
493
if (base == null)
494             {
495                 return null;
496             }
497
498             // Evaluate the index
499
Object JavaDoc indexVal = getIndex().evaluate(variableResolver, functions,
500                 logger);
501             if (indexVal == null)
502             {
503                 return null;
504             }
505
506             Integer JavaDoc index = toIndex(base, indexVal);
507             if (index == null)
508             {
509                 return _application.getPropertyResolver().getValue(base,
510                     indexVal);
511             }
512             else
513             {
514                 return _application.getPropertyResolver().getValue(base,
515                     index.intValue());
516             }
517         }
518     }
519
520     public static class MyPropertySuffix extends PropertySuffix
521     {
522         private Application _application;
523
524         public MyPropertySuffix(PropertySuffix propertySuffix,
525             Application application)
526         {
527             super(propertySuffix.getName());
528             _application = application;
529         }
530
531         /**
532          * Evaluates the expression in the given context, operating on the given
533          * value, using JSF property resolver.
534          */

535         public Object JavaDoc evaluate(Object JavaDoc base, VariableResolver JavaDoc variableResolver,
536             FunctionMapper JavaDoc functions, Logger logger)
537             throws ELException JavaDoc
538         {
539             // Check for null value
540
if (base == null)
541             {
542                 return null;
543             }
544
545             // Evaluate the index
546
String JavaDoc indexVal = getName();
547             if (indexVal == null)
548             {
549                 return null;
550             }
551
552             Integer JavaDoc index = toIndex(base, indexVal);
553             if (index == null)
554             {
555                 return _application.getPropertyResolver().getValue(base,
556                     indexVal);
557             }
558             else
559             {
560                 return _application.getPropertyResolver().getValue(base,
561                     index.intValue());
562             }
563         }
564     }
565 }
Popular Tags