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