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 }