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.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_IMMEDIATE;
023    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_HEIGHT;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SHOW_NAVIGATION_BAR;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SELECTED_INDEX;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SWITCH_TYPE;
028    import org.apache.myfaces.tobago.ajax.api.AjaxComponent;
029    import org.apache.myfaces.tobago.ajax.api.AjaxUtils;
030    import org.apache.myfaces.tobago.event.TabChangeListener;
031    import org.apache.myfaces.tobago.event.TabChangeSource;
032    import org.apache.myfaces.tobago.event.TabChangeEvent;
033    
034    import javax.faces.component.UIComponent;
035    import javax.faces.context.FacesContext;
036    import javax.faces.el.EvaluationException;
037    import javax.faces.el.MethodBinding;
038    import javax.faces.el.ValueBinding;
039    import javax.faces.event.AbortProcessingException;
040    import javax.faces.event.FacesEvent;
041    import javax.faces.event.PhaseId;
042    import java.io.IOException;
043    import java.util.ArrayList;
044    import java.util.List;
045    import java.util.Collection;
046    
047    public class UITabGroup extends UIPanelBase implements TabChangeSource, AjaxComponent {
048    
049      private static final Log LOG = LogFactory.getLog(UITabGroup.class);
050    
051      public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.TabGroup";
052    
053      private Integer selectedIndex;
054      private int renderedIndex;
055      private String switchType;
056      private Boolean immediate;
057      private Boolean showNavigationBar;
058      private MethodBinding tabChangeListener = null;
059    
060      public static final String SWITCH_TYPE_CLIENT = "client";
061      public static final String SWITCH_TYPE_RELOAD_PAGE = "reloadPage";
062      public static final String SWITCH_TYPE_RELOAD_TAB = "reloadTab";
063    
064      @Override
065      public boolean getRendersChildren() {
066        return true;
067      }
068    
069      @Override
070      public void encodeBegin(FacesContext facesContext) throws IOException {
071        super.encodeBegin(facesContext);
072      }
073    
074      public void setImmediate(boolean immediate) {
075        this.immediate = immediate;
076      }
077    
078      public boolean isImmediate() {
079        if (immediate != null) {
080          return immediate;
081        }
082        ValueBinding vb = getValueBinding(ATTR_IMMEDIATE);
083        if (vb != null) {
084          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
085        } else {
086          return false;
087        }
088      }
089    
090      public boolean isShowNavigationBar() {
091        if (showNavigationBar != null) {
092          return showNavigationBar;
093        }
094        ValueBinding vb = getValueBinding(ATTR_SHOW_NAVIGATION_BAR);
095        if (vb != null) {
096          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
097        } else {
098          return true;
099        }
100      }
101    
102      public void setShowNavigationBar(boolean showNavigationBar) {
103        this.showNavigationBar = showNavigationBar;
104      }
105    
106    
107    
108      public void queueEvent(FacesEvent event) {
109        if (this == event.getSource()) {
110          if (isImmediate()) {
111            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
112          } else {
113            event.setPhaseId(PhaseId.INVOKE_APPLICATION);
114          }
115        }
116        super.queueEvent(event);
117      }
118    
119      @Override
120      public void encodeChildren(FacesContext context)
121          throws IOException {
122      }
123    
124      @Override
125      public void encodeEnd(FacesContext facesContext) throws IOException {
126        resetTabLayout();
127        super.encodeEnd(facesContext);
128        setRenderedIndex(getSelectedIndex());
129      }
130    
131      private void resetTabLayout() {
132        for (UIComponent component : (List<UIComponent>) getChildren()) {
133          component.getAttributes().remove(ATTR_LAYOUT_WIDTH);
134          component.getAttributes().remove(ATTR_LAYOUT_HEIGHT);
135        }
136      }
137    
138      public UIPanelBase[] getTabs() {
139        List<UIPanelBase> tabs = new ArrayList<UIPanelBase>();
140        for (Object o : getChildren()) {
141          UIComponent kid = (UIComponent) o;
142          if (kid instanceof UIPanelBase) {
143            //if (kid.isRendered()) {
144            tabs.add((UIPanelBase) kid);
145            //}
146          } else {
147            LOG.error("Invalid component in UITabGroup: " + kid);
148          }
149        }
150        return tabs.toArray(new UIPanelBase[tabs.size()]);
151      }
152    
153      public UIPanelBase getActiveTab() {
154        return getTab(getSelectedIndex());
155      }
156    
157    
158      @Override
159      public void processDecodes(FacesContext context) {
160        if (!isClientType()) {
161    
162          if (context == null) {
163            throw new NullPointerException("context");
164          }
165          if (!isRendered()) {
166            return;
167          }
168          UIPanelBase renderedTab = getRenderedTab();
169          renderedTab.processDecodes(context);
170          for (UIComponent facet : (Collection<UIComponent>) getFacets().values()) {
171            facet.processDecodes(context);
172          }
173          try {
174            decode(context);
175          } catch (RuntimeException e) {
176            context.renderResponse();
177            throw e;
178          }
179        } else {
180          super.processDecodes(context);
181        }
182      }
183    
184      @Override
185      public void processValidators(FacesContext context) {
186        if (!isClientType()) {
187          if (context == null) {
188            throw new NullPointerException("context");
189          }
190          if (!isRendered()) {
191            return;
192          }
193          UIPanelBase renderedTab = getRenderedTab();
194          renderedTab.processValidators(context);
195          for (UIComponent facet : (Collection<UIComponent>) getFacets().values()) {
196            facet.processValidators(context);
197          }
198        } else {
199          super.processValidators(context);
200        }
201      }
202    
203      @Override
204      public void processUpdates(FacesContext context) {
205        if (!isClientType()) {
206          if (context == null) {
207            throw new NullPointerException("context");
208          }
209          if (!isRendered()) {
210            return;
211          }
212          UIPanelBase renderedTab = getRenderedTab();
213          renderedTab.processUpdates(context);
214          for (UIComponent facet : (Collection<UIComponent>) getFacets().values()) {
215            facet.processUpdates(context);
216          }
217    
218        } else {
219          super.processUpdates(context);
220        }
221      }
222    
223      public void broadcast(FacesEvent facesEvent) throws AbortProcessingException {
224        super.broadcast(facesEvent);
225        if (facesEvent instanceof TabChangeEvent && facesEvent.getComponent() == this) {
226          Integer index = ((TabChangeEvent) facesEvent).getNewTabIndex();
227          ValueBinding vb = getValueBinding(ATTR_SELECTED_INDEX);
228          if (vb != null) {
229            vb.setValue(getFacesContext(), index);
230          } else {
231            setSelectedIndex(index);
232          }
233          MethodBinding tabChangeListenerBinding = getTabChangeListener();
234          if (tabChangeListenerBinding != null) {
235            try {
236              tabChangeListenerBinding.invoke(getFacesContext(), new Object[]{facesEvent});
237            } catch (EvaluationException e) {
238              Throwable cause = e.getCause();
239              if (cause != null && cause instanceof AbortProcessingException) {
240                throw (AbortProcessingException) cause;
241              } else {
242                throw e;
243              }
244            }
245          }
246          getFacesContext().renderResponse();
247        }
248      }
249    
250      public void setTabChangeListener(MethodBinding tabStateChangeListener) {
251        this.tabChangeListener = tabStateChangeListener;
252      }
253    
254      public MethodBinding getTabChangeListener() {
255        return tabChangeListener;
256      }
257    
258    
259      public void addTabChangeListener(TabChangeListener listener) {
260        if (LOG.isWarnEnabled() && isClientType()) {
261          LOG.warn("Adding TabChangeListener to Client side Tabgroup!");
262        }
263        addFacesListener(listener);
264      }
265    
266      private boolean isClientType() {
267        return (switchType == null || switchType.equals(SWITCH_TYPE_CLIENT));
268      }
269    
270      public void removeTabChangeListener(TabChangeListener listener) {
271        removeFacesListener(listener);
272      }
273    
274      public TabChangeListener[] getTabChangeListeners() {
275        return (TabChangeListener[]) getFacesListeners(TabChangeListener.class);
276      }
277    
278      public Object saveState(FacesContext context) {
279        Object[] state = new Object[7];
280        state[0] = super.saveState(context);
281        state[1] = renderedIndex;
282        state[2] = selectedIndex;
283        state[3] = saveAttachedState(context, tabChangeListener);
284        state[4] = switchType;
285        state[5] = immediate;
286        state[6] = showNavigationBar;
287        return state;
288      }
289    
290      public void restoreState(FacesContext context, Object state) {
291        Object[] values = (Object[]) state;
292        super.restoreState(context, values[0]);
293        renderedIndex = (Integer) values[1];
294        selectedIndex = (Integer) values[2];
295        tabChangeListener = (MethodBinding) restoreAttachedState(context, values[3]);
296        switchType = (String) values[4];
297        immediate = (Boolean) values[5];
298        showNavigationBar = (Boolean) values[6];
299      }
300    
301      public void encodeAjax(FacesContext facesContext) throws IOException {
302        setRenderedIndex(getSelectedIndex());
303        AjaxUtils.encodeAjaxComponent(facesContext, this);
304      }
305    
306      public int getSelectedIndex() {
307        if (selectedIndex != null) {
308          return selectedIndex;
309        }
310        ValueBinding vb = getValueBinding(ATTR_SELECTED_INDEX);
311        if (vb != null) {
312          Integer value = (Integer) vb.getValue(getFacesContext());
313          if (value != null) {
314            return value;
315          }
316        }
317        return 0;
318      }
319    
320      public void setSelectedIndex(int selectedIndex) {
321        this.selectedIndex = selectedIndex;
322      }
323    
324      private void setRenderedIndex(int index) {
325        renderedIndex = index;
326      }
327    
328      public int getRenderedIndex() {
329        return renderedIndex;
330      }
331    
332      public String getSwitchType() {
333        String value = null;
334        if (switchType != null) {
335          value = switchType;
336        } else {
337          ValueBinding vb = getValueBinding(ATTR_SWITCH_TYPE);
338          if (vb != null) {
339            value = (String) vb.getValue(FacesContext.getCurrentInstance());
340          }
341        }
342    
343        if (SWITCH_TYPE_CLIENT.equals(value)
344            || SWITCH_TYPE_RELOAD_PAGE.equals(value)
345            || SWITCH_TYPE_RELOAD_TAB.equals(value)) {
346          return value;
347        } else if (value == null) {
348          // return default
349          return SWITCH_TYPE_CLIENT;
350        } else {
351          LOG.warn("Illegal value for attribute switchtype : " + switchType
352              + " Using default value " + SWITCH_TYPE_CLIENT);
353          return SWITCH_TYPE_CLIENT;
354        }
355      }
356    
357      public void setSwitchType(String switchType) {
358        this.switchType = switchType;
359      }
360    
361      private UIPanelBase getTab(int index) {
362        int i = 0;
363        for (UIComponent component : (List<UIComponent>) getChildren()) {
364          if (component instanceof UIPanelBase) {
365            if (i == index) {
366              return (UIPanelBase) component;
367            }
368            i++;
369          } else {
370            LOG.error("Invalid component in UITabGroup: " + component);
371          }
372        }
373        LOG.error("Found no component with index: " + index + " childCount: " + getChildCount());
374        return null;
375      }
376    
377      private UIPanelBase getRenderedTab() {
378        return getTab(getRenderedIndex());
379      }
380    }