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