Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.component » [javadoc | source]
    1   /*
    2    * $Id: UIViewRoot.java,v 1.49.4.5 2008/06/06 19:19:37 edburns Exp $
    3    */
    4   
    5   /*
    6    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    7    * 
    8    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    9    * 
   10    * The contents of this file are subject to the terms of either the GNU
   11    * General Public License Version 2 only ("GPL") or the Common Development
   12    * and Distribution License("CDDL") (collectively, the "License").  You
   13    * may not use this file except in compliance with the License. You can obtain
   14    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   15    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   16    * language governing permissions and limitations under the License.
   17    * 
   18    * When distributing the software, include this License Header Notice in each
   19    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   20    * Sun designates this particular file as subject to the "Classpath" exception
   21    * as provided by Sun in the GPL Version 2 section of the License file that
   22    * accompanied this code.  If applicable, add the following below the License
   23    * Header, with the fields enclosed by brackets [] replaced by your own
   24    * identifying information: "Portions Copyrighted [year]
   25    * [name of copyright owner]"
   26    * 
   27    * Contributor(s):
   28    * 
   29    * If you wish your version of this file to be governed by only the CDDL or
   30    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   31    * elects to include this software in this distribution under the [CDDL or GPL
   32    * Version 2] license."  If you don't indicate a single choice of license, a
   33    * recipient has the option to distribute your version of this file under
   34    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   35    * its licensees as provided above.  However, if you add GPL Version 2 code
   36    * and therefore, elected the GPL Version 2 license, then the option applies
   37    * only if the new code is made subject to such option by the copyright
   38    * holder.
   39    */
   40   
   41   package javax.faces.component;
   42   
   43   
   44   import javax.el.ELException;
   45   import javax.el.MethodExpression;
   46   import javax.el.ValueExpression;
   47   import javax.faces.FacesException;
   48   import javax.faces.FactoryFinder;
   49   import javax.faces.context.FacesContext;
   50   import javax.faces.event.AbortProcessingException;
   51   import javax.faces.event.FacesEvent;
   52   import javax.faces.event.PhaseEvent;
   53   import javax.faces.event.PhaseId;
   54   import javax.faces.event.PhaseListener;
   55   import javax.faces.lifecycle.Lifecycle;
   56   import javax.faces.lifecycle.LifecycleFactory;
   57   import javax.faces.render.RenderKit;
   58   import javax.faces.webapp.FacesServlet;
   59   
   60   import java.io.IOException;
   61   import java.util.ArrayList;
   62   import java.util.Iterator;
   63   import java.util.List;
   64   import java.util.Locale;
   65   import java.util.ListIterator;
   66   import java.util.logging.Level;
   67   import java.util.logging.Logger;
   68   
   69   
   70   /**
   71    * <p><strong>UIViewRoot</strong> is the UIComponent that represents the
   72    * root of the UIComponent tree.  This component has no rendering, it
   73    * just serves as the root of the component tree, and as a place to hang
   74    * per-view {@link PhaseListener}s.</p>
   75    * <p/>
   76    * <p>For each of the following lifecycle phase methods:</p>
   77    * <p/>
   78    * <ul>
   79    * <p/>
   80    * <li><p>{@link #processDecodes} </p></li>
   81    * <p/>
   82    * <li><p>{@link #processValidators} </p></li>
   83    * <p/>
   84    * <li><p>{@link #processUpdates} </p></li>
   85    * <p/>
   86    * <li><p>{@link #processApplication} </p></li>
   87    * <p/>
   88    * <li><p>RenderResponse, via {@link #encodeBegin} and {@link
   89    * #encodeEnd} </p></li>
   90    * <p/>
   91    * </ul>
   92    * <p/>
   93    * <p>Take the following action regarding
   94    * <code>PhaseListener</code>s.</p>
   95    * <p/>
   96    * <ul>
   97    * <p/>
   98    * <p>Initialize a state flag to <code>false</code>.</p>
   99    * <p/>
  100    * <p>If {@link #getBeforePhaseListener} returns non-<code>null</code>,
  101    * invoke the listener, passing in the correct corresponding {@link
  102    * PhaseId} for this phase.</p>
  103    * <p/>
  104    * <p>Upon return from the listener, call {@link
  105    * FacesContext#getResponseComplete} and {@link
  106    * FacesContext#getRenderResponse}.  If either return <code>true</code>
  107    * set the internal state flag to <code>true</code>. </p>
  108    * <p/>
  109    * <p>If or one or more listeners have been added by a call to {@link
  110    * #addPhaseListener}, invoke the <code>beforePhase</code> method on
  111    * each one whose {@link PhaseListener#getPhaseId} matches the current
  112    * phaseId, passing in the same <code>PhaseId</code> as in the previous
  113    * step.</p>
  114    * <p/>
  115    * <p>Upon return from each listener, call {@link
  116    * FacesContext#getResponseComplete} and {@link
  117    * FacesContext#getRenderResponse}.  If either return <code>true</code>
  118    * set the internal state flag to <code>true</code>. </p>
  119    * <p/>
  120    * <p/>
  121    * <p>Execute any processing for this phase if the internal state flag
  122    * was not set.</p>
  123    * <p/>
  124    * <p>If {@link #getAfterPhaseListener} returns non-<code>null</code>,
  125    * invoke the listener, passing in the correct corresponding {@link
  126    * PhaseId} for this phase.</p>
  127    * <p/>
  128    * <p>If or one or more listeners have been added by a call to {@link
  129    * #addPhaseListener}, invoke the <code>afterPhase</code> method on each
  130    * one whose {@link PhaseListener#getPhaseId} matches the current
  131    * phaseId, passing in the same <code>PhaseId</code> as in the previous
  132    * step.</p>
  133    * <p/>
  134    * <p/>
  135    * </ul>
  136    */
  137   
  138   public class UIViewRoot extends UIComponentBase {
  139   
  140       // ------------------------------------------------------ Manifest Constants
  141   
  142   
  143       /** <p>The standard component type for this component.</p> */
  144       public static final String COMPONENT_TYPE = "javax.faces.ViewRoot";
  145   
  146   
  147       /** <p>The standard component family for this component.</p> */
  148       public static final String COMPONENT_FAMILY = "javax.faces.ViewRoot";
  149   
  150   
  151       /**
  152        * <p>The prefix that will be used for identifiers generated
  153        * by the <code>createUniqueId()</code> method.
  154        */
  155       static public final String UNIQUE_ID_PREFIX = "j_id";
  156   
  157       private static Lifecycle lifecycle;
  158   
  159       private static final Logger LOGGER =
  160             Logger.getLogger("javax.faces", "javax.faces.LogStrings");
  161   
  162       // ------------------------------------------------------------ Constructors
  163   
  164   
  165       /**
  166        * <p>Create a new {@link UIViewRoot} instance with default property
  167        * values.</p>
  168        */
  169       public UIViewRoot() {
  170   
  171           super();
  172           setRendererType(null);
  173   
  174       }
  175   
  176       // ------------------------------------------------------ Instance Variables
  177   
  178       private int lastId = 0;
  179   
  180       /**
  181        * <p>Set and cleared during the lifetime of a lifecycle phase.  Has
  182        * no meaning between phases.  If <code>true</code>, the lifecycle
  183        * processing for the current phase must not take place.</p>
  184        */
  185       private boolean skipPhase;
  186   
  187       /**
  188        * <p>Set and cleared during the lifetime of a lifecycle phase.  Has
  189        * no meaning between phases.  If <code>true</code>, the <code>MethodExpression</code>
  190        * associated with <code>afterPhase</code> will not be invoked nor will any
  191        * PhaseListeners associated with this UIViewRoot.
  192        */
  193       private boolean beforeMethodException;
  194   
  195       /**
  196        * <p>Set and cleared during the lifetime of a lifecycle phase.  Has
  197        * no meaning between phases.
  198        */
  199       private ListIterator<PhaseListener> phaseListenerIterator;
  200   
  201   
  202       // -------------------------------------------------------------- Properties
  203   
  204   
  205       public String getFamily() {
  206   
  207           return (COMPONENT_FAMILY);
  208   
  209       }
  210   
  211   
  212       /**
  213        * <p>The render kit identifier of the {@link RenderKit} associated
  214        * wth this view.</p>
  215        */
  216       private String renderKitId = null;
  217   
  218   
  219       /**
  220        * <p>Return the render kit identifier of the {@link RenderKit}
  221        * associated with this view.  Unless explicitly set, as in {@link
  222        * javax.faces.application.ViewHandler#createView}, the returned
  223        * value will be <code>null.</code></p>
  224        */
  225       public String getRenderKitId() {
  226   
  227           String result;
  228           if (null != renderKitId) {
  229               result = this.renderKitId;
  230           } else {
  231               ValueExpression vb = getValueExpression("renderKitId");
  232               FacesContext context = getFacesContext();
  233               if (vb != null) {
  234                   try {
  235                       result = (String) vb.getValue(context.getELContext());
  236                   }
  237                   catch (ELException e) {
  238                       if (LOGGER.isLoggable(Level.SEVERE)) {
  239                           LOGGER.log(Level.SEVERE,
  240                                      "severe.component.unable_to_process_expression",
  241                                      new Object[] { vb.getExpressionString(), "renderKitId"});
  242                       }
  243                       result = null;
  244                   }
  245               } else {
  246                   result = null;
  247               }
  248           }
  249           return result;
  250       }
  251   
  252   
  253       /**
  254        * <p>Set the render kit identifier of the {@link RenderKit}
  255        * associated with this view.  This method may be called at any time
  256        * between the end of <em>Apply Request Values</em> phase of the
  257        * request processing lifecycle (i.e. when events are being broadcast)
  258        * and the beginning of the <em>Render Response</em> phase.</p>
  259        *
  260        * @param renderKitId The new {@link RenderKit} identifier,
  261        *                    or <code>null</code> to disassociate this view with any
  262        *                    specific {@link RenderKit} instance
  263        */
  264       public void setRenderKitId(String renderKitId) {
  265   
  266           this.renderKitId = renderKitId;
  267   
  268       }
  269   
  270   
  271       /** <p>The view identifier of this view.</p> */
  272       private String viewId = null;
  273   
  274   
  275       /** <p>Return the view identifier for this view.</p> */
  276       public String getViewId() {
  277   
  278           return (this.viewId);
  279   
  280       }
  281   
  282   
  283       /**
  284        * <p>Set the view identifier for this view.</p>
  285        *
  286        * @param viewId The new view identifier
  287        */
  288       public void setViewId(String viewId) {
  289   
  290           this.viewId = viewId;
  291   
  292       }
  293   
  294       // ------------------------------------------------ Event Management Methods
  295   
  296       private MethodExpression beforePhase = null;
  297       private MethodExpression afterPhase = null;
  298   
  299       /**
  300        * @return the {@link MethodExpression} that will be invoked before
  301        *         this view is rendered.
  302        */
  303   
  304       public MethodExpression getBeforePhaseListener() {
  305           return beforePhase;
  306       }
  307   
  308       /**
  309        * <p>Allow an arbitrary method to be called for the "beforePhase"
  310        * event as the UIViewRoot runs through its lifecycle.  This method
  311        * will be called for all phases except {@link
  312        * PhaseId#RESTORE_VIEW}.  Unlike a true {@link PhaseListener},
  313        * this approach doesn't allow for only receiving {@link
  314        * PhaseEvent}s for a given phase.</p>
  315        * <p/>
  316        * <p>The method must conform to the signature of {@link
  317        * PhaseListener#beforePhase}.</p>
  318        *
  319        * @param newBeforePhase the {@link MethodExpression} that will be
  320        *                       invoked before this view is rendered.
  321        */
  322   
  323       public void setBeforePhaseListener(MethodExpression newBeforePhase) {
  324           beforePhase = newBeforePhase;
  325       }
  326   
  327       /**
  328        * @return the {@link MethodExpression} that will be invoked after
  329        *         this view is rendered.
  330        */
  331   
  332       public MethodExpression getAfterPhaseListener() {
  333           return afterPhase;
  334       }
  335   
  336       /**
  337        * <p>Allow an arbitrary method to be called for the "afterPhase"
  338        * event as the UIViewRoot runs through its lifecycle.  This method
  339        * will be called for all phases except {@link
  340        * PhaseId#RESTORE_VIEW}.  Unlike a true {@link PhaseListener},
  341        * this approach doesn't allow for only receiving {@link
  342        * PhaseEvent}s for a given phase.</p>
  343        * <p/>
  344        * <p>The method must conform to the signature of {@link
  345        * PhaseListener#afterPhase}.</p>
  346        *
  347        * @param newAfterPhase the {@link MethodExpression} that will be
  348        *                      invoked after this view is rendered.
  349        */
  350   
  351       public void setAfterPhaseListener(MethodExpression newAfterPhase) {
  352           afterPhase = newAfterPhase;
  353       }
  354   
  355       private List<PhaseListener> phaseListeners = null;
  356   
  357       public void removePhaseListener(PhaseListener toRemove) {
  358           if (null != phaseListeners) {
  359               phaseListeners.remove(toRemove);
  360           }
  361       }
  362   
  363       public void addPhaseListener(PhaseListener newPhaseListener) {
  364           if (null == phaseListeners) {
  365               //noinspection CollectionWithoutInitialCapacity
  366               phaseListeners = new ArrayList<PhaseListener>();
  367           }
  368           phaseListeners.add(newPhaseListener);
  369       }
  370   
  371       /**
  372        * <p>An array of Lists of events that have been queued for later
  373        * broadcast, with one List for each lifecycle phase.  The list
  374        * indices match the ordinals of the PhaseId instances.  This
  375        * instance is lazily instantiated.  This list is
  376        * <strong>NOT</strong> part of the state that is saved and restored
  377        * for this component.</p>
  378        */
  379       private List<List<FacesEvent>> events = null;
  380   
  381   
  382       /**
  383        * <p>Override the default {@link UIComponentBase#queueEvent} behavior to
  384        * accumulate the queued events for later broadcasting.</p>
  385        *
  386        * @param event {@link FacesEvent} to be queued
  387        *
  388        * @throws IllegalStateException if this component is not a
  389        *                               descendant of a {@link UIViewRoot}
  390        * @throws NullPointerException  if <code>event</code>
  391        *                               is <code>null</code>
  392        */
  393       public void queueEvent(FacesEvent event) {
  394   
  395           if (event == null) {
  396               throw new NullPointerException();
  397           }
  398           int i;
  399           int len = PhaseId.VALUES.size();
  400           // We are a UIViewRoot, so no need to check for the ISE
  401           if (events == null) {
  402               List<List<FacesEvent>> events = new ArrayList<List<FacesEvent>>(len);
  403               for (i = 0; i < len; i++) {
  404                   events.add(new ArrayList<FacesEvent>(5));
  405               }
  406               this.events = events;
  407           }
  408           events.get(event.getPhaseId().getOrdinal()).add(event);
  409       }
  410   
  411   
  412       /**
  413        * <p>Broadcast any events that have been queued.</p>
  414        *
  415        * @param context {@link FacesContext} for the current request
  416        * @param phaseId {@link PhaseId} of the current phase
  417        */
  418       private void broadcastEvents(FacesContext context, PhaseId phaseId) {
  419   
  420           if (null == events) {
  421               // no events have been queued
  422               return;
  423           }
  424           boolean hasMoreAnyPhaseEvents;
  425           boolean hasMoreCurrentPhaseEvents;
  426   
  427           List<FacesEvent> eventsForPhaseId =
  428                events.get(PhaseId.ANY_PHASE.getOrdinal());
  429   
  430           // keep iterating till we have no more events to broadcast.
  431           // This is necessary for events that cause other events to be
  432           // queued.  PENDING(edburns): here's where we'd put in a check
  433           // to prevent infinite event queueing.
  434           do {
  435               // broadcast the ANY_PHASE events first
  436               if (null != eventsForPhaseId) {
  437                   // We cannot use an Iterator because we will get
  438                   // ConcurrentModificationException errors, so fake it
  439                   while (!eventsForPhaseId.isEmpty()) {
  440                       FacesEvent event =
  441                             eventsForPhaseId.get(0);
  442                       UIComponent source = event.getComponent();
  443                       try {
  444                           source.broadcast(event);
  445                       } catch (AbortProcessingException e) {
  446                           if (LOGGER.isLoggable(Level.SEVERE)) {
  447                               UIComponent component = event.getComponent();
  448                               String id = "";
  449                               if (component != null) {
  450                                   id = component.getId();
  451                                   if (id == null) {
  452                                       id = component.getClientId(context);
  453                                   }
  454                               }
  455                               LOGGER.log(Level.SEVERE,
  456                                          "error.component.abortprocessing_thrown",
  457                                          new Object[]{event.getClass().getName(),
  458                                                       phaseId.toString(),
  459                                                       id});
  460                               LOGGER.log(Level.SEVERE, e.toString(), e);
  461                           }
  462                       }
  463                       eventsForPhaseId.remove(0); // Stay at current position
  464                   }
  465               }
  466   
  467               // then broadcast the events for this phase.
  468               if (null != (eventsForPhaseId = events.get(phaseId.getOrdinal()))) {
  469                   // We cannot use an Iterator because we will get
  470                   // ConcurrentModificationException errors, so fake it
  471                   while (!eventsForPhaseId.isEmpty()) {
  472                       FacesEvent event = eventsForPhaseId.get(0);
  473                       UIComponent source = event.getComponent();
  474                       try {
  475                           source.broadcast(event);
  476                       } catch (AbortProcessingException e) {
  477                           ; // A "return" here would abort remaining events too
  478                       }
  479                       eventsForPhaseId.remove(0); // Stay at current position
  480                   }
  481               }
  482   
  483               // true if we have any more ANY_PHASE events
  484               hasMoreAnyPhaseEvents =
  485                     (null != (eventsForPhaseId =
  486                           events.get(PhaseId.ANY_PHASE.getOrdinal()))) &&
  487                           !eventsForPhaseId.isEmpty();
  488               // true if we have any more events for the argument phaseId
  489               hasMoreCurrentPhaseEvents =
  490                     (null != events.get(phaseId.getOrdinal())) &&
  491                     !events.get(phaseId.getOrdinal()).isEmpty();
  492   
  493           } while (hasMoreAnyPhaseEvents || hasMoreCurrentPhaseEvents);
  494   
  495       }
  496   
  497       // ------------------------------------------------ Lifecycle Phase Handlers
  498   
  499       private void initState() {
  500           skipPhase = false;
  501           beforeMethodException = false;
  502           phaseListenerIterator =
  503                 ((phaseListeners != null) ? phaseListeners.listIterator() : null);
  504       }
  505   
  506       // avoid creating the PhaseEvent if possible by doing redundant
  507       // null checks.
  508       private void notifyBefore(FacesContext context, PhaseId phaseId) {
  509           if (null != beforePhase || null != phaseListenerIterator) {
  510               notifyPhaseListeners(context, phaseId, true);
  511           }
  512       }
  513       
  514       // avoid creating the PhaseEvent if possible by doing redundant
  515       // null checks.
  516       private void notifyAfter(FacesContext context, PhaseId phaseId) {
  517           if (null != afterPhase || null != phaseListenerIterator) {
  518               notifyPhaseListeners(context, phaseId, false);
  519           }
  520       }
  521   
  522       /**
  523        * <p>Override the default {@link UIComponentBase#processDecodes}
  524        * behavior to broadcast any queued events after the default
  525        * processing has been completed and to clear out any events
  526        * for later phases if the event processing for this phase caused {@link
  527        * FacesContext#renderResponse} or {@link FacesContext#responseComplete}
  528        * to be called.</p>
  529        *
  530        * @param context {@link FacesContext} for the request we are processing
  531        *
  532        * @throws NullPointerException if <code>context</code>
  533        *                              is <code>null</code>
  534        */
  535       @Override
  536       public void processDecodes(FacesContext context) {
  537           initState();
  538           notifyBefore(context, PhaseId.APPLY_REQUEST_VALUES);
  539           if (!skipPhase) {
  540               super.processDecodes(context);
  541               broadcastEvents(context, PhaseId.APPLY_REQUEST_VALUES);
  542           }
  543           clearFacesEvents(context);
  544           notifyAfter(context, PhaseId.APPLY_REQUEST_VALUES);
  545       }
  546   
  547       /**
  548        * <p>Override the default {@link UIComponentBase#encodeBegin}
  549        * behavior.  If
  550        * {@link #getBeforePhaseListener} returns non-<code>null</code>,
  551        * invoke it, passing a {@link PhaseEvent} for the {@link
  552        * PhaseId#RENDER_RESPONSE} phase.  If the internal list populated
  553        * by calls to {@link #addPhaseListener} is non-empty, any listeners
  554        * in that list must have their {@link PhaseListener#beforePhase}
  555        * method called, passing the <code>PhaseEvent</code>.  Any errors
  556        * that occur during invocation of any of the the beforePhase
  557        * listeners must be logged and swallowed.  After listeners are invoked
  558        * call superclass processing.</p>
  559        */
  560       @Override
  561       public void encodeBegin(FacesContext context) throws IOException {
  562   
  563           initState();
  564           notifyBefore(context, PhaseId.RENDER_RESPONSE);
  565   
  566           if (!skipPhase) {
  567               super.encodeBegin(context);
  568           }
  569       }
  570   
  571       /**
  572        * <p>Override the default {@link UIComponentBase#encodeEnd}
  573        * behavior.  If {@link #getAfterPhaseListener} returns
  574        * non-<code>null</code>, invoke it, passing a {@link PhaseEvent}
  575        * for the {@link PhaseId#RENDER_RESPONSE} phase.  Any errors that
  576        * occur during invocation of the afterPhase listener must be
  577        * logged and swallowed.</p>
  578        */
  579       @Override
  580       public void encodeEnd(FacesContext context) throws IOException {
  581           super.encodeEnd(context);
  582   
  583           notifyAfter(context, PhaseId.RENDER_RESPONSE);
  584       }
  585   
  586       /**
  587        * <p>Utility method that notifies phaseListeners for the given
  588        * phaseId.  Assumes that either or both the MethodExpression or
  589        * phaseListeners data structure are non-null.</p>
  590        *
  591        * @param context   the context for this request
  592        * @param phaseId   the {@link PhaseId} of the current phase
  593        * @param isBefore, if true, notify beforePhase listeners.  Notify
  594        *                  afterPhase listeners otherwise.
  595        */
  596       private void notifyPhaseListeners(FacesContext context,
  597                                         PhaseId phaseId,
  598                                         boolean isBefore) {
  599           PhaseEvent event = createPhaseEvent(context, phaseId);
  600   
  601           boolean hasPhaseMethodExpression =
  602                 (isBefore && (null != beforePhase)) ||
  603                 (!isBefore && (null != afterPhase) && !beforeMethodException);
  604           MethodExpression expression = isBefore ? beforePhase : afterPhase;
  605   
  606           if (hasPhaseMethodExpression) {
  607               try {
  608                   expression.invoke(context.getELContext(), new Object[]{event});
  609                   skipPhase = context.getResponseComplete() ||
  610                               context.getRenderResponse();
  611               }
  612               catch (Exception e) {
  613                   if (isBefore) {
  614                       beforeMethodException = true;
  615                   }
  616                   if (LOGGER.isLoggable(Level.SEVERE)) {
  617                       LOGGER.log(Level.SEVERE, "Exception", e);
  618                       LOGGER.log(Level.SEVERE,
  619                                  "severe.component.unable_to_process_expression",
  620                                  new Object[] { expression.getExpressionString(),
  621                                                 (isBefore ? "beforePhase" : "afterPhase")});
  622                   }
  623                   return;
  624               }
  625           }
  626           if (phaseListenerIterator != null && !beforeMethodException) {
  627               while ((isBefore)
  628                      ? phaseListenerIterator.hasNext()
  629                      : phaseListenerIterator.hasPrevious()) {
  630                   PhaseListener curListener = ((isBefore)
  631                                                ? phaseListenerIterator.next()
  632                                                : phaseListenerIterator
  633                                                      .previous());
  634                   if (phaseId == curListener.getPhaseId() ||
  635                       PhaseId.ANY_PHASE == curListener.getPhaseId()) {
  636                       try {                        
  637                           if (isBefore) {
  638                               curListener.beforePhase(event);
  639                           } else {
  640                               curListener.afterPhase(event);
  641                           }
  642                           skipPhase = context.getResponseComplete() ||
  643                                       context.getRenderResponse();
  644                       }
  645                       catch (Exception e) {
  646                           if (isBefore && phaseListenerIterator.hasPrevious()) {
  647                               phaseListenerIterator.previous();
  648                           }
  649                           if (LOGGER.isLoggable(Level.SEVERE)) {
  650                               LOGGER.log(Level.SEVERE,
  651                                          "severe.component.uiviewroot_error_invoking_phaselistener",
  652                                          curListener.getClass().getName());
  653                           }
  654                           return;
  655                       }
  656                   }
  657               }
  658           }
  659       }
  660   
  661       private static PhaseEvent createPhaseEvent(FacesContext context,
  662                                                  PhaseId phaseId)
  663       throws FacesException {
  664           if (lifecycle == null) {
  665               LifecycleFactory lifecycleFactory = (LifecycleFactory)
  666                     FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
  667               String lifecycleId =
  668                     context.getExternalContext()
  669                           .getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
  670               if (lifecycleId == null) {
  671                   lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
  672               }
  673               lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
  674           }
  675   
  676           return (new PhaseEvent(context, phaseId, lifecycle));
  677   
  678       }
  679   
  680   
  681       /**
  682        * <p>Override the default {@link UIComponentBase#processValidators}
  683        * behavior to broadcast any queued events after the default
  684        * processing has been completed and to clear out any events
  685        * for later phases if the event processing for this phase caused {@link
  686        * FacesContext#renderResponse} or {@link FacesContext#responseComplete}
  687        * to be called.</p>
  688        *
  689        * @param context {@link FacesContext} for the request we are processing
  690        *
  691        * @throws NullPointerException if <code>context</code>
  692        *                              is <code>null</code>
  693        */
  694       @Override
  695       public void processValidators(FacesContext context) {
  696           initState();
  697           notifyBefore(context, PhaseId.PROCESS_VALIDATIONS);
  698           try {
  699               if (!skipPhase) {
  700                   super.processValidators(context);
  701                   broadcastEvents(context, PhaseId.PROCESS_VALIDATIONS);
  702               }
  703           } finally {
  704               clearFacesEvents(context);
  705               notifyAfter(context, PhaseId.PROCESS_VALIDATIONS);
  706           }
  707       }
  708   
  709   
  710       /**
  711        * <p>Override the default {@link UIComponentBase} behavior to broadcast
  712        * any queued events after the default processing has been completed
  713        * and to clear out any events for later phases if the event processing
  714        * for this phase caused {@link FacesContext#renderResponse} or
  715        * {@link FacesContext#responseComplete} to be called.</p>
  716        *
  717        * @param context {@link FacesContext} for the request we are processing
  718        *
  719        * @throws NullPointerException if <code>context</code>
  720        *                              is <code>null</code>
  721        */
  722       @Override
  723       public void processUpdates(FacesContext context) {
  724           initState();
  725           notifyBefore(context, PhaseId.UPDATE_MODEL_VALUES);
  726           try {
  727               if (!skipPhase) {
  728                   super.processUpdates(context);
  729                   broadcastEvents(context, PhaseId.UPDATE_MODEL_VALUES);
  730               }
  731           } finally {
  732               clearFacesEvents(context);
  733               notifyAfter(context, PhaseId.UPDATE_MODEL_VALUES);
  734           }
  735       }
  736   
  737   
  738       /**
  739        * <p>Broadcast any events that have been queued for the <em>Invoke
  740        * Application</em> phase of the request processing lifecycle
  741        * and to clear out any events for later phases if the event processing
  742        * for this phase caused {@link FacesContext#renderResponse} or
  743        * {@link FacesContext#responseComplete} to be called.</p>
  744        *
  745        * @param context {@link FacesContext} for the request we are processing
  746        *
  747        * @throws NullPointerException if <code>context</code>
  748        *                              is <code>null</code>
  749        */
  750       public void processApplication(FacesContext context) {
  751           initState();
  752           notifyBefore(context, PhaseId.INVOKE_APPLICATION);
  753           try {
  754               if (!skipPhase) {
  755                   // NOTE - no tree walk is performed; this is a UIViewRoot-only operation
  756                   broadcastEvents(context, PhaseId.INVOKE_APPLICATION);
  757               }
  758           } finally {
  759               clearFacesEvents(context);
  760               notifyAfter(context, PhaseId.INVOKE_APPLICATION);
  761           }
  762       }
  763   
  764   
  765       // clear out the events if we're skipping to render-response
  766       // or if there is a response complete signal.
  767       private void clearFacesEvents(FacesContext context) {
  768           if (context.getRenderResponse() || context.getResponseComplete()) {
  769               if (events != null) {
  770                   for (List<FacesEvent> eventList : events) {
  771                       if (eventList != null) {
  772                           eventList.clear();
  773                       }
  774                   }
  775                   events = null;
  776               }
  777           }
  778       }
  779   
  780       /**
  781        * <p>Generate an identifier for a component.  The identifier will
  782        * be prefixed with UNIQUE_ID_PREFIX, and will be unique within
  783        * this UIViewRoot.</p>
  784        */
  785       public String createUniqueId() {
  786           return UNIQUE_ID_PREFIX + lastId++;
  787       }
  788   
  789       /*
  790       * <p>The locale for this view.</p>
  791       */
  792       private Locale locale = null;
  793   
  794       /**
  795        * <p>Return the <code>Locale</code> to be used in localizing the
  796        * response being created for this view.</p>
  797        * <p/>
  798        * <p>Algorithm:</p>
  799        * <p/>
  800        * <p>If we have a <code>locale</code> ivar, return it.  If we have
  801        * a value expression for "locale", get its value.  If the value is
  802        * <code>null</code>, return the result of calling {@link
  803        * javax.faces.application.ViewHandler#calculateLocale}.  If the
  804        * value is an instance of <code>java.util.Locale</code> return it.
  805        * If the value is a String, convert it to a
  806        * <code>java.util.Locale</code> and return it.  If there is no
  807        * value expression for "locale", return the result of calling {@link
  808        * javax.faces.application.ViewHandler#calculateLocale}.</p>
  809        *
  810        * @return The current <code>Locale</code> obtained by executing the
  811        *         above algorithm.
  812        */
  813       public Locale getLocale() {
  814           Locale result = null;
  815           if (null != locale) {
  816               result = this.locale;
  817           } else {
  818               ValueExpression vb = getValueExpression("locale");
  819               FacesContext context = getFacesContext();
  820               if (vb != null) {
  821                   Object resultLocale = null;
  822   
  823                   try {
  824                       resultLocale = vb.getValue(context.getELContext());
  825                   }
  826                   catch (ELException e) {
  827                       if (LOGGER.isLoggable(Level.SEVERE)) {
  828                           LOGGER.log(Level.SEVERE,
  829                                      "severe.component.unable_to_process_expression",
  830                                      new Object[]{vb.getExpressionString(), "locale"});
  831                       }
  832                   }
  833   
  834                   if (null == resultLocale) {
  835                       result =
  836                             context.getApplication().getViewHandler()
  837                                   .calculateLocale(context);
  838                   } else if (resultLocale instanceof Locale) {
  839                       result = (Locale) resultLocale;
  840                   } else if (resultLocale instanceof String) {
  841                       result = getLocaleFromString((String) resultLocale);
  842                   }
  843               } else {
  844                   result =
  845                         context.getApplication().getViewHandler()
  846                               .calculateLocale(context);
  847               }
  848           }
  849           return result;
  850       }
  851   
  852   
  853      // W3C XML specification refers to IETF RFC 1766 for language code
  854       // structure, therefore the value for the xml:lang attribute should
  855       // be in the form of language or language-country or
  856       // language-country-variant.
  857   
  858       private static Locale getLocaleFromString(String localeStr)
  859           throws IllegalArgumentException {
  860           // length must be at least 2.
  861           if (null == localeStr || localeStr.length() < 2) {
  862               throw new IllegalArgumentException("Illegal locale String: " +
  863                                                  localeStr);
  864           }
  865   
  866           Locale result = null;
  867           String lang = null;
  868           String country = null;
  869           String variant = null;
  870           char[] seps = {
  871               '-',
  872               '_'
  873           };
  874           int i = 0;
  875           int j = 0;
  876           int inputLength = localeStr.length();
  877   
  878           // to have a language, the length must be >= 2
  879           if ((inputLength >= 2) &&
  880               ((i = indexOfSet(localeStr, seps, 0)) == -1)) {
  881               // we have only Language, no country or variant
  882               if (localeStr.length() != 2) {
  883                   throw new
  884                       IllegalArgumentException("Illegal locale String: " +
  885                                                localeStr);
  886               }
  887               lang = localeStr.toLowerCase();
  888           }
  889   
  890           // we have a separator, it must be either '-' or '_'
  891           if (i != -1) {
  892               lang = localeStr.substring(0, i);
  893               // look for the country sep.
  894               // to have a country, the length must be >= 5
  895               if ((inputLength >= 5) &&
  896                   ((j = indexOfSet(localeStr, seps, i + 1)) == -1)) {
  897                   // no further separators, length must be 5
  898                   if (inputLength != 5) {
  899                       throw new
  900                           IllegalArgumentException("Illegal locale String: " +
  901                                                    localeStr);
  902                   }
  903                   country = localeStr.substring(i + 1);
  904               }
  905               if (j != -1) {
  906                   country = localeStr.substring(i + 1, j);
  907                   // if we have enough separators for language, locale,
  908                   // and variant, the length must be >= 8.
  909                   if (inputLength >= 8) {
  910                       variant = localeStr.substring(j + 1);
  911                   } else {
  912                       throw new
  913                           IllegalArgumentException("Illegal locale String: " +
  914                                                    localeStr);
  915                   }
  916               }
  917           }
  918           if (variant != null && country != null && lang != null) {
  919               result = new Locale(lang, country, variant);
  920           } else if (lang != null && country != null) {
  921               result = new Locale(lang, country);
  922           } else if (lang != null) {
  923               result = new Locale(lang, "");
  924           }
  925           return result;
  926       }
  927   
  928   
  929       /**
  930        * @param str local string
  931        * @param set the substring
  932        * @param fromIndex starting index
  933        * @return starting at <code>fromIndex</code>, the index of the
  934        *         first occurrence of any substring from <code>set</code> in
  935        *         <code>toSearch</code>, or -1 if no such match is found
  936        */
  937       private static int indexOfSet(String str, char[] set, int fromIndex) {
  938           int result = -1;
  939           for (int i = fromIndex, len = str.length(); i < len; i++) {
  940               for (int j = 0, innerLen = set.length; j < innerLen; j++) {
  941                   if (str.charAt(i) == set[j]) {
  942                       result = i;
  943                       break;
  944                   }
  945               }
  946               if (result != -1) {
  947                   break;
  948               }
  949           }
  950           return result;
  951       }
  952   
  953       /**
  954        * <p>Set the <code>Locale</code> to be used in localizing the
  955        * response being created for this view. </p>
  956        *
  957        * @param locale The new localization Locale
  958        */
  959       public void setLocale(Locale locale) {
  960           this.locale = locale;
  961           // Make sure to appraise the EL of this switch in Locale.
  962           FacesContext.getCurrentInstance().getELContext().setLocale(locale);
  963       }
  964   
  965       // ----------------------------------------------------- StateHolder Methods
  966   
  967   
  968       private Object[] values;
  969   
  970       @Override
  971       public Object saveState(FacesContext context) {
  972   
  973           if (values == null) {
  974               values = new Object[8];
  975           }
  976   
  977           values[0] = super.saveState(context);
  978           values[1] = renderKitId;
  979           values[2] = viewId;
  980           values[3] = locale;
  981           values[4] = lastId;
  982           values[5] = saveAttachedState(context, beforePhase);
  983           values[6] = saveAttachedState(context, afterPhase);
  984           values[7] = saveAttachedState(context, phaseListeners);
  985           return (values);
  986   
  987       }
  988   
  989       @Override
  990       public void restoreState(FacesContext context, Object state) {
  991   
  992           values = (Object[]) state;
  993           super.restoreState(context, values[0]);
  994           renderKitId = (String) values[1];
  995           viewId = (String) values[2];
  996           locale = (Locale) values[3];
  997           lastId = ((Integer) values[4]).intValue();
  998           beforePhase =
  999                 (MethodExpression) restoreAttachedState(context, values[5]);
 1000           afterPhase =
 1001                 (MethodExpression) restoreAttachedState(context, values[6]);
 1002           phaseListeners = TypedCollections.dynamicallyCastList((List)
 1003                 restoreAttachedState(context, values[7]), PhaseListener.class);
 1004       }
 1005   
 1006   
 1007   }

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.component » [javadoc | source]