/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.tobago.internal.component;
import org.apache.myfaces.tobago.compat.FacesUtilsEL;
import org.apache.myfaces.tobago.component.Attributes;
import org.apache.myfaces.tobago.component.ComponentTypes;
import org.apache.myfaces.tobago.component.Facets;
import org.apache.myfaces.tobago.component.OnComponentPopulated;
import org.apache.myfaces.tobago.component.RendererTypes;
import org.apache.myfaces.tobago.component.SupportsRenderedPartially;
import org.apache.myfaces.tobago.component.UITab;
import org.apache.myfaces.tobago.event.TabChangeEvent;
import org.apache.myfaces.tobago.event.TabChangeListener;
import org.apache.myfaces.tobago.event.TabChangeSource2;
import org.apache.myfaces.tobago.internal.layout.LayoutUtils;
import org.apache.myfaces.tobago.layout.LayoutComponent;
import org.apache.myfaces.tobago.layout.LayoutContainer;
import org.apache.myfaces.tobago.layout.LayoutManager;
import org.apache.myfaces.tobago.util.CreateComponentUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.ActionSource2;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionListener;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractUITabGroup extends AbstractUIPanelBase
implements TabChangeSource2, ActionSource2, LayoutContainer, LayoutComponent, OnComponentPopulated,
SupportsRenderedPartially {
private static final Logger LOG = LoggerFactory.getLogger(AbstractUITabGroup.class);
public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.TabGroup";
public static final String SWITCH_TYPE_CLIENT = "client";
public static final String SWITCH_TYPE_RELOAD_PAGE = "reloadPage";
public static final String SWITCH_TYPE_RELOAD_TAB = "reloadTab";
@Override
public void encodeBegin(final FacesContext facesContext) throws IOException {
super.encodeBegin(facesContext);
((AbstractUILayoutBase) getLayoutManager()).encodeBegin(facesContext);
}
@Override
public void encodeChildren(final FacesContext facesContext) throws IOException {
// ((AbstractUILayoutBase) getLayoutManager()).encodeChildren(facesContext);
}
@Override
public void encodeEnd(final FacesContext facesContext) throws IOException {
((AbstractUILayoutBase) getLayoutManager()).encodeEnd(facesContext);
resetTabLayout();
super.encodeEnd(facesContext);
setRenderedIndex(getSelectedIndex());
}
@Override
public boolean getRendersChildren() {
return true;
}
public void queueEvent(final FacesEvent event) {
if (this == event.getSource()) {
if (isImmediate() || isSwitchTypeClient()) {
// if switch type client event is always immediate
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
super.queueEvent(event);
}
private void resetTabLayout() {
for (final UIComponent component : getChildren()) {
component.getAttributes().remove(Attributes.LAYOUT_WIDTH);
component.getAttributes().remove(Attributes.LAYOUT_HEIGHT);
}
}
public AbstractUIPanelBase[] getTabs() {
final List<AbstractUIPanelBase> tabs = new ArrayList<AbstractUIPanelBase>();
for (final UIComponent kid : getChildren()) {
if (kid instanceof AbstractUIPanelBase) {
//if (kid.isRendered()) {
tabs.add((AbstractUIPanelBase) kid);
//}
} else {
LOG.error("Invalid component in UITabGroup: " + kid);
}
}
return tabs.toArray(new AbstractUIPanelBase[tabs.size()]);
}
public AbstractUIPanelBase getActiveTab() {
return getTab(getSelectedIndex());
}
@Override
public void processDecodes(final FacesContext context) {
if (!isSwitchTypeClient()) {
if (context == null) {
throw new NullPointerException("context");
}
if (!isRendered()) {
return;
}
int index = 0;
for (final UIComponent child : getChildren()) {
if (child instanceof UITab) {
final UITab tab = (UITab) child;
if (tab.isRendered()) {
if (getRenderedIndex() == index) {
tab.processDecodes(context);
} else {
UIComponent facet = tab.getFacet(Facets.TOOL_BAR);
if (facet != null) {
facet.processDecodes(context);
}
}
}
index++;
}
}
// final AbstractUIPanelBase renderedTab = getRenderedTab();
// renderedTab.processDecodes(context);
for (final UIComponent facet : getFacets().values()) {
facet.processDecodes(context);
}
try {
decode(context);
} catch (final RuntimeException e) {
context.renderResponse();
throw e;
}
} else {
super.processDecodes(context);
}
}
@Override
public void processValidators(final FacesContext context) {
if (!isSwitchTypeClient()) {
if (context == null) {
throw new NullPointerException("context");
}
if (!isRendered()) {
return;
}
final AbstractUIPanelBase renderedTab = getRenderedTab();
renderedTab.processValidators(context);
for (final UIComponent facet : getFacets().values()) {
facet.processValidators(context);
}
} else {
super.processValidators(context);
}
}
@Override
public void processUpdates(final FacesContext context) {
if (!isSwitchTypeClient()) {
if (context == null) {
throw new NullPointerException("context");
}
if (!isRendered()) {
return;
}
final AbstractUIPanelBase renderedTab = getRenderedTab();
renderedTab.processUpdates(context);
for (final UIComponent facet : getFacets().values()) {
facet.processUpdates(context);
}
} else {
super.processUpdates(context);
}
}
public void broadcast(final FacesEvent facesEvent) throws AbortProcessingException {
super.broadcast(facesEvent);
if (facesEvent instanceof TabChangeEvent && facesEvent.getComponent() == this) {
final TabChangeEvent event = (TabChangeEvent) facesEvent;
final MethodExpression methodExpression = getTabChangeListenerExpression();
if (methodExpression != null) {
FacesUtilsEL.invokeMethodExpression(FacesContext.getCurrentInstance(), methodExpression, facesEvent);
}
// switched off, because this is already called in super.broadcast()
// final ActionListener[] actionListeners = getActionListeners();
// for (ActionListener listener : actionListeners) {
// listener.processAction(event);
// }
// XXX is this needed?
if (!isSwitchTypeClient()) {
final ActionListener defaultActionListener = getFacesContext().getApplication().getActionListener();
if (defaultActionListener != null) {
defaultActionListener.processAction(event);
}
}
final Integer index = event.getNewTabIndex();
final ValueExpression expression = getValueExpression(Attributes.SELECTED_INDEX);
if (expression != null) {
expression.setValue(getFacesContext().getELContext(), index);
} else {
setSelectedIndex(index);
}
}
}
public void addTabChangeListener(final TabChangeListener listener) {
if (LOG.isWarnEnabled() && isSwitchTypeClient()) {
LOG.warn("Adding TabChangeListener to client side TabGroup!");
}
addFacesListener(listener);
}
public boolean isSwitchTypeClient() {
final String switchType = getSwitchType();
return (switchType == null || switchType.equals(SWITCH_TYPE_CLIENT));
}
public void removeTabChangeListener(final TabChangeListener listener) {
removeFacesListener(listener);
}
public TabChangeListener[] getTabChangeListeners() {
return (TabChangeListener[]) getFacesListeners(TabChangeListener.class);
}
public abstract Integer getRenderedIndex();
public abstract void setRenderedIndex(Integer index);
public abstract Integer getSelectedIndex();
public abstract void setSelectedIndex(Integer index);
public abstract String getSwitchType();
private AbstractUIPanelBase getTab(final int index) {
int i = 0;
for (final UIComponent component : getChildren()) {
if (component instanceof AbstractUIPanelBase) {
if (i == index) {
return (AbstractUIPanelBase) component;
}
i++;
} else {
LOG.error("Invalid component in UITabGroup: " + component);
}
}
LOG.error("Found no component with index: " + index + " childCount: " + getChildCount());
return null;
}
private AbstractUIPanelBase getRenderedTab() {
return getTab(getRenderedIndex());
}
/**
* @since 1.5.0
*/
public void addActionListener(final ActionListener listener) {
addFacesListener(listener);
}
/**
* @since 1.5.0
*/
public ActionListener[] getActionListeners() {
return (ActionListener[]) getFacesListeners(ActionListener.class);
}
/**
* @since 1.5.0
*/
public void removeActionListener(final ActionListener listener) {
removeFacesListener(listener);
}
public List<LayoutComponent> getComponents() {
return LayoutUtils.findLayoutChildren(this);
}
public void onComponentPopulated(final FacesContext facesContext, final UIComponent parent) {
if (getLayoutManager() == null) {
setLayoutManager(CreateComponentUtils.createAndInitLayout(
facesContext, ComponentTypes.TAB_GROUP_LAYOUT, RendererTypes.TAB_GROUP_LAYOUT, parent));
}
}
public LayoutManager getLayoutManager() {
return (LayoutManager) getFacet(Facets.LAYOUT);
}
public void setLayoutManager(final LayoutManager layoutManager) {
getFacets().put(Facets.LAYOUT, (AbstractUILayoutBase) layoutManager);
}
public boolean isLayoutChildren() {
return isRendered();
}
}