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.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.apache.myfaces.tobago.context.ClientProperties;
023    import org.apache.myfaces.tobago.context.ResourceManagerImpl;
024    
025    import javax.faces.FacesException;
026    import javax.faces.component.UIComponent;
027    import javax.faces.context.FacesContext;
028    import javax.faces.event.AbortProcessingException;
029    import javax.faces.event.FacesEvent;
030    import javax.faces.event.PhaseId;
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    public class UIViewRoot extends javax.faces.component.UIViewRoot {
038    
039      private static final Log LOG = LogFactory.getLog(UIViewRoot.class);
040    
041      private static final String EVENT_LIST_KEY = UIViewRoot.class.getName() + ".EventList";
042      public static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal();
043    
044      private ResourceManagerImpl.CacheKey rendererCacheKey;
045    
046      private ClientProperties clientProperties;
047    
048      /**
049       * <p>Create a new {@link UIViewRoot} instance with default property
050       * values.</p>
051       */
052      public UIViewRoot() {
053        super();
054        updateRendererCachePrefix();
055      }
056    
057      public ClientProperties getClientProperties() {
058        return clientProperties;
059      }
060    
061      public void setClientProperties(final ClientProperties clientProperties) {
062        this.clientProperties = clientProperties;
063        updateRendererCachePrefix();
064      }
065    
066      @Override
067      public void setLocale(final Locale locale) {
068        super.setLocale(locale);
069        updateRendererCachePrefix();
070      }
071    
072      public ResourceManagerImpl.CacheKey getRendererCacheKey() {
073        return rendererCacheKey;
074      }
075    
076    
077      public void updateRendererCachePrefix() {
078        rendererCacheKey = ResourceManagerImpl.getRendererCacheKey(
079            clientProperties != null ? clientProperties.getId() : "null", getLocale());
080    //    LOG.info("updateRendererCachePrefix :" + rendererCachePrefix);
081      }
082    
083      public void broadcastEventsForPhase(final FacesContext context, final PhaseId phaseId) {
084        broadcastForPhase(phaseId);
085        if (context.getRenderResponse() || context.getResponseComplete()) {
086          clearEvents(context);
087        }
088      }
089    
090    // -----------------------------------------------------------------------------
091    // -----------------------------------------------------------------------------
092    //
093    //  The following code is copied from myfaces implementation!
094    //  In suns jsf-api 1.1.01 are the events not cleared if renderResponse is true
095    //  after processUpdates, seems to be a bug. This is fixed at least in
096    //  Nightly Snapshot from 15.08.2005, but not in stable yet.
097    //  Events are private member of UIViewRoot, so we have to copy anny code
098    //  accessing them.
099    //
100      // TODO: remove if fixed in stable release!
101    
102      @Override
103      public void queueEvent(final FacesEvent event) {
104        if (event == null) {
105          throw new NullPointerException("event");
106        }
107        getEvents(FacesContext.getCurrentInstance(), true).add(event);
108      }
109    
110    
111      private void broadcastForPhase(final PhaseId phaseId) {
112        List<FacesEvent> events = getEvents(FacesContext.getCurrentInstance(), false);
113        if (events == null) {
114          return;
115        }
116    
117        boolean abort = false;
118    
119        int phaseIdOrdinal = phaseId.getOrdinal();
120        for (ListIterator listiterator = events.listIterator(); listiterator.hasNext();) {
121          FacesEvent event = (FacesEvent) listiterator.next();
122          int ordinal = event.getPhaseId().getOrdinal();
123          if (ordinal == ANY_PHASE_ORDINAL
124              || ordinal == phaseIdOrdinal) {
125            UIComponent source = event.getComponent();
126            try {
127              source.broadcast(event);
128            } catch (FacesException e) {
129              Throwable fe = e;
130              while (fe != null) {
131                if (fe instanceof AbortProcessingException) {
132                  if (LOG.isTraceEnabled()) {
133                    LOG.trace("AbortProcessingException caught!");
134                  }
135                  // abort event processing
136                  // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
137                  //  that no further broadcast of this event, or any further events, should take place."
138                  abort = true;
139                  break;
140                }
141                fe = fe.getCause();
142              }
143              if (!abort) {
144                throw e;
145              } else {
146                break;
147              }
148            } finally {
149              listiterator.remove();
150            }
151          }
152        }
153    
154        if (abort) {
155          // TODO: abort processing of any event of any phase or just of any event of the current phase???
156          clearEvents(FacesContext.getCurrentInstance());
157        }
158      }
159    
160    
161      private void clearEvents(final FacesContext context) {
162        context.getExternalContext().getRequestMap().remove(EVENT_LIST_KEY);
163      }
164    
165    
166      @Override
167      public void processDecodes(final FacesContext context) {
168        if (context == null) {
169          throw new NullPointerException("context");
170        }
171        super.processDecodes(context);
172        broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
173        if (context.getRenderResponse() || context.getResponseComplete()) {
174          clearEvents(context);
175        }
176      }
177    
178      @Override
179      public void processValidators(final FacesContext context) {
180        if (context == null) {
181          throw new NullPointerException("context");
182        }
183        super.processValidators(context);
184        broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
185        if (context.getRenderResponse() || context.getResponseComplete()) {
186          clearEvents(context);
187        }
188      }
189    
190      @Override
191      public void processUpdates(final FacesContext context) {
192        if (context == null) {
193          throw new NullPointerException("context");
194        }
195        super.processUpdates(context);
196        broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
197        if (context.getRenderResponse() || context.getResponseComplete()) {
198          clearEvents(context);
199        }
200      }
201    
202      @Override
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    }