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