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.ConcurrentModificationException;
033    import java.util.List;
034    import java.util.ListIterator;
035    import java.util.Locale;
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    
046      private ResourceManagerImpl.CacheKey rendererCacheKey;
047    
048      private ClientProperties clientProperties;
049    
050      public static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal();
051      private List events = null;
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(ClientProperties clientProperties) {
068        this.clientProperties = clientProperties;
069        updateRendererCachePrefix();
070      }
071    
072      public void setLocale(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(FacesContext context, PhaseId phaseId) {
089        broadcastForPhase(phaseId);
090        if (context.getRenderResponse() || context.getResponseComplete()) {
091          clearEvents();
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(FacesEvent event) {
108        if (event == null) {
109          throw new NullPointerException("event");
110        }
111        if (events == null) {
112          events = new ArrayList();
113        }
114        events.add(event);
115      }
116    
117    
118      private void broadcastForPhase(PhaseId phaseId) {
119        if (events == null) {
120          return;
121        }
122    
123        boolean abort = false;
124    
125        int phaseIdOrdinal = phaseId.getOrdinal();
126        for (ListIterator listiterator = events.listIterator(); listiterator.hasNext();) {
127          FacesEvent event = (FacesEvent) listiterator.next();
128          int ordinal = event.getPhaseId().getOrdinal();
129          if (ordinal == ANY_PHASE_ORDINAL
130              || ordinal == phaseIdOrdinal) {
131            UIComponent source = event.getComponent();
132            try {
133              source.broadcast(event);
134            } catch (FacesException e) {
135              Throwable fe = e;
136              while (fe != null) {
137                if (fe instanceof AbortProcessingException) {
138                  if (LOG.isTraceEnabled()) {
139                    LOG.trace("AbortProcessingException caught!");
140                  }
141                  // abort event processing
142                  // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
143                  //  that no further broadcast of this event, or any further events, should take place."
144                  abort = true;
145                  break;
146                }
147                fe = fe.getCause();
148              }
149              if (!abort) {
150                throw e;
151              } else {
152                break;
153              }
154            } finally {
155    
156              try {
157                listiterator.remove();
158              } catch (ConcurrentModificationException cme) {
159                int eventIndex = listiterator.previousIndex();
160                events.remove(eventIndex);
161                //listiterator = events.listIterator();
162              }
163            }
164          }
165        }
166    
167        if (abort) {
168          // TODO: abort processing of any event of any phase or just of any event of the current phase???
169          clearEvents();
170        }
171      }
172    
173    
174      private void clearEvents() {
175        events = null;
176      }
177    
178    
179      public void processDecodes(FacesContext context) {
180        if (context == null) {
181          throw new NullPointerException("context");
182        }
183        super.processDecodes(context);
184        broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
185        if (context.getRenderResponse() || context.getResponseComplete()) {
186          clearEvents();
187        }
188      }
189    
190      public void processValidators(FacesContext context) {
191        if (context == null) {
192          throw new NullPointerException("context");
193        }
194        super.processValidators(context);
195        broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
196        if (context.getRenderResponse() || context.getResponseComplete()) {
197          clearEvents();
198        }
199      }
200    
201      public void processUpdates(FacesContext context) {
202        if (context == null) {
203          throw new NullPointerException("context");
204        }
205        super.processUpdates(context);
206        broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
207        if (context.getRenderResponse() || context.getResponseComplete()) {
208          clearEvents();
209        }
210      }
211    
212      public void processApplication(FacesContext context) {
213        if (context == null) {
214          throw new NullPointerException("context");
215        }
216        broadcastForPhase(PhaseId.INVOKE_APPLICATION);
217        if (context.getRenderResponse() || context.getResponseComplete()) {
218          clearEvents();
219        }
220      }
221    }