http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidator.java new file mode 100644 index 0000000..572d307 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/DateRangeFieldValidator.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import java.util.Date; + +/** + * <!-- START SNIPPET: javadoc --> + * + * Field Validator that checks if the date supplied is within a specific range. + * + * <b>NOTE:</b> If no date converter is specified, XWorkBasicConverter will kick + * in to do the date conversion, which by default using the <code>Date.SHORT</code> format using + * the a problematically specified locale else falling back to the system + * default locale. + * + * + * <!-- END SNIPPET: javadoc --> + * + * <p/> + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>min - the min date range. If not specified will not be checked.</li> + * <li>max - the max date range. If not specified will not be checked.</li> + * <li>parse - if set to true, minExpression and maxExpression will be evaluated to find min/max</li> + * <li>minExpression - expression to calculate the minimum value (if none is specified, it will not be checked) </li> + * <li>maxExpression - expression to calculate the maximum value (if none is specified, it will not be checked) </li> + * </ul> + * + * You can either use the min / max value or minExpression / maxExpression (when parse is set to true) - + * using expression can be slightly slower, see the example below. + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${minExpression} and ${maxExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: examples --> + * <validators> + * <!-- Plain Validator syntax --> + * <validator type="date"> + * <param name="fieldName">birthday</param> + * <param name="min">01/01/1990</param> + * <param name="max">01/01/2000</param> + * <message>Birthday must be within ${min} and ${max}</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="birthday"> + * <field-validator type="date"> + * <param name="min">01/01/1990</param> + * <param name="max">01/01/2000</param> + * <message>Birthday must be within ${min} and ${max}</message> + * </field> + * </field> + * + * <!-- Field Validator Syntax with expression --> + * <field name="birthday"> + * <field-validator type="date"> + * <param name="minExpression">${minValue}</param> <!-- will be evaluated as: Date getMinValue() --> + * <param name="maxExpression">${maxValue}</param> <!-- will be evaluated as: Date getMaxValue() --> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: examples --> + * </pre> + * + * + * @author Jason Carreira + * @version $Date$ $Id$ + */ +public final class DateRangeFieldValidator extends RangeValidatorSupport<Date> { + + public DateRangeFieldValidator() { + super(Date.class); + } + +}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/DoubleRangeFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/DoubleRangeFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/DoubleRangeFieldValidator.java new file mode 100644 index 0000000..f0dfd80 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/DoubleRangeFieldValidator.java @@ -0,0 +1,196 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ + +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; +import org.apache.commons.lang3.StringUtils; + +/** + * <!-- START SNIPPET: javadoc --> + * Field Validator that checks if the double specified is within a certain range. + * <!-- END SNIPPET: javadoc --> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>minInclusive - the minimum inclusive value in FloatValue format specified by Java language (if none is specified, it will not be checked) </li> + * <li>maxInclusive - the maximum inclusive value in FloatValue format specified by Java language (if none is specified, it will not be checked) </li> + * <li>minExclusive - the minimum exclusive value in FloatValue format specified by Java language (if none is specified, it will not be checked) </li> + * <li>maxExclusive - the maximum exclusive value in FloatValue format specified by Java language (if none is specified, it will not be checked) </li> + * <li>minInclusiveExpression - the minimum inclusive value specified as a OGNL expression (if none is specified, it will not be checked) </li> + * <li>maxInclusiveExpression - the maximum inclusive value specified as a OGNL expression (if none is specified, it will not be checked) </li> + * <li>minExclusiveExpression - the minimum exclusive value specified as a OGNL expression (if none is specified, it will not be checked) </li> + * <li>maxExclusiveExpression - the maximum exclusive value specified as a OGNL expression (if none is specified, it will not be checked) </li> + * </ul> + * + * You can specify either minInclusive, maxInclusive, minExclusive and maxExclusive or minInclusiveExpression, maxInclusiveExpression, + * minExclusiveExpression and maxExclusiveExpression as a OGNL expression, see example below. You can always try to mix params + * but be aware that such behaviour was not tested. + * <!-- END SNIPPET: parameters --> + * + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${minInclusiveExpression}, ${maxInclusiveExpression}, ${minExclusiveExpressionExpression} and ${maxExclusive} + * as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * + * <pre> + * <!-- START SNIPPET: examples --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="double"> + * <param name="fieldName">percentage</param> + * <param name="minInclusive">20.1</param> + * <param name="maxInclusive">50.1</param> + * <message>Age needs to be between ${minInclusive} and ${maxInclusive} (inclusive)</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="percentage"> + * <field-validator type="double"> + * <param name="minExclusive">0.123</param> + * <param name="maxExclusive">99.98</param> + * <message>Percentage needs to be between ${minExclusive} and ${maxExclusive} (exclusive)</message> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expression --> + * <field name="percentage"> + * <field-validator type="double"> + * <param name="minExclusiveExpression">${minExclusiveValue}</param> <!-- will be evaluated as: Double getMinExclusiveValue() --> + * <param name="maxExclusiveExpression">${maxExclusiveValue}</param> <!-- will be evaluated as: Double getMaxExclusiveValue() --> + * <message>Percentage needs to be between ${minExclusive} and ${maxExclusive} (exclusive)</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: examples --> + * </pre> + * + * @author Rainer Hermanns + * @author Rene Gielen + * + * @version $Id$ + */ +public class DoubleRangeFieldValidator extends FieldValidatorSupport { + + private Double maxInclusive = null; + private Double minInclusive = null; + private Double minExclusive = null; + private Double maxExclusive = null; + + private String minInclusiveExpression; + private String maxInclusiveExpression; + private String minExclusiveExpression; + private String maxExclusiveExpression; + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + Double value; + try { + Object obj = this.getFieldValue(fieldName, object); + if (obj == null) { + return; + } + value = Double.valueOf(obj.toString()); + } catch (NumberFormatException e) { + return; + } + + Double maxInclusiveToUse = getMaxInclusive(); + Double minInclusiveToUse = getMinInclusive(); + Double maxExclusiveToUse = getMaxExclusive(); + Double minExclusiveToUse = getMinExclusive(); + + if ((maxInclusiveToUse != null && value.compareTo(maxInclusiveToUse) > 0) || + (minInclusiveToUse != null && value.compareTo(minInclusiveToUse) < 0) || + (maxExclusiveToUse != null && value.compareTo(maxExclusiveToUse) >= 0) || + (minExclusiveToUse != null && value.compareTo(minExclusiveToUse) <= 0)) { + addFieldError(fieldName, object); + } + } + + public void setMaxInclusive(Double maxInclusive) { + this.maxInclusive = maxInclusive; + } + + public Double getMaxInclusive() { + if (maxInclusive != null) { + return maxInclusive; + } else if (StringUtils.isNotEmpty(maxInclusiveExpression)) { + return (Double) parse(maxInclusiveExpression, Double.class); + } + return maxInclusive; + } + + public void setMinInclusive(Double minInclusive) { + this.minInclusive = minInclusive; + } + + public Double getMinInclusive() { + if (minInclusive != null) { + return minInclusive; + } else if (StringUtils.isNotEmpty(minInclusiveExpression)) { + return (Double) parse(minInclusiveExpression, Double.class); + } + return null; + } + + public void setMinExclusive(Double minExclusive) { + this.minExclusive = minExclusive; + } + + public Double getMinExclusive() { + if (minExclusive != null) { + return minExclusive; + } else if (StringUtils.isNotEmpty(minExclusiveExpression)) { + return (Double) parse(minExclusiveExpression, Double.class); + } + return null; + } + + public void setMaxExclusive(Double maxExclusive) { + this.maxExclusive = maxExclusive; + } + + public Double getMaxExclusive() { + if (maxExclusive != null) { + return maxExclusive; + } else if (StringUtils.isNotEmpty(maxExclusiveExpression)) { + return (Double) parse(maxExclusiveExpression, Double.class); + } + return null; + } + + public void setMinInclusiveExpression(String minInclusiveExpression) { + this.minInclusiveExpression = minInclusiveExpression; + } + + public void setMaxInclusiveExpression(String maxInclusiveExpression) { + this.maxInclusiveExpression = maxInclusiveExpression; + } + + public void setMinExclusiveExpression(String minExclusiveExpression) { + this.minExclusiveExpression = minExclusiveExpression; + } + + public void setMaxExclusiveExpression(String maxExclusiveExpression) { + this.maxExclusiveExpression = maxExclusiveExpression; + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/EmailValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/EmailValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/EmailValidator.java new file mode 100644 index 0000000..d0dcf75 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/EmailValidator.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + + +/** + * <!-- START SNIPPET: javadoc --> + * EmailValidator checks that a given String field, if not empty, is a valid email address. + * + * The regular expression used to validate that the string is an email address is: + * + * <pre> + * \\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2}|aero|arpa|asia|biz|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|nato|net|org|pro|tel|travel|xxx)$\\b + * </pre> + * + * You can also specify expression, caseSensitive and trim params as a OGNL expression, see the example below. + * <!-- END SNIPPET: javadoc --> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * </ul> + * Check also documentation of the RegexpValidator for more details - the EmailValidator bases on it. + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${regexExpression}, ${caseSensitiveExpression} and ${trimExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: example --> + * <!-- Plain Validator Syntax --> + * <validators> + * <validator type="email"> + * <param name="fieldName">myEmail</param> + * <message>Must provide a valid email</message> + * </validator> + * </validators> + * + * <!-- Field Validator Syntax --> + * <field name="myEmail"> + * <field-validator type="email"> + * <message>Must provide a valid email</message> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expressions --> + * <!-- Only available when used with xml based configuration, if you want to have the same + * flexibility with annotations use @RegexFieldValidator instead --> + * <field name="myEmail"> + * <field-validator type="email"> + * <param name="regexExpression">${emailPattern}</param> <!-- will be evaluated as: String getEmailPattern() --> + * <param name="caseSensitiveExpression">${emailCaseSensitive}</param> <!-- will be evaluated as: boolean getEmailCaseSensitive() --> + * <param name="trimExpression">${trimEmail}</param> <!-- will be evaluated as: boolean getTrimEmail() --> + * <message>Must provide a valid email</message> + * </field-validator> + * </field> + * <!-- END SNIPPET: example --> + * </pre> + * + * @author jhouse + * @author tm_jee + * @version $Date$ $Id$ + */ +public class EmailValidator extends RegexFieldValidator { + + // see XW-371 + public static final String EMAIL_ADDRESS_PATTERN = + "\\b^['_a-z0-9-\\+]+(\\.['_a-z0-9-\\+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2}|aero|arpa|asia|biz|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|nato|net|org|pro|tel|travel|xxx)$\\b"; + + public EmailValidator() { + setRegex(EMAIL_ADDRESS_PATTERN); + setCaseSensitive(false); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/ExpressionValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/ExpressionValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/ExpressionValidator.java new file mode 100644 index 0000000..d7f129b --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/ExpressionValidator.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; + +/** + * <!-- START SNIPPET: javadoc --> + * A Non-Field Level validator that validates based on regular expression supplied. + * <!-- END SNIPPET: javadoc --> + * <p/> + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>expression - the Ognl expression to be evaluated against the stack (Must evaluate to a Boolean)</li> + * </ul> + * <!-- END SNIPPET: parameters --> + * + * + * <pre> + * <!-- START SNIPPET: example --> + * <validators> + * <validator type="expression"> + * <param name="expression"> .... </param> + * <message>Failed to meet Ognl Expression .... </message> + * </validator> + * </validators> + * <!-- END SNIPPET: example --> + * </pre> + * + * @author Jason Carreira + */ +public class ExpressionValidator extends ValidatorSupport { + + private String expression; + + public void setExpression(String expression) { + this.expression = expression; + } + + public String getExpression() { + return expression; + } + + public void validate(Object object) throws ValidationException { + Boolean answer = Boolean.FALSE; + Object obj = null; + + try { + obj = getFieldValue(expression, object); + } catch (ValidationException e) { + throw e; + } catch (Exception e) { + // let this pass, but it will be logged right below + } + + if ((obj != null) && (obj instanceof Boolean)) { + answer = (Boolean) obj; + } else { + log.warn("Got result of [{}] when trying to get Boolean.", obj); + } + + if (!answer) { + log.debug("Validation failed on expression [{}] with validated object [{}]", expression, object); + addActionError(object); + } + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldExpressionValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldExpressionValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldExpressionValidator.java new file mode 100644 index 0000000..197314b --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldExpressionValidator.java @@ -0,0 +1,97 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; + + +/** + * <!-- START SNIPPET: javadoc --> + * Validates a field using an OGNL expression. + * <!-- END SNIPPET: javadoc --> + * <p/> + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>expression - The Ognl expression (must evaluate to a boolean) which is to be evalidated the stack</li> + * </ul> + * <!-- END SNIPPET: parameters --> + * + * <pre> + * <!-- START SNIPPET: example --> + * <!-- Plain Validator Syntax --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="fieldexpression"> + * <param name="fieldName">myField</param> + * <param name="expression"><![CDATA[#myCreditLimit > #myGirfriendCreditLimit]]></param> + * <message>My credit limit should be MORE than my girlfriend</message> + * <validator> + * + * <!-- Field Validator Syntax --> + * <field name="myField"> + * <field-validator type="fieldexpression"> + * <param name="expression"><![CDATA[#myCreditLimit > #myGirfriendCreditLimit]]></param> + * <message>My credit limit should be MORE than my girlfriend</message> + * </field-validator> + * </field> + * + * </vaidators> + * <!-- END SNIPPET: example --> + * </pre> + * + * + * @author $Author$ + * @version $Revision$ + */ +public class FieldExpressionValidator extends FieldValidatorSupport { + + private String expression; + + public void setExpression(String expression) { + this.expression = expression; + } + + public String getExpression() { + return expression; + } + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + + Boolean answer = Boolean.FALSE; + Object obj = null; + + try { + obj = getFieldValue(expression, object); + } catch (ValidationException e) { + throw e; + } catch (Exception e) { + // let this pass, but it will be logged right below + } + + if ((obj != null) && (obj instanceof Boolean)) { + answer = (Boolean) obj; + } else { + log.warn("Got result of {} when trying to get Boolean.", obj); + } + + if (!answer.booleanValue()) { + addFieldError(fieldName, object); + } + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldValidatorSupport.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldValidatorSupport.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldValidatorSupport.java new file mode 100644 index 0000000..4a3f147 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/FieldValidatorSupport.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.FieldValidator; + + +/** + * Base class for field validators. + * + * @author Jason Carreira + */ +public abstract class FieldValidatorSupport extends ValidatorSupport implements FieldValidator { + + private String fieldName; + private String type; + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public String getFieldName() { + return fieldName; + } + + @Override + public void setValidatorType(String type) { + this.type = type; + } + + @Override + public String getValidatorType() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/IntRangeFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/IntRangeFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/IntRangeFieldValidator.java new file mode 100644 index 0000000..bcc95d4 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/IntRangeFieldValidator.java @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +/** + * <!-- START SNIPPET: javadoc --> + * Field Validator that checks if the integer specified is within a certain range. + * <!-- END SNIPPET: javadoc --> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>min - the minimum value (if none is specified, it will not be checked) </li> + * <li>max - the maximum value (if none is specified, it will not be checked) </li> + * <li>parse - if set to true, minExpression and maxExpression will be evaluated to find min/max</li> + * <li>minExpression - expression to calculate the minimum value (if none is specified, it will not be checked) </li> + * <li>maxExpression - expression to calculate the maximum value (if none is specified, it will not be checked) </li> + * </ul> + * + * You can either use the min / max value or minExpression / maxExpression (when parse is set to true) - + * using expression can be slightly slower, see the example below. + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${minExpression} and ${maxExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: examples --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="int"> + * <param name="fieldName">age</param> + * <param name="min">20</param> + * <param name="max">50</param> + * <message>Age needs to be between ${min} and ${max}</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="age"> + * <field-validator type="int"> + * <param name="min">20</param> + * <param name="max">50</param> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expression --> + * <field name="age"> + * <field-validator type="int"> + * <param name="minExpression">${minValue}</param> <!-- will be evaluated as: Integer getMinValue() --> + * <param name="maxExpression">${maxValue}</param> <!-- will be evaluated as: Integer getMaxValue() --> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: examples --> + * </pre> + * + * @author Jason Carreira + * @version $Date$ $Id$ + */ +public final class IntRangeFieldValidator extends RangeValidatorSupport<Integer> { + + public IntRangeFieldValidator() { + super(Integer.class); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/LongRangeFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/LongRangeFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/LongRangeFieldValidator.java new file mode 100644 index 0000000..d5503c3 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/LongRangeFieldValidator.java @@ -0,0 +1,82 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +/** + * <!-- START SNIPPET: javadoc --> + * Field Validator that checks if the long specified is within a certain range. + * <!-- END SNIPPET: javadoc --> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>min - the minimum value (if none is specified, it will not be checked) </li> + * <li>max - the maximum value (if none is specified, it will not be checked) </li> + * <li>parse - if set to true, minExpression and maxExpression will be evaluated to find min/max</li> + * <li>minExpression - expression to calculate the minimum value (if none is specified, it will not be checked) </li> + * <li>maxExpression - expression to calculate the maximum value (if none is specified, it will not be checked) </li> + * </ul> + * + * You can either use the min / max value or minExpression / maxExpression (when parse is set to true) - + * using expression can be slightly slower, see the example below. + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${minExpression} and ${maxExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: examples --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="long"> + * <param name="fieldName">age</param> + * <param name="min">20</param> + * <param name="max">50</param> + * <message>Age needs to be between ${min} and ${max}</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="age"> + * <field-validator type="long"> + * <param name="min">20</param> + * <param name="max">50</param> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expression --> + * <field name="age"> + * <field-validator type="long"> + * <param name="minExpression">${minValue}</param> <!-- will be evaluated as: Long getMinValue() --> + * <param name="maxExpression">${maxValue}</param> <!-- will be evaluated as: Long getMaxValue() --> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: examples --> + * </pre> + * + * @version $Date$ + */ +public final class LongRangeFieldValidator extends RangeValidatorSupport<Long> { + + public LongRangeFieldValidator() { + super(Long.class); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/RangeValidatorSupport.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/RangeValidatorSupport.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RangeValidatorSupport.java new file mode 100644 index 0000000..39c0e86 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RangeValidatorSupport.java @@ -0,0 +1,102 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import com.opensymphony.xwork2.validator.ValidationException; +import org.apache.commons.lang3.StringUtils; + +/** + * Base class for range based validators. Use this class to develop any other custom range validators. + */ +public abstract class RangeValidatorSupport<T extends Comparable> extends FieldValidatorSupport { + + private static final Logger LOG = LogManager.getLogger(RangeValidatorSupport.class); + + private final Class<T> type; + + private T min; + private String minExpression; + private T max; + private String maxExpression; + + protected RangeValidatorSupport(Class<T> type) { + this.type = type; + } + + public void validate(Object object) throws ValidationException { + Object obj = getFieldValue(getFieldName(), object); + Comparable<T> value = (Comparable<T>) obj; + + // if there is no value - don't do comparison + // if a value is required, a required validator should be added to the field + if (value == null) { + return; + } + + // only check for a minimum value if the min parameter is set + T minComparatorValue = getMin(); + if ((minComparatorValue != null) && (value.compareTo(minComparatorValue) < 0)) { + addFieldError(getFieldName(), object); + } + + // only check for a maximum value if the max parameter is set + T maxComparatorValue = getMax(); + if ((maxComparatorValue != null) && (value.compareTo(maxComparatorValue) > 0)) { + addFieldError(getFieldName(), object); + } + } + + public void setMin(T min) { + this.min = min; + } + + public T getMin() { + if (min != null) { + return min; + } else if (StringUtils.isNotEmpty(minExpression)) { + return (T) parse(minExpression, type); + } else { + return null; + } + } + + public void setMinExpression(String minExpression) { + LOG.debug("${minExpression} was defined as [{}]", minExpression); + this.minExpression = minExpression; + } + + public void setMax(T max) { + this.max = max; + } + + public T getMax() { + if (max != null) { + return max; + } else if (StringUtils.isNotEmpty(maxExpression)) { + return (T) parse(maxExpression, type); + } else { + return null; + } + } + + public void setMaxExpression(String maxExpression) { + LOG.debug("${maxExpression} was defined as [{}]", maxExpression); + this.maxExpression = maxExpression; + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/RegexFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/RegexFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RegexFieldValidator.java new file mode 100644 index 0000000..a107387 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RegexFieldValidator.java @@ -0,0 +1,213 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ + +package com.opensymphony.xwork2.validator.validators; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import com.opensymphony.xwork2.validator.ValidationException; +import org.apache.commons.lang3.StringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * <!-- START SNIPPET: javadoc --> + * Validates a string field using a regular expression. + * <!-- END SNIPPET: javadoc --> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>regexp - The RegExp expression</li> + * <li>caseSensitive - Boolean (Optional). Sets whether the expression should be matched against in a case-sensitive way. Default is <code>true</code>.</li> + * <li>trim - Boolean (Optional). Sets whether the expression should be trimmed before matching. Default is <code>true</code>.</li> + * <li>regexExpression - String (Optional). Defines regExp expression as an OGNL expression - will be evaluated to String</li> + * <li>caseSensitiveExpression - String (Optional). Defines caseSensitive param as an OGNL expression - will be evaluated to Boolean.</li> + * <li>trimExpression - String (Optional). Defines trim param as an OGNL expression - will be evaluated to Boolean</li> + * </ul> + * You can mix normal params with expression aware params but thus was not tested + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${regexExpression}, ${caseSensitiveExpression} and ${trimExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: example --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="regex"> + * <param name="fieldName">myStrangePostcode</param> + * <param name="regex"><![CDATA[([aAbBcCdD][123][eEfFgG][456])]]></param> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="myStrangePostcode"> + * <field-validator type="regex"> + * <param name="regex"><![CDATA[([aAbBcCdD][123][eEfFgG][456])]]></param> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expressions --> + * <field name="myStrangePostcode"> + * <field-validator type="regex"> + * <param name="regexExpression">${regexValue}</param> <!-- will be evaluated as: String getRegexValue() --> + * <param name="caseSensitiveExpression">${caseSensitiveValue}</param> <!-- will be evaluated as: boolean getCaseSensitiveValue() --> + * <param name="trimExpression">${trimValue}</param> <!-- will be evaluated as: boolean getTrimValue() --> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: example --> + * </pre> + * + * @author Quake Wang + * @version $Date$ $Revision$ + */ +public class RegexFieldValidator extends FieldValidatorSupport { + + private static final Logger LOG = LogManager.getLogger(RegexFieldValidator.class); + + private String regex; + private String regexExpression; + private Boolean caseSensitive = true; + private String caseSensitiveExpression = ""; + private Boolean trim = true; + private String trimExpression = ""; + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + Object value = this.getFieldValue(fieldName, object); + // if there is no value - don't do comparison + // if a value is required, a required validator should be added to the field + String regexToUse = getRegex(); + LOG.debug("Defined regexp as [{}]", regexToUse); + + if (value == null || regexToUse == null) { + return; + } + + // XW-375 - must be a string + if (!(value instanceof String)) { + return; + } + + // string must not be empty + String str = ((String) value).trim(); + if (str.length() == 0) { + return; + } + + // match against expression + Pattern pattern; + if (isCaseSensitive()) { + pattern = Pattern.compile(regexToUse); + } else { + pattern = Pattern.compile(regexToUse, Pattern.CASE_INSENSITIVE); + } + + String compare = (String) value; + if ( isTrimed() ) { + compare = compare.trim(); + } + Matcher matcher = pattern.matcher( compare ); + + if (!matcher.matches()) { + addFieldError(fieldName, object); + } + } + + /** + * @return Returns the regular expression to be matched. + */ + public String getRegex() { + if (StringUtils.isNotEmpty(regex)) { + return regex; + } else if (StringUtils.isNotEmpty(regexExpression)) { + return (String) parse(regexExpression, String.class); + } else { + return null; + } + } + + /** + * Sets the regular expression to be matched + */ + public void setRegex(String regex) { + this.regex = regex; + } + + /** + * Sets the regular expression as an OGNL expression to be matched + */ + public void setRegexExpression(String regexExpression) { + this.regexExpression = regexExpression; + } + + /** + * @return Returns whether the expression should be matched against in + * a case-sensitive way. Default is <code>true</code>. + */ + public boolean isCaseSensitive() { + if (StringUtils.isNotEmpty(caseSensitiveExpression)) { + return (Boolean) parse(caseSensitiveExpression, Boolean.class); + } + return caseSensitive; + } + + /** + * Sets whether the expression should be matched against in + * a case-sensitive way. Default is <code>true</code>. + */ + public void setCaseSensitive(Boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + /** + * Allows specify caseSensitive param as an OGNL expression + */ + public void setCaseSensitiveExpression(String caseSensitiveExpression) { + this.caseSensitiveExpression = caseSensitiveExpression; + } + + /** + * @return Returns whether the expression should be trimed before matching. + * Default is <code>true</code>. + */ + public boolean isTrimed() { + if (StringUtils.isNotEmpty(trimExpression)) { + return (Boolean) parse(trimExpression, Boolean.class); + } + return trim; + } + + /** + * Sets whether the expression should be trimed before matching. + * Default is <code>true</code>. + */ + public void setTrim(Boolean trim) { + this.trim = trim; + } + + /** + * Allows specify trim param as an OGNL expression + */ + public void setTrimExpression(String trimExpression) { + this.trimExpression = trimExpression; + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/RepopulateConversionErrorFieldValidatorSupport.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/RepopulateConversionErrorFieldValidatorSupport.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RepopulateConversionErrorFieldValidatorSupport.java new file mode 100644 index 0000000..6dcb60f --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RepopulateConversionErrorFieldValidatorSupport.java @@ -0,0 +1,203 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.interceptor.PreResultListener; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.validator.ValidationException; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * + * + * An abstract base class that adds in the capability to populate the stack with + * a fake parameter map when a conversion error has occurred and the 'repopulateField' + * property is set to "true". + * + * <p/> + * + * + * <!-- START SNIPPET: javadoc --> + * + * The capability of auto-repopulating the stack with a fake parameter map when + * a conversion error has occurred can be done with 'repopulateField' property + * set to "true". + * + * <p/> + * + * This is typically usefull when one wants to repopulate the field with the original value + * when a conversion error occurred. Eg. with a textfield that only allows an Integer + * (the action class have an Integer field declared), upon conversion error, the incorrectly + * entered integer (maybe a text 'one') will not appear when dispatched back. With 'repopulateField' + * porperty set to true, it will, meaning the textfield will have 'one' as its value + * upon conversion error. + * + * <!-- END SNIPPET: javadoc --> + * + * <p/> + * + * <pre> + * <!-- START SNIPPET: exampleJspPage --> + * + * <!-- myJspPage.jsp --> + * <ww:form action="someAction" method="POST"> + * .... + * <ww:textfield + * label="My Integer Field" + * name="myIntegerField" /> + * .... + * <ww:submit /> + * </ww:form> + * + * <!-- END SNIPPET: exampleJspPage --> + * </pre> + * + * <pre> + * <!-- START SNIPPET: exampleXwork --> + * + * <!-- xwork.xml --> + * <xwork> + * <include file="xwork-default.xml" /> + * .... + * <package name="myPackage" extends="xwork-default"> + * .... + * <action name="someAction" class="example.MyActionSupport.java"> + * <result name="input">myJspPage.jsp</result> + * <result>success.jsp</result> + * </action> + * .... + * </package> + * .... + * </xwork> + * + * <!-- END SNIPPET:exampleXwork --> + * </pre> + * + * + * <pre> + * <!-- START SNIPPET: exampleJava --> + * + * <!-- MyActionSupport.java --> + * public class MyActionSupport extends ActionSupport { + * private Integer myIntegerField; + * + * public Integer getMyIntegerField() { return this.myIntegerField; } + * public void setMyIntegerField(Integer myIntegerField) { + * this.myIntegerField = myIntegerField; + * } + * } + * + * <!-- END SNIPPET: exampleJava --> + * </pre> + * + * + * <pre> + * <!-- START SNIPPET: exampleValidation --> + * + * <!-- MyActionSupport-someAction-validation.xml --> + * <validators> + * ... + * <field name="myIntegerField"> + * <field-validator type="conversion"> + * <param name="repopulateField">true</param> + * <message>Conversion Error (Integer Wanted)</message> + * </field-validator> + * </field> + * ... + * </validators> + * + * <!-- END SNIPPET: exampleValidation --> + * </pre> + * + * @author tm_jee + * @version $Date$ $Id$ + */ +public abstract class RepopulateConversionErrorFieldValidatorSupport extends FieldValidatorSupport { + + private static final Logger LOG = LogManager.getLogger(RepopulateConversionErrorFieldValidatorSupport.class); + + private boolean repopulateField = false; + + public boolean isRepopulateField() { + return repopulateField; + } + + public void setRepopulateField(boolean repopulateField) { + this.repopulateField = repopulateField; + } + + public void validate(Object object) throws ValidationException { + doValidate(object); + if (repopulateField) { + repopulateField(object); + } + } + + public void repopulateField(Object object) throws ValidationException { + + ActionInvocation invocation = ActionContext.getContext().getActionInvocation(); + Map<String, Object> conversionErrors = ActionContext.getContext().getConversionErrors(); + + String fieldName = getFieldName(); + String fullFieldName = getValidatorContext().getFullFieldName(fieldName); + if (conversionErrors.containsKey(fullFieldName)) { + Object value = conversionErrors.get(fullFieldName); + + final Map<Object, Object> fakeParams = new LinkedHashMap<Object, Object>(); + boolean doExprOverride = false; + + if (value instanceof String[]) { + // take the first element, if possible + String[] tmpValue = (String[]) value; + if ((tmpValue.length > 0)) { + doExprOverride = true; + fakeParams.put(fullFieldName, escape(tmpValue[0])); + } else { + LOG.warn("value is an empty array of String or with first element in it as null [{}], will not repopulate conversion error", value); + } + } else if (value instanceof String) { + String tmpValue = (String) value; + doExprOverride = true; + fakeParams.put(fullFieldName, escape(tmpValue)); + } else { + // opps... it should be + LOG.warn("conversion error value is not a String or array of String but instead is [{}], will not repopulate conversion error", value); + } + + if (doExprOverride) { + invocation.addPreResultListener(new PreResultListener() { + public void beforeResult(ActionInvocation invocation, String resultCode) { + ValueStack stack = ActionContext.getContext().getValueStack(); + stack.setExprOverrides(fakeParams); + } + }); + } + } + } + + protected String escape(String value) { + return "\"" + StringEscapeUtils.escapeJava(value) + "\""; + } + + protected abstract void doValidate(Object object) throws ValidationException; +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredFieldValidator.java new file mode 100644 index 0000000..6b103f2 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredFieldValidator.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; + + +/** + * <!-- START SNIPPET: javadoc --> + * RequiredFieldValidator checks if the specified field is not null. + * <!-- END SNIPPET: javadoc --> + * <p/> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - field name if plain-validator syntax is used, not needed if field-validator syntax is used</li> + * </ul> + * <!-- END SNIPPET: parameters --> + * + * + * <pre> + * <!-- START SNIPPET: example --> + * <validators> + * + * <!-- Plain Validator Syntax --> + * <validator type="required"> + * <param name="fieldName">username</param> + * <message>username must not be null</message> + * </validator> + * + * + * <!-- Field Validator Syntax --> + * <field name="username"> + * <field-validator type="required"> + * <message>username must not be null</message> + * </field-validator> + * </field> + * + * </validators> + * <!-- END SNIPPET: example --> + * </pre> + * + * + * + * @author rainerh + * @version $Revision$ + */ +public class RequiredFieldValidator extends FieldValidatorSupport { + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + Object value = this.getFieldValue(fieldName, object); + + if (value == null) { + addFieldError(fieldName, object); + } + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredStringValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredStringValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredStringValidator.java new file mode 100644 index 0000000..7b2cbfd --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/RequiredStringValidator.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; + + +/** + * <!-- START SNIPPET: javadoc --> + * RequiredStringValidator checks that a String field is non-null and has a length > 0. + * (i.e. it isn't ""). The "trim" parameter determines whether it will {@link String#trim() trim} + * the String before performing the length check. If unspecified, the String will be trimmed. + * <!-- END SNIPPET: javadoc --> + * <p/> + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>trim - (Optional) Boolean, default true. Trims the field name value before validating.</li> + * <li>trimExpression - (Optional) String. Specifies the trim param as an OGNL expression.</li> + * </ul> + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${trimExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: examples --> + * <validators> + * <!-- Plain-Validator Syntax --> + * <validator type="requiredstring"> + * <param name="fieldName">username</param> + * <param name="trim">true</param> + * <message>username is required</message> + * </validator> + * + * <!-- Field-Validator Syntax --> + * <field name="username"> + * <field-validator type="requiredstring"> + * <param name="trim">true</param> + * <message>username is required</message> + * </field-validator> + * </field> + * + * <!-- Field-Validator Syntax with expression --> + * <field name="username"> + * <field-validator type="requiredstring"> + * <param name="trimExpression">${trimValue}</param> <!-- will be evaluated as: boolean getTrimValue() --> + * <message>username is required</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: examples --> + * </pre> + * + * @author rainerh + * @version $Date$ $Id$ + */ +public class RequiredStringValidator extends FieldValidatorSupport { + + private boolean trim = true; + + public void setTrim(boolean trim) { + this.trim = trim; + } + + public void setTrimExpression(String trimExpression) { + trim = (Boolean) parse(trimExpression, Boolean.class); + } + + public boolean isTrim() { + return trim; + } + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + Object value = this.getFieldValue(fieldName, object); + + if (!(value instanceof String)) { + addFieldError(fieldName, object); + } else { + String s = (String) value; + + if (trim) { + s = s.trim(); + } + + if (s.length() == 0) { + addFieldError(fieldName, object); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/ShortRangeFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/ShortRangeFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/ShortRangeFieldValidator.java new file mode 100644 index 0000000..24a20e2 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/ShortRangeFieldValidator.java @@ -0,0 +1,82 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +/** + * <!-- START SNIPPET: javadoc --> + * Field Validator that checks if the short specified is within a certain range. + * <!-- END SNIPPET: javadoc --> + * + * + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>min - the minimum value (if none is specified, it will not be checked) </li> + * <li>max - the maximum value (if none is specified, it will not be checked) </li> + * <li>parse - if set to true, minExpression and maxExpression will be evaluated to find min/max</li> + * <li>minExpression - expression to calculate the minimum value (if none is specified, it will not be checked) </li> + * <li>maxExpression - expression to calculate the maximum value (if none is specified, it will not be checked) </li> + * </ul> + * + * You can either use the min / max value or minExpression / maxExpression (when parse is set to true) - + * using expression can be slightly slower, see the example below. + * <!-- END SNIPPET: parameters --> + * + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${minExpression} and ${maxExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * + * <pre> + * <!-- START SNIPPET: examples --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="short"> + * <param name="fieldName">age</param> + * <param name="min">20</param> + * <param name="max">50</param> + * <message>Age needs to be between ${min} and ${max}</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="age"> + * <field-validator type="short"> + * <param name="min">20</param> + * <param name="max">50</param> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expression --> + * <field name="age"> + * <field-validator type="short"> + * <param name="minExpression">${minValue}</param> <!-- will be evaluated as: Short getMinValue() --> + * <param name="maxExpression">${maxValue}</param> <!-- will be evaluated as: Short getMaxValue() --> + * <message>Age needs to be between ${min} and ${max}</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: examples --> + * </pre> + * + * @version $Date$ + */ +public final class ShortRangeFieldValidator extends RangeValidatorSupport<Short> { + + public ShortRangeFieldValidator() { + super(Short.class); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/StringLengthFieldValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/StringLengthFieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/StringLengthFieldValidator.java new file mode 100644 index 0000000..47916d0 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/StringLengthFieldValidator.java @@ -0,0 +1,169 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; +import org.apache.commons.lang3.StringUtils; + +/** + * <!-- START SNIPPET: javadoc --> + * StringLengthFieldValidator checks that a String field is of a certain length. If the "minLength" + * parameter is specified, it will make sure that the String has at least that many characters. If + * the "maxLength" parameter is specified, it will make sure that the String has at most that many + * characters. The "trim" parameter determines whether it will {@link String#trim() trim} the + * String before performing the length check. If unspecified, the String will be trimmed. + * <!-- END SNIPPET: javadoc --> + * <p/> + * <p/> + * <p/> + * <!-- START SNIPPET: parameters --> + * <ul> + * <li>fieldName - The field name this validator is validating. Required if using Plain-Validator Syntax otherwise not required</li> + * <li>maxLength - Integer. The max length of the field value. Default ignore.</li> + * <li>minLength - Integer. The min length of the field value. Default ignore.</li> + * <li>trim - (Optional) Boolean, default true. Trim the field value before evaluating its min/max length. Default true.</li> + * <li>maxLengthExpression - (Optional) String. Defines the max length param as an OGNL expression</li> + * <li>minLengthExpression - (Optional) String. Defines the min length param as an OGNL expression</li> + * <li>trimExpression - (Optional) String. Defines th trim param as an OGNL expression</li> + * </ul> + * <!-- END SNIPPET: parameters --> + * <p/> + * <!-- START SNIPPET: parameters-warning --> + * Do not use ${minLengthExpression}, ${maxLengthExpression} and ${trimExpression} as an expression as this will turn into infinitive loop! + * <!-- END SNIPPET: parameters-warning --> + * <p/> + * <pre> + * <!--START SNIPPET: example --> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="stringlength"> + * <param name="fieldName">myPurchaseCode</param> + * <param name="minLength">10</param> + * <param name="maxLength">10</param> + * <param name="trim">true</param> + * <message>Your purchase code needs to be 10 characters long</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="myPurchaseCode"> + * <field-validator type="stringlength"> + * <param name="minLength">10</param> + * <param name="maxLength">10</param> + * <param name="trim">true</param> + * <message>Your purchase code needs to be 10 characters long</message> + * </field-validator> + * </field> + * + * <!-- Field Validator Syntax with expression --> + * <field name="myPurchaseCode"> + * <field-validator type="stringlength"> + * <param name="minLengthExpression">${minLengthValue}</param> <!-- will be evaluated as: Integer getMinLengthValue() --> + * <param name="maxLengthExpression">${maxLengthValue}</param> <!-- will be evaluated as: Integer getMaxLengthValue() --> + * <param name="trimExpression">${trimValue}</param> <!-- will be evaluated as: boolean getTrimValue() --> + * <message>Your purchase code needs to be 10 characters long</message> + * </field-validator> + * </field> + * </validators> + * <!-- END SNIPPET: example --> + * </pre> + * + * @author Jason Carreira + * @author Mark Woon + * @author tmjee + * @version $Date$ $Id$ + */ +public class StringLengthFieldValidator extends FieldValidatorSupport { + + private boolean trim = true; + private int maxLength = -1; + private int minLength = -1; + + private String maxLengthExpression; + private String minLengthExpression; + private String trimExpression; + + public void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } + + public void setMaxLengthExpression(String maxLengthExpression) { + this.maxLengthExpression = maxLengthExpression; + } + + public int getMaxLength() { + if (StringUtils.isNotEmpty(maxLengthExpression)) { + return (Integer) parse(maxLengthExpression, Integer.class); + } + return maxLength; + } + + public void setMinLength(int minLength) { + this.minLength = minLength; + } + + public void setMinLengthExpression(String minLengthExpression) { + this.minLengthExpression = minLengthExpression; + } + + public int getMinLength() { + if (StringUtils.isNotEmpty(minLengthExpression)) { + return (Integer) parse(minLengthExpression, Integer.class); + } + return minLength; + } + + public void setTrim(boolean trim) { + this.trim = trim; + } + + public void setTrimExpression(String trimExpression) { + this.trimExpression = trimExpression; + } + + public boolean isTrim() { + if (StringUtils.isNotEmpty(trimExpression)) { + return (Boolean) parse(trimExpression, Boolean.class); + } + return trim; + } + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + String val = (String) getFieldValue(fieldName, object); + + if (StringUtils.isEmpty(val)) { + // use a required validator for these + return; + } + if (isTrim()) { + val = val.trim(); + if (val.length() <= 0) { + // use a required validator + return; + } + } + + int minLengthToUse = getMinLength(); + int maxLengthToUse = getMaxLength(); + + if ((minLengthToUse > -1) && (val.length() < minLengthToUse)) { + addFieldError(fieldName, object); + } else if ((maxLengthToUse > -1) && (val.length() > maxLengthToUse)) { + addFieldError(fieldName, object); + } + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/URLValidator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/URLValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/URLValidator.java new file mode 100644 index 0000000..767416d --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/URLValidator.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.validator.ValidationException; +import com.opensymphony.xwork2.util.URLUtil; +import org.apache.commons.lang3.StringUtils; + +/** + * URLValidator checks that a given field is a String and a valid URL + * + * <pre> + * <validators> + * <!-- Plain Validator Syntax --> + * <validator type="url"> + * <param name="fieldName">myHomePage</param> + * <message>Invalid homepage url</message> + * </validator> + * + * <!-- Field Validator Syntax --> + * <field name="myHomepage"> + * <field-validator type="url"> + * <message>Invalid homepage url</message> + * </field-validator> + * </field> + * </validators> + * </pre> + */ +public class URLValidator extends FieldValidatorSupport { + + private String urlRegex; + private String urlRegexExpression; + + public void validate(Object object) throws ValidationException { + String fieldName = getFieldName(); + Object value = this.getFieldValue(fieldName, object); + + // if there is no value - don't do comparison + // if a value is required, a required validator should be added to the field + if (value == null || value.toString().length() == 0) { + return; + } + + // FIXME deprecated! the same regex below should be used instead + // replace logic with next major release + if (!(value.getClass().equals(String.class)) || !URLUtil.verifyUrl((String) value)) { + addFieldError(fieldName, object); + } + } + + /** + * This is used to support client-side validation, it's based on + * http://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url + * + * @return regex to validate URLs + */ + public String getUrlRegex() { + if (StringUtils.isNotEmpty(urlRegexExpression)) { + return (String) parse(urlRegexExpression, String.class); + } else if (StringUtils.isNotEmpty(urlRegex)) { + return urlRegex; + } else { + return "^(https?|ftp):\\/\\/" + + "(([a-z0-9$_\\.\\+!\\*\\'\\(\\),;\\?&=-]|%[0-9a-f]{2})+" + + "(:([a-z0-9$_\\.\\+!\\*\\'\\(\\),;\\?&=-]|%[0-9a-f]{2})+)?" + + "@)?(#?" + + ")((([a-z0-9]\\.|[a-z0-9][a-z0-9-]*[a-z0-9]\\.)*" + + "[a-z][a-z0-9-]*[a-z0-9]" + + "|((\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])\\.){3}" + + "(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])" + + ")(:\\d+)?" + + ")(((\\/+([a-z0-9$_\\.\\+!\\*\\'\\(\\),;:@&=-]|%[0-9a-f]{2})*)*" + + "(\\?([a-z0-9$_\\.\\+!\\*\\'\\(\\),;:@&=-]|%[0-9a-f]{2})*)" + + "?)?)?" + + "(#([a-z0-9$_\\.\\+!\\*\\'\\(\\),;:@&=-]|%[0-9a-f]{2})*)?" + + "$"; + } + } + + public void setUrlRegex(String urlRegex) { + this.urlRegex = urlRegex; + } + + public void setUrlRegexExpression(String urlRegexExpression) { + this.urlRegexExpression = urlRegexExpression; + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/validators/ValidatorSupport.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/validators/ValidatorSupport.java b/core/src/main/java/com/opensymphony/xwork2/validator/validators/ValidatorSupport.java new file mode 100644 index 0000000..1ca43b0 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/validator/validators/ValidatorSupport.java @@ -0,0 +1,205 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * 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. + */ +package com.opensymphony.xwork2.validator.validators; + +import com.opensymphony.xwork2.util.TextParseUtil; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.validator.*; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Abstract implementation of the Validator interface suitable for subclassing. + * + * @author Jason Carreira + * @author tm_jee + * @author Martin Gilday + */ +public abstract class ValidatorSupport implements Validator, ShortCircuitableValidator { + + protected final Logger log = LogManager.getLogger(this.getClass()); + + protected String defaultMessage = ""; + protected String messageKey; + private ValidatorContext validatorContext; + private boolean shortCircuit; + private String type; + private String[] messageParameters; + protected ValueStack stack; + + + public void setValueStack(ValueStack stack) { + this.stack = stack; + } + + public void setDefaultMessage(String message) { + if (StringUtils.isNotEmpty(message)) { + this.defaultMessage = message; + } + } + + public String getDefaultMessage() { + return defaultMessage; + } + + public String getMessage(Object object) { + String message; + boolean pop = false; + + if (!stack.getRoot().contains(object)) { + stack.push(object); + pop = true; + } + + stack.push(this); + + if (messageKey != null) { + if ((defaultMessage == null) || ("".equals(defaultMessage.trim()))) { + defaultMessage = messageKey; + } + if (validatorContext == null) { + validatorContext = new DelegatingValidatorContext(object); + } + List<Object> parsedMessageParameters = null; + if (messageParameters != null) { + parsedMessageParameters = new ArrayList<>(); + for (String messageParameter : messageParameters) { + if (messageParameter != null) { + try { + Object val = stack.findValue(messageParameter); + parsedMessageParameters.add(val); + } catch (Exception e) { + // if there's an exception in parsing, we'll just treat the expression itself as the + // parameter + log.warn("exception while parsing message parameter [{}]", messageParameter, e); + parsedMessageParameters.add(messageParameter); + } + } + } + } + + message = validatorContext.getText(messageKey, defaultMessage, parsedMessageParameters); + } else { + message = defaultMessage; + } + + if (StringUtils.isNotBlank(message)) + message = TextParseUtil.translateVariables(message, stack); + + stack.pop(); + + if (pop) { + stack.pop(); + } + + return message; + } + + public void setMessageKey(String key) { + messageKey = key; + } + + public String getMessageKey() { + return messageKey; + } + + public String[] getMessageParameters() { + return this.messageParameters; + } + + public void setMessageParameters(String[] messageParameters) { + this.messageParameters = messageParameters; + } + + public void setShortCircuit(boolean shortcircuit) { + shortCircuit = shortcircuit; + } + + public boolean isShortCircuit() { + return shortCircuit; + } + + public void setValidatorContext(ValidatorContext validatorContext) { + this.validatorContext = validatorContext; + } + + public ValidatorContext getValidatorContext() { + return validatorContext; + } + + public void setValidatorType(String type) { + this.type = type; + } + + public String getValidatorType() { + return type; + } + + /** + * Parse <code>expression</code> passed in against value stack. + * + * @param expression an OGNL expression + * @param type type to return + * @return Object + */ + protected Object parse(String expression, Class type) { + if (expression == null) { + return null; + } + return TextParseUtil.translateVariables('$', expression, stack, type); + } + + /** + * Return the field value named <code>name</code> from <code>object</code>, + * <code>object</code> should have the appropriate getter/setter. + * + * @param name name of the field + * @param object to search field name on + * @return Object as field value + * @throws ValidationException + */ + protected Object getFieldValue(String name, Object object) throws ValidationException { + + boolean pop = false; + + if (!stack.getRoot().contains(object)) { + stack.push(object); + pop = true; + } + + Object retVal = stack.findValue(name); + + if (pop) { + stack.pop(); + } + + return retVal; + } + + protected void addActionError(Object object) { + validatorContext.addActionError(getMessage(object)); + } + + protected void addFieldError(String propertyName, Object object) { + validatorContext.addFieldError(propertyName, getMessage(object)); + } + +}
