http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/MenuListTag.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/MenuListTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/MenuListTag.java index 4078ee1..a7258db 100644 --- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/MenuListTag.java +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/MenuListTag.java @@ -35,13 +35,16 @@ public class MenuListTag extends UIOutput implements NamingContainer // Logger // private static final Logger log = LoggerFactory.getLogger(MenuTag.class); - protected final TagEncodingHelper helper = new TagEncodingHelper(this, "eMenuList"); + private final TagEncodingHelper helper = new TagEncodingHelper(this, "eMenuList"); private String currentId = null; private String currentClass = null; + // private String prevMenuId = null; // private String enabledClass = null; + private String parentClass = null; private String disabledClass = null; private String expandedClass = null; + private String itemWrapTag = null; private String defaultItemClass = null; // e.g. "level{}" private int level = 0; @@ -58,7 +61,7 @@ public class MenuListTag extends UIOutput implements NamingContainer // call base super.encodeBegin(context); - initMenuAttributes(); + initMenuAttributes(context); // render components ResponseWriter writer = context.getResponseWriter(); @@ -66,16 +69,19 @@ public class MenuListTag extends UIOutput implements NamingContainer // writeAttribute(writer, map, "id"); helper.writeAttribute(writer, "class", helper.getTagAttributeString("styleClass")); helper.writeAttribute(writer, "style", helper.getTagAttributeString("style")); + // previousId + /* + if (prevMenuId!=null) + helper.writeAttribute(writer, "previousId", prevMenuId); + */ } - /* @Override public boolean getRendersChildren() { boolean test = super.getRendersChildren(); return test; } - */ @Override public void encodeChildren(FacesContext context) @@ -95,29 +101,48 @@ public class MenuListTag extends UIOutput implements NamingContainer writer.endElement("ul"); } - private void initMenuAttributes() + private void initMenuAttributes(FacesContext context) { - currentId = helper.getTagAttributeString("currentId"); - currentClass = helper.getTagAttributeString("currentClass"); - // enabledClass = StringUtils.toString(map.get("enabledClass")); - disabledClass = helper.getTagAttributeString("disabledClass"); - expandedClass = helper.getTagAttributeString("expandedClass"); + currentId = helper.getTagAttributeString("currentId"); + currentClass = helper.getTagAttributeString("currentClass"); + // enabledClass = StringUtils.toString(map.get("enabledClass")); + disabledClass = helper.getTagAttributeString("disabledClass"); + parentClass = helper.getTagAttributeString("parentClass"); + expandedClass = helper.getTagAttributeString("expandedClass"); + itemWrapTag = helper.getTagAttributeString("itemWrapTag"); defaultItemClass = helper.getTagAttributeString("defaultItemClass"); - + + // remember previousMenu (may be used by JavaScript) + /* + if (currentId!=null) + { // StoreID on Session and set lastId + Map<String,Object> sessionMap = context.getExternalContext().getSessionMap(); + String attrName = this.getClientId()+":prevMenuId"; + prevMenuId = StringUtils.toString(sessionMap.get(attrName)); + if (StringUtils.compareEqual(prevMenuId, currentId, false)==false) + sessionMap.put(attrName, currentId); + } + */ + + // find parent MenuListTag parent = getParentMenu(); if (parent==null) return; if (currentId==null) - currentId = parent.getCurrentId(); + currentId = parent.getCurrentId(); if (currentClass==null) currentClass = parent.getCurrentClass(); // if (enabledClass==null) // enabledClass = parent.getEnabledClass(); if (disabledClass==null) disabledClass = parent.getDisabledClass(); + if (parentClass==null) + parentClass = parent.getParentClass(); if (expandedClass==null) expandedClass = parent.getExpandedClass(); + if (itemWrapTag==null) + itemWrapTag = parent.itemWrapTag; if (defaultItemClass==null) defaultItemClass = parent.defaultItemClass; @@ -149,6 +174,13 @@ public class MenuListTag extends UIOutput implements NamingContainer { return currentClass; } + + /* + public String getPreviousMenuId() + { + return prevMenuId; + } + */ /* public String getEnabledClass() @@ -162,10 +194,20 @@ public class MenuListTag extends UIOutput implements NamingContainer return disabledClass; } + public String getParentClass() + { + return parentClass; + } + public String getExpandedClass() { return expandedClass; } + + public String getItemWrapTag() + { + return itemWrapTag; + } public int getLevel() { @@ -204,9 +246,34 @@ public class MenuListTag extends UIOutput implements NamingContainer this.disabledClass = disabledClass; } + public void setParentClass(String parentClass) + { + this.parentClass = parentClass; + } + public void setExpandedClass(String expandedClass) { this.expandedClass = expandedClass; } + public void setItemWrapTag(String itemWrapTag) + { + this.itemWrapTag = itemWrapTag; + } + + /* + protected void writeAttribute(ResponseWriter writer, Map<String, Object> map, String attribute, String targetName) + throws IOException + { + Object value = map.get(attribute); + if (value != null) + writer.writeAttribute(targetName, value, null); + } + protected void writeAttribute(ResponseWriter writer, Map<String, Object> map, String attribute) + throws IOException + { + writeAttribute(writer, map, attribute, attribute); + } + */ + }
http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java index 41c47cf..635234b 100644 --- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/SelectTag.java @@ -19,12 +19,17 @@ package org.apache.empire.jsf2.components; import java.io.IOException; +import java.util.List; import java.util.Map; import javax.faces.component.NamingContainer; +import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.html.HtmlSelectOneMenu; +import javax.faces.component.visit.VisitCallback; +import javax.faces.component.visit.VisitContext; import javax.faces.context.FacesContext; +import javax.faces.view.AttachedObjectHandler; import org.apache.empire.commons.ObjectUtils; import org.apache.empire.commons.Options; @@ -42,6 +47,8 @@ public class SelectTag extends UIInput implements NamingContainer // Logger private static final Logger log = LoggerFactory.getLogger(SelectTag.class); + private SelectInputControl control = null; + public SelectTag() { log.trace("component select created"); @@ -52,6 +59,32 @@ public class SelectTag extends UIInput implements NamingContainer { return "javax.faces.NamingContainer"; } + + /** + * remember original clientId + * necessary only inside UIData + */ + private String treeClientId = null; + + @Override + public boolean visitTree(VisitContext visitContext, VisitCallback callback) + { + FacesContext context = visitContext.getFacesContext(); + treeClientId = getClientId(context); + return super.visitTree(visitContext, callback); + } + + @Override + public String getClientId(FacesContext context) + { + // Check if dynamic components are being created + if (this.treeClientId!=null && control!=null && control.isCreatingComponents()) + { // return the original tree client id + return treeClientId; + } + // default behavior + return super.getClientId(context); + } @Override public void encodeBegin(FacesContext context) @@ -65,18 +98,18 @@ public class SelectTag extends UIInput implements NamingContainer inputComponent = getInputComponent(); if (inputComponent instanceof HtmlSelectOneMenu) { - SelectInputControl control = (SelectInputControl)InputControlManager.getControl(SelectInputControl.NAME); + this.control = (SelectInputControl) InputControlManager.getControl(SelectInputControl.NAME); // disabled boolean disabled = isDisabled(); - ((HtmlSelectOneMenu)inputComponent).setDisabled(disabled); + ((HtmlSelectOneMenu) inputComponent).setDisabled(disabled); // Options (sync) Options options = getOptionList(); boolean hasEmpty = isAllowNull() && !options.contains(""); - control.syncOptions((HtmlSelectOneMenu)inputComponent, textResolver, options, hasEmpty, getNullText()); - setInputValue((HtmlSelectOneMenu)inputComponent); + control.syncOptions((HtmlSelectOneMenu) inputComponent, textResolver, options, hasEmpty, getNullText(), false); + setInputValue((HtmlSelectOneMenu) inputComponent); } else - { // Something's wrong here? + { // Something's wrong here? log.warn("WARN: Unexpected child node for {}! Child item type is {}.", getClass().getName(), inputComponent.getClass().getName()); inputComponent = null; } @@ -85,6 +118,8 @@ public class SelectTag extends UIInput implements NamingContainer { inputComponent = createSelectOneMenu(textResolver); this.getChildren().add(0, inputComponent); + // attach events + attachEvents(context, inputComponent); } // render components inputComponent.encodeAll(context); @@ -93,14 +128,24 @@ public class SelectTag extends UIInput implements NamingContainer } @Override + public void decode(FacesContext context) + { + for (UIComponent c : getChildren()) + { + c.decode(context); + } + super.decode(context); + } + + @Override public void updateModel(FacesContext context) { // check read only if (!isDisabled()) { UIInput inputComponent = getInputComponent(); - - Object value = inputComponent == null ? "" : inputComponent.getValue(); + + Object value = (inputComponent==null ? "" : inputComponent.getValue()); if (value == null) value = ""; setValue(value); @@ -133,7 +178,7 @@ public class SelectTag extends UIInput implements NamingContainer { return null; } - + return (UIInput) getChildren().get(0); } @@ -141,7 +186,9 @@ public class SelectTag extends UIInput implements NamingContainer { Object options = getAttributes().get("options"); if (!(options instanceof Options)) + { return new Options(); + } return ((Options) options); } @@ -165,11 +212,11 @@ public class SelectTag extends UIInput implements NamingContainer private UIInput createSelectOneMenu(TextResolver textResolver) { - SelectInputControl control = (SelectInputControl)InputControlManager.getControl(SelectInputControl.NAME); + this.control = (SelectInputControl) InputControlManager.getControl(SelectInputControl.NAME); HtmlSelectOneMenu input = control.createMenuComponent(this); // css style String userStyle = StringUtils.toString(getAttributes().get("styleClass")); - String cssStyle = TagEncodingHelper.getTagStyleClass("eSelect", null, null, userStyle); + String cssStyle = TagEncodingHelper.getTagStyleClass("eSelect", null, null, userStyle); input.setStyleClass(cssStyle); // other attributes copyAttributes(input); @@ -187,14 +234,14 @@ public class SelectTag extends UIInput implements NamingContainer setInputValue(input); return input; } - + private void setInputValue(HtmlSelectOneMenu input) { Object value = getValue(); - if (value!=null) + if (value != null) { if (value.getClass().isEnum()) - value = ((Enum<?>)value).name(); + value = ((Enum<?>) value).name(); else value = String.valueOf(value); } @@ -203,14 +250,49 @@ public class SelectTag extends UIInput implements NamingContainer private void copyAttributes(HtmlSelectOneMenu input) { + // set id + String inputId = this.getId(); + if (StringUtils.isNotEmpty(inputId)) + { // remove trailing underscore (workaround since parent and child may not have the same name) + if (inputId.endsWith("_")) + { + inputId = inputId.substring(0, inputId.length() - 1); + } + input.setId(inputId); + } + Map<String, Object> attr = getAttributes(); Object value; if ((value = attr.get("style")) != null) + { input.setStyle(StringUtils.toString(value)); + } if ((value = attr.get("tabindex")) != null) + { input.setTabindex(StringUtils.toString(value)); + } if ((value = attr.get("onchange")) != null) + { input.setOnchange(StringUtils.toString(value)); + } + } + + protected void attachEvents(FacesContext context, UIInput inputComponent) + { + // Events available? + @SuppressWarnings("unchecked") + List<AttachedObjectHandler> result = (List<AttachedObjectHandler>) getAttributes().get("javax.faces.RetargetableHandlers"); + if (result == null) + { + return; + } + // Attach Events + for (AttachedObjectHandler aoh : result) + { + aoh.applyAttachedObject(context, inputComponent); + } + // remove + result.clear(); + getAttributes().remove("javax.faces.RetargetableHandlers"); } - } http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabPageTag.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabPageTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabPageTag.java new file mode 100644 index 0000000..5d847f0 --- /dev/null +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabPageTag.java @@ -0,0 +1,92 @@ +/* + * 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 javax.faces.component.NamingContainer; +import javax.faces.component.UINamingContainer; +import javax.faces.component.UIOutput; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; + +import org.apache.empire.jsf2.controls.InputControl; +import org.apache.empire.jsf2.utils.TagEncodingHelper; + +public class TabPageTag extends UIOutput implements NamingContainer +{ + // Logger + // private static final Logger log = LoggerFactory.getLogger(MenuTag.class); + + private final TagEncodingHelper helper = new TagEncodingHelper(this, "eTabPage"); + + @Override + public String getFamily() + { + return UINamingContainer.COMPONENT_FAMILY; + } + + @Override + public void encodeBegin(FacesContext context) + throws IOException + { + // call base + super.encodeBegin(context); + + // render components + ResponseWriter writer = context.getResponseWriter(); + writer.startElement(InputControl.HTML_TAG_TR, this); + writer.writeAttribute(InputControl.HTML_ATTR_ID, getClientId(), null); + helper.writeAttribute(writer, InputControl.HTML_ATTR_CLASS, helper.getTagAttributeString("styleClass")); + helper.writeAttribute(writer, InputControl.HTML_ATTR_STYLE, helper.getTagAttributeString("style")); + // TabPage + writer.startElement(InputControl.HTML_TAG_TD, this); + writer.writeAttribute(InputControl.HTML_ATTR_CLASS, "eTabPage", 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_TD); + writer.endElement(InputControl.HTML_TAG_TR); + } + + public String getTabLabel() + { + return helper.getTagAttributeString("title"); + } +} http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabViewTag.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabViewTag.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabViewTag.java new file mode 100644 index 0000000..23046c1 --- /dev/null +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/components/TabViewTag.java @@ -0,0 +1,387 @@ +/* + * 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.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); + + private final String TAB_STYLE_CLASS = "eTabView"; + + private final String TAB_ACTIVE_INDEX = "activeIndex"; + + private final String TABLINK_ID_PREFIX = "tabLink"; + + private final String TAB_RENDERED_ATTRIBUTE = "visible"; + + private final TagEncodingHelper helper = new TagEncodingHelper(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 + 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); + } + */ + + private 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; + } + + 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++; + } + } + + private 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! + } + + private 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 }); + } + + } +} http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java index fba03fd..9ab268b 100644 --- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/CheckboxInputControl.java @@ -27,14 +27,13 @@ import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import org.apache.empire.commons.ObjectUtils; -import org.apache.empire.exceptions.InternalException; import org.apache.empire.exceptions.UnexpectedReturnValueException; public class CheckboxInputControl extends InputControl { public static final String NAME = "checkbox"; - private Class<? extends javax.faces.component.html.HtmlSelectBooleanCheckbox> inputComponentClass; + private final Class<? extends javax.faces.component.html.HtmlSelectBooleanCheckbox> inputComponentClass; public CheckboxInputControl(Class<? extends HtmlSelectBooleanCheckbox> inputComponentClass) { @@ -47,57 +46,69 @@ public class CheckboxInputControl extends InputControl this(javax.faces.component.html.HtmlSelectBooleanCheckbox.class); } - @Override - public void renderValue(ValueInfo vi, ResponseWriter writer) - throws IOException - { - boolean value = (Boolean) vi.getValue(true); - writer.startElement("div", null); - writer.writeAttribute("class", value ? "eTypeBoolTrue" : "eTypeBoolFalse", null); - writer.append(" "); - writer.endElement("div"); - } - + @Override + public void renderValue(ValueInfo vi, ResponseWriter writer) + throws IOException + { + boolean value = (Boolean) vi.getValue(true); + writer.startElement(HTML_TAG_DIV, null); + writer.writeAttribute(HTML_ATTR_CLASS, value ? "eTypeBoolTrue" : "eTypeBoolFalse", null); + writer.append(HTML_EXPR_NBSP); + writer.endElement(HTML_TAG_DIV); + } + @Override protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList) { HtmlSelectBooleanCheckbox input; - if (compList.size()==0) - { try { - input = inputComponentClass.newInstance(); - } catch (InstantiationException e1) { - throw new InternalException(e1); - } catch (IllegalAccessException e2) { - throw new InternalException(e2); - } + if (compList.size() == 0) + { // create component + input = InputControlManager.createComponent(context, this.inputComponentClass); + // copy attributes copyAttributes(parent, ii, input); // add compList.add(input); } else - { // check type + { // check type UIComponent comp = compList.get(0); if (!(comp instanceof HtmlSelectBooleanCheckbox)) throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get"); // cast - input = (HtmlSelectBooleanCheckbox)comp; + input = (HtmlSelectBooleanCheckbox) comp; } // disabled - boolean disabled = ii.isDisabled(); + boolean disabled = ii.isDisabled(); input.setDisabled(disabled); // style addRemoveDisabledStyle(input, input.isDisabled()); - + // Set Value setInputValue(input, ii); } - + + @Override + protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context) + { + UIComponent comp = compList.get(0); + if (!(comp instanceof HtmlSelectBooleanCheckbox)) + { + throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get(0)"); + } + HtmlSelectBooleanCheckbox input = (HtmlSelectBooleanCheckbox) comp; + // disabled + boolean disabled = ii.isDisabled(); + input.setDisabled(disabled); + // style + addRemoveDisabledStyle(input, input.isDisabled()); + } + @Override protected Object parseInputValue(String value, InputInfo ii) { return ObjectUtils.getBoolean(value); } - + } http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java index de9de79..190777e 100644 --- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControl.java @@ -42,38 +42,60 @@ import org.slf4j.LoggerFactory; public abstract class InputControl { - private static final Logger log = LoggerFactory.getLogger(InputControl.class); - - // Special Input Column Attributes - public static final String NUMBER_TYPE_ATTRIBUTE = "numberType"; // "Integer", "Currency", "Percent" - public static final String NUMBER_GROUPSEP_ATTRIBUTE = "numberGroupSeparator"; // boolean - public static final String NUMBER_FRACTION_DIGITS = "numberFractionDigits"; // integer - public static final String MINVALUE_ATTRIBUTE = "minValue"; - public static final String MAXVALUE_ATTRIBUTE = "maxValue"; - public static final String CURRENCY_CODE_ATTRIBUTE = "currencyCode"; // "ISO 4217 code of the currency" + + private static final Logger log = LoggerFactory.getLogger(InputControl.class); // format attributes - public static final String FORMAT_NULL = "null:"; - public static final String FORMAT_NULL_ATTRIBUTE = "format:null"; + public static final String FORMAT_NULL = "null:"; + public static final String FORMAT_NULL_ATTRIBUTE = "format:null"; + + // HTML-TAGS + public static final String HTML_TAG_DIV = "div"; + public static final String HTML_TAG_SPAN = "span"; + public static final String HTML_TAG_TABLE = "table"; + public static final String HTML_TAG_TR = "tr"; + public static final String HTML_TAG_TD = "td"; + public static final String HTML_TAG_INPUT = "input"; + public static final String HTML_TAG_LABEL = "label"; + + // HTML-ATTRIBUTES + public static final String HTML_ATTR_ID = "id"; + public static final String HTML_ATTR_CLASS = "class"; + public static final String HTML_ATTR_STYLE = "style"; + public static final String HTML_ATTR_TYPE = "type"; + public static final String HTML_ATTR_DISABLED = "disabled"; + public static final String HTML_ATTR_CHECKED = "checked"; + // HTML + public static String HTML_EXPR_NBSP = " "; + public InputControl() { - log.info("InputControl of class {} created.", getClass().getName()); + InputControl.log.info("InputControl of class {} created.", getClass().getName()); } - + /** * This interface allows access to a value and its metainformation * used with the renderData function - */ + */ public interface ValueInfo { Column getColumn(); + Options getOptions(); + Object getValue(boolean evalExpression); - String getFormat(); // Custom Formatting options specific to each InputControl-type + + String getFormat(); // Custom Formatting options specific to each InputControl-type + Locale getLocale(); + String getText(String key); + TextResolver getTextResolver(); + + String getStyleClass(String addlStyle); + /* Object getNullValue(); String getOnclick(); @@ -82,24 +104,32 @@ public abstract class InputControl String getCssStyle(); String getId(); */ + + boolean isInsideUIData(); } /** * This interface extends the value information by information about the input control * used with the renderInput function - */ + */ public interface InputInfo extends ValueInfo { // perform action void setValue(Object value); + void validate(Object value); + boolean isRequired(); + boolean isDisabled(); // disabled or readOnly + boolean isFieldReadOnly(); // not disabled only readOnly (for input[type=text] only!) // input + String getInputId(); - String getStyleClass(String addlStyle); + boolean hasError(); + /* String getName(); String getTabindex(); @@ -109,74 +139,105 @@ public abstract class InputControl String getOnfocus(); String getOnblur(); */ - Object getAttribute(String name); /* gets tag attribute only */ + Object getAttribute(String name); /* gets tag attribute only */ + Object getAttributeEx(String name); /* check Column attributes too, and resolves references to other columns. */ } - + private String name; - + protected InputControl(String name) { this.name = name; } - + public final String getName() { - return name; + return this.name; } - + public String getLabelForId(InputInfo ii) { return ii.getInputId(); } - + + /** + * Flag indicating whether child components are being created + */ + private boolean creatingComponents = false; + public boolean isCreatingComponents() + { + return this.creatingComponents; + } + /* Value */ public void renderValue(ValueInfo vi, ResponseWriter writer) throws IOException { String text = formatValue(vi); - writer.append((StringUtils.isEmpty(text) ? " " : text)); + writer.append((StringUtils.isEmpty(text) ? HTML_EXPR_NBSP : text)); } - /* Input */ + /* renderInput */ public void renderInput(UIComponent comp, InputInfo ii, FacesContext context, boolean encode) throws IOException { - // createInputComponents - createInputComponents(comp, ii, context, comp.getChildren()); + boolean resetChildId = comp.getChildren().isEmpty(); + // createInputComponents + try { + this.creatingComponents = true; + createInputComponents(comp, ii, context, comp.getChildren()); + } finally { + this.creatingComponents = false; + } + // Encode all if (!encode) return; for (UIComponent child : comp.getChildren()) - { + { // reset child-id + // necessary only inside UIData + if (resetChildId && child.getId()!=null) + child.setId(child.getId()); + // encode now child.encodeAll(context); } } + public void updateInputState(UIComponent parent, InputInfo ii, FacesContext context) + { + List<UIComponent> cl = parent.getChildren(); + if (cl.isEmpty()) + return; + updateInputState(cl, ii, context); + } + public void postUpdateModel(UIComponent comp, InputInfo ii, FacesContext fc) { UIInput input = getInputComponent(comp); - if (input==null) + if (input == null) return; /* May want to override this */ // Clear submitted value clearSubmittedValue(input); } - + public Object getInputValue(UIComponent comp, InputInfo ii, boolean submitted) { UIInput input = getInputComponent(comp); - if (input==null) + if (input == null) { // throw new ObjectNotValidException(this); return null; // ignore - } + } // Get value from Input Object value; if (submitted) { // get submitted value value = input.getSubmittedValue(); - if (value == null && input.isLocalValueSet()) // MyFaces Patch! + if (value == null && input.isLocalValueSet()) // required for MyFaces! { // take local value + if (log.isDebugEnabled()) + log.debug("No submitted value but local value available for InputComponent {}. Local value is '{}'", input.getClientId(), input.getLocalValue()); value = input.getLocalValue(); if (value == null) { // Empty-String @@ -222,43 +283,43 @@ public abstract class InputControl protected void setInputValue(UIInput input, InputInfo ii) { - // Restore submitted value FacesContext fc = FacesContext.getCurrentInstance(); Map<String, Object> reqMap = fc.getExternalContext().getRequestMap(); String clientId = input.getClientId(); if (reqMap.containsKey(clientId)) - { // Set the local value from the request map + { // Set the local value from the request map Object value = reqMap.get(clientId); - if (input.isLocalValueSet()==false) // Patch for MyFaces? - input.setSubmittedValue(value); // Always for Mojarra! + if (input.isLocalValueSet() == false) + input.setSubmittedValue(value); return; } - else if (input.getSubmittedValue()!=null) // && FacesUtils.isClearSubmittedValues(fc) - { // Clear submitted value - if (log.isDebugEnabled()) - log.debug("clearing submitted value for {}. value is {}.", ii.getColumn().getName(), input.getSubmittedValue()); + else if (input.getSubmittedValue() != null) // && FacesUtils.isClearSubmittedValues(fc) + { // Clear submitted value + if (InputControl.log.isDebugEnabled()) + InputControl.log.debug("clearing submitted value for {}. value is {}.", ii.getColumn().getName(), input.getSubmittedValue()); input.setSubmittedValue(null); } - + /* -------------------------------------- */ // Assign value Object value = ii.getValue(false); if (value instanceof ValueExpression) - { input.setValue(null); + { + input.setValue(null); input.setLocalValueSet(false); - input.setValueExpression("value", (ValueExpression)value); - + input.setValueExpression("value", (ValueExpression) value); + // Object check = ((ValueExpression)value).getValue(FacesContext.getCurrentInstance().getELContext()); // log.info("Expression value is {}.", check); - } + } else - { // Set the value + { // Set the value value = formatInputValue(value, ii); input.setValue(value); - } - } + } + } protected void clearSubmittedValue(UIInput input) { @@ -269,10 +330,11 @@ public abstract class InputControl String clientId = input.getClientId(); if (reqMap.containsKey(clientId)) reqMap.remove(clientId); - } + } /** * Override this to format a value for output + * * @param value * @param ii * @return @@ -281,12 +343,12 @@ public abstract class InputControl { return value; } - + protected Object parseInputValue(String value, InputInfo ii) { return value; } - + /* validate public boolean validateValue(UIComponent comp, InputInfo ii, FacesContext context) { @@ -325,31 +387,28 @@ public abstract class InputControl return true; } */ - + /* Input helpers */ protected abstract void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList); + + protected abstract void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context); - /** - * returns the first UIInput component that is a direct child of the parent component - * @param parent the parent component which node to search for - * @return the first child that is a UIInput - */ protected UIInput getInputComponent(UIComponent parent) { // default implementation - int count = parent.getChildCount(); - if (count<1) + int count = parent.getChildCount(); + if (count < 1) return null; - // find the UIInput component (only one allowed) + // find the UIInput component (only one allowed here) UIInput inp = null; - for (int i=0; i<count; i++) - { // check UIInput - UIComponent comp = parent.getChildren().get(i); - if (comp instanceof UIInput) - { if (inp!=null) - throw new UnexpectedReturnValueException(comp, "comp.getChildren().get("+String.valueOf(i)+")"); - inp = (UIInput)comp; - } + for (int i = 0; i < count; i++) + { // check UIInput + UIComponent comp = parent.getChildren().get(i); + if (comp instanceof UIInput) + { if (inp != null) + throw new UnexpectedReturnValueException(comp, "comp.getChildren().get(" + String.valueOf(i) + ")"); + inp = (UIInput) comp; + } } // No UIInput found if (inp == null) @@ -364,26 +423,21 @@ public abstract class InputControl // Should not happen! throw new UnexpectedReturnValueException(null, "comp.getChildren().get()"); } - // done + // found one return inp; } - /** - * copies standard input attributes such as styleClass, style, tabindex and event handlers (onclick, onblur, etc.) from the parent component to the input - * @param parent (not used) - * @param ii the input info from which to obtain the attribute values - * @param input the input component on which to set the attributes - * @param additonalStyle additional style classes - */ protected void copyAttributes(UIComponent parent, InputInfo ii, UIInput input, String additonalStyle) { String inputId = ii.getInputId(); if (StringUtils.isNotEmpty(inputId)) + { input.getAttributes().put("id", inputId); - + } + String styleClass = ii.getStyleClass(additonalStyle); input.getAttributes().put("styleClass", styleClass); - + copyAttribute(ii, input, "style"); copyAttribute(ii, input, "tabindex"); copyAttribute(ii, input, "onchange"); @@ -395,62 +449,52 @@ public abstract class InputControl // immediate Object immediate = ii.getAttribute("immediate"); - if (immediate!=null && ObjectUtils.getBoolean(immediate)) + if (immediate != null && ObjectUtils.getBoolean(immediate)) { - log.warn("Immediate attribute is not yet supported for {}!", ii.getColumn().getName()); + InputControl.log.warn("Immediate attribute is not yet supported for {}!", ii.getColumn().getName()); // input.setImmediate(true); - } + } // validator // input.addValidator(new ColumnValueValidator(ii.getColumn())); } - /** - * copies standard input attributes such as styleClass, style, tabindex and event handlers (onclick, onblur, etc.) from the parent component to the input - * @param parent (not used) - * @param ii the input info - * @param input the input component on which to set the attributes - */ protected final void copyAttributes(UIComponent parent, InputInfo ii, UIInput input) { copyAttributes(parent, ii, input, (ii.isRequired() ? "eInpReq" : null)); } - /** - * copies a single attribute - * @param ii - * @param input - * @param name - */ protected void copyAttribute(InputInfo ii, UIInput input, String name) { - if (ii==null) + if (ii == null) throw new InvalidArgumentException("InputInfo", ii); // get Attribute Object value = ii.getAttribute(name); - if (value!=null) - input.getAttributes().put(name, value); + if (value == null) + value = ii.getColumn().getAttribute(name); + if (value != null) + input.getAttributes().put(name, String.valueOf(value)); } - + public void addRemoveDisabledStyle(UIInput input, boolean disabled) { addRemoveStyle(input, " eInpDis", disabled); } - + public void addRemoveInvalidStyle(UIInput input, boolean invalid) { addRemoveStyle(input, " eInvalid", invalid); } - + public void addRemoveStyle(UIInput input, String styleName, boolean setStyle) { String styleClass = StringUtils.toString(input.getAttributes().get("styleClass"), ""); - boolean hasStyle = (styleClass.indexOf(styleName)>=0); - if (setStyle==hasStyle) + boolean hasStyle = (styleClass.indexOf(styleName) >= 0); + if (setStyle == hasStyle) return; // Nothing to do // Special IceFaces patch if (styleClass.endsWith("-dis")) - styleClass = styleClass.substring(0, styleClass.length()-4); + styleClass = styleClass.substring(0, styleClass.length() - 4); // add or remove disabled style if (setStyle) styleClass += styleName; @@ -459,20 +503,30 @@ public abstract class InputControl // add Style input.getAttributes().put("styleClass", styleClass); } - + /** * Returns the value formated as a string * this is a simple default implementation that does no type-secific formatting * Derived classes may override formatString an provide further formmatting * see TextInputControl for details * - * @param value the value to be formatted - * @param vi Meta-information about the value - * - * @return the formatted value + * @param value + * the value to be formatted + * @param vi + * Meta-information about the value + * @return the formatted value */ protected String formatValue(Object value, ValueInfo vi) { + // For Enums use toString() to retrieve Value + if (value != null && value.getClass().isEnum() && !hasFormatOption(vi, "nolookup")) + { // Handle enum + String text = ((Enum<?>) value).toString(); + if (text != null) + return vi.getText(text); + // Error + InputControl.log.error("The enum '" + ((Enum<?>) value).name() + "' has no text!"); + } // Lookup and Print value Options options = vi.getOptions(); if (options != null && !options.isEmpty() && !hasFormatOption(vi, "nolookup")) @@ -481,11 +535,11 @@ public abstract class InputControl if (text != null) return vi.getText(text); // Error - log.error("The element '" + String.valueOf(value) + "' is not part of the supplied option list."); + InputControl.log.error("The element '" + String.valueOf(value) + "' is not part of the supplied option list."); } // value - if (value==null) - value = getFormatOption(vi, FORMAT_NULL, FORMAT_NULL_ATTRIBUTE); + if (value == null) + value = getFormatOption(vi, InputControl.FORMAT_NULL, InputControl.FORMAT_NULL_ATTRIBUTE); // Convert to String String s = StringUtils.toString(value, ""); if (hasFormatOption(vi, "noencode")) @@ -504,9 +558,10 @@ public abstract class InputControl // boolean hasError = ((vi instanceof InputInfo) && !((InputInfo)vi).isValid()); return formatValue(vi.getValue(true), vi); } - + /** * escapes a String for html + * * @param text * @return the escaped html String */ @@ -515,24 +570,27 @@ public abstract class InputControl // TODO return text; } - + /** * checks if a particular formating option has been specified. - * @param vi the value info - * @param option the formating option to check - * @return true if the requested formating option has been specified or false otherwise + * + * @param vi + * the value info + * @param option + * the formating option to check + * @return true if the requested formating option has been specified or false otherwise */ protected boolean hasFormatOption(ValueInfo vi, String option) { String format = vi.getFormat(); - return (format!=null ? format.indexOf(option)>=0 : false); + return (format != null ? format.indexOf(option) >= 0 : false); } - + protected String getFormatOption(ValueInfo vi, String option) { // Is unit supplied with format String format = vi.getFormat(); - if (format==null) + if (format == null) return null; // Check for option int beg = format.indexOf(option); @@ -540,7 +598,7 @@ public abstract class InputControl return null; // Find beg = beg + option.length(); - int end = format.indexOf(';', beg+1); + int end = format.indexOf(';', beg + 1); if (end < beg) return format.substring(beg); // The cbValue @@ -550,7 +608,7 @@ public abstract class InputControl protected Object getFormatOption(ValueInfo vi, String option, String columnAttributeName) { String format = getFormatOption(vi, option); - return (format!=null) ? format : vi.getColumn().getAttribute(columnAttributeName); + return (format != null) ? format : vi.getColumn().getAttribute(columnAttributeName); } protected String getFormatString(ValueInfo vi, String option, String columnAttributeName) @@ -562,5 +620,5 @@ public abstract class InputControl { return ObjectUtils.getInteger(getFormatOption(vi, option, columnAttributeName)); } - + } http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java index c9e13a0..c2fc8f7 100644 --- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/InputControlManager.java @@ -18,12 +18,26 @@ */ package org.apache.empire.jsf2.controls; +import java.lang.reflect.Field; import java.util.HashMap; +import java.util.Map; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; + +import org.apache.empire.commons.StringUtils; +import org.apache.empire.exceptions.InternalException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public final class InputControlManager { + private static final Logger log = LoggerFactory.getLogger(InputControlManager.class); + private static Class<? extends javax.faces.component.html.HtmlOutputLabel> labelComponentClass = javax.faces.component.html.HtmlOutputLabel.class; + private static boolean showLabelRequiredMark = false; + public static Class<? extends javax.faces.component.html.HtmlOutputLabel> getLabelComponentClass() { return labelComponentClass; @@ -33,39 +47,110 @@ public final class InputControlManager { InputControlManager.labelComponentClass = labelComponentClass; } + + public static boolean isShowLabelRequiredMark() + { + return showLabelRequiredMark; + } + + public static void setShowLabelRequiredMark(boolean showLabelRequiredMark) + { + InputControlManager.showLabelRequiredMark = showLabelRequiredMark; + } static HashMap<String, InputControl> controlMap = null; - - static { - + + static + { + controlMap = new HashMap<String, InputControl>(); - + registerControl(new TextInputControl()); registerControl(new SelectInputControl()); registerControl(new TextAreaInputControl()); registerControl(new CheckboxInputControl()); + registerControl(new RadioInputControl()); /* - registerControl("phone", new PhoneInputControl()); - registerControl("radio", new RadioInputControl()); - registerControl("email", new EMailInputControl()); - registerControl("hlink", new HLinkInputControl()); - registerControl("password", new PasswordInputControl()); + registerControl(new PhoneInputControl()); + registerControl(new EMailInputControl()); + registerControl(new HLinkInputControl()); + registerControl(new PasswordInputControl()); */ } - + private InputControlManager() { // Default Constructor } - + public static void registerControl(InputControl control) { controlMap.put(control.getName(), control); } - + public static InputControl getControl(String name) { return controlMap.get(name); } - + + private static Map<Class<? extends UIComponent>, String> componentTypeMap = new HashMap<Class<? extends UIComponent>, String>(); + + @SuppressWarnings("unchecked") + public static <T extends UIComponent> T createComponent(FacesContext context, Class<T> clazz) + { + // Get component type from class + String type = componentTypeMap.get(clazz); + if (type == null) + { // Detect type + try + { // Detect component type + Field field = clazz.getDeclaredField("COMPONENT_TYPE"); + if (field != null) + type = StringUtils.toString(field.get(null), ""); // Empty string is default + else + type = ""; // Empty string is default + // show + log.debug("Component-Type for class {} is {}", clazz.getName(), type); + } + catch (SecurityException e) + { + throw new InternalException(e); + } + catch (NoSuchFieldException e) + { // No COMPONENT_TYPE field + log.debug("No Component-Type available for class {}!", clazz.getName()); + type = ""; // Empty string is default + } + catch (IllegalArgumentException e) + { + throw new InternalException(e); + } + catch (IllegalAccessException e) + { + throw new InternalException(e); + } + // put in map + componentTypeMap.put(clazz, type); + } + // Now, create the instance + if (StringUtils.isEmpty(type)) + { + try + { // create instance directly + return clazz.newInstance(); + } + catch (InstantiationException e) + { + throw new InternalException(e); + } + catch (IllegalAccessException e) + { + throw new InternalException(e); + } + } + // otherwise ask the application + return (T) context.getApplication().createComponent(type); + + } + } http://git-wip-us.apache.org/repos/asf/empire-db/blob/9176b225/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java ---------------------------------------------------------------------- diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java new file mode 100644 index 0000000..9726bc5 --- /dev/null +++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/controls/RadioInputControl.java @@ -0,0 +1,437 @@ +/* + * 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.controls; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.List; + +import javax.el.ValueExpression; +import javax.faces.component.UIComponent; +import javax.faces.component.UIInput; +import javax.faces.component.UISelectItem; +import javax.faces.component.html.HtmlSelectOneRadio; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; + +import org.apache.empire.commons.ObjectUtils; +import org.apache.empire.commons.OptionEntry; +import org.apache.empire.commons.Options; +import org.apache.empire.data.Column; +import org.apache.empire.exceptions.InternalException; +import org.apache.empire.exceptions.ItemNotFoundException; +import org.apache.empire.exceptions.UnexpectedReturnValueException; +import org.apache.empire.jsf2.app.TextResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RadioInputControl extends InputControl +{ + private static final Logger log = LoggerFactory.getLogger(RadioInputControl.class); + + public static final String COLATTR_ABBR_OPTIONS = "ABBR_OPTIONS"; // Option list for abbreviations + + public static final String VALUE_EXPRESSION_FLAG = "VALUE_EXPRESSION_FLAG"; + + public static final String NAME = "radio"; + + private final Class<? extends HtmlSelectOneRadio> inputComponentClass; + + public RadioInputControl(Class<? extends HtmlSelectOneRadio> inputComponentClass) + { + super(RadioInputControl.NAME); + this.inputComponentClass = inputComponentClass; + } + + public RadioInputControl() + { + this(HtmlSelectOneRadio.class); + } + + /* Value */ + @Override + public void renderValue(ValueInfo vi, ResponseWriter writer) + throws IOException + { + Object value = vi.getValue(true); + String style = vi.getStyleClass("eCtlRadio")+" eInpDis"; + writer.startElement(HTML_TAG_DIV, null); + writer.writeAttribute(HTML_ATTR_CLASS, style, null); + writer.startElement(HTML_TAG_TABLE, null); + writer.writeAttribute(HTML_ATTR_CLASS, style, null); + writer.startElement(HTML_TAG_TR, null); + Options o = vi.getOptions(); + for (OptionEntry e : o) + { + writer.startElement(HTML_TAG_TD, null); + // input + writer.startElement(HTML_TAG_INPUT, null); + writer.writeAttribute(HTML_ATTR_TYPE, "radio", null); + writer.writeAttribute(HTML_ATTR_DISABLED, "disabled", null); + if (ObjectUtils.compareEqual(e.getValue(), value)) + writer.writeAttribute(HTML_ATTR_CHECKED, "checked", null); + writer.endElement(HTML_TAG_INPUT); + // label + writer.startElement(HTML_TAG_LABEL, null); + writer.writeAttribute(HTML_ATTR_CLASS, "eCtlRadio", null); + String text = e.getText(); + text = vi.getTextResolver().resolveText(text); + writer.writeText(text, null); + writer.endElement(HTML_TAG_LABEL); + // end + writer.endElement(HTML_TAG_TD); + } + writer.endElement(HTML_TAG_TR); + writer.endElement(HTML_TAG_TABLE); + writer.endElement(HTML_TAG_DIV); + } + + @Override + protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList) + { + HtmlSelectOneRadio input; + if (compList.size() == 0) + { // create component + input = InputControlManager.createComponent(context, this.inputComponentClass); + // setValueExpressionFlag + Object value = ii.getValue(false); + input.getAttributes().put(RadioInputControl.VALUE_EXPRESSION_FLAG, (value instanceof ValueExpression)); + // copy Attributes + copyAttributes(parent, ii, input); + // disabled + boolean disabled = ii.isDisabled(); + input.setDisabled(disabled); + // Options + Options options = ii.getOptions(); + boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains(""); + String nullText = (addEmpty) ? getNullText(ii) : ""; + initOptions(input, ii.getTextResolver(), options, addEmpty, nullText); + // add + compList.add(input); + } + else + { // check type + UIComponent comp = compList.get(0); + if (!(comp instanceof HtmlSelectOneRadio)) + { + throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get"); + } + // cast + input = (HtmlSelectOneRadio) comp; + // disabled + boolean disabled = ii.isDisabled(); + input.setDisabled(disabled); + // Options (sync) + Options options = ii.getOptions(); + boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains(""); + String nullText = (addEmpty) ? getNullText(ii) : ""; + syncOptions(input, ii.getTextResolver(), options, addEmpty, nullText); + } + + // style + addRemoveDisabledStyle(input, input.isDisabled()); + addRemoveInvalidStyle(input, ii.hasError()); + + // Set Value + setInputValue(input, ii); + } + + @Override + protected void copyAttributes(UIComponent parent, InputInfo ii, UIInput input, String additonalStyle) + { + if (additonalStyle!=null) + additonalStyle = additonalStyle+" eCtlRadio"; + else + additonalStyle = "eCtlRadio"; + // copy + super.copyAttributes(parent, ii, input, additonalStyle); + } + + @Override + protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context) + { + UIComponent comp = compList.get(0); + if (!(comp instanceof HtmlSelectOneRadio)) + { + throw new UnexpectedReturnValueException(comp.getClass().getName(), "parent.getChildren()"); + } + HtmlSelectOneRadio input = (HtmlSelectOneRadio)comp; + // disabled + boolean disabled = ii.isDisabled(); + input.setDisabled(disabled); + // Options (sync) + Options options = ii.getOptions(); + boolean addEmpty = getEmptyEntryRequired(ii, disabled) && !options.contains(""); + String nullText = (addEmpty) ? getNullText(ii) : ""; + syncOptions(input, ii.getTextResolver(), options, addEmpty, nullText); + } + + private boolean getEmptyEntryRequired(InputInfo ii, boolean disabled) + { + if (!ii.isRequired() && !(disabled && ii.getColumn().isRequired())) + { + return true; + } + // Check Value + return (ii.getValue(true) == null); + } + + public void initOptions(HtmlSelectOneRadio input, TextResolver textResolver, Options options, boolean addEmpty, String nullText) + { + if (addEmpty) + { // Empty entry + addSelectItem(input, textResolver, new OptionEntry(null, nullText)); + } + if (options != null && options.size() > 0) + { // Add options + for (OptionEntry e : options) + { // Option entries + addSelectItem(input, textResolver, e); + } + } + } + + public void syncOptions(HtmlSelectOneRadio input, TextResolver textResolver, Options options, boolean hasEmpty, String nullText) + { + // Compare child-items with options + Iterator<OptionEntry> ioe = options.iterator(); + OptionEntry oe = (ioe.hasNext() ? ioe.next() : null); + List<UIComponent> childList = input.getChildren(); + Iterator<UIComponent> ico = childList.iterator(); + int lastIndex = 0; + while (ico.hasNext()) + { + lastIndex++; + UIComponent co = ico.next(); + if (!(co instanceof UISelectItem)) + { + continue; + } + UISelectItem si = (UISelectItem) co; + Object ov = si.getItemValue(); + if (ObjectUtils.isEmpty(ov) && hasEmpty) + { + continue; + } + if (oe == null) + { // remove obsolete items + lastIndex--; + for (int index = childList.size() - 1; index >= lastIndex; index--) + { + childList.remove(index); + } + // done + return; + } + if (ObjectUtils.compareEqual(ov, oe.getValue())) + { // next + oe = (ioe.hasNext() ? ioe.next() : null); + continue; + } + // Not equal - do a full reload + input.getChildren().clear(); + if (hasEmpty) + { + addSelectItem(input, textResolver, new OptionEntry("", nullText)); + } + for (OptionEntry e : options) + { // Option entries + addSelectItem(input, textResolver, e); + } + // done + return; + } + // Are there any items left? + while (oe != null) + { // add missing item + addSelectItem(input, textResolver, oe); + oe = (ioe.hasNext() ? ioe.next() : null); + } + } + + public void addSelectItem(UIComponent input, TextResolver textResolver, OptionEntry e) + { + UISelectItem selectItem = new UISelectItem(); + // set value + Object value; + Object valueExpressionFlag = input.getAttributes().get(RadioInputControl.VALUE_EXPRESSION_FLAG); + if (ObjectUtils.getBoolean(valueExpressionFlag)) + { // Use value as is + value = e.getValue(); + } + else + { // Convert to String + value = e.getValueString(); + } + selectItem.setItemValue(value); + // set text + String text = e.getText(); + text = textResolver.resolveText(text); + selectItem.setItemLabel(text); + // add item + input.getChildren().add(selectItem); + } + + private String getNullText(InputInfo ii) + { + String nullText = getFormatString(ii, InputControl.FORMAT_NULL, InputControl.FORMAT_NULL_ATTRIBUTE); + return (nullText != null) ? ii.getText(nullText) : ""; + } + + @Override + protected String formatValue(Object value, ValueInfo vi) + { + // Lookup and Print value + if (vi.getOptions() == null) + { + RadioInputControl.log.warn("Select field {} has no Option list attached!", vi.getColumn().getName()); + return super.formatValue(value, vi); + } + // Check for Abbreviation + if (hasFormatOption(vi, "short")) + { + Column column = vi.getColumn(); + if (column != null) + { // Check for Abbreviation option list + Object attrValue = column.getAttribute(RadioInputControl.COLATTR_ABBR_OPTIONS); + if (attrValue instanceof Options) + { // Check for Options + String text = ((Options) attrValue).get(value); + if (text != null) + { + return vi.getText(text); + } + // Error + RadioInputControl.log.error("The element '" + String.valueOf(value) + "' is not part of the supplied option list."); + } + } + } + return super.formatValue(value, vi); + } + + @Override + protected Object formatInputValue(Object value, InputInfo ii) + { + // the enum Value + if (value != null && value.getClass().isEnum()) + { + return ((Enum<?>) value).name(); + } + // the value + return value; + } + + @Override + protected Object parseInputValue(String value, InputInfo ii) + { + Object enumType = ii.getColumn().getAttribute(Column.COLATTR_ENUMTYPE); + if (enumType != null) + { try + { // get enum + Class<?> enumClass = (Class<?>) enumType; + Field field = enumClass.getDeclaredField(value); + return field.get(null); + } + catch (NoSuchFieldException e) + { + throw new ItemNotFoundException(value); + } + catch (SecurityException e) + { + throw new InternalException(e); + } + catch (IllegalArgumentException e) + { + throw new InternalException(e); + } + catch (IllegalAccessException e) + { + throw new InternalException(e); + } + } + return value; + } + + /* + @Override + public void renderInput(ResponseWriter writer, ControlInfo ci) + { + boolean disabled = ci.getDisabled(); + + HtmlTag input = writer.startTag("select"); + input.addAttribute("id", ci.getId()); + input.addAttribute("class", ci.getCssClass()); + input.addAttribute("style", ci.getCssStyle()); + if (disabled) + { + input.addAttribute("disabled"); + } else + { + input.addAttribute("name", ci.getName()); + } + // Event Attributes + input.addAttribute("onclick", ci.getOnclick()); + input.addAttribute("onchange", ci.getOnchange()); + input.addAttribute("onfocus", ci.getOnfocus()); + input.addAttribute("onblur", ci.getOnblur()); + input.beginBody(true); + // Render List of Options + Options options = ci.getOptions(); + if (options!=null) + { // Render option list + Object current = ci.getValue(); + if (hasFormatOption(ci, "allownull") && options.contains(null)==false) + { // add an empty entry + addEmtpyEntry(writer, ObjectUtils.isEmpty(current)); + } + for (OptionEntry entry : options) + { + Object value = entry.getValue(); + boolean isCurrent = ObjectUtils.compareEqual(current, value); + if (isCurrent == false && disabled) + continue; // + // Add Option entry + HtmlTag option = writer.startTag("option"); + option.addAttributeNoCheck("value", value, true); + option.addAttribute("selected", isCurrent); + option.beginBody(ci.getTranslation(entry.getText())); + option.endTag(true); + } + } + else + { // No Option list available + log.error("No options available for select input control."); + } + // done + input.endTag(); + } + + private void addEmtpyEntry(HtmlWriter writer, boolean isCurrent) + { + // Add Option entry + HtmlTag option = writer.startTag("option"); + option.addAttributeNoCheck("value", "", false); + option.addAttribute("selected", isCurrent); + option.beginBody(""); + option.endTag(true); + } + */ + +}
