View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.component;
20  
21  import java.util.ArrayList;
22  import java.util.ConcurrentModificationException;
23  import java.util.List;
24  import java.util.ListIterator;
25  import java.util.Locale;
26  import javax.faces.context.ExternalContext;
27  
28  import javax.faces.context.FacesContext;
29  import javax.faces.el.ValueBinding;
30  import javax.faces.event.AbortProcessingException;
31  import javax.faces.event.FacesEvent;
32  import javax.faces.event.PhaseId;
33  
34  /**
35   * Creates a JSF View, which is a container that holds all of the
36   * components that are part of the view.
37   * <p> 
38   * Unless otherwise specified, all attributes accept static values or EL expressions.
39   * </p>
40   * See Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a>
41   *
42   * @JSFComponent
43   *   name = "f:view"
44   *   bodyContent = "JSP"
45   *   tagClass = "org.apache.myfaces.taglib.core.ViewTag"
46   *
47   * @JSFJspProperty name = "binding" returnType = "java.lang.String" tagExcluded = "true"
48   *
49   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
50   * @version $Revision: 949070 $ $Date: 2010-05-27 21:22:03 -0500 (Thu, 27 May 2010) $
51   */
52  public class UIViewRoot extends UIComponentBase
53  {
54      public static final String COMPONENT_TYPE = "javax.faces.ViewRoot";
55      public static final String COMPONENT_FAMILY = "javax.faces.ViewRoot";
56  
57      public static final String UNIQUE_ID_PREFIX = "_id";
58  
59      private static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal();
60  
61      /**
62       * The counter which will ensure a unique component id
63       * for every component instance in the tree that doesn't have
64       * an id attribute set.
65       */
66      private long _uniqueIdCounter = 0;
67  
68      private String _renderKitId = null;
69      private String _viewId = null;
70      private Locale _locale = null;
71      private List _events = null;
72  
73      public String getViewId()
74      {
75          return _viewId;
76      }
77  
78      public void setViewId(String viewId)
79      {
80          // It really doesn't make much sense to allow null here.
81          // However the TCK does not check for it, and sun's implementation
82          // allows it so here we allow it too.
83          _viewId = viewId;
84      }
85  
86      public void queueEvent(FacesEvent event)
87      {
88          if (event == null)
89          {
90              throw new NullPointerException("event");
91          }
92          if (_events == null)
93          {
94              _events = new ArrayList();
95          }
96          _events.add(event);
97      }
98  
99      private void _broadcastForPhase(PhaseId phaseId)
100     {
101         if (_events == null) return;
102 
103         boolean abort = false;
104 
105         int phaseIdOrdinal = phaseId.getOrdinal();
106         for (ListIterator listiterator = _events.listIterator(); listiterator.hasNext();)
107         {
108             FacesEvent event = (FacesEvent) listiterator.next();
109             int ordinal = event.getPhaseId().getOrdinal();
110             if (ordinal == ANY_PHASE_ORDINAL || ordinal == phaseIdOrdinal)
111             {
112                 UIComponent source = event.getComponent();
113                 try
114                 {
115                     source.broadcast(event);
116                 }
117                 catch (AbortProcessingException e)
118                 {
119                     // abort event processing
120                     // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
121                     //  that no further broadcast of this event, or any further events, should take place."
122                     abort = true;
123                     break;
124                 }
125                 finally
126                 {
127                     try
128                     {
129                         listiterator.remove();
130                     }
131                     catch(ConcurrentModificationException cme)
132                     {
133                         int eventIndex = listiterator.previousIndex();
134                         _events.remove(eventIndex);
135                         listiterator = _events.listIterator();
136                     }
137                 }
138             }
139         }
140 
141         if (abort)
142         {
143             // TODO: abort processing of any event of any phase or just of any event of the current phase???
144             clearEvents();
145         }
146     }
147 
148 
149     private void clearEvents()
150     {
151         _events = null;
152     }
153 
154 
155     public void processDecodes(FacesContext context)
156     {
157         if (context == null) throw new NullPointerException("context");
158         super.processDecodes(context);
159         _broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
160         if (context.getRenderResponse() || context.getResponseComplete())
161         {
162             clearEvents();
163         }
164     }
165 
166     public void processValidators(FacesContext context)
167     {
168         if (context == null) throw new NullPointerException("context");
169         super.processValidators(context);
170         _broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
171         if (context.getRenderResponse() || context.getResponseComplete())
172         {
173             clearEvents();
174         }
175     }
176 
177     public void processUpdates(FacesContext context)
178     {
179         if (context == null) throw new NullPointerException("context");
180         super.processUpdates(context);
181         _broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
182         if (context.getRenderResponse() || context.getResponseComplete())
183         {
184             clearEvents();
185         }
186     }
187 
188     public void processApplication(FacesContext context)
189     {
190         if (context == null) throw new NullPointerException("context");
191         _broadcastForPhase(PhaseId.INVOKE_APPLICATION);
192         if (context.getRenderResponse() || context.getResponseComplete())
193         {
194             clearEvents();
195         }
196     }
197 
198     public void encodeBegin(FacesContext context)
199             throws java.io.IOException
200     {
201         clearEvents();
202         super.encodeBegin(context);
203     }
204 
205     /**
206      *  Provides a unique id for this component instance.
207      */
208     public String createUniqueId()
209     {
210         ExternalContext extCtx = getFacesContext().getExternalContext();
211         return extCtx.encodeNamespace(UNIQUE_ID_PREFIX + _uniqueIdCounter++);
212     }
213 
214     /**
215      * The locale of this view.  Default: the default locale from the configuration file.
216      * 
217      * @JSFProperty
218      */    
219     public Locale getLocale()
220     {
221         if (_locale != null) return _locale;
222         ValueBinding vb = getValueBinding("locale");
223         FacesContext facesContext = getFacesContext();
224         if (vb == null)
225         {
226             return facesContext.getApplication().getViewHandler().calculateLocale(facesContext);
227         }
228         Object locale = vb.getValue(facesContext);
229         if (locale == null)
230         {
231             return facesContext.getApplication().getViewHandler().calculateLocale(facesContext);
232         }
233         if (locale instanceof Locale)
234         {
235             return (Locale)locale;
236         }
237         else if (locale instanceof String)
238         {
239             return getLocale((String)locale);
240         }
241         else
242         {
243             throw new IllegalArgumentException("locale binding"); //TODO: not specified!?
244         }
245     }
246 
247     public void setLocale(Locale locale)
248     {
249         _locale = locale;
250     }
251 
252     /**
253      * Create Locale from String representation.
254      *
255      * http://java.sun.com/j2se/1.4.2/docs/api/java/util/Locale.html
256      *
257      * @param locale locale representation in String.
258      * @return Locale instance
259      */
260     private static Locale getLocale(String locale){
261         int cnt = 0;
262         int pos = 0;
263         int prev = 0;
264 
265         // store locale variation.
266         // ex. "ja_JP_POSIX"
267         //  lv[0] : language(ja)
268         //  lv[1] : country(JP)
269         //  lv[2] : variant(POSIX)
270         String[] lv = new String[3];
271         Locale l=null;
272 
273         while((pos=locale.indexOf('_',prev))!=-1){
274              lv[cnt++] = locale.substring(prev,pos);
275              prev = pos + 1;
276         }
277 
278         lv[cnt++] = locale.substring(prev,locale.length());
279 
280         switch(cnt){
281             case 1:
282                 // create Locale from language.
283                 l = new Locale(lv[0]);
284                 break;
285             case 2:
286                 // create Locale from language and country.
287                 l = new Locale(lv[0],lv[1]);
288                 break;
289             case 3:
290                 // create Locale from language, country and variant.
291                 l = new Locale(lv[0], lv[1], lv[2]);
292                 break;
293         }
294         return l;
295     }
296 
297     public String getFamily()
298     {
299         return COMPONENT_FAMILY;
300     }
301 
302     /**
303      * Defines what renderkit should be used to render this view.
304      * <p>
305      * Note that in JSF1.1 this property cannot be set via the f:view tag
306      * (this is possible in JSF1.2).
307      */
308     public String getRenderKitId()
309     {
310         if (_renderKitId != null) return _renderKitId;
311         ValueBinding vb = getValueBinding("renderKitId");
312         return vb != null ? _ComponentUtils.getStringValue(getFacesContext(), vb) : null; //DEFAULT_RENDERKITID
313     }
314 
315     public void setRenderKitId(String renderKitId)
316     {
317         _renderKitId = renderKitId;
318     }
319 
320     /**
321      * DO NOT USE.
322      * <p>
323      * Although this class extends a base-class that defines a read/write
324      * rendered property, it makes no sense for this particular subclass to
325      * support it. Yes, this is broken OO design: direct all complaints to
326      * the JSF spec group.
327      * <p>
328      * Ideally this method would throw an UnsupportedOperationException when
329      * invoked. Unfortunately, the JSF TCK calls this method, so instead we
330      * just pass on the call to the default inherited implementation.
331      *
332      * @JSFProperty tagExcluded="true"
333      */
334     public void setRendered(boolean state)
335     {
336        super.setRendered(state);
337     }
338 
339     public boolean isRendered()
340     {
341         // Should just return true. But due to damned faulty TCK, we cannot do that.
342         return super.isRendered();
343     }
344 
345     /**
346      * DO NOT USE.
347      * <p>
348      * Although this class extends a base-class that defines a read/write
349      * id property, it makes no sense for this particular subclass to support
350      * it. The tag library does not export this property for use, but there 
351      * is no way to "undeclare" a java method. Yes, this is broken OO design:
352      * direct all complaints to the JSF spec group.
353      * <p>
354      * This property should be disabled (ie throw an exception if invoked).
355      * However there are currently several places that call this method
356      * (eg during restoreState) so it just does the normal thing for the
357      * moment. TODO: fix callers then make this throw an exception.
358      *
359      * @JSFProperty tagExcluded="true"
360      */
361     public void setId(String id)
362     {
363         //  throw new UnsupportedOperationException();
364 
365         // Leave enabled for now. Things like the TreeStructureManager call this,
366         // even though they probably should not.
367         super.setId(id);
368     }
369 
370     public String getId()
371     {
372         // Should just return null. But as setId passes the method on, do same here.
373         return super.getId();
374     }
375 
376     /**
377      * DO NOT USE.
378      * <p>
379      * As this component has no "id" property, it has no clientId property either.
380      * <p>
381      * Ideally, this method would just return null when called (or even throw an
382      * exception) Unforunately, the TCK invokes it so we just invoke the interited
383      * implementation instead.
384      */
385     public String getClientId(FacesContext context)
386     {
387         // Should return null, but cannot due to flawed TCK tests.
388         return super.getClientId(context);
389     }
390 
391     public Object saveState(FacesContext context)
392     {
393         Object values[] = new Object[5];
394         values[0] = super.saveState(context);
395         values[1] = _locale;
396         values[2] = _renderKitId;
397         values[3] = _viewId;
398         values[4] = new Long(_uniqueIdCounter);
399         return values;
400     }
401 
402     public void restoreState(FacesContext context, Object state)
403     {
404         Object values[] = (Object[])state;
405         super.restoreState(context, values[0]);
406         _locale = (Locale)values[1];
407         _renderKitId = (String)values[2];
408         _viewId = (String)values[3];
409         _uniqueIdCounter = values[4]==null?0:((Long)values[4]).longValue();
410     }
411 }