001    package org.apache.myfaces.tobago.component;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.myfaces.tobago.context.ClientProperties;
021    import org.apache.myfaces.tobago.context.ResourceManagerImpl;
022    import org.apache.commons.logging.Log;
023    import org.apache.commons.logging.LogFactory;
024    
025    import javax.faces.component.UIComponent;
026    import javax.faces.context.FacesContext;
027    import javax.faces.event.AbortProcessingException;
028    import javax.faces.event.FacesEvent;
029    import javax.faces.event.PhaseId;
030    import javax.faces.FacesException;
031    import java.util.ArrayList;
032    import java.util.List;
033    import java.util.ListIterator;
034    import java.util.Locale;
035    import java.util.Map;
036    
037    /*
038     * User: weber
039     * Date: Jun 13, 2005
040     * Time: 5:19:31 PM
041     */
042    public class UIViewRoot extends javax.faces.component.UIViewRoot {
043    
044      private static final Log LOG = LogFactory.getLog(UIViewRoot.class);
045      private static final String EVENT_LIST_KEY = UIViewRoot.class.getName() + ".EventList";
046    
047      private ResourceManagerImpl.CacheKey rendererCacheKey;
048    
049      private ClientProperties clientProperties;
050    
051      public static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal();
052    
053    
054      /**
055       * <p>Create a new {@link UIViewRoot} instance with default property
056       * values.</p>
057       */
058      public UIViewRoot() {
059        super();
060        updateRendererCachePrefix();
061      }
062    
063      public ClientProperties getClientProperties() {
064        return clientProperties;
065      }
066    
067      public void setClientProperties(final ClientProperties clientProperties) {
068        this.clientProperties = clientProperties;
069        updateRendererCachePrefix();
070      }
071    
072      public void setLocale(final Locale locale) {
073        super.setLocale(locale);
074        updateRendererCachePrefix();
075      }
076    
077      public ResourceManagerImpl.CacheKey getRendererCacheKey() {
078        return rendererCacheKey;
079      }
080    
081    
082      public void updateRendererCachePrefix() {
083        rendererCacheKey = ResourceManagerImpl.getRendererCacheKey(
084            clientProperties != null ? clientProperties.getId() : "null", getLocale());
085    //    LOG.info("updateRendererCachePrefix :" + rendererCachePrefix);
086      }
087    
088      public void broadcastEventsForPhase(final FacesContext context, final PhaseId phaseId) {
089        broadcastForPhase(phaseId);
090        if (context.getRenderResponse() || context.getResponseComplete()) {
091          clearEvents(context);
092        }
093      }
094    
095    // -----------------------------------------------------------------------------
096    // -----------------------------------------------------------------------------
097    //
098    //  The following code is copied from myfaces implementation!
099    //  In suns jsf-api 1.1.01 are the events not cleared if renderResponse is true
100    //  after processUpdates, seems to be a bug. This is fixed at least in
101    //  Nightly Snapshot from 15.08.2005, but not in stable yet.
102    //  Events are private member of UIViewRoot, so we have to copy anny code
103    //  accessing them.
104    //
105      // TODO: remove if fixed in stable release!
106    
107      public void queueEvent(final FacesEvent event) {
108        if (event == null) {
109          throw new NullPointerException("event");
110        }
111        getEvents(FacesContext.getCurrentInstance(), true).add(event);
112      }
113    
114    
115      private void broadcastForPhase(final PhaseId phaseId) {
116        List<FacesEvent> events = getEvents(FacesContext.getCurrentInstance(), false);
117        if (events == null) {
118          return;
119        }
120    
121        boolean abort = false;
122    
123        int phaseIdOrdinal = phaseId.getOrdinal();
124        for (ListIterator listiterator = events.listIterator(); listiterator.hasNext();) {
125          FacesEvent event = (FacesEvent) listiterator.next();
126          int ordinal = event.getPhaseId().getOrdinal();
127          if (ordinal == ANY_PHASE_ORDINAL
128              || ordinal == phaseIdOrdinal) {
129            UIComponent source = event.getComponent();
130            try {
131              source.broadcast(event);
132            } catch (FacesException e) {
133              Throwable fe = e;
134              while (fe != null) {
135                if (fe instanceof AbortProcessingException) {
136                  if (LOG.isTraceEnabled()) {
137                    LOG.trace("AbortProcessingException caught!");
138                  }
139                  // abort event processing
140                  // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
141                  //  that no further broadcast of this event, or any further events, should take place."
142                  abort = true;
143                  break;
144                }
145                fe = fe.getCause();
146              }
147              if (!abort) {
148                throw e;
149              } else {
150                break;
151              }
152            } finally {
153              listiterator.remove();
154            }
155          }
156        }
157    
158        if (abort) {
159          // TODO: abort processing of any event of any phase or just of any event of the current phase???
160          clearEvents(FacesContext.getCurrentInstance());
161        }
162      }
163    
164    
165      private void clearEvents(final FacesContext context) {
166        context.getExternalContext().getRequestMap().remove(EVENT_LIST_KEY);
167      }
168    
169    
170      public void processDecodes(final FacesContext context) {
171        if (context == null) {
172          throw new NullPointerException("context");
173        }
174        super.processDecodes(context);
175        broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
176        if (context.getRenderResponse() || context.getResponseComplete()) {
177          clearEvents(context);
178        }
179      }
180    
181      public void processValidators(final FacesContext context) {
182        if (context == null) {
183          throw new NullPointerException("context");
184        }
185        super.processValidators(context);
186        broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
187        if (context.getRenderResponse() || context.getResponseComplete()) {
188          clearEvents(context);
189        }
190      }
191    
192      public void processUpdates(final FacesContext context) {
193        if (context == null) {
194          throw new NullPointerException("context");
195        }
196        super.processUpdates(context);
197        broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
198        if (context.getRenderResponse() || context.getResponseComplete()) {
199          clearEvents(context);
200        }
201      }
202    
203      public void processApplication(final FacesContext context) {
204        if (context == null) {
205          throw new NullPointerException("context");
206        }
207        broadcastForPhase(PhaseId.INVOKE_APPLICATION);
208        if (context.getRenderResponse() || context.getResponseComplete()) {
209          clearEvents(context);
210        }
211      }
212    
213      private List<FacesEvent> getEvents(final FacesContext context, boolean create) {
214        final Map requestMap = context.getExternalContext().getRequestMap();
215        List<FacesEvent> events = (List<FacesEvent>) requestMap.get(EVENT_LIST_KEY);
216        if (events == null && create) {
217          events = new ArrayList<FacesEvent>();
218          requestMap.put(EVENT_LIST_KEY, events);
219        }
220        return events;
221      }
222    }