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    
023    import javax.faces.component.UIComponent;
024    import javax.faces.context.FacesContext;
025    import javax.faces.event.AbortProcessingException;
026    import javax.faces.event.FacesEvent;
027    import javax.faces.event.PhaseId;
028    import java.util.ArrayList;
029    import java.util.ConcurrentModificationException;
030    import java.util.List;
031    import java.util.ListIterator;
032    import java.util.Locale;
033    
034    /*
035     * User: weber
036     * Date: Jun 13, 2005
037     * Time: 5:19:31 PM
038     */
039    public class UIViewRoot extends javax.faces.component.UIViewRoot {
040    
041      private ResourceManagerImpl.CacheKey rendererCacheKey;
042    
043      private ClientProperties clientProperties;
044    
045      public static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal();
046      private List events = null;
047    
048    
049      /**
050       * <p>Create a new {@link UIViewRoot} instance with default property
051       * values.</p>
052       */
053      public UIViewRoot() {
054        super();
055        updateRendererCachePrefix();
056      }
057    
058      public ClientProperties getClientProperties() {
059        return clientProperties;
060      }
061    
062      public void setClientProperties(ClientProperties clientProperties) {
063        this.clientProperties = clientProperties;
064        updateRendererCachePrefix();
065      }
066    
067      public void setLocale(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(FacesContext context, PhaseId phaseId) {
084        broadcastForPhase(phaseId);
085        if (context.getRenderResponse() || context.getResponseComplete()) {
086          clearEvents();
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      public void queueEvent(FacesEvent event) {
103        if (event == null) {
104          throw new NullPointerException("event");
105        }
106        if (events == null) {
107          events = new ArrayList();
108        }
109        events.add(event);
110      }
111    
112    
113      private void broadcastForPhase(PhaseId phaseId) {
114        if (events == null) {
115          return;
116        }
117    
118        boolean abort = false;
119    
120        int phaseIdOrdinal = phaseId.getOrdinal();
121        for (ListIterator listiterator = events.listIterator(); listiterator.hasNext();) {
122          FacesEvent event = (FacesEvent) listiterator.next();
123          int ordinal = event.getPhaseId().getOrdinal();
124          if (ordinal == ANY_PHASE_ORDINAL
125              || ordinal == phaseIdOrdinal) {
126            UIComponent source = event.getComponent();
127            try {
128              source.broadcast(event);
129            } catch (AbortProcessingException e) {
130              // abort event processing
131              // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
132              //  that no further broadcast of this event, or any further events, should take place."
133              abort = true;
134              break;
135            } finally {
136    
137              try {
138                listiterator.remove();
139              } catch (ConcurrentModificationException cme) {
140                int eventIndex = listiterator.previousIndex();
141                events.remove(eventIndex);
142                //listiterator = events.listIterator();
143              }
144            }
145          }
146        }
147    
148        if (abort) {
149          // TODO: abort processing of any event of any phase or just of any event of the current phase???
150          clearEvents();
151        }
152      }
153    
154    
155      private void clearEvents() {
156        events = null;
157      }
158    
159    
160      public void processDecodes(FacesContext context) {
161        if (context == null) {
162          throw new NullPointerException("context");
163        }
164        super.processDecodes(context);
165        broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
166        if (context.getRenderResponse() || context.getResponseComplete()) {
167          clearEvents();
168        }
169      }
170    
171      public void processValidators(FacesContext context) {
172        if (context == null) {
173          throw new NullPointerException("context");
174        }
175        super.processValidators(context);
176        broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
177        if (context.getRenderResponse() || context.getResponseComplete()) {
178          clearEvents();
179        }
180      }
181    
182      public void processUpdates(FacesContext context) {
183        if (context == null) {
184          throw new NullPointerException("context");
185        }
186        super.processUpdates(context);
187        broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
188        if (context.getRenderResponse() || context.getResponseComplete()) {
189          clearEvents();
190        }
191      }
192    
193      public void processApplication(FacesContext context) {
194        if (context == null) {
195          throw new NullPointerException("context");
196        }
197        broadcastForPhase(PhaseId.INVOKE_APPLICATION);
198        if (context.getRenderResponse() || context.getResponseComplete()) {
199          clearEvents();
200        }
201      }
202    }