Save This Page
Home » glassfish-v2ur2-b04-src » javax » el » [javadoc | source]
    1   /*
    2    * The contents of this file are subject to the terms
    3    * of the Common Development and Distribution License
    4    * (the "License").  You may not use this file except
    5    * in compliance with the License.
    6    *
    7    * You can obtain a copy of the license at
    8    * glassfish/bootstrap/legal/CDDLv1.0.txt or
    9    * https://glassfish.dev.java.net/public/CDDLv1.0.html.
   10    * See the License for the specific language governing
   11    * permissions and limitations under the License.
   12    *
   13    * When distributing Covered Code, include this CDDL
   14    * HEADER in each file and include the License file at
   15    * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable,
   16    * add the following below this CDDL HEADER, with the
   17    * fields enclosed by brackets "[]" replaced with your
   18    * own identifying information: Portions Copyright [yyyy]
   19    * [name of copyright owner]
   20    *
   21    * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
   22    */ 
   23   
   24   package javax.el;
   25   
   26   import java.util.ArrayList;
   27   import java.util.Iterator;
   28   import java.beans.FeatureDescriptor;
   29   
   30   /**
   31    * Maintains an ordered composite list of child <code>ELResolver</code>s.
   32    *
   33    * <p>Though only a single <code>ELResolver</code> is associated with an
   34    * <code>ELContext</code>, there are usually multiple resolvers considered
   35    * for any given variable or property resolution. <code>ELResolver</code>s
   36    * are combined together using a <code>CompositeELResolver</code>, to define
   37    * rich semantics for evaluating an expression.</p>
   38    *
   39    * <p>For the {@link #getValue}, {@link #getType}, {@link #setValue} and
   40    * {@link #isReadOnly} methods, an <code>ELResolver</code> is not
   41    * responsible for resolving all possible (base, property) pairs. In fact,
   42    * most resolvers will only handle a <code>base</code> of a single type.
   43    * To indicate that a resolver has successfully resolved a particular
   44    * (base, property) pair, it must set the <code>propertyResolved</code>
   45    * property of the <code>ELContext</code> to <code>true</code>. If it could 
   46    * not handle the given pair, it must leave this property alone. The caller
   47    * must ignore the return value of the method if <code>propertyResolved</code>
   48    * is <code>false</code>.</p>
   49    *
   50    * <p>The <code>CompositeELResolver</code> initializes the
   51    * <code>ELContext.propertyResolved</code> flag to <code>false</code>, and uses 
   52    * it as a stop condition for iterating through its component resolvers.</p>
   53    *
   54    * <p>The <code>ELContext.propertyResolved</code> flag is not used for the 
   55    * design-time methods {@link #getFeatureDescriptors} and
   56    * {@link #getCommonPropertyType}. Instead, results are collected and 
   57    * combined from all child <code>ELResolver</code>s for these methods.</p>
   58    *
   59    * @see ELContext
   60    * @see ELResolver
   61    * @since JSP 2.1
   62    */
   63   public class CompositeELResolver extends ELResolver {
   64   
   65       /**
   66        * Adds the given resolver to the list of component resolvers.
   67        *
   68        * <p>Resolvers are consulted in the order in which they are added.</p>
   69        *
   70        * @param elResolver The component resolver to add.
   71        * @throws NullPointerException If the provided resolver is
   72        *     <code>null</code>.
   73        */
   74       public void add(ELResolver elResolver) {
   75   
   76           if (elResolver == null) {
   77               throw new NullPointerException();
   78           }
   79                                                                                   
   80           elResolvers.add(elResolver);
   81       }
   82   
   83       /**
   84        * Attempts to resolve the given <code>property</code> object on the given
   85        * <code>base</code> object by querying all component resolvers.
   86        *
   87        * <p>If this resolver handles the given (base, property) pair, 
   88        * the <code>propertyResolved</code> property of the 
   89        * <code>ELContext</code> object must be set to <code>true</code>
   90        * by the resolver, before returning. If this property is not 
   91        * <code>true</code> after this method is called, the caller should ignore 
   92        * the return value.</p>
   93        *
   94        * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
   95        * the provided <code>ELContext</code>.</p>
   96        *
   97        * <p>Next, for each component resolver in this composite:
   98        * <ol>
   99        *   <li>The <code>getValue()</code> method is called, passing in
  100        *       the provided <code>context</code>, <code>base</code> and 
  101        *       <code>property</code>.</li>
  102        *   <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  103        *       flag is <code>false</code> then iteration continues.</li>
  104        *   <li>Otherwise, iteration stops and no more component resolvers are
  105        *       considered. The value returned by <code>getValue()</code> is
  106        *       returned by this method.</li>
  107        * </ol></p>
  108        *
  109        * <p>If none of the component resolvers were able to perform this
  110        * operation, the value <code>null</code> is returned and the
  111        * <code>propertyResolved</code> flag remains set to 
  112        * <code>false</code></p>.
  113        *
  114        * <p>Any exception thrown by component resolvers during the iteration
  115        * is propagated to the caller of this method.</p>
  116        *
  117        * @param context The context of this evaluation.
  118        * @param base The base object whose property value is to be returned,
  119        *     or <code>null</code> to resolve a top-level variable.
  120        * @param property The property or variable to be resolved.
  121        * @return If the <code>propertyResolved</code> property of 
  122        *     <code>ELContext</code> was set to <code>true</code>, then
  123        *     the result of the variable or property resolution; otherwise
  124        *     undefined.
  125        * @throws NullPointerException if context is <code>null</code>
  126        * @throws PropertyNotFoundException if the given (base, property) pair
  127        *     is handled by this <code>ELResolver</code> but the specified
  128        *     variable or property does not exist or is not readable.
  129        * @throws ELException if an exception was thrown while performing
  130        *     the property or variable resolution. The thrown exception
  131        *     must be included as the cause property of this exception, if
  132        *     available.
  133        */
  134       public Object getValue(ELContext context,
  135                              Object base,
  136                              Object property) {
  137           context.setPropertyResolved(false);
  138           int i = 0, len = this.elResolvers.size();
  139           ELResolver elResolver;
  140           Object value; 
  141           while (i < len) {
  142               elResolver = this.elResolvers.get(i);
  143               value = elResolver.getValue(context, base, property);
  144               if (context.isPropertyResolved()) {
  145                   return value;
  146               }
  147               i++;
  148           } 
  149           return null;
  150       }
  151   
  152       /**
  153        * For a given <code>base</code> and <code>property</code>, attempts to
  154        * identify the most general type that is acceptable for an object to be 
  155        * passed as the <code>value</code> parameter in a future call 
  156        * to the {@link #setValue} method. The result is obtained by 
  157        * querying all component resolvers.
  158        *
  159        * <p>If this resolver handles the given (base, property) pair, 
  160        * the <code>propertyResolved</code> property of the 
  161        * <code>ELContext</code> object must be set to <code>true</code>
  162        * by the resolver, before returning. If this property is not 
  163        * <code>true</code> after this method is called, the caller should ignore 
  164        * the return value.</p>
  165        *
  166        * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  167        * the provided <code>ELContext</code>.</p>
  168        *
  169        * <p>Next, for each component resolver in this composite:
  170        * <ol>
  171        *   <li>The <code>getType()</code> method is called, passing in
  172        *       the provided <code>context</code>, <code>base</code> and 
  173        *       <code>property</code>.</li>
  174        *   <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  175        *       flag is <code>false</code> then iteration continues.</li>
  176        *   <li>Otherwise, iteration stops and no more component resolvers are
  177        *       considered. The value returned by <code>getType()</code> is
  178        *       returned by this method.</li>
  179        * </ol></p>
  180        *
  181        * <p>If none of the component resolvers were able to perform this
  182        * operation, the value <code>null</code> is returned and the
  183        * <code>propertyResolved</code> flag remains set to 
  184        * <code>false</code></p>.
  185        *
  186        * <p>Any exception thrown by component resolvers during the iteration
  187        * is propagated to the caller of this method.</p>
  188        *
  189        * @param context The context of this evaluation.
  190        * @param base The base object whose property value is to be analyzed,
  191        *     or <code>null</code> to analyze a top-level variable.
  192        * @param property The property or variable to return the acceptable 
  193        *     type for.
  194        * @return If the <code>propertyResolved</code> property of 
  195        *     <code>ELContext</code> was set to <code>true</code>, then
  196        *     the most general acceptable type; otherwise undefined.
  197        * @throws NullPointerException if context is <code>null</code>
  198        * @throws PropertyNotFoundException if the given (base, property) pair
  199        *     is handled by this <code>ELResolver</code> but the specified
  200        *     variable or property does not exist or is not readable.
  201        * @throws ELException if an exception was thrown while performing
  202        *     the property or variable resolution. The thrown exception
  203        *     must be included as the cause property of this exception, if
  204        *     available.
  205        */
  206       public Class<?> getType(ELContext context,
  207                            Object base,
  208                            Object property) {
  209           context.setPropertyResolved(false);
  210           int i = 0, len = this.elResolvers.size();
  211           ELResolver elResolver;
  212           Class<?> type;  
  213           while (i < len) {
  214               elResolver = this.elResolvers.get(i);
  215               type = elResolver.getType(context, base, property);
  216               if (context.isPropertyResolved()) {
  217                   return type;
  218               }
  219               i++;
  220           }
  221           return null;
  222       }
  223   
  224       /**
  225        * Attempts to set the value of the given <code>property</code> 
  226        * object on the given <code>base</code> object. All component
  227        * resolvers are asked to attempt to set the value.
  228        *
  229        * <p>If this resolver handles the given (base, property) pair, 
  230        * the <code>propertyResolved</code> property of the 
  231        * <code>ELContext</code> object must be set to <code>true</code>
  232        * by the resolver, before returning. If this property is not 
  233        * <code>true</code> after this method is called, the caller can
  234        * safely assume no value has been set.</p>
  235        *
  236        * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  237        * the provided <code>ELContext</code>.</p>
  238        *
  239        * <p>Next, for each component resolver in this composite:
  240        * <ol>
  241        *   <li>The <code>setValue()</code> method is called, passing in
  242        *       the provided <code>context</code>, <code>base</code>, 
  243        *       <code>property</code> and <code>value</code>.</li>
  244        *   <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  245        *       flag is <code>false</code> then iteration continues.</li>
  246        *   <li>Otherwise, iteration stops and no more component resolvers are
  247        *       considered.</li>
  248        * </ol></p>
  249        *
  250        * <p>If none of the component resolvers were able to perform this
  251        * operation, the <code>propertyResolved</code> flag remains set to 
  252        * <code>false</code></p>.
  253        *
  254        * <p>Any exception thrown by component resolvers during the iteration
  255        * is propagated to the caller of this method.</p>
  256        *
  257        * @param context The context of this evaluation.
  258        * @param base The base object whose property value is to be set,
  259        *     or <code>null</code> to set a top-level variable.
  260        * @param property The property or variable to be set.
  261        * @param val The value to set the property or variable to.
  262        * @throws NullPointerException if context is <code>null</code>
  263        * @throws PropertyNotFoundException if the given (base, property) pair
  264        *     is handled by this <code>ELResolver</code> but the specified
  265        *     variable or property does not exist.
  266        * @throws PropertyNotWritableException if the given (base, property)
  267        *     pair is handled by this <code>ELResolver</code> but the specified
  268        *     variable or property is not writable.
  269        * @throws ELException if an exception was thrown while attempting to
  270        *     set the property or variable. The thrown exception
  271        *     must be included as the cause property of this exception, if
  272        *     available.
  273        */
  274       public void setValue(ELContext context,
  275                            Object base,
  276                            Object property,
  277                            Object val) {
  278           context.setPropertyResolved(false);
  279           int i = 0, len = this.elResolvers.size();
  280           ELResolver elResolver;
  281           while (i < len) {
  282               elResolver = this.elResolvers.get(i);
  283               elResolver.setValue(context, base, property, val);
  284               if (context.isPropertyResolved()) {
  285                   return;
  286               }
  287               i++;
  288           }
  289       }
  290   
  291       /**
  292        * For a given <code>base</code> and <code>property</code>, attempts to
  293        * determine whether a call to {@link #setValue} will always fail. The
  294        * result is obtained by querying all component resolvers.
  295        *
  296        * <p>If this resolver handles the given (base, property) pair, 
  297        * the <code>propertyResolved</code> property of the 
  298        * <code>ELContext</code> object must be set to <code>true</code>
  299        * by the resolver, before returning. If this property is not 
  300        * <code>true</code> after this method is called, the caller should ignore 
  301        * the return value.</p>
  302        *
  303        * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  304        * the provided <code>ELContext</code>.</p>
  305        *
  306        * <p>Next, for each component resolver in this composite:
  307        * <ol>
  308        *   <li>The <code>isReadOnly()</code> method is called, passing in
  309        *       the provided <code>context</code>, <code>base</code> and 
  310        *       <code>property</code>.</li>
  311        *   <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  312        *       flag is <code>false</code> then iteration continues.</li>
  313        *   <li>Otherwise, iteration stops and no more component resolvers are
  314        *       considered. The value returned by <code>isReadOnly()</code> is
  315        *       returned by this method.</li>
  316        * </ol></p>
  317        *
  318        * <p>If none of the component resolvers were able to perform this
  319        * operation, the value <code>false</code> is returned and the
  320        * <code>propertyResolved</code> flag remains set to 
  321        * <code>false</code></p>.
  322        *
  323        * <p>Any exception thrown by component resolvers during the iteration
  324        * is propagated to the caller of this method.</p>
  325        *
  326        * @param context The context of this evaluation.
  327        * @param base The base object whose property value is to be analyzed,
  328        *     or <code>null</code> to analyze a top-level variable.
  329        * @param property The property or variable to return the read-only status
  330        *     for.
  331        * @return If the <code>propertyResolved</code> property of 
  332        *     <code>ELContext</code> was set to <code>true</code>, then
  333        *     <code>true</code> if the property is read-only or
  334        *     <code>false</code> if not; otherwise undefined.
  335        * @throws NullPointerException if context is <code>null</code>
  336        * @throws PropertyNotFoundException if the given (base, property) pair
  337        *     is handled by this <code>ELResolver</code> but the specified
  338        *     variable or property does not exist.
  339        * @throws ELException if an exception was thrown while performing
  340        *     the property or variable resolution. The thrown exception
  341        *     must be included as the cause property of this exception, if
  342        *     available.
  343        */
  344       public boolean isReadOnly(ELContext context,
  345                                 Object base,
  346                                 Object property) {
  347           context.setPropertyResolved(false);
  348           int i = 0, len = this.elResolvers.size();
  349           ELResolver elResolver;
  350           boolean readOnly;
  351           while (i < len) {
  352               elResolver = this.elResolvers.get(i);
  353               readOnly = elResolver.isReadOnly(context, base, property);
  354               if (context.isPropertyResolved()) {
  355                   return readOnly;
  356               }
  357               i++;
  358           }
  359           return false; // Does not matter
  360       }
  361   
  362       /**
  363        * Returns information about the set of variables or properties that 
  364        * can be resolved for the given <code>base</code> object. One use for
  365        * this method is to assist tools in auto-completion. The results are
  366        * collected from all component resolvers.
  367        *
  368        * <p>The <code>propertyResolved</code> property of the 
  369        * <code>ELContext</code> is not relevant to this method.
  370        * The results of all <code>ELResolver</code>s are concatenated.</p>
  371        *
  372        * <p>The <code>Iterator</code> returned is an iterator over the
  373        * collection of <code>FeatureDescriptor</code> objects returned by
  374        * the iterators returned by each component resolver's 
  375        * <code>getFeatureDescriptors</code> method. If <code>null</code> is 
  376        * returned by a resolver, it is skipped.</p>
  377        * 
  378        * @param context The context of this evaluation.
  379        * @param base The base object whose set of valid properties is to
  380        *     be enumerated, or <code>null</code> to enumerate the set of
  381        *     top-level variables that this resolver can evaluate.
  382        * @return An <code>Iterator</code> containing zero or more (possibly
  383        *     infinitely more) <code>FeatureDescriptor</code> objects, or 
  384        *     <code>null</code> if this resolver does not handle the given 
  385        *     <code>base</code> object or that the results are too complex to 
  386        *     represent with this method
  387        */
  388       public Iterator<FeatureDescriptor> getFeatureDescriptors(
  389                                             ELContext context,
  390                                             Object base) {
  391           return new CompositeIterator(elResolvers.iterator(), context, base);
  392       }
  393   
  394       /**
  395        * Returns the most general type that this resolver accepts for the
  396        * <code>property</code> argument, given a <code>base</code> object.
  397        * One use for this method is to assist tools in auto-completion. The
  398        * result is obtained by querying all component resolvers.
  399        *
  400        * <p>The <code>Class</code> returned is the most specific class that is
  401        * a common superclass of all the classes returned by each component
  402        * resolver's <code>getCommonPropertyType</code> method. If 
  403        * <code>null</code> is returned by a resolver, it is skipped.</p>
  404        *
  405        * @param context The context of this evaluation.
  406        * @param base The base object to return the most general property
  407        *     type for, or <code>null</code> to enumerate the set of
  408        *     top-level variables that this resolver can evaluate.
  409        * @return <code>null</code> if this <code>ELResolver</code> does not
  410        *     know how to handle the given <code>base</code> object; otherwise
  411        *     <code>Object.class</code> if any type of <code>property</code>
  412        *     is accepted; otherwise the most general <code>property</code>
  413        *     type accepted for the given <code>base</code>.
  414        */
  415       public Class<?> getCommonPropertyType(ELContext context,
  416                                                  Object base) {
  417           Class<?> commonPropertyType = null;
  418           Iterator<ELResolver> iter = elResolvers.iterator();
  419           while (iter.hasNext()) {
  420               ELResolver elResolver = iter.next();
  421               Class<?> type = elResolver.getCommonPropertyType(context, base);
  422               if (type == null) {
  423                   // skip this EL Resolver
  424                   continue;
  425               } else if (commonPropertyType == null) {
  426                   commonPropertyType = type;
  427               } else if (commonPropertyType.isAssignableFrom(type)) {
  428                   continue;
  429               } else if (type.isAssignableFrom(commonPropertyType)) {
  430                   commonPropertyType = type;
  431               } else {
  432                   // Don't have a commonPropertyType
  433                   return null;
  434               }
  435           }
  436           return commonPropertyType;
  437       }
  438   
  439       private final ArrayList<ELResolver> elResolvers =
  440                                               new ArrayList<ELResolver>();
  441   
  442       private static class CompositeIterator
  443               implements Iterator<FeatureDescriptor> {
  444   
  445           Iterator<ELResolver> compositeIter;
  446           Iterator<FeatureDescriptor> propertyIter;
  447           ELContext context;
  448           Object base;
  449   
  450           CompositeIterator(Iterator<ELResolver> iter,
  451                             ELContext context,
  452                             Object base) {
  453               compositeIter = iter;
  454               this.context = context;
  455               this.base = base;
  456           }
  457   
  458           public boolean hasNext() {
  459               if (propertyIter == null || !propertyIter.hasNext()) {
  460                   while (compositeIter.hasNext()) {
  461                       ELResolver elResolver = compositeIter.next();
  462                       propertyIter = elResolver.getFeatureDescriptors(
  463                           context, base);
  464                       if (propertyIter != null) {
  465                           return propertyIter.hasNext();
  466                       }
  467                   }
  468                   return false;
  469               }
  470               return propertyIter.hasNext();
  471           }
  472   
  473           public FeatureDescriptor next() {
  474               if (propertyIter == null || !propertyIter.hasNext()) {
  475                   while (compositeIter.hasNext()) {
  476                       ELResolver elResolver = compositeIter.next();
  477                       propertyIter = elResolver.getFeatureDescriptors(
  478                           context, base);
  479                       if (propertyIter != null) {
  480                           return propertyIter.next();
  481                       }
  482                   }
  483                   return null;
  484               }
  485               return propertyIter.next();
  486           }
  487   
  488           public void remove() {
  489               throw new UnsupportedOperationException();
  490           }
  491       }
  492   }
  493   

Save This Page
Home » glassfish-v2ur2-b04-src » javax » el » [javadoc | source]