Permalink
Browse files

Merge remote-tracking branch 'origin/develop'

  • Loading branch information...
2 parents ba9caf1 + 3c2054f commit 37b0a86468192913754511dbc5d269cb045deaf8 @BalusC BalusC committed Feb 2, 2017
@@ -75,6 +75,8 @@
private static final String DEFAULT_REQUIRED_MESSAGE = "{0}: Value is required";
+ private static volatile Boolean interpretEmptyStringSubmittedValuesAsNull;
+
@SuppressWarnings("unused") // Workaround for OpenWebBeans not properly passing it as produce() method argument.
@Inject
private InjectionPoint injectionPoint;
@@ -135,6 +137,11 @@
for (int i = 0; i < submittedValues.length; i++) {
String submittedValue = submittedValues[i];
+ if (submittedValue != null && interpretEmptyStringSubmittedValuesAsNull(context) && submittedValue.isEmpty()) {
+ submittedValue = null;
+ submittedValues[i] = null;
+ }
+
try {
convertedValues[i] = (converter != null) ? converter.getAsObject(context, component, submittedValue) : submittedValue;
}
@@ -156,6 +163,17 @@
return convertedValues;
}
+ private static boolean interpretEmptyStringSubmittedValuesAsNull(FacesContext context) {
+ if (interpretEmptyStringSubmittedValuesAsNull != null) {
+ return interpretEmptyStringSubmittedValuesAsNull;
+ }
+
+ interpretEmptyStringSubmittedValuesAsNull = parseBoolean(context.getExternalContext()
+ .getInitParameter("javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL"));
+
+ return interpretEmptyStringSubmittedValuesAsNull;
+ }
+
@SuppressWarnings("unchecked")
static <V> V coerceValues(Type type, Object... values) {
if (type instanceof ParameterizedType) {
@@ -58,7 +58,6 @@
import javax.el.ValueExpression;
import javax.el.ValueReference;
import javax.faces.FacesException;
-import javax.faces.component.ContextCallback;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.component.UIForm;
@@ -335,7 +334,7 @@ protected void processValidateBean(FacesContext context, UIComponent component)
if (value != null) {
final Object[] found = new Object[1];
- form.invokeOnComponent(context, component.getClientId(), new ContextCallback() { @Override public void invokeContextCallback(FacesContext context, UIComponent target) {
+ forEachComponent(context).fromRoot(form).invoke(new Callback.WithArgument<UIComponent>() { @Override public void invoke(UIComponent target) {
found[0] = value.getValue(getELContext());
}});
@@ -557,12 +557,21 @@ public VisitResult visit(VisitContext context, UIComponent target) {
* @param operation the operation to invoke on each component
*/
public void invoke(final VisitCallback operation) {
- VisitCallback callback = operation;
- if (types != null) {
- callback = new TypesVisitCallback(types, callback);
- }
+ final VisitContext visitContext = createVisitContext(getFacesContext(), getIds(), getHints());
+ final VisitCallback visitCallback = (types == null) ? operation : new TypesVisitCallback(types, operation);
+ UIViewRoot viewRoot = getFacesContext().getViewRoot();
- getRoot().visitTree(createVisitContext(getContext(), getIds(), getHints()), callback);
+ if (viewRoot.equals(getRoot())) {
+ viewRoot.visitTree(visitContext, visitCallback);
+ }
+ else {
+ forEachComponent().havingIds(getRoot().getClientId()).invoke(new Callback.WithArgument<UIComponent>() {
+ @Override
+ public void invoke(UIComponent root) {
+ root.visitTree(visitContext, visitCallback);
+ }
+ });
+ }
}
protected FacesContext getFacesContext() {
@@ -13,6 +13,7 @@
package org.omnifaces.util;
import static java.lang.String.format;
+import static org.omnifaces.util.Components.getClosestParent;
import static org.omnifaces.util.FacesLocal.getApplicationAttribute;
import static org.omnifaces.util.FacesLocal.getContextAttribute;
import static org.omnifaces.util.FacesLocal.getInitParameter;
@@ -28,6 +29,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -65,6 +67,9 @@
private static final String RICHFACES_RLF_CLASS_NAME =
"org.richfaces.resource.ResourceLibraryFactoryImpl";
+ private static final Class<UIComponent> PRIMEFACES_DIALOG_CLASS =
+ toClassOrNull("org.primefaces.component.dialog.Dialog");
+
private static final String MYFACES_PACKAGE_PREFIX = "org.apache.myfaces.";
private static final String MYFACES_RENDERED_SCRIPT_RESOURCES_KEY =
"org.apache.myfaces.RENDERED_SCRIPT_RESOURCES_SET";
@@ -461,6 +466,26 @@ public static boolean isPrimeFacesDynamicResourceRequest(FacesContext context) {
return "primefaces".equals(params.get("ln")) && params.get("pfdrid") != null;
}
+ /**
+ * Returns true if the given components are nested in (same) PrimeFaces dialog.
+ * @param components The components to be checked.
+ * @return Whether the given components are nested in (same) PrimeFaces dialog.
+ * @since 2.6
+ */
+ public static boolean isNestedInPrimeFacesDialog(UIComponent... components) {
+ if (PRIMEFACES_DIALOG_CLASS == null) {
+ return false;
+ }
+
+ Set<UIComponent> dialogs = new HashSet<>();
+
+ for (UIComponent component : components) {
+ dialogs.add(getClosestParent(component, PRIMEFACES_DIALOG_CLASS));
+ }
+
+ return dialogs.size() == 1 && dialogs.iterator().next() != null;
+ }
+
// Tomcat related -------------------------------------------------------------------------------------------------
/**
@@ -214,7 +214,7 @@ public ViewHandler getWrapped() {
public void invoke(UIForm form) {
UIForm nestedParent = getClosestParent(form, UIForm.class);
- if (nestedParent != null) {
+ if (nestedParent != null && (!Hacks.isNestedInPrimeFacesDialog(form) || Hacks.isNestedInPrimeFacesDialog(form, nestedParent))) {
throw new IllegalStateException(
format(ERROR_NESTED_FORM_ENCOUNTERED, form.getClientId(), nestedParent.getClientId()));
}
@@ -140,6 +140,42 @@
@FindBy(id="validateClassLevelWithMessageForViolating:command")
private WebElement validateClassLevelWithMessageForViolatingCommand;
+ @FindBy(id="validateClassLevelWithInputEntityComposite:composite:number1")
+ private WebElement validateClassLevelWithInputEntityCompositeNumber1;
+
+ @FindBy(id="validateClassLevelWithInputEntityComposite:composite:number1Message")
+ private WebElement validateClassLevelWithInputEntityCompositeNumber1Message;
+
+ @FindBy(id="validateClassLevelWithInputEntityComposite:composite:number2")
+ private WebElement validateClassLevelWithInputEntityCompositeNumber2;
+
+ @FindBy(id="validateClassLevelWithInputEntityComposite:composite:number2Message")
+ private WebElement validateClassLevelWithInputEntityCompositeNumber2Message;
+
+ @FindBy(id="validateClassLevelWithInputEntityComposite:formMessage")
+ private WebElement validateClassLevelWithInputEntityCompositeFormMessage;
+
+ @FindBy(id="validateClassLevelWithInputEntityComposite:command")
+ private WebElement validateClassLevelWithInputEntityCompositeCommand;
+
+ @FindBy(id="validateClassLevelWithFormEntityComposite:form:number1")
+ private WebElement validateClassLevelWithFormEntityCompositeNumber1;
+
+ @FindBy(id="validateClassLevelWithFormEntityComposite:form:number1Message")
+ private WebElement validateClassLevelWithFormEntityCompositeNumber1Message;
+
+ @FindBy(id="validateClassLevelWithFormEntityComposite:form:number2")
+ private WebElement validateClassLevelWithFormEntityCompositeNumber2;
+
+ @FindBy(id="validateClassLevelWithFormEntityComposite:form:number2Message")
+ private WebElement validateClassLevelWithFormEntityCompositeNumber2Message;
+
+ @FindBy(id="validateClassLevelWithFormEntityComposite:form:formMessage")
+ private WebElement validateClassLevelWithFormEntityCompositeFormMessage;
+
+ @FindBy(id="validateClassLevelWithFormEntityComposite:form:command")
+ private WebElement validateClassLevelWithFormEntityCompositeCommand;
+
@Deployment(testable=false)
public static WebArchive createDeployment() {
return buildWebArchive(ValidateBeanIT.class)
@@ -312,12 +348,50 @@ public void validateClassLevelWithMessageForViolating() {
assertEquals("invalidEntity", validateClassLevelWithMessageForViolatingNumber1Message.getText());
assertEquals("", validateClassLevelWithMessageForViolatingNumber2Message.getText());
assertEquals("", validateClassLevelWithMessageForViolatingFormMessage.getText());
+ assertEquals("", messages.getText());
validateClassLevelWithMessageForViolatingNumber2.sendKeys("0");
guardAjax(validateClassLevelWithMessageForViolatingCommand).click();
assertEquals("", validateClassLevelWithMessageForViolatingNumber1Message.getText());
assertEquals("", validateClassLevelWithMessageForViolatingNumber2Message.getText());
assertEquals("", validateClassLevelWithMessageForViolatingFormMessage.getText());
+ assertEquals("actionSuccess", messages.getText());
+ }
+
+ @Test
+ public void validateClassLevelWithInputEntityComposite() {
+ validateClassLevelWithInputEntityCompositeNumber1.sendKeys("2");
+ validateClassLevelWithInputEntityCompositeNumber2.sendKeys("1");
+ guardAjax(validateClassLevelWithInputEntityCompositeCommand).click();
+ assertEquals("invalidEntity", validateClassLevelWithInputEntityCompositeNumber1Message.getText());
+ assertEquals("", validateClassLevelWithInputEntityCompositeNumber2Message.getText());
+ assertEquals("", validateClassLevelWithInputEntityCompositeFormMessage.getText());
+ assertEquals("", messages.getText());
+
+ validateClassLevelWithInputEntityCompositeNumber2.sendKeys("0");
+ guardAjax(validateClassLevelWithInputEntityCompositeCommand).click();
+ assertEquals("", validateClassLevelWithInputEntityCompositeNumber1Message.getText());
+ assertEquals("", validateClassLevelWithInputEntityCompositeNumber2Message.getText());
+ assertEquals("", validateClassLevelWithInputEntityCompositeFormMessage.getText());
+ assertEquals("actionSuccess", messages.getText());
+ }
+
+ @Test
+ public void validateClassLevelWithFormEntityComposite() {
+ validateClassLevelWithFormEntityCompositeNumber1.sendKeys("2");
+ validateClassLevelWithFormEntityCompositeNumber2.sendKeys("1");
+ guardAjax(validateClassLevelWithFormEntityCompositeCommand).click();
+ assertEquals("invalidEntity", validateClassLevelWithFormEntityCompositeNumber1Message.getText());
+ assertEquals("", validateClassLevelWithFormEntityCompositeNumber2Message.getText());
+ assertEquals("", validateClassLevelWithFormEntityCompositeFormMessage.getText());
+ assertEquals("", messages.getText());
+
+ validateClassLevelWithFormEntityCompositeNumber2.sendKeys("0");
+ guardAjax(validateClassLevelWithFormEntityCompositeCommand).click();
+ assertEquals("", validateClassLevelWithFormEntityCompositeNumber1Message.getText());
+ assertEquals("", validateClassLevelWithFormEntityCompositeNumber2Message.getText());
+ assertEquals("", validateClassLevelWithFormEntityCompositeFormMessage.getText());
+ assertEquals("actionSuccess", messages.getText());
}
}
@@ -18,14 +18,13 @@
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+ xmlns:cc="http://xmlns.jcp.org/jsf/composite/components"
xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions"
>
<h:head />
<h:body>
- <h:messages id="messages" />
-
<h:form id="validateByCommand">
<h:inputText id="input" value="#{validateBeanITDefaultBean.input}" />
@@ -101,7 +100,7 @@
<h:inputText id="number2" value="#{validateBeanITClassLevelBean.entity.number2}" />
<h:commandButton id="command" action="#{validateBeanITClassLevelBean.action}">
- <f:ajax execute="@form" render="formMessage" />
+ <f:ajax execute="@form" render="formMessage :messages" />
</h:commandButton>
<h:message id="formMessage" for="validateClassLevelWithMessageForForm" />
@@ -118,7 +117,7 @@
<h:message id="number2Message" for="number2" />
<h:commandButton id="command" action="#{validateBeanITClassLevelBean.action}">
- <f:ajax execute="@form" render="number1Message number2Message" />
+ <f:ajax execute="@form" render="number1Message number2Message :messages" />
</h:commandButton>
<o:validateBean value="#{validateBeanITClassLevelBean.entity}"
@@ -150,12 +149,32 @@
<h:message id="formMessage" for="validateClassLevelWithMessageForViolating" />
<h:commandButton id="command" action="#{validateBeanITClassLevelBean.action}">
- <f:ajax execute="@form" render="number1Message number2Message" />
+ <f:ajax execute="@form" render="number1Message number2Message formMessage :messages" />
</h:commandButton>
<o:validateBean value="#{validateBeanITClassLevelBean.entity}"
validationGroups="org.omnifaces.test.taghandler.validatebean.ValidateBeanITValidationGroup"
showMessageFor="@violating" />
</h:form>
+
+ <h:form id="validateClassLevelWithInputEntityComposite">
+ <cc:inputEntity id="composite" value="#{validateBeanITClassLevelBean.entity}" />
+
+ <h:message id="formMessage" for="validateClassLevelWithInputEntityComposite" />
+
+ <h:commandButton id="command" action="#{validateBeanITClassLevelBean.action}">
+ <f:ajax execute="@form" render="composite:number1Message composite:number2Message formMessage :messages" />
+ </h:commandButton>
+
+ <o:validateBean value="#{validateBeanITClassLevelBean.entity}"
+ validationGroups="org.omnifaces.test.taghandler.validatebean.ValidateBeanITValidationGroup"
+ showMessageFor="@violating" />
+ </h:form>
+
+ <cc:formEntity id="validateClassLevelWithFormEntityComposite"
+ value="#{validateBeanITClassLevelBean.entity}"
+ action="#{validateBeanITClassLevelBean.action}" />
+
+ <h:messages id="messages" redisplay="false" />
</h:body>
</html>
@@ -0,0 +1,46 @@
+<!--
+
+ Copyright 2017 OmniFaces
+
+ Licensed 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.
+
+-->
+<ui:component
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:f="http://xmlns.jcp.org/jsf/core"
+ xmlns:h="http://xmlns.jcp.org/jsf/html"
+ xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+ xmlns:cc="http://xmlns.jcp.org/jsf/composite"
+ xmlns:o="http://omnifaces.org/ui"
+ xmlns:of="http://omnifaces.org/functions"
+>
+ <cc:interface>
+ <cc:attribute name="value" type="org.omnifaces.test.taghandler.validatebean.ValidateBeanITEntity" />
+ <cc:attribute name="action" method-signature="void action()" />
+ </cc:interface>
+ <cc:implementation>
+ <h:form id="form">
+ <h:inputText id="number1" value="#{cc.attrs.value.number1}" />
+ <h:message id="number1Message" for="number1" />
+ <h:inputText id="number2" value="#{cc.attrs.value.number2}" />
+ <h:message id="number2Message" for="number2" />
+
+ <h:message id="formMessage" for="form" />
+
+ <h:commandButton id="command" action="#{cc.attrs.action}">
+ <f:ajax execute="@form" render="number1Message number2Message formMessage :messages" />
+ </h:commandButton>
+
+ <o:validateBean value="#{cc.attrs.value}"
+ validationGroups="org.omnifaces.test.taghandler.validatebean.ValidateBeanITValidationGroup"
+ showMessageFor="@violating" />
+ </h:form>
+ </cc:implementation>
+</ui:component>
@@ -0,0 +1,37 @@
+<!--
+
+ Copyright 2017 OmniFaces
+
+ Licensed 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.
+
+-->
+<ui:component
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:f="http://xmlns.jcp.org/jsf/core"
+ xmlns:h="http://xmlns.jcp.org/jsf/html"
+ xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+ xmlns:cc="http://xmlns.jcp.org/jsf/composite"
+ xmlns:o="http://omnifaces.org/ui"
+ xmlns:of="http://omnifaces.org/functions"
+>
+ <cc:interface>
+ <cc:attribute name="value" type="org.omnifaces.test.taghandler.validatebean.ValidateBeanITEntity" />
+ </cc:interface>
+ <cc:implementation>
+ <h:inputText id="number1" value="#{cc.attrs.value.number1}" />
+ <h:message id="number1Message" for="number1" />
+ <h:inputText id="number2" value="#{cc.attrs.value.number2}" />
+ <h:message id="number2Message" for="number2" />
+ </cc:implementation>
+</ui:component>
+
+
+
+

0 comments on commit 37b0a86

Please sign in to comment.