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