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 }