diff --git a/api/src/main/java/org/jboss/seam/faces/component/AbstractUIInputContainer.java b/api/src/main/java/org/jboss/seam/faces/component/AbstractUIInputContainer.java deleted file mode 100644 index 2029a69..0000000 --- a/api/src/main/java/org/jboss/seam/faces/component/AbstractUIInputContainer.java +++ /dev/null @@ -1,405 +0,0 @@ -package org.jboss.seam.faces.component; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.el.ValueExpression; -import javax.el.ValueReference; -import javax.faces.FacesException; -import javax.faces.application.FacesMessage; -import javax.faces.component.EditableValueHolder; -import javax.faces.component.NamingContainer; -import javax.faces.component.UIComponent; -import javax.faces.component.UIComponentBase; -import javax.faces.component.UIMessage; -import javax.faces.component.UINamingContainer; -import javax.faces.component.UIViewRoot; -import javax.faces.component.html.HtmlOutputLabel; -import javax.faces.context.FacesContext; -import javax.faces.validator.BeanValidator; -import javax.validation.Validation; -import javax.validation.ValidationException; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.metadata.BeanDescriptor; -import javax.validation.metadata.PropertyDescriptor; - -/** - * AbstractUIInputContainer is an abstraction to hold common functionalities for a InputContainer. - * - * - * @author Jose Rodolfo freitas - */ -public abstract class AbstractUIInputContainer extends UIComponentBase implements NamingContainer { - - protected static final String HTML_ID_ATTR_NAME = "id"; - protected static final String HTML_CLASS_ATTR_NAME = "class"; - protected static final String HTML_STYLE_ATTR_NAME = "style"; - - protected boolean beanValidationPresent = false; - - public AbstractUIInputContainer() { - beanValidationPresent = isClassPresent("javax.validation.Validator"); - } - - @Override - public String getFamily() { - return UINamingContainer.COMPONENT_FAMILY; - } - - /** - * The name of the auto-generated composite component attribute that holds a boolean indicating whether the the template - * contains an invalid input. - */ - public String getInvalidAttributeName() { - return "invalid"; - } - - /** - * The name of the auto-generated composite component attribute that holds a boolean indicating whether the template - * contains a required input. - */ - public String getRequiredAttributeName() { - return "required"; - } - - /** - * The name of the composite component attribute that holds the string label for this set of inputs. If the label attribute - * is not provided, one will be generated from the id of the composite component or, if the id is defaulted, the name of the - * property bound to the first input. - */ - public String getLabelAttributeName() { - return "label"; - } - - /** - * The name of the auto-generated composite component attribute that holds the elements in this input container. The - * elements include the label, a list of inputs and a cooresponding list of messages. - */ - public String getElementsAttributeName() { - return "elements"; - } - - /** - * The name of the composite component attribute that holds a boolean indicating whether the component template should be - * enclosed in an HTML element, so that it be referenced from JavaScript. - */ - public String getEncloseAttributeName() { - return "enclose"; - } - - public String getContainerElementName() { - return "div"; - } - - public String getDefaultLabelId() { - return "label"; - } - - public String getDefaultInputId() { - return "input"; - } - - public String getDefaultMessageId() { - return "message"; - } - - @Override - public void encodeBegin(final FacesContext context) throws IOException { - if (!isRendered()) { - return; - } - - super.encodeBegin(context); - - InputContainerElements elements = scan(getFacet(UIComponent.COMPOSITE_FACET_NAME), null, context); - // assignIds(elements, context); - postScan(context, elements); - - wire(elements, context); - - getAttributes().put(getElementsAttributeName(), elements); - - if (elements.hasValidationError()) { - getAttributes().put(getInvalidAttributeName(), true); - } - - // set the required attribute, but only if the user didn't already assign it - if (!getAttributes().containsKey(getRequiredAttributeName()) && elements.hasRequiredInput()) { - getAttributes().put(getRequiredAttributeName(), true); - } - - /* - * EL expressions are never pushed to the attributes map. There's a separate store, under the set/get ValueExpression - * methods. - * - * Non-el expressions are put into the attributes map, unless they are properties, in which case they get pushed into - * the getter/setter. - */ - if (getValueExpression(getLabelAttributeName()) == null - && (!getAttributes().containsKey(getLabelAttributeName()) || labelHasEmptyValue(elements))) { - getAttributes().put(getLabelAttributeName(), generateLabel(elements, context)); - } - - if (Boolean.TRUE.equals(getAttributes().get(getEncloseAttributeName()))) { - startContainerElement(context); - } - } - - abstract protected void postScan(final FacesContext context, InputContainerElements elements); - - @Override - public void encodeEnd(final FacesContext context) throws IOException { - if (!isRendered()) { - return; - } - - super.encodeEnd(context); - - if (Boolean.TRUE.equals(getAttributes().get(getEncloseAttributeName()))) { - endContainerElement(context); - } - } - - // assigning ids seems to break form submissions, but I don't know why - public void assignIds(final InputContainerElements elements, final FacesContext context) { - boolean refreshIds = false; - if (getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { - setId(elements.getPropertyName(context)); - refreshIds = true; - } - UIComponent label = elements.getLabel(); - if (label != null) { - if (label.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { - label.setId(getDefaultLabelId()); - } else if (refreshIds) { - label.setId(label.getId()); - } - } - for (int i = 0, len = elements.getInputs().size(); i < len; i++) { - UIComponent input = (UIComponent) elements.getInputs().get(i); - if (input.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { - input.setId(getDefaultInputId() + (i == 0 ? "" : (i + 1))); - } else if (refreshIds) { - input.setId(input.getId()); - } - } - for (int i = 0, len = elements.getMessages().size(); i < len; i++) { - UIComponent msg = elements.getMessages().get(i); - if (msg.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { - msg.setId(getDefaultMessageId() + (i == 0 ? "" : (i + 1))); - } else if (refreshIds) { - msg.setId(msg.getId()); - } - } - } - - protected void startContainerElement(final FacesContext context) throws IOException { - context.getResponseWriter().startElement(getContainerElementName(), this); - String style = (getAttributes().get("style") != null ? getAttributes().get("style").toString().trim() : null); - if (style.length() > 0) { - context.getResponseWriter().writeAttribute(HTML_STYLE_ATTR_NAME, style, HTML_STYLE_ATTR_NAME); - } - String styleClass = (getAttributes().get("styleClass") != null ? getAttributes().get("styleClass").toString().trim() - : null); - if (styleClass.length() > 0) { - context.getResponseWriter().writeAttribute(HTML_CLASS_ATTR_NAME, styleClass, HTML_CLASS_ATTR_NAME); - } - context.getResponseWriter().writeAttribute(HTML_ID_ATTR_NAME, getClientId(context), HTML_ID_ATTR_NAME); - } - - protected void endContainerElement(final FacesContext context) throws IOException { - context.getResponseWriter().endElement(getContainerElementName()); - } - - protected String generateLabel(final InputContainerElements elements, final FacesContext context) { - String name = getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ? elements.getPropertyName(context) : getId(); - return name.substring(0, 1).toUpperCase() + name.substring(1); - } - - /** - * Walk the component tree branch built by the composite component and locate the input container elements. - * - * @return a composite object of the input container elements - */ - protected InputContainerElements scan(final UIComponent component, InputContainerElements elements, - final FacesContext context) { - if (elements == null) { - elements = new InputContainerElements(); - } - - // NOTE we need to walk the tree ignoring rendered attribute because it's condition - // could be based on what we discover - if ((elements.getLabel() == null) && (component instanceof HtmlOutputLabel)) { - elements.setLabel((HtmlOutputLabel) component); - } else if (component instanceof EditableValueHolder) { - elements.registerInput((EditableValueHolder) component, getDefaultValidator(context), context); - } else if (component instanceof UIMessage) { - elements.registerMessage((UIMessage) component); - } - // may need to walk smarter to ensure "element of least suprise" - for (UIComponent child : component.getChildren()) { - scan(child, elements, context); - } - - return elements; - } - - /** - * Wire the label and messages to the input(s) - */ - protected void wire(final InputContainerElements elements, final FacesContext context) { - elements.wire(context); - } - - /** - * Get the default Bean Validation Validator to read the contraints for a property. - */ - private Validator getDefaultValidator(final FacesContext context) throws FacesException { - if (!beanValidationPresent) { - return null; - } - - ValidatorFactory validatorFactory; - Object cachedObject = context.getExternalContext().getApplicationMap().get(BeanValidator.VALIDATOR_FACTORY_KEY); - if (cachedObject instanceof ValidatorFactory) { - validatorFactory = (ValidatorFactory) cachedObject; - } else { - try { - validatorFactory = Validation.buildDefaultValidatorFactory(); - } catch (ValidationException e) { - throw new FacesException("Could not build a default Bean Validator factory", e); - } - context.getExternalContext().getApplicationMap().put(BeanValidator.VALIDATOR_FACTORY_KEY, validatorFactory); - } - return validatorFactory.getValidator(); - } - - private boolean labelHasEmptyValue(InputContainerElements elements) { - if (elements.getLabel() == null || elements.getLabel().getValue() == null) - return false; - return (elements.getLabel().getValue().toString().trim().equals(":") || elements.getLabel().getValue().toString() - .trim().equals("")); - } - - private boolean isClassPresent(final String fqcn) { - try { - if (Thread.currentThread().getContextClassLoader() != null) { - return Thread.currentThread().getContextClassLoader().loadClass(fqcn) != null; - } else { - return Class.forName(fqcn) != null; - } - } catch (ClassNotFoundException e) { - return false; - } catch (NoClassDefFoundError e) { - return false; - } - } - - public static class InputContainerElements { - private String propertyName; - private HtmlOutputLabel label; - private final List inputs = new ArrayList(); - private final List messages = new ArrayList(); - private boolean validationError = false; - private boolean requiredInput = false; - - public HtmlOutputLabel getLabel() { - return label; - } - - public void setLabel(final HtmlOutputLabel label) { - this.label = label; - } - - public List getInputs() { - return inputs; - } - - public void registerInput(final EditableValueHolder input, final Validator validator, final FacesContext context) { - inputs.add(input); - if (input.isRequired() || isRequiredByConstraint(input, validator, context)) { - requiredInput = true; - } - if (!input.isValid()) { - validationError = true; - } - // optimization to avoid loop if already flagged - else if (!validationError) { - Iterator it = context.getMessages(((UIComponent) input).getClientId(context)); - while (it.hasNext()) { - if (it.next().getSeverity().compareTo(FacesMessage.SEVERITY_WARN) >= 0) { - validationError = true; - break; - } - } - } - } - - public List getMessages() { - return messages; - } - - public void registerMessage(final UIMessage message) { - messages.add(message); - } - - public boolean hasValidationError() { - return validationError; - } - - public boolean hasRequiredInput() { - return requiredInput; - } - - private boolean isRequiredByConstraint(final EditableValueHolder input, final Validator validator, - final FacesContext context) { - if (validator == null) { - return false; - } - - // NOTE believe it or not, getValueReference on ValueExpression is broken, so we have to do it ourselves - ValueExpression valueExpression = ((UIComponent) input).getValueExpression("value"); - if (valueExpression != null) { - ValueExpressionAnalyzer valueExpressionAnalyzer = new ValueExpressionAnalyzer(valueExpression); - ValueReference vref = valueExpressionAnalyzer.getValueReference(context.getELContext()); - if (vref != null) { - BeanDescriptor constraintsForClass = validator.getConstraintsForClass(vref.getBase().getClass()); - PropertyDescriptor d = constraintsForClass.getConstraintsForProperty((String) vref.getProperty()); - return (d != null) && d.hasConstraints(); - } - } - return false; - } - - public String getPropertyName(final FacesContext context) { - if (propertyName != null) { - return propertyName; - } - - if (inputs.size() == 0) { - return null; - } - - propertyName = (String) new ValueExpressionAnalyzer(((UIComponent) inputs.get(0)).getValueExpression("value")) - .getValueReference(context.getELContext()).getProperty(); - return propertyName; - } - - public void wire(final FacesContext context) { - int numInputs = inputs.size(); - if (numInputs > 0) { - if (label != null) { - label.setFor(((UIComponent) inputs.get(0)).getClientId(context)); - } - for (int i = 0, len = messages.size(); i < len; i++) { - if (i < numInputs) { - messages.get(i).setFor(((UIComponent) inputs.get(i)).getClientId(context)); - } - } - } - } - } -} diff --git a/api/src/main/java/org/jboss/seam/faces/component/UIFacetInputContainer.java b/api/src/main/java/org/jboss/seam/faces/component/UIFacetInputContainer.java deleted file mode 100644 index 1d1c9b1..0000000 --- a/api/src/main/java/org/jboss/seam/faces/component/UIFacetInputContainer.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.jboss.seam.faces.component; - -import javax.faces.component.FacesComponent; -import javax.faces.context.FacesContext; - -/** - * UIFacetInputContainer is a component to replace UIInputContainer while JSF Bugs: - * http://java.net/jira/browse/JAVASERVERFACES-1991 and http://java.net/jira/browse/JAVASERVERFACES-2040 are not fixed. This - * component will probably be deprecated after those bugfixes. - * - *

- * Composite component definition example (minus layout): - *

- * - *
- * <cc:interface componentType="org.jboss.seam.faces.InputContainer"/>
- * <cc:implementation>
- *   <h:outputLabel id="label" value="#{cc.attrs.label}:" styleClass="#{cc.attrs.invalid ? 'invalid' : ''}">
- *     <h:ouputText styleClass="required" rendered="#{cc.attrs.required}" value="*"/>
- *   </h:outputLabel>
- *   <cc:renderFacet name="inputs" />
- *   <h:message id="message" errorClass="invalid message" rendered="#{cc.attrs.invalid}"/>
- * </cc:implementation>
- * 
- * - *

- * Composite component usage example: - *

- * - *
- * <example:facetInputContainer id="name">
- *  <f:facet name="inputs">
- *   <h:inputText id="input" value="#{person.name}"/>
- *  </f:facet>
- * </example:facetInputContainer>
- * 
- * - *

- * Possible enhancements: - *

- *
    - *
  • append styleClass "invalid" to label, inputs and messages when invalid
  • - *
- * - *

- * NOTE: Firefox does not properly associate a label with the target input if the input id contains a colon (:), the default - * separator character in JSF. JSF 2 allows developers to set the value via an initialization parameter (context-param in - * web.xml) keyed to javax.faces.SEPARATOR_CHAR. We recommend that you override this setting to make the separator an underscore - * (_). - *

- * - * @author Dan Allen - * @author Jose Rodolfo freitas - */ -@FacesComponent(UIFacetInputContainer.COMPONENT_TYPE) -public class UIFacetInputContainer extends AbstractUIInputContainer { - /** - * The standard component type for this component. - */ - public static final String COMPONENT_TYPE = "org.jboss.seam.faces.FacetInputContainer"; - - @Override - protected void postScan(FacesContext context, InputContainerElements elements) { - scan(getFacet("inputs"), elements, context); - } - -} diff --git a/api/src/main/java/org/jboss/seam/faces/component/UIInputContainer.java b/api/src/main/java/org/jboss/seam/faces/component/UIInputContainer.java index b0caa0a..ee389eb 100644 --- a/api/src/main/java/org/jboss/seam/faces/component/UIInputContainer.java +++ b/api/src/main/java/org/jboss/seam/faces/component/UIInputContainer.java @@ -1,7 +1,31 @@ package org.jboss.seam.faces.component; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.el.ValueExpression; +import javax.el.ValueReference; +import javax.faces.FacesException; +import javax.faces.application.FacesMessage; +import javax.faces.component.EditableValueHolder; import javax.faces.component.FacesComponent; +import javax.faces.component.NamingContainer; +import javax.faces.component.UIComponent; +import javax.faces.component.UIComponentBase; +import javax.faces.component.UIMessage; +import javax.faces.component.UINamingContainer; +import javax.faces.component.UIViewRoot; +import javax.faces.component.html.HtmlOutputLabel; import javax.faces.context.FacesContext; +import javax.faces.validator.BeanValidator; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.BeanDescriptor; +import javax.validation.metadata.PropertyDescriptor; /** * UIInputContainer is a supplemental component for a JSF 2.0 composite component encapsulating one or more @@ -58,14 +82,369 @@ * @author Jose Rodolfo freitas */ @FacesComponent(UIInputContainer.COMPONENT_TYPE) -public class UIInputContainer extends AbstractUIInputContainer { +public class UIInputContainer extends UIComponentBase implements NamingContainer { /** * The standard component type for this component. */ public static final String COMPONENT_TYPE = "org.jboss.seam.faces.InputContainer"; + protected static final String HTML_ID_ATTR_NAME = "id"; + protected static final String HTML_CLASS_ATTR_NAME = "class"; + protected static final String HTML_STYLE_ATTR_NAME = "style"; + + private boolean beanValidationPresent = false; + + public UIInputContainer() { + beanValidationPresent = isClassPresent("javax.validation.Validator"); + } + + @Override + public String getFamily() { + return UINamingContainer.COMPONENT_FAMILY; + } + + /** + * The name of the auto-generated composite component attribute that holds a boolean indicating whether the the template + * contains an invalid input. + */ + public String getInvalidAttributeName() { + return "invalid"; + } + + /** + * The name of the auto-generated composite component attribute that holds a boolean indicating whether the template + * contains a required input. + */ + public String getRequiredAttributeName() { + return "required"; + } + + /** + * The name of the composite component attribute that holds the string label for this set of inputs. If the label attribute + * is not provided, one will be generated from the id of the composite component or, if the id is defaulted, the name of the + * property bound to the first input. + */ + public String getLabelAttributeName() { + return "label"; + } + + /** + * The name of the auto-generated composite component attribute that holds the elements in this input container. The + * elements include the label, a list of inputs and a cooresponding list of messages. + */ + public String getElementsAttributeName() { + return "elements"; + } + + /** + * The name of the composite component attribute that holds a boolean indicating whether the component template should be + * enclosed in an HTML element, so that it be referenced from JavaScript. + */ + public String getEncloseAttributeName() { + return "enclose"; + } + + public String getContainerElementName() { + return "div"; + } + + public String getDefaultLabelId() { + return "label"; + } + + public String getDefaultInputId() { + return "input"; + } + + public String getDefaultMessageId() { + return "message"; + } + + @Override + public void encodeBegin(final FacesContext context) throws IOException { + if (!isRendered()) { + return; + } + + super.encodeBegin(context); + + InputContainerElements elements = scan(getFacet(UIComponent.COMPOSITE_FACET_NAME), null, context); + // assignIds(elements, context); + wire(elements, context); + + getAttributes().put(getElementsAttributeName(), elements); + + if (elements.hasValidationError()) { + getAttributes().put(getInvalidAttributeName(), true); + } + + // set the required attribute, but only if the user didn't already assign it + if (!getAttributes().containsKey(getRequiredAttributeName()) && elements.hasRequiredInput()) { + getAttributes().put(getRequiredAttributeName(), true); + } + + /* + * for some reason, Mojarra is not filling Attribute Map with "label" key if label attr has an EL value, so I added a + * labelHasEmptyValue to guarantee that there was no label setted. + */ + if (getValueExpression(getLabelAttributeName()) == null + && (!getAttributes().containsKey(getLabelAttributeName()) || labelHasEmptyValue(elements))) { + getAttributes().put(getLabelAttributeName(), generateLabel(elements, context)); + } + + if (Boolean.TRUE.equals(getAttributes().get(getEncloseAttributeName()))) { + startContainerElement(context); + } + } + @Override - protected void postScan(FacesContext context, InputContainerElements elements) { + public void encodeEnd(final FacesContext context) throws IOException { + if (!isRendered()) { + return; + } + + super.encodeEnd(context); + + if (Boolean.TRUE.equals(getAttributes().get(getEncloseAttributeName()))) { + endContainerElement(context); + } + } + + protected void startContainerElement(final FacesContext context) throws IOException { + context.getResponseWriter().startElement(getContainerElementName(), this); + String style = (getAttributes().get("style") != null ? getAttributes().get("style").toString().trim() : null); + if (style.length() > 0) { + context.getResponseWriter().writeAttribute(HTML_STYLE_ATTR_NAME, style, HTML_STYLE_ATTR_NAME); + } + String styleClass = (getAttributes().get("styleClass") != null ? getAttributes().get("styleClass").toString().trim() + : null); + if (styleClass.length() > 0) { + context.getResponseWriter().writeAttribute(HTML_CLASS_ATTR_NAME, styleClass, HTML_CLASS_ATTR_NAME); + } + context.getResponseWriter().writeAttribute(HTML_ID_ATTR_NAME, getClientId(context), HTML_ID_ATTR_NAME); + } + + protected void endContainerElement(final FacesContext context) throws IOException { + context.getResponseWriter().endElement(getContainerElementName()); + } + + protected String generateLabel(final InputContainerElements elements, final FacesContext context) { + String name = getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ? elements.getPropertyName(context) : getId(); + return name.substring(0, 1).toUpperCase() + name.substring(1); } -} + /** + * Walk the component tree branch built by the composite component and locate the input container elements. + * + * @return a composite object of the input container elements + */ + protected InputContainerElements scan(final UIComponent component, InputContainerElements elements, + final FacesContext context) { + if (elements == null) { + elements = new InputContainerElements(); + } + + // NOTE we need to walk the tree ignoring rendered attribute because it's condition + // could be based on what we discover + if ((elements.getLabel() == null) && (component instanceof HtmlOutputLabel)) { + elements.setLabel((HtmlOutputLabel) component); + } else if (component instanceof EditableValueHolder) { + elements.registerInput((EditableValueHolder) component, getDefaultValidator(context), context); + } else if (component instanceof UIMessage) { + elements.registerMessage((UIMessage) component); + } + // may need to walk smarter to ensure "element of least suprise" + for (UIComponent child : component.getChildren()) { + scan(child, elements, context); + } + + return elements; + } + + // assigning ids seems to break form submissions, but I don't know why + public void assignIds(final InputContainerElements elements, final FacesContext context) { + boolean refreshIds = false; + if (getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { + setId(elements.getPropertyName(context)); + refreshIds = true; + } + UIComponent label = elements.getLabel(); + if (label != null) { + if (label.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { + label.setId(getDefaultLabelId()); + } else if (refreshIds) { + label.setId(label.getId()); + } + } + for (int i = 0, len = elements.getInputs().size(); i < len; i++) { + UIComponent input = (UIComponent) elements.getInputs().get(i); + if (input.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { + input.setId(getDefaultInputId() + (i == 0 ? "" : (i + 1))); + } else if (refreshIds) { + input.setId(input.getId()); + } + } + for (int i = 0, len = elements.getMessages().size(); i < len; i++) { + UIComponent msg = elements.getMessages().get(i); + if (msg.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { + msg.setId(getDefaultMessageId() + (i == 0 ? "" : (i + 1))); + } else if (refreshIds) { + msg.setId(msg.getId()); + } + } + } + + /** + * Wire the label and messages to the input(s) + */ + protected void wire(final InputContainerElements elements, final FacesContext context) { + elements.wire(context); + } + + /** + * Get the default Bean Validation Validator to read the contraints for a property. + */ + private Validator getDefaultValidator(final FacesContext context) throws FacesException { + if (!beanValidationPresent) { + return null; + } + + ValidatorFactory validatorFactory; + Object cachedObject = context.getExternalContext().getApplicationMap().get(BeanValidator.VALIDATOR_FACTORY_KEY); + if (cachedObject instanceof ValidatorFactory) { + validatorFactory = (ValidatorFactory) cachedObject; + } else { + try { + validatorFactory = Validation.buildDefaultValidatorFactory(); + } catch (ValidationException e) { + throw new FacesException("Could not build a default Bean Validator factory", e); + } + context.getExternalContext().getApplicationMap().put(BeanValidator.VALIDATOR_FACTORY_KEY, validatorFactory); + } + return validatorFactory.getValidator(); + } + + private boolean isClassPresent(final String fqcn) { + try { + if (Thread.currentThread().getContextClassLoader() != null) { + return Thread.currentThread().getContextClassLoader().loadClass(fqcn) != null; + } else { + return Class.forName(fqcn) != null; + } + } catch (ClassNotFoundException e) { + return false; + } catch (NoClassDefFoundError e) { + return false; + } + } + + private boolean labelHasEmptyValue(InputContainerElements elements) { + if (elements.getLabel() == null || elements.getLabel().getValue() == null) + return false; + return (elements.getLabel().getValue().toString().trim().equals(":") || elements.getLabel().getValue().toString() + .trim().equals("")); + } + + public static class InputContainerElements { + private String propertyName; + private HtmlOutputLabel label; + private final List inputs = new ArrayList(); + private final List messages = new ArrayList(); + private boolean validationError = false; + private boolean requiredInput = false; + + public HtmlOutputLabel getLabel() { + return label; + } + + public void setLabel(final HtmlOutputLabel label) { + this.label = label; + } + + public List getInputs() { + return inputs; + } + + public void registerInput(final EditableValueHolder input, final Validator validator, final FacesContext context) { + inputs.add(input); + if (input.isRequired() || isRequiredByConstraint(input, validator, context)) { + requiredInput = true; + } + if (!input.isValid()) { + validationError = true; + } + // optimization to avoid loop if already flagged + else if (!validationError) { + Iterator it = context.getMessages(((UIComponent) input).getClientId(context)); + while (it.hasNext()) { + if (it.next().getSeverity().compareTo(FacesMessage.SEVERITY_WARN) >= 0) { + validationError = true; + break; + } + } + } + } + + public List getMessages() { + return messages; + } + + public void registerMessage(final UIMessage message) { + messages.add(message); + } + + public boolean hasValidationError() { + return validationError; + } + + public boolean hasRequiredInput() { + return requiredInput; + } + + private boolean isRequiredByConstraint(final EditableValueHolder input, final Validator validator, + final FacesContext context) { + if (validator == null) { + return false; + } + + // NOTE believe it or not, getValueReference on ValueExpression is broken, so we have to do it ourselves + ValueExpression valueExpression = ((UIComponent) input).getValueExpression("value"); + if (valueExpression != null) { + ValueExpressionAnalyzer valueExpressionAnalyzer = new ValueExpressionAnalyzer(valueExpression); + ValueReference vref = valueExpressionAnalyzer.getValueReference(context.getELContext()); + BeanDescriptor constraintsForClass = validator.getConstraintsForClass(vref.getBase().getClass()); + PropertyDescriptor d = constraintsForClass.getConstraintsForProperty((String) vref.getProperty()); + return (d != null) && d.hasConstraints(); + } + return false; + } + + public String getPropertyName(final FacesContext context) { + if (propertyName != null) { + return propertyName; + } + + if (inputs.size() == 0) { + return null; + } + + propertyName = (String) new ValueExpressionAnalyzer(((UIComponent) inputs.get(0)).getValueExpression("value")) + .getValueReference(context.getELContext()).getProperty(); + return propertyName; + } + + public void wire(final FacesContext context) { + int numInputs = inputs.size(); + if (numInputs > 0) { + if (label != null) { + label.setFor(((UIComponent) inputs.get(0)).getClientId(context)); + } + for (int i = 0, len = messages.size(); i < len; i++) { + if (i < numInputs) { + messages.get(i).setFor(((UIComponent) inputs.get(i)).getClientId(context)); + } + } + } + } + } +} \ No newline at end of file diff --git a/docs/reference/src/main/docbook/en-US/faces-components.xml b/docs/reference/src/main/docbook/en-US/faces-components.xml index 3c00bb5..948c08a 100644 --- a/docs/reference/src/main/docbook/en-US/faces-components.xml +++ b/docs/reference/src/main/docbook/en-US/faces-components.xml @@ -449,25 +449,18 @@ public class FooValidator implements Validator { - + + + ]]> - While http://java.net/jira/browse/JAVASERVERFACES-1991 and http://java.net/jira/browse/JAVASERVERFACES-2040 are not fixed, - please make use of facetInputContainer, it works the same way as the normal InputContainer, except that inputs have to be surrounded by a facet - called "inputs" as stated below. + it's currently required to wrap the insertChildren tag with a jsf panelGroup. Please see http://java.net/jira/browse/JAVASERVERFACES-1991 for more details. - - - - - ]]> - NOTE: Firefox does not properly associate a label with the target input if the input id diff --git a/impl/src/main/resources/META-INF/resources/components/seamfaces/facetInputContainer.xhtml b/impl/src/main/resources/META-INF/resources/components/seamfaces/facetInputContainer.xhtml deleted file mode 100644 index ee01ead..0000000 --- a/impl/src/main/resources/META-INF/resources/components/seamfaces/facetInputContainer.xhtml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - -
- - * - - - - - - - - - -
- -
- -
diff --git a/impl/src/main/resources/META-INF/resources/components/seamfaces/inputContainer.xhtml b/impl/src/main/resources/META-INF/resources/components/seamfaces/inputContainer.xhtml index dbeb9e6..213ace7 100644 --- a/impl/src/main/resources/META-INF/resources/components/seamfaces/inputContainer.xhtml +++ b/impl/src/main/resources/META-INF/resources/components/seamfaces/inputContainer.xhtml @@ -19,9 +19,11 @@ * - - - + + + + +