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 }