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    //import org.apache.myfaces.tobago.util.FacesVersion;
025    
026    import javax.faces.component.UIComponent;
027    //import javax.faces.context.ExternalContext;
028    import javax.faces.context.FacesContext;
029    import javax.faces.event.AbortProcessingException;
030    import javax.faces.event.FacesEvent;
031    import javax.faces.event.PhaseId;
032    import javax.faces.FacesException;
033    import java.util.ArrayList;
034    import java.util.List;
035    import java.util.ListIterator;
036    import java.util.Locale;
037    import java.util.Map;
038    
039    /*
040     * User: weber
041     * Date: Jun 13, 2005
042     * Time: 5:19:31 PM
043     */
044    public class UIViewRoot extends javax.faces.component.UIViewRoot {
045    
046      private static final Log LOG = LogFactory.getLog(UIViewRoot.class);
047      private static final String EVENT_LIST_KEY = UIViewRoot.class.getName() + ".EventList";
048    
049      private ResourceManagerImpl.CacheKey rendererCacheKey;
050    
051      private ClientProperties clientProperties;
052    
053      //private int nextUniqueId;
054    
055      public static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal();
056    
057    
058      /**
059       * <p>Create a new {@link UIViewRoot} instance with default property
060       * values.</p>
061       */
062      public UIViewRoot() {
063        super();
064        updateRendererCachePrefix();
065      }
066    
067      public ClientProperties getClientProperties() {
068        return clientProperties;
069      }
070    
071      public void setClientProperties(final ClientProperties clientProperties) {
072        this.clientProperties = clientProperties;
073        updateRendererCachePrefix();
074      }
075    
076      public void setLocale(final Locale locale) {
077        super.setLocale(locale);
078        updateRendererCachePrefix();
079      }
080    
081      public ResourceManagerImpl.CacheKey getRendererCacheKey() {
082        return rendererCacheKey;
083      }
084    
085    
086      public void updateRendererCachePrefix() {
087        rendererCacheKey = ResourceManagerImpl.getRendererCacheKey(
088            clientProperties != null ? clientProperties.getId() : "null", getLocale());
089    //    LOG.info("updateRendererCachePrefix :" + rendererCachePrefix);
090      }
091    
092      public void broadcastEventsForPhase(final FacesContext context, final PhaseId phaseId) {
093        broadcastForPhase(phaseId);
094        if (context.getRenderResponse() || context.getResponseComplete()) {
095          clearEvents(context);
096        }
097      }
098    
099    // -----------------------------------------------------------------------------
100    // -----------------------------------------------------------------------------
101    //
102    //  The following code is copied from myfaces implementation!
103    //  In suns jsf-api 1.1.01 are the events not cleared if renderResponse is true
104    //  after processUpdates, seems to be a bug. This is fixed at least in
105    //  Nightly Snapshot from 15.08.2005, but not in stable yet.
106    //  Events are private member of UIViewRoot, so we have to copy anny code
107    //  accessing them.
108    //
109      // TODO: remove if fixed in stable release!
110    
111      public void queueEvent(final FacesEvent event) {
112        if (event == null) {
113          throw new NullPointerException("event");
114        }
115        getEvents(FacesContext.getCurrentInstance(), true).add(event);
116      }
117    
118    
119      private void broadcastForPhase(final PhaseId phaseId) {
120        List<FacesEvent> events = getEvents(FacesContext.getCurrentInstance(), false);
121        if (events == null) {
122          return;
123        }
124    
125        boolean abort = false;
126    
127        int phaseIdOrdinal = phaseId.getOrdinal();
128        for (ListIterator listiterator = events.listIterator(); listiterator.hasNext();) {
129          FacesEvent event = (FacesEvent) listiterator.next();
130          int ordinal = event.getPhaseId().getOrdinal();
131          if (ordinal == ANY_PHASE_ORDINAL
132              || ordinal == phaseIdOrdinal) {
133            UIComponent source = event.getComponent();
134            try {
135              source.broadcast(event);
136            } catch (FacesException e) {
137              Throwable fe = e;
138              while (fe != null) {
139                if (fe instanceof AbortProcessingException) {
140                  if (LOG.isTraceEnabled()) {
141                    LOG.trace("AbortProcessingException caught!");
142                  }
143                  // abort event processing
144                  // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
145                  //  that no further broadcast of this event, or any further events, should take place."
146                  abort = true;
147                  break;
148                }
149                fe = fe.getCause();
150              }
151              if (!abort) {
152                throw e;
153              } else {
154                break;
155              }
156            } finally {
157              listiterator.remove();
158            }
159          }
160        }
161    
162        if (abort) {
163          // TODO: abort processing of any event of any phase or just of any event of the current phase???
164          clearEvents(FacesContext.getCurrentInstance());
165        }
166      }
167    
168    
169      private void clearEvents(final FacesContext context) {
170        context.getExternalContext().getRequestMap().remove(EVENT_LIST_KEY);
171      }
172    
173    
174      public void processDecodes(final FacesContext context) {
175        if (context == null) {
176          throw new NullPointerException("context");
177        }
178        super.processDecodes(context);
179        broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
180        if (context.getRenderResponse() || context.getResponseComplete()) {
181          clearEvents(context);
182        }
183      }
184    
185      public void processValidators(final FacesContext context) {
186        if (context == null) {
187          throw new NullPointerException("context");
188        }
189        super.processValidators(context);
190        broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
191        if (context.getRenderResponse() || context.getResponseComplete()) {
192          clearEvents(context);
193        }
194      }
195    
196      public void processUpdates(final FacesContext context) {
197        if (context == null) {
198          throw new NullPointerException("context");
199        }
200        super.processUpdates(context);
201        broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
202        if (context.getRenderResponse() || context.getResponseComplete()) {
203          clearEvents(context);
204        }
205      }
206    
207      public void processApplication(final FacesContext context) {
208        if (context == null) {
209          throw new NullPointerException("context");
210        }
211        broadcastForPhase(PhaseId.INVOKE_APPLICATION);
212        if (context.getRenderResponse() || context.getResponseComplete()) {
213          clearEvents(context);
214        }
215      }
216    
217      private List<FacesEvent> getEvents(final FacesContext context, boolean create) {
218        final Map requestMap = context.getExternalContext().getRequestMap();
219        List<FacesEvent> events = (List<FacesEvent>) requestMap.get(EVENT_LIST_KEY);
220        if (events == null && create) {
221          events = new ArrayList<FacesEvent>();
222          requestMap.put(EVENT_LIST_KEY, events);
223        }
224        return events;
225      }
226    
227      /*
228    
229      public Object saveState(FacesContext facesContext) {
230        if (FacesVersion.supports12()) {
231          return super.saveState(facesContext);
232        } else {
233          Object[] state = new Object[2];
234          state[0] = super.saveState(facesContext);
235          state[1] = nextUniqueId;
236          return state;
237        }
238      }
239    
240      public void restoreState(FacesContext facesContext, Object o) {
241        Object[] state = (Object[]) o;
242        super.restoreState(facesContext, state[0]);
243        if (!FacesVersion.supports12()) {
244          nextUniqueId = (Integer) state[1];
245        }
246      }
247    
248      public String createUniqueId() {
249        if (FacesVersion.supports12()) {
250          return super.createUniqueId();
251        } else {
252          ExternalContext extCtx = FacesContext.getCurrentInstance().getExternalContext();
253          return extCtx.encodeNamespace(UNIQUE_ID_PREFIX + nextUniqueId++);
254        }
255      } */
256    
257    }