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  
20  package org.apache.myfaces.tobago.internal.component;
21  
22  import org.apache.myfaces.tobago.compat.FacesUtilsEL;
23  import org.apache.myfaces.tobago.component.Attributes;
24  import org.apache.myfaces.tobago.component.Facets;
25  import org.apache.myfaces.tobago.component.UITab;
26  import org.apache.myfaces.tobago.component.Visual;
27  import org.apache.myfaces.tobago.event.TabChangeEvent;
28  import org.apache.myfaces.tobago.event.TabChangeListener;
29  import org.apache.myfaces.tobago.event.TabChangeSource2;
30  import org.apache.myfaces.tobago.model.SwitchType;
31  import org.apache.myfaces.tobago.util.ComponentUtils;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import javax.el.MethodExpression;
36  import javax.el.ValueExpression;
37  import javax.faces.component.ActionSource2;
38  import javax.faces.component.UIComponent;
39  import javax.faces.component.behavior.ClientBehaviorHolder;
40  import javax.faces.context.FacesContext;
41  import javax.faces.event.AbortProcessingException;
42  import javax.faces.event.ActionListener;
43  import javax.faces.event.FacesEvent;
44  import javax.faces.event.PhaseId;
45  import java.io.IOException;
46  import java.util.ArrayList;
47  import java.util.Collection;
48  import java.util.Collections;
49  import java.util.List;
50  
51  public abstract class AbstractUITabGroup extends AbstractUIPanelBase
52      implements TabChangeSource2, ActionSource2, ClientBehaviorHolder, Visual {
53  
54    private static final Logger LOG = LoggerFactory.getLogger(AbstractUITabGroup.class);
55  
56    // todo generate
57    private static final Collection<String> EVENT_NAMES = Collections.singletonList("reload");
58    @Override
59    public void encodeChildren(final FacesContext facesContext) throws IOException {
60    }
61  
62    @Override
63    public void encodeEnd(final FacesContext facesContext) throws IOException {
64      super.encodeEnd(facesContext);
65      setRenderedIndex(getSelectedIndex());
66    }
67  
68  
69    @Override
70    public boolean getRendersChildren() {
71      return true;
72    }
73  
74    @Override
75    public void queueEvent(final FacesEvent event) {
76      if (this == event.getSource()) {
77        if (isImmediate() || getSwitchType() == SwitchType.client) {
78          // if switch type client event is always immediate
79          event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
80        } else {
81          event.setPhaseId(PhaseId.INVOKE_APPLICATION);
82        }
83      }
84      super.queueEvent(event);
85    }
86  
87    public AbstractUITab[] getTabs() {
88      final List<AbstractUITab> tabs = new ArrayList<AbstractUITab>();
89      for (final UIComponent kid : getChildren()) {
90        if (kid instanceof AbstractUITab) {
91          //if (kid.isRendered()) {
92          tabs.add((AbstractUITab) kid);
93          //}
94        } else {
95          LOG.error("Invalid component in UITabGroup: " + kid);
96        }
97      }
98      return tabs.toArray(new AbstractUITab[tabs.size()]);
99    }
100 
101   public AbstractUITab getActiveTab() {
102     return getTab(getSelectedIndex());
103   }
104 
105 
106   @Override
107   public void processDecodes(final FacesContext context) {
108     if (!(getSwitchType() == SwitchType.client)) {
109 
110       if (context == null) {
111         throw new NullPointerException("context");
112       }
113       if (!isRendered()) {
114         return;
115       }
116       int index = 0;
117       for (final UIComponent child : getChildren()) {
118         if (child instanceof UITab) {
119           final UITab tab = (UITab) child;
120           if (tab.isRendered()) {
121             if (getRenderedIndex() == index) {
122               tab.processDecodes(context);
123             } else {
124               UIComponent facet = ComponentUtils.getFacet(tab, Facets.toolBar);
125               if (facet != null) {
126                 facet.processDecodes(context);
127               }
128             }
129           }
130           index++;
131         }
132       }
133 //      final AbstractUIPanelBase renderedTab = getRenderedTab();
134 //      renderedTab.processDecodes(context);
135       for (final UIComponent facet : getFacets().values()) {
136         facet.processDecodes(context);
137       }
138       try {
139         decode(context);
140       } catch (final RuntimeException e) {
141         context.renderResponse();
142         throw e;
143       }
144     } else {
145       super.processDecodes(context);
146     }
147   }
148 
149   @Override
150   public void processValidators(final FacesContext context) {
151     if (!(getSwitchType() == SwitchType.client)) {
152       if (context == null) {
153         throw new NullPointerException("context");
154       }
155       if (!isRendered()) {
156         return;
157       }
158       final AbstractUITab renderedTab = getRenderedTab();
159       renderedTab.processValidators(context);
160       for (final UIComponent facet : getFacets().values()) {
161         facet.processValidators(context);
162       }
163     } else {
164       super.processValidators(context);
165     }
166   }
167 
168   @Override
169   public void processUpdates(final FacesContext context) {
170     if (!(getSwitchType() == SwitchType.client)) {
171       if (context == null) {
172         throw new NullPointerException("context");
173       }
174       if (!isRendered()) {
175         return;
176       }
177       final AbstractUITab renderedTab = getRenderedTab();
178       renderedTab.processUpdates(context);
179       for (final UIComponent facet : getFacets().values()) {
180         facet.processUpdates(context);
181       }
182 
183     } else {
184       super.processUpdates(context);
185     }
186   }
187 
188   @Override
189   public void broadcast(final FacesEvent facesEvent) throws AbortProcessingException {
190     super.broadcast(facesEvent);
191     if (facesEvent instanceof TabChangeEvent && facesEvent.getComponent() == this) {
192       final TabChangeEvent event = (TabChangeEvent) facesEvent;
193 
194       final MethodExpression methodExpression = getTabChangeListenerExpression();
195       if (methodExpression != null) {
196         FacesUtilsEL.invokeMethodExpression(FacesContext.getCurrentInstance(), methodExpression, facesEvent);
197       }
198 
199 // switched off, because this is already called in super.broadcast()
200 //      final ActionListener[] actionListeners = getActionListeners();
201 //      for (ActionListener listener : actionListeners) {
202 //        listener.processAction(event);
203 //      }
204 
205       // XXX is this needed?
206       if (!(getSwitchType() == SwitchType.client)) {
207         final ActionListener defaultActionListener = getFacesContext().getApplication().getActionListener();
208         if (defaultActionListener != null) {
209           defaultActionListener.processAction(event);
210         }
211       }
212       final Integer index = event.getNewTabIndex();
213       final ValueExpression expression = getValueExpression(Attributes.selectedIndex.getName());
214       if (expression != null) {
215         expression.setValue(getFacesContext().getELContext(), index);
216       } else {
217         setSelectedIndex(index);
218       }
219     }
220   }
221 
222   @Override
223   public void addTabChangeListener(final TabChangeListener listener) {
224     if (LOG.isWarnEnabled() && getSwitchType() == SwitchType.client) {
225       LOG.warn("Adding TabChangeListener to client side TabGroup!");
226     }
227     addFacesListener(listener);
228   }
229 
230   @Override
231   public void removeTabChangeListener(final TabChangeListener listener) {
232     removeFacesListener(listener);
233   }
234 
235   @Override
236   public TabChangeListener[] getTabChangeListeners() {
237     return (TabChangeListener[]) getFacesListeners(TabChangeListener.class);
238   }
239 
240   public abstract Integer getRenderedIndex();
241 
242   public abstract void setRenderedIndex(Integer index);
243 
244   public abstract Integer getSelectedIndex();
245 
246   public abstract void setSelectedIndex(Integer index);
247 
248   public abstract SwitchType getSwitchType();
249 
250   private AbstractUITab getTab(final int index) {
251     int i = 0;
252     for (final UIComponent component : getChildren()) {
253       if (component instanceof AbstractUITab) {
254         if (i == index) {
255           return (AbstractUITab) component;
256         }
257         i++;
258       } else {
259         LOG.error("Invalid component in UITabGroup: " + component);
260       }
261     }
262     LOG.error("Found no component with index: " + index + " childCount: " + getChildCount());
263     return null;
264   }
265 
266   private AbstractUITab getRenderedTab() {
267     return getTab(getRenderedIndex());
268   }
269 
270 
271   /**
272    * @since 1.5.0
273    */
274   @Override
275   public void addActionListener(final ActionListener listener) {
276     addFacesListener(listener);
277   }
278 
279   /**
280    * @since 1.5.0
281    */
282   @Override
283   public ActionListener[] getActionListeners() {
284     return (ActionListener[]) getFacesListeners(ActionListener.class);
285   }
286 
287   /**
288    * @since 1.5.0
289    */
290   @Override
291   public void removeActionListener(final ActionListener listener) {
292     removeFacesListener(listener);
293   }
294 
295   @Override
296   public String getDefaultEventName() {
297     return "reload";
298   }
299 
300   @Override
301   public Collection<String> getEventNames() {
302     return EVENT_NAMES;
303   }
304 }