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