/* | |
* 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.empire.jsf2.components; | |
import java.io.IOException; | |
import java.util.Iterator; | |
import java.util.List; | |
import javax.el.MethodExpression; | |
import javax.el.ValueExpression; | |
import javax.faces.component.NamingContainer; | |
import javax.faces.component.StateHolder; | |
import javax.faces.component.UIComponent; | |
import javax.faces.component.UINamingContainer; | |
import javax.faces.component.UIOutput; | |
import javax.faces.component.html.HtmlCommandLink; | |
import javax.faces.context.FacesContext; | |
import javax.faces.context.ResponseWriter; | |
import javax.faces.event.AbortProcessingException; | |
import javax.faces.event.ActionEvent; | |
import javax.faces.event.ActionListener; | |
import org.apache.empire.commons.ObjectUtils; | |
import org.apache.empire.commons.StringUtils; | |
import org.apache.empire.exceptions.UnexpectedReturnValueException; | |
import org.apache.empire.jsf2.app.FacesUtils; | |
import org.apache.empire.jsf2.controls.InputControl; | |
import org.apache.empire.jsf2.controls.InputControlManager; | |
import org.apache.empire.jsf2.utils.TagEncodingHelper; | |
import org.apache.empire.jsf2.utils.TagEncodingHelperFactory; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
public class TabViewTag extends UIOutput implements NamingContainer | |
{ | |
// Logger | |
// private static final Logger log = LoggerFactory.getLogger(MenuTag.class); | |
private static final Logger log = LoggerFactory.getLogger(TabViewTag.class); | |
protected final String TAB_STYLE_CLASS = "eTabView"; | |
protected final String TAB_ACTIVE_INDEX = "activeIndex"; | |
protected final String TABLINK_ID_PREFIX = "tabLink"; | |
protected final String TAB_RENDERED_ATTRIBUTE = "visible"; | |
protected final TagEncodingHelper helper = TagEncodingHelperFactory.create(this, this.TAB_STYLE_CLASS); | |
public static class TabPageActionListener implements ActionListener, StateHolder | |
{ | |
// private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; | |
// private static final Object[] EMPTY_PARAMS = new Object[0]; | |
private String clientId; | |
private boolean isTransient = false; | |
/** Creates a new instance of MethodExpressionActionListener */ | |
public TabPageActionListener() | |
{ | |
// constructor for state-saving | |
} | |
public TabPageActionListener(String clientId) | |
{ | |
// constructor for state-saving | |
this.clientId = clientId; | |
} | |
@Override | |
public void processAction(ActionEvent actionEvent) | |
throws AbortProcessingException | |
{ | |
// UIComponent findBase = ComponentUtils.findComponent(null, clientId, separatorChar); | |
FacesContext fc = FacesContext.getCurrentInstance(); | |
UIComponent tabView = FacesUtils.getWebApplication().findComponent(fc, this.clientId, null); | |
if (!(tabView instanceof TabViewTag)) | |
{ | |
throw new UnexpectedReturnValueException(tabView, "findComponent"); | |
} | |
// Invoke | |
TabViewTag tvt = (TabViewTag) tabView; | |
tvt.setActiveTab(actionEvent); | |
} | |
@Override | |
public void restoreState(FacesContext context, Object state) | |
{ | |
// clientId = (String) ((Object[]) state)[0]; | |
this.clientId = (String) state; | |
} | |
@Override | |
public Object saveState(FacesContext context) | |
{ | |
// return new Object[] { clientId }; | |
return this.clientId; | |
} | |
@Override | |
public void setTransient(boolean newTransientValue) | |
{ | |
this.isTransient = newTransientValue; | |
} | |
@Override | |
public boolean isTransient() | |
{ | |
return this.isTransient; | |
} | |
} | |
public TabViewTag() | |
{ | |
log.trace("TabViewTag created"); | |
} | |
@Override | |
public String getFamily() | |
{ | |
return UINamingContainer.COMPONENT_FAMILY; | |
} | |
@Override | |
public void encodeBegin(FacesContext context) | |
throws IOException | |
{ | |
// call base | |
super.encodeBegin(context); | |
// registerTabViewBean | |
// context.getExternalContext().getRequestMap().put("tabView", this); | |
// render components | |
ResponseWriter writer = context.getResponseWriter(); | |
writer.startElement(InputControl.HTML_TAG_DIV, this); | |
writer.writeAttribute(InputControl.HTML_ATTR_ID, getClientId(), null); | |
writer.writeAttribute(InputControl.HTML_ATTR_CLASS, this.helper.getTagStyleClass(), null); | |
this.helper.writeAttribute(writer, InputControl.HTML_ATTR_STYLE, this.helper.getTagAttributeString("style")); | |
// The Tabs | |
if (ObjectUtils.getBoolean(this.helper.getTagAttributeValue("hideTabBar"))) | |
{ // hide bar | |
encodeTabs(context, null); | |
} | |
else | |
{ // show bar | |
writer.startElement(InputControl.HTML_TAG_TABLE, this); | |
writer.writeAttribute(InputControl.HTML_ATTR_CLASS, "eTabBar", null); | |
writer.startElement(InputControl.HTML_TAG_TR, this); | |
encodeTabs(context, writer); | |
writer.startElement(InputControl.HTML_TAG_TD, this); | |
writer.writeAttribute(InputControl.HTML_ATTR_CLASS, "eTabBarEmpty", null); | |
writer.endElement(InputControl.HTML_TAG_TD); | |
writer.endElement(InputControl.HTML_TAG_TR); | |
writer.endElement(InputControl.HTML_TAG_TABLE); | |
} | |
// The Pages | |
writer.startElement(InputControl.HTML_TAG_TABLE, this); | |
writer.writeAttribute(InputControl.HTML_ATTR_CLASS, "eTabPanel", null); | |
String minHeight = this.helper.getTagAttributeString("minHeight"); | |
if (StringUtils.isNotEmpty(minHeight)) | |
{ | |
writer.writeAttribute(InputControl.HTML_ATTR_STYLE, "min-height:" + minHeight, null); | |
} | |
} | |
@Override | |
public boolean getRendersChildren() | |
{ | |
return super.getRendersChildren(); | |
} | |
@Override | |
public void encodeChildren(FacesContext context) | |
throws IOException | |
{ | |
super.encodeChildren(context); | |
} | |
@Override | |
public void encodeEnd(FacesContext context) | |
throws IOException | |
{ | |
// call base | |
super.encodeEnd(context); | |
// close | |
ResponseWriter writer = context.getResponseWriter(); | |
writer.endElement(InputControl.HTML_TAG_TABLE); | |
writer.endElement(InputControl.HTML_TAG_DIV); | |
} | |
@Override | |
public void decode(FacesContext context) | |
{ | |
for (UIComponent c : getChildren()) | |
{ | |
c.decode(context); | |
} | |
super.decode(context); | |
} | |
/* | |
@Override | |
public void processDecodes(FacesContext context) | |
{ | |
super.processDecodes(context); | |
} | |
*/ | |
protected void encodeTabs(FacesContext context, ResponseWriter writer) | |
throws IOException | |
{ | |
Iterator<UIComponent> ci = getFacetsAndChildren(); | |
if (ci.hasNext() == false) | |
{ | |
log.warn("Invalid TabPage definition!"); | |
return; | |
} | |
UIComponent panel = ci.next(); | |
int index = 0; | |
int activeIndex = getActivePageIndex(); | |
// Patch for MOJARRA: Remove HtmlCommandLinks | |
List<UIComponent> chk = panel.getChildren(); | |
for (int i = chk.size() - 1; i >= 0; i--) | |
{ | |
if ((chk.get(i) instanceof HtmlCommandLink)) | |
chk.remove(i); | |
} | |
// Create Page Links | |
for (UIComponent c : panel.getChildren()) | |
{ // Find Tab pages | |
if (!(c instanceof TabPageTag)) | |
{ | |
continue; | |
} | |
// found | |
boolean active = (index == activeIndex); | |
TabPageTag page = (TabPageTag) c; | |
// render tab-link? default is true | |
boolean rendered = ObjectUtils.getBoolean(ObjectUtils.coalesce(page.getAttributes().get(this.TAB_RENDERED_ATTRIBUTE), true)); | |
if (!rendered) | |
{ | |
// dont render content | |
page.setRendered(false); | |
continue; | |
} | |
if (writer!=null) | |
{ // encode Tab | |
boolean disabled = ObjectUtils.getBoolean(TagEncodingHelper.getTagAttributeValue(page, "disabled")); | |
writer.startElement(InputControl.HTML_TAG_TD, this); | |
// tab label | |
String styleClass = "eTabLabel"; | |
if (active) | |
{ | |
styleClass += " eTabActive"; | |
} | |
else if (disabled) | |
{ | |
styleClass += " eTabDisabled"; | |
} | |
writer.writeAttribute(InputControl.HTML_ATTR_CLASS, styleClass, null); | |
// encode Link | |
encodeTabLink(context, writer, index, page, (active || disabled)); | |
writer.endElement(InputControl.HTML_TAG_TD); | |
} | |
// set rendered | |
page.setRendered(active); | |
// next | |
index++; | |
} | |
} | |
protected void encodeTabLink(FacesContext context, ResponseWriter writer, int index, TabPageTag page, boolean disabled) | |
throws IOException | |
{ | |
// Add component | |
HtmlCommandLink link = null; | |
List<UIComponent> tabLinks = getChildren(); | |
if (tabLinks.size() > index) | |
{ | |
UIComponent c = tabLinks.get(index); | |
if (c instanceof HtmlCommandLink) | |
{ | |
link = (HtmlCommandLink) c; | |
} | |
else | |
{ // Something's wrong here? | |
log.error("INFO: Unexpected child node for {}! Child item type is {}.", getClass().getName(), c.getClass().getName()); | |
// encode anyway | |
c.setRendered(true); | |
c.encodeAll(context); | |
c.setRendered(false); // Don't render twice! | |
return; | |
} | |
} | |
if (link == null) | |
{ // create the tab-Link | |
String linkId = this.TABLINK_ID_PREFIX + String.valueOf(index); | |
link = createCommandLink(context, linkId); | |
tabLinks.add(index, link); | |
// Set TabPageActionListener | |
TabPageActionListener tpal = new TabPageActionListener(this.getClientId()); | |
link.addActionListener(tpal); | |
} | |
// init linkComponent | |
link.setValue(page.getTabLabel()); | |
link.setDisabled(disabled); | |
// Set Style | |
String styleClass = "eTabLink"; | |
link.setStyleClass(styleClass); | |
// encode link | |
link.setRendered(true); | |
link.encodeAll(context); | |
link.setRendered(false); // Don't render twice! | |
} | |
protected HtmlCommandLink createCommandLink(FacesContext context, String linkId) | |
{ | |
// CommandLink link | |
HtmlCommandLink link = InputControlManager.createComponent(context, HtmlCommandLink.class); | |
link.setId(linkId); | |
return link; | |
} | |
public int getActivePageIndex() | |
{ | |
Object active = this.helper.getTagAttributeValue(this.TAB_ACTIVE_INDEX); | |
return ObjectUtils.getInteger(active); | |
} | |
public void setActivePageIndex(int activeIndex) | |
{ | |
ValueExpression ve = this.getValueExpression(this.TAB_ACTIVE_INDEX); | |
if (ve != null) | |
{ // set active index | |
FacesContext fc = FacesUtils.getContext(); | |
ve.setValue(fc.getELContext(), activeIndex); | |
} | |
else | |
{ // save activeIndex | |
getAttributes().put(this.TAB_ACTIVE_INDEX, activeIndex); | |
} | |
} | |
public void setActiveTab(ActionEvent event) | |
{ | |
log.debug("setActiveTab"); | |
// done | |
UIComponent comp = event.getComponent(); | |
String tabNo = comp.getId().substring(this.TABLINK_ID_PREFIX.length()); | |
int pageIndex = ObjectUtils.getInteger(tabNo); | |
if (pageIndex == getActivePageIndex()) | |
{ // already set | |
log.warn("setActiveTab is called for active page!"); | |
return; | |
} | |
// set new Page | |
setActivePageIndex(pageIndex); | |
// TabChangeListener | |
Object tcl = getAttributes().get("tabChangedListener"); | |
if (tcl != null) | |
{ | |
if (!(tcl instanceof MethodExpression)) | |
{ | |
log.error("tabChangedListener is not a valid method expression!"); | |
return; | |
} | |
FacesContext fc = FacesUtils.getContext(); | |
MethodExpression methodExpression = (MethodExpression) tcl; | |
methodExpression.invoke(fc.getELContext(), new Object[] { pageIndex }); | |
} | |
} | |
} |