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 org.apache.myfaces.custom.tabbedpane;
20  
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import javax.faces.component.PartialStateHolder;
25  import javax.faces.component.StateHolder;
26  import javax.faces.component.UIComponent;
27  import javax.faces.component.UIForm;
28  import javax.faces.component.UINamingContainer;
29  import javax.faces.component.behavior.ClientBehaviorHolder;
30  import javax.faces.component.html.HtmlPanelGroup;
31  import javax.faces.context.FacesContext;
32  import javax.faces.el.EvaluationException;
33  import javax.faces.el.MethodBinding;
34  import javax.faces.event.AbortProcessingException;
35  import javax.faces.event.FacesEvent;
36  import javax.faces.event.PhaseId;
37  
38  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
39  import org.apache.myfaces.component.AlignProperty;
40  import org.apache.myfaces.component.AttachedDeltaWrapper;
41  import org.apache.myfaces.component.DataProperties;
42  import org.apache.myfaces.component.EventAware;
43  import org.apache.myfaces.component.PanelProperties;
44  import org.apache.myfaces.component.UniversalProperties;
45  import org.apache.myfaces.component.UserRoleAware;
46  
47  /**
48   * TODO: Document this component.
49   * 
50   * Unless otherwise specified, all attributes accept static values or EL expressions.
51   * 
52   * @JSFComponent
53   *   name = "t:panelTabbedPane"
54   *   class = "org.apache.myfaces.custom.tabbedpane.HtmlPanelTabbedPane"
55   *   tagClass = "org.apache.myfaces.custom.tabbedpane.HtmlPanelTabbedPaneTag"
56   *   tagHandler = "org.apache.myfaces.custom.tabbedpane.HtmlPanelTabbedPaneTagHandler"
57   * 
58   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
59   * @version $Revision: 745263 $ $Date: 2009-02-17 16:51:58 -0500 (mar, 17 feb 2009) $
60   */
61  public abstract class AbstractHtmlPanelTabbedPane
62          extends HtmlPanelGroup
63          implements UniversalProperties, EventAware, PanelProperties,
64          AlignProperty, DataProperties, UserRoleAware, ClientBehaviorHolder
65          
66  {
67      //private static final Log log = LogFactory.getLog(HtmlPanelTabbedPane.class);
68  
69      public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlPanelTabbedPane";
70      public static final String COMPONENT_FAMILY = "javax.faces.Panel";
71      private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.TabbedPane";
72      private static final int DEFAULT_SELECTEDINDEX = 0;
73      private static final boolean DEFAULT_SERVER_SIDE_TAB_SWITCH = false;
74  
75      private MethodBinding _tabChangeListener = null;
76      private static final int DEFAULT_BORDER = Integer.MIN_VALUE;
77  
78      public void decode(FacesContext context)
79      {
80          super.decode(context);    //To change body of overridden methods use File | Settings | File Templates.
81      }
82  
83      public void processDecodes(javax.faces.context.FacesContext context)
84     {
85         if (context == null) throw new NullPointerException("context");
86         decode(context);
87  
88         int tabIdx = 0;
89         int selectedIndex = getSelectedIndex();
90  
91         Iterator it = getFacetsAndChildren();
92  
93         while (it.hasNext())
94         {
95             UIComponent childOrFacet = getUIComponent((UIComponent) it.next());
96             if (childOrFacet instanceof HtmlPanelTab) {
97                 if (isClientSide() || selectedIndex == tabIdx) {
98                     childOrFacet.processDecodes(context);
99                 }
100                tabIdx++;
101            } else {
102                childOrFacet.processDecodes(context);
103            }
104        }
105    }
106 
107     public void processValidators(FacesContext context)
108     {
109         if (context == null) throw new NullPointerException("context");
110         if (!isRendered()) return;
111 
112         int tabIdx = 0;
113         int selectedIndex = getSelectedIndex();
114 
115         Iterator it = getFacetsAndChildren();
116 
117         while (it.hasNext())
118         {
119             UIComponent childOrFacet = getUIComponent((UIComponent) it.next());
120             if (childOrFacet instanceof HtmlPanelTab) {
121                 if (isClientSide() || selectedIndex == tabIdx) {
122                     childOrFacet.processValidators(context);
123                 }
124                 tabIdx++;
125             } else {
126                 childOrFacet.processValidators(context);
127             }
128         }
129     }
130 
131     public void processUpdates(FacesContext context)
132     {
133         if (context == null) throw new NullPointerException("context");
134         if (!isRendered()) return;
135 
136         int tabIdx = 0;
137         int selectedIndex = getSelectedIndex();
138 
139         Iterator it = getFacetsAndChildren();
140 
141         while (it.hasNext())
142         {
143             UIComponent childOrFacet = getUIComponent((UIComponent) it.next());
144             if (childOrFacet instanceof HtmlPanelTab) {
145                 if (isClientSide() || selectedIndex == tabIdx) {
146                     childOrFacet.processUpdates(context);
147                 }
148                 tabIdx++;
149             } else {
150                 childOrFacet.processUpdates(context);
151             }
152         }
153     }
154 
155     private UIComponent getUIComponent(UIComponent uiComponent)
156     {
157         /*todo: checking for UIForm is not enough - Trinidad form, etc.*/
158         if (uiComponent instanceof UINamingContainer || uiComponent instanceof UIForm)
159         {
160             List children = uiComponent.getChildren();
161             for (int i = 0, len = children.size(); i < len; i++)
162             {
163                 uiComponent = getUIComponent((UIComponent)children.get(i));
164             }
165         }
166         return uiComponent;
167     }
168 
169     public void addTabChangeListener(TabChangeListener listener)
170     {
171         addFacesListener(listener);
172     }
173 
174     public void removeTabChangeListener(TabChangeListener listener)
175     {
176         removeFacesListener(listener);
177     }
178 
179     /**
180      * TODO: This should be something like this:
181      * 
182      * JSFProperty
183      *   returnSignature = "void"
184      *   methodSignature = "org.apache.myfaces.custom.tabbedpane.TabChangeEvent"
185      * 
186      * And be added on tld. But you can do the same with TabChangeListenerTag. 
187      * 
188      * @return
189      */
190     public MethodBinding getTabChangeListener()
191     {
192         return _tabChangeListener;
193     }
194     
195     private boolean _isSetTabChangeListener()
196     {
197         Boolean value = (Boolean) getStateHelper().get(PropertyKeys.tabChangeListenerSet);
198         return value == null ? false : value;
199     }
200     
201     public void setTabChangeListener(MethodBinding tabChangeListener)
202     {
203         _tabChangeListener = tabChangeListener;
204     }
205     
206     public Object saveState(FacesContext facesContext)
207     {
208         if (initialStateMarked())
209         {
210             boolean nullDelta = true;
211             Object parentSaved = super.saveState(facesContext);
212             Object tabChangeListenerSaved = null;
213             if (!_isSetTabChangeListener() &&
214                 _tabChangeListener != null && _tabChangeListener instanceof PartialStateHolder)
215             {
216                 //Delta
217                 StateHolder holder = (StateHolder) _tabChangeListener;
218                 if (!holder.isTransient())
219                 {
220                     Object attachedState = holder.saveState(facesContext);
221                     if (attachedState != null)
222                     {
223                         nullDelta = false;
224                     }
225                     tabChangeListenerSaved = new AttachedDeltaWrapper(_tabChangeListener.getClass(),
226                         attachedState);
227                 }
228             }
229             else  if (_isSetTabChangeListener() || _tabChangeListener != null)
230             {
231                 //Full
232                 tabChangeListenerSaved = saveAttachedState(facesContext,_tabChangeListener);
233                 nullDelta = false;
234             }        
235             if (parentSaved == null && nullDelta)
236             {
237                 //No values
238                 return null;
239             }
240             
241             Object[] values = new Object[2];
242             values[0] = parentSaved;
243             values[1] = tabChangeListenerSaved;
244             return values;
245         }
246         else
247         {
248             Object[] values = new Object[2];
249             values[0] = super.saveState(facesContext);
250             values[1] = saveAttachedState(facesContext,_tabChangeListener);
251             return values;
252         }
253     }
254 
255     public void restoreState(FacesContext facesContext, Object state)
256     {
257         if (state == null)
258         {
259             return;
260         }
261         
262         Object[] values = (Object[])state;
263         super.restoreState(facesContext,values[0]);
264         if (values[1] instanceof AttachedDeltaWrapper)
265         {
266             //Delta
267             ((StateHolder)_tabChangeListener).restoreState(facesContext, ((AttachedDeltaWrapper) values[1]).getWrappedStateObject());
268         }
269         else
270         {
271             //Full
272             _tabChangeListener = (javax.faces.el.MethodBinding) restoreAttachedState(facesContext,values[1]);
273         }
274     }    
275 
276     public void broadcast(FacesEvent event) throws AbortProcessingException
277     {
278         if (event instanceof TabChangeEvent)
279         {
280             TabChangeEvent tabChangeEvent = (TabChangeEvent)event;
281             if (tabChangeEvent.getComponent() == this)
282             {
283                 setSelectedIndex(tabChangeEvent.getNewTabIndex());
284                 getFacesContext().renderResponse();
285             }
286         }
287         super.broadcast(event);
288 
289         MethodBinding tabChangeListenerBinding = getTabChangeListener();
290         if (tabChangeListenerBinding != null)
291         {
292             try
293             {
294                 tabChangeListenerBinding.invoke(getFacesContext(), new Object[]{event});
295             }
296             catch (EvaluationException e)
297             {
298                 Throwable cause = e.getCause();
299                 if (cause != null && cause instanceof AbortProcessingException)
300                 {
301                     throw (AbortProcessingException)cause;
302                 }
303                 else
304                 {
305                     throw e;
306                 }
307             }
308         }
309     }
310     
311     /**
312      * Write out information about the toggling mode - the component might
313      * be toggled server side or client side.
314      */
315     public boolean isClientSide()
316     {
317         return !isServerSideTabSwitch(); 
318     }
319 
320     /**
321      * 
322      */
323     @JSFProperty(
324        tagExcluded = true)
325     public abstract String getActiveTabVar();
326     
327     /**
328      * Boolean Variable that is set in request scope when 
329      * rendering a panelTab. True means that the currently 
330      * rendered panelTab is active.
331      * 
332      */
333     @JSFProperty
334     public abstract Boolean getActivePanelTabVar();
335 
336     /**
337      * Index of tab that is selected by default.
338      * 
339      */
340     @JSFProperty(
341        defaultValue = "0")
342     public abstract int getSelectedIndex();
343     
344     public abstract void setSelectedIndex(int selectedIndex);
345 
346     /**
347      * Style class of the active tab cell.
348      * 
349      */
350     @JSFProperty
351     public abstract String getActiveTabStyleClass();
352 
353     /**
354      * Style class of the inactive tab cells.
355      * 
356      */
357     @JSFProperty
358     public abstract String getInactiveTabStyleClass();
359 
360     /**
361      * Style class of the active tab sub cell.
362      * 
363      */
364     @JSFProperty
365     public abstract String getActiveSubStyleClass();
366 
367     /**
368      * Style class of the inactive tab sub cells.
369      * 
370      */
371     @JSFProperty
372     public abstract String getInactiveSubStyleClass();
373 
374     /**
375      * Style class of the active tab content cell.
376      * 
377      */
378     @JSFProperty
379     public abstract String getTabContentStyleClass();
380 
381     /**
382      * Style class of the disabled tab cells.
383      * 
384      */
385     @JSFProperty
386     public abstract String getDisabledTabStyleClass();
387 
388     /**
389      * Toggle client-side/server-side tab switches.
390      * 
391      */
392     @JSFProperty(
393        defaultValue = "false")
394     public abstract boolean isServerSideTabSwitch();
395     
396     public boolean getServerSideTabSwitch()
397     {
398         return isServerSideTabSwitch();
399     }
400 
401     /**
402      * Define if the process validation and update model phases
403      * should be executed before change between tabs, when
404      * serverSideTabSwitch = true (if is false, the switch
405      * is done by other way so this property does not have any
406      * effect).
407      * 
408      * Note that if this property is set as false, only a tab 
409      * change is done if all input fields inside the form are valid 
410      * (including input components outside this panel). 
411      * 
412      * By default is true, so both phases are not executed.
413      * 
414      */
415     @JSFProperty(
416        defaultValue = "true")
417     public abstract boolean isImmediateTabChange();
418     
419     public void queueEvent(FacesEvent event)
420     {
421         if (event != null && event instanceof TabChangeEvent)
422         {
423             if (isImmediateTabChange())
424             {
425                 event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
426             }
427             else
428             {
429                 event.setPhaseId(PhaseId.INVOKE_APPLICATION);
430             }
431         }
432         super.queueEvent(event);
433     }
434     
435     enum PropertyKeys
436     {
437         tabChangeListenerSet
438     }
439 }