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 }